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<DependencyNode>( 0 );
074        aliases = relocations = Collections.emptyList();
075        repositories = Collections.emptyList();
076        context = "";
077        data = Collections.emptyMap();
078    }
079
080    /**
081     * Creates a new root node with the specified artifact as its label. Note that the new node has no dependency, i.e.
082     * {@link #getDependency()} will return {@code null}. Put differently, the specified artifact will not be subject to
083     * dependency collection/resolution.
084     * 
085     * @param artifact The artifact to use as label for this node, may be {@code null}.
086     */
087    public DefaultDependencyNode( Artifact artifact )
088    {
089        this.artifact = artifact;
090        children = new ArrayList<DependencyNode>( 0 );
091        aliases = relocations = Collections.emptyList();
092        repositories = Collections.emptyList();
093        context = "";
094        data = Collections.emptyMap();
095    }
096
097    /**
098     * Creates a mostly shallow clone of the specified node. The new node has its own copy of any custom data and
099     * initially no children.
100     * 
101     * @param node The node to copy, must not be {@code null}.
102     */
103    public DefaultDependencyNode( DependencyNode node )
104    {
105        dependency = node.getDependency();
106        artifact = node.getArtifact();
107        children = new ArrayList<DependencyNode>( 0 );
108        setAliases( node.getAliases() );
109        setRequestContext( node.getRequestContext() );
110        setManagedBits( node.getManagedBits() );
111        setRelocations( node.getRelocations() );
112        setRepositories( node.getRepositories() );
113        setVersion( node.getVersion() );
114        setVersionConstraint( node.getVersionConstraint() );
115        Map<?, ?> data = node.getData();
116        setData( data.isEmpty() ? null : new HashMap<Object, Object>( data ) );
117    }
118
119    public List<DependencyNode> getChildren()
120    {
121        return children;
122    }
123
124    public void setChildren( List<DependencyNode> children )
125    {
126        if ( children == null )
127        {
128            this.children = new ArrayList<DependencyNode>( 0 );
129        }
130        else
131        {
132            this.children = children;
133        }
134    }
135
136    public Dependency getDependency()
137    {
138        return dependency;
139    }
140
141    public Artifact getArtifact()
142    {
143        return artifact;
144    }
145
146    public void setArtifact( Artifact artifact )
147    {
148        if ( dependency == null )
149        {
150            throw new IllegalStateException( "node does not have a dependency" );
151        }
152        dependency = dependency.setArtifact( artifact );
153        this.artifact = dependency.getArtifact();
154    }
155
156    public List<? extends Artifact> getRelocations()
157    {
158        return relocations;
159    }
160
161    /**
162     * Sets the sequence of relocations that was followed to resolve this dependency's artifact.
163     * 
164     * @param relocations The sequence of relocations, may be {@code null}.
165     */
166    public void setRelocations( List<? extends Artifact> relocations )
167    {
168        if ( relocations == null || relocations.isEmpty() )
169        {
170            this.relocations = Collections.emptyList();
171        }
172        else
173        {
174            this.relocations = relocations;
175        }
176    }
177
178    public Collection<? extends Artifact> getAliases()
179    {
180        return aliases;
181    }
182
183    /**
184     * Sets the known aliases for this dependency's artifact.
185     * 
186     * @param aliases The known aliases, may be {@code null}.
187     */
188    public void setAliases( Collection<? extends Artifact> aliases )
189    {
190        if ( aliases == null || aliases.isEmpty() )
191        {
192            this.aliases = Collections.emptyList();
193        }
194        else
195        {
196            this.aliases = aliases;
197        }
198    }
199
200    public VersionConstraint getVersionConstraint()
201    {
202        return versionConstraint;
203    }
204
205    /**
206     * Sets the version constraint that was parsed from the dependency's version declaration.
207     * 
208     * @param versionConstraint The version constraint for this node, may be {@code null}.
209     */
210    public void setVersionConstraint( VersionConstraint versionConstraint )
211    {
212        this.versionConstraint = versionConstraint;
213    }
214
215    public Version getVersion()
216    {
217        return version;
218    }
219
220    /**
221     * Sets the version that was selected for the dependency's target artifact.
222     * 
223     * @param version The parsed version, may be {@code null}.
224     */
225    public void setVersion( Version version )
226    {
227        this.version = version;
228    }
229
230    public void setScope( String scope )
231    {
232        if ( dependency == null )
233        {
234            throw new IllegalStateException( "node does not have a dependency" );
235        }
236        dependency = dependency.setScope( scope );
237    }
238
239    public void setOptional( Boolean optional )
240    {
241        if ( dependency == null )
242        {
243            throw new IllegalStateException( "node does not have a dependency" );
244        }
245        dependency = dependency.setOptional( optional );
246    }
247
248    public int getManagedBits()
249    {
250        return managedBits;
251    }
252
253    /**
254     * Sets a bit field indicating which attributes of this node were subject to dependency management.
255     * 
256     * @param managedBits The bit field indicating the managed attributes or {@code 0} if dependency management wasn't
257     *            applied.
258     */
259    public void setManagedBits( int managedBits )
260    {
261        this.managedBits = (byte) ( managedBits & 0x1F );
262    }
263
264    public List<RemoteRepository> getRepositories()
265    {
266        return repositories;
267    }
268
269    /**
270     * Sets the remote repositories from which this node's artifact shall be resolved.
271     * 
272     * @param repositories The remote repositories to use for artifact resolution, may be {@code null}.
273     */
274    public void setRepositories( List<RemoteRepository> repositories )
275    {
276        if ( repositories == null || repositories.isEmpty() )
277        {
278            this.repositories = Collections.emptyList();
279        }
280        else
281        {
282            this.repositories = repositories;
283        }
284    }
285
286    public String getRequestContext()
287    {
288        return context;
289    }
290
291    public void setRequestContext( String context )
292    {
293        this.context = ( context != null ) ? context : "";
294    }
295
296    public Map<Object, Object> getData()
297    {
298        return data;
299    }
300
301    public void setData( Map<Object, Object> data )
302    {
303        if ( data == null )
304        {
305            this.data = Collections.emptyMap();
306        }
307        else
308        {
309            this.data = data;
310        }
311    }
312
313    public void setData( Object key, Object value )
314    {
315        requireNonNull( key, "key cannot be null" );
316
317        if ( value == null )
318        {
319            if ( !data.isEmpty() )
320            {
321                data.remove( key );
322
323                if ( data.isEmpty() )
324                {
325                    data = Collections.emptyMap();
326                }
327            }
328        }
329        else
330        {
331            if ( data.isEmpty() )
332            {
333                data = new HashMap<Object, Object>( 1, 2 ); // nodes can be numerous so let's be space conservative
334            }
335            data.put( key, value );
336        }
337    }
338
339    public boolean accept( DependencyVisitor visitor )
340    {
341        if ( visitor.visitEnter( this ) )
342        {
343            for ( DependencyNode child : children )
344            {
345                if ( !child.accept( visitor ) )
346                {
347                    break;
348                }
349            }
350        }
351
352        return visitor.visitLeave( this );
353    }
354
355    @Override
356    public String toString()
357    {
358        Dependency dep = getDependency();
359        if ( dep == null )
360        {
361            return String.valueOf( getArtifact() );
362        }
363        return dep.toString();
364    }
365
366}