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