001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.graph; 020 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027 028import org.eclipse.aether.artifact.Artifact; 029import org.eclipse.aether.repository.RemoteRepository; 030import org.eclipse.aether.version.Version; 031import org.eclipse.aether.version.VersionConstraint; 032 033import static java.util.Objects.requireNonNull; 034 035/** 036 * A node within a dependency graph. 037 */ 038public final class DefaultDependencyNode implements DependencyNode { 039 040 private List<DependencyNode> children; 041 042 private Dependency dependency; 043 044 private Artifact artifact; 045 046 private List<? extends Artifact> relocations; 047 048 private Collection<? extends Artifact> aliases; 049 050 private VersionConstraint versionConstraint; 051 052 private Version version; 053 054 private byte managedBits; 055 056 private List<RemoteRepository> repositories; 057 058 private String context; 059 060 private Map<Object, Object> data; 061 062 /** 063 * Creates a new node with the specified dependency. 064 * 065 * @param dependency The dependency associated with this node, may be {@code null} for a root node. 066 */ 067 public DefaultDependencyNode(Dependency dependency) { 068 this.dependency = dependency; 069 artifact = (dependency != null) ? dependency.getArtifact() : null; 070 children = new ArrayList<>(0); 071 aliases = Collections.emptyList(); 072 relocations = Collections.emptyList(); 073 repositories = Collections.emptyList(); 074 context = ""; 075 data = Collections.emptyMap(); 076 } 077 078 /** 079 * Creates a new root node with the specified artifact as its label. Note that the new node has no dependency, i.e. 080 * {@link #getDependency()} will return {@code null}. Put differently, the specified artifact will not be subject to 081 * dependency collection/resolution. 082 * 083 * @param artifact The artifact to use as label for this node, may be {@code null}. 084 */ 085 public DefaultDependencyNode(Artifact artifact) { 086 this.artifact = artifact; 087 children = new ArrayList<>(0); 088 aliases = Collections.emptyList(); 089 relocations = Collections.emptyList(); 090 repositories = Collections.emptyList(); 091 context = ""; 092 data = Collections.emptyMap(); 093 } 094 095 /** 096 * Creates a mostly shallow clone of the specified node. The new node has its own copy of any custom data and 097 * initially no children. 098 * 099 * @param node The node to copy, must not be {@code null}. 100 */ 101 public DefaultDependencyNode(DependencyNode node) { 102 dependency = node.getDependency(); 103 artifact = node.getArtifact(); 104 children = new ArrayList<>(0); 105 setAliases(node.getAliases()); 106 setRequestContext(node.getRequestContext()); 107 setManagedBits(node.getManagedBits()); 108 setRelocations(node.getRelocations()); 109 setRepositories(node.getRepositories()); 110 setVersion(node.getVersion()); 111 setVersionConstraint(node.getVersionConstraint()); 112 Map<?, ?> data = node.getData(); 113 setData(data.isEmpty() ? null : new HashMap<>(data)); 114 } 115 116 public List<DependencyNode> getChildren() { 117 return children; 118 } 119 120 public void setChildren(List<DependencyNode> children) { 121 if (children == null) { 122 this.children = new ArrayList<>(0); 123 } else { 124 this.children = children; 125 } 126 } 127 128 public Dependency getDependency() { 129 return dependency; 130 } 131 132 public Artifact getArtifact() { 133 return artifact; 134 } 135 136 public void setArtifact(Artifact artifact) { 137 if (dependency == null) { 138 throw new IllegalStateException("node does not have a dependency"); 139 } 140 dependency = dependency.setArtifact(artifact); 141 this.artifact = dependency.getArtifact(); 142 } 143 144 public List<? extends Artifact> getRelocations() { 145 return relocations; 146 } 147 148 /** 149 * Sets the sequence of relocations that was followed to resolve this dependency's artifact. 150 * 151 * @param relocations The sequence of relocations, may be {@code null}. 152 */ 153 public void setRelocations(List<? extends Artifact> relocations) { 154 if (relocations == null || relocations.isEmpty()) { 155 this.relocations = Collections.emptyList(); 156 } else { 157 this.relocations = relocations; 158 } 159 } 160 161 public Collection<? extends Artifact> getAliases() { 162 return aliases; 163 } 164 165 /** 166 * Sets the known aliases for this dependency's artifact. 167 * 168 * @param aliases The known aliases, may be {@code null}. 169 */ 170 public void setAliases(Collection<? extends Artifact> aliases) { 171 if (aliases == null || aliases.isEmpty()) { 172 this.aliases = Collections.emptyList(); 173 } else { 174 this.aliases = aliases; 175 } 176 } 177 178 public VersionConstraint getVersionConstraint() { 179 return versionConstraint; 180 } 181 182 /** 183 * Sets the version constraint that was parsed from the dependency's version declaration. 184 * 185 * @param versionConstraint The version constraint for this node, may be {@code null}. 186 */ 187 public void setVersionConstraint(VersionConstraint versionConstraint) { 188 this.versionConstraint = versionConstraint; 189 } 190 191 public Version getVersion() { 192 return version; 193 } 194 195 /** 196 * Sets the version that was selected for the dependency's target artifact. 197 * 198 * @param version The parsed version, may be {@code null}. 199 */ 200 public void setVersion(Version version) { 201 this.version = version; 202 } 203 204 public void setScope(String scope) { 205 if (dependency == null) { 206 throw new IllegalStateException("node does not have a dependency"); 207 } 208 dependency = dependency.setScope(scope); 209 } 210 211 public void setOptional(Boolean optional) { 212 if (dependency == null) { 213 throw new IllegalStateException("node does not have a dependency"); 214 } 215 dependency = dependency.setOptional(optional); 216 } 217 218 public int getManagedBits() { 219 return managedBits; 220 } 221 222 /** 223 * Sets a bit field indicating which attributes of this node were subject to dependency management. 224 * 225 * @param managedBits The bit field indicating the managed attributes or {@code 0} if dependency management wasn't 226 * applied. 227 */ 228 public void setManagedBits(int managedBits) { 229 this.managedBits = (byte) (managedBits & 0x1F); 230 } 231 232 public List<RemoteRepository> getRepositories() { 233 return repositories; 234 } 235 236 /** 237 * Sets the remote repositories from which this node's artifact shall be resolved. 238 * 239 * @param repositories The remote repositories to use for artifact resolution, may be {@code null}. 240 */ 241 public void setRepositories(List<RemoteRepository> repositories) { 242 if (repositories == null || repositories.isEmpty()) { 243 this.repositories = Collections.emptyList(); 244 } else { 245 this.repositories = repositories; 246 } 247 } 248 249 public String getRequestContext() { 250 return context; 251 } 252 253 public void setRequestContext(String context) { 254 this.context = (context != null) ? context : ""; 255 } 256 257 public Map<Object, Object> getData() { 258 return data; 259 } 260 261 public void setData(Map<Object, Object> data) { 262 if (data == null) { 263 this.data = Collections.emptyMap(); 264 } else { 265 this.data = data; 266 } 267 } 268 269 public void setData(Object key, Object value) { 270 requireNonNull(key, "key cannot be null"); 271 272 if (value == null) { 273 if (!data.isEmpty()) { 274 data.remove(key); 275 276 if (data.isEmpty()) { 277 data = Collections.emptyMap(); 278 } 279 } 280 } else { 281 if (data.isEmpty()) { 282 data = new HashMap<>(1, 2); // nodes can be numerous so let's be space conservative 283 } 284 data.put(key, value); 285 } 286 } 287 288 public boolean accept(DependencyVisitor visitor) { 289 if (Thread.currentThread().isInterrupted()) { 290 throw new RuntimeException(new InterruptedException("Thread interrupted")); 291 } 292 if (visitor.visitEnter(this)) { 293 for (DependencyNode child : children) { 294 if (!child.accept(visitor)) { 295 break; 296 } 297 } 298 } 299 300 return visitor.visitLeave(this); 301 } 302 303 @Override 304 public String toString() { 305 Dependency dep = getDependency(); 306 if (dep == null) { 307 return String.valueOf(getArtifact()); 308 } 309 return dep.toString(); 310 } 311}