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}