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;
028
029import org.eclipse.aether.artifact.Artifact;
030import org.eclipse.aether.repository.RemoteRepository;
031import org.eclipse.aether.version.Version;
032import org.eclipse.aether.version.VersionConstraint;
033
034/**
035 * A node within a dependency graph.
036 */
037public final class DefaultDependencyNode
038    implements DependencyNode
039{
040
041    private List<DependencyNode> children;
042
043    private Dependency dependency;
044
045    private Artifact artifact;
046
047    private List<? extends Artifact> relocations;
048
049    private Collection<? extends Artifact> aliases;
050
051    private VersionConstraint versionConstraint;
052
053    private Version version;
054
055    private byte managedBits;
056
057    private List<RemoteRepository> repositories;
058
059    private String context;
060
061    private Map<Object, Object> data;
062
063    /**
064     * Creates a new node with the specified dependency.
065     * 
066     * @param dependency The dependency associated with this node, may be {@code null} for a root node.
067     */
068    public DefaultDependencyNode( Dependency dependency )
069    {
070        this.dependency = dependency;
071        artifact = ( dependency != null ) ? dependency.getArtifact() : null;
072        children = new ArrayList<DependencyNode>( 0 );
073        aliases = relocations = Collections.emptyList();
074        repositories = Collections.emptyList();
075        context = "";
076        data = Collections.emptyMap();
077    }
078
079    /**
080     * Creates a new root node with the specified artifact as its label. Note that the new node has no dependency, i.e.
081     * {@link #getDependency()} will return {@code null}. Put differently, the specified artifact will not be subject to
082     * dependency collection/resolution.
083     * 
084     * @param artifact The artifact to use as label for this node, may be {@code null}.
085     */
086    public DefaultDependencyNode( Artifact artifact )
087    {
088        this.artifact = artifact;
089        children = new ArrayList<DependencyNode>( 0 );
090        aliases = relocations = Collections.emptyList();
091        repositories = Collections.emptyList();
092        context = "";
093        data = Collections.emptyMap();
094    }
095
096    /**
097     * Creates a mostly shallow clone of the specified node. The new node has its own copy of any custom data and
098     * initially no children.
099     * 
100     * @param node The node to copy, must not be {@code null}.
101     */
102    public DefaultDependencyNode( DependencyNode node )
103    {
104        dependency = node.getDependency();
105        artifact = node.getArtifact();
106        children = new ArrayList<DependencyNode>( 0 );
107        setAliases( node.getAliases() );
108        setRequestContext( node.getRequestContext() );
109        setManagedBits( node.getManagedBits() );
110        setRelocations( node.getRelocations() );
111        setRepositories( node.getRepositories() );
112        setVersion( node.getVersion() );
113        setVersionConstraint( node.getVersionConstraint() );
114        Map<?, ?> data = node.getData();
115        setData( data.isEmpty() ? null : new HashMap<Object, Object>( data ) );
116    }
117
118    public List<DependencyNode> getChildren()
119    {
120        return children;
121    }
122
123    public void setChildren( List<DependencyNode> children )
124    {
125        if ( children == null )
126        {
127            this.children = new ArrayList<DependencyNode>( 0 );
128        }
129        else
130        {
131            this.children = children;
132        }
133    }
134
135    public Dependency getDependency()
136    {
137        return dependency;
138    }
139
140    public Artifact getArtifact()
141    {
142        return artifact;
143    }
144
145    public void setArtifact( Artifact artifact )
146    {
147        if ( dependency == null )
148        {
149            throw new UnsupportedOperationException( "node does not have a dependency" );
150        }
151        dependency = dependency.setArtifact( artifact );
152        this.artifact = dependency.getArtifact();
153    }
154
155    public List<? extends Artifact> getRelocations()
156    {
157        return relocations;
158    }
159
160    /**
161     * Sets the sequence of relocations that was followed to resolve this dependency's artifact.
162     * 
163     * @param relocations The sequence of relocations, may be {@code null}.
164     */
165    public void setRelocations( List<? extends Artifact> relocations )
166    {
167        if ( relocations == null || relocations.isEmpty() )
168        {
169            this.relocations = Collections.emptyList();
170        }
171        else
172        {
173            this.relocations = relocations;
174        }
175    }
176
177    public Collection<? extends Artifact> getAliases()
178    {
179        return aliases;
180    }
181
182    /**
183     * Sets the known aliases for this dependency's artifact.
184     * 
185     * @param aliases The known aliases, may be {@code null}.
186     */
187    public void setAliases( Collection<? extends Artifact> aliases )
188    {
189        if ( aliases == null || aliases.isEmpty() )
190        {
191            this.aliases = Collections.emptyList();
192        }
193        else
194        {
195            this.aliases = aliases;
196        }
197    }
198
199    public VersionConstraint getVersionConstraint()
200    {
201        return versionConstraint;
202    }
203
204    /**
205     * Sets the version constraint that was parsed from the dependency's version declaration.
206     * 
207     * @param versionConstraint The version constraint for this node, may be {@code null}.
208     */
209    public void setVersionConstraint( VersionConstraint versionConstraint )
210    {
211        this.versionConstraint = versionConstraint;
212    }
213
214    public Version getVersion()
215    {
216        return version;
217    }
218
219    /**
220     * Sets the version that was selected for the dependency's target artifact.
221     * 
222     * @param version The parsed version, may be {@code null}.
223     */
224    public void setVersion( Version version )
225    {
226        this.version = version;
227    }
228
229    public void setScope( String scope )
230    {
231        if ( dependency == null )
232        {
233            throw new UnsupportedOperationException( "node does not have a dependency" );
234        }
235        dependency = dependency.setScope( scope );
236    }
237
238    public void setOptional( Boolean optional )
239    {
240        if ( dependency == null )
241        {
242            throw new UnsupportedOperationException( "node does not have a dependency" );
243        }
244        dependency = dependency.setOptional( optional );
245    }
246
247    public int getManagedBits()
248    {
249        return managedBits;
250    }
251
252    /**
253     * Sets a bit field indicating which attributes of this node were subject to dependency management.
254     * 
255     * @param managedBits The bit field indicating the managed attributes or {@code 0} if dependency management wasn't
256     *            applied.
257     */
258    public void setManagedBits( int managedBits )
259    {
260        this.managedBits = (byte) ( managedBits & 0x1F );
261    }
262
263    public List<RemoteRepository> getRepositories()
264    {
265        return repositories;
266    }
267
268    /**
269     * Sets the remote repositories from which this node's artifact shall be resolved.
270     * 
271     * @param repositories The remote repositories to use for artifact resolution, may be {@code null}.
272     */
273    public void setRepositories( List<RemoteRepository> repositories )
274    {
275        if ( repositories == null || repositories.isEmpty() )
276        {
277            this.repositories = Collections.emptyList();
278        }
279        else
280        {
281            this.repositories = repositories;
282        }
283    }
284
285    public String getRequestContext()
286    {
287        return context;
288    }
289
290    public void setRequestContext( String context )
291    {
292        this.context = ( context != null ) ? context : "";
293    }
294
295    public Map<Object, Object> getData()
296    {
297        return data;
298    }
299
300    public void setData( Map<Object, Object> data )
301    {
302        if ( data == null )
303        {
304            this.data = Collections.emptyMap();
305        }
306        else
307        {
308            this.data = data;
309        }
310    }
311
312    public void setData( Object key, Object value )
313    {
314        if ( key == null )
315        {
316            throw new IllegalArgumentException( "key must not be null" );
317        }
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<Object, Object>( 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}