001package org.apache.maven.artifact.resolver;
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.Collections;
024import java.util.Iterator;
025import java.util.LinkedList;
026import java.util.List;
027import java.util.Set;
028
029import org.apache.maven.artifact.Artifact;
030import org.apache.maven.artifact.repository.ArtifactRepository;
031import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
032import org.apache.maven.artifact.versioning.ArtifactVersion;
033import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
034
035public class ResolutionNode
036{
037    private Artifact artifact;
038
039    private List<ResolutionNode> children;
040
041    private final List<Object> parents;
042
043    private final int depth;
044
045    private final ResolutionNode parent;
046
047    private final List<ArtifactRepository> remoteRepositories;
048
049    private boolean active = true;
050
051    private List<Artifact> trail;
052
053    public ResolutionNode( Artifact artifact, List<ArtifactRepository> remoteRepositories )
054    {
055        this.artifact = artifact;
056        this.remoteRepositories = remoteRepositories;
057        depth = 0;
058        parents = Collections.emptyList();
059        parent = null;
060    }
061
062    public ResolutionNode( Artifact artifact, List<ArtifactRepository> remoteRepositories, ResolutionNode parent )
063    {
064        this.artifact = artifact;
065        this.remoteRepositories = remoteRepositories;
066        depth = parent.depth + 1;
067        parents = new ArrayList<Object>();
068        parents.addAll( parent.parents );
069        parents.add( parent.getKey() );
070        this.parent = parent;
071    }
072
073    public Artifact getArtifact()
074    {
075        return artifact;
076    }
077
078    public Object getKey()
079    {
080        return artifact.getDependencyConflictId();
081    }
082
083    public void addDependencies( Set<Artifact> artifacts, List<ArtifactRepository> remoteRepositories,
084                                 ArtifactFilter filter )
085        throws CyclicDependencyException, OverConstrainedVersionException
086    {
087        if ( artifacts != null && !artifacts.isEmpty() )
088        {
089            children = new ArrayList<ResolutionNode>( artifacts.size() );
090
091            for ( Artifact a : artifacts )
092            {
093                if ( parents.contains( a.getDependencyConflictId() ) )
094                {
095                    a.setDependencyTrail( getDependencyTrail() );
096
097                    throw new CyclicDependencyException( "A dependency has introduced a cycle", a );
098                }
099
100                children.add( new ResolutionNode( a, remoteRepositories, this ) );
101            }
102        }
103        else
104        {
105            children = Collections.emptyList();
106        }
107        trail = null;
108    }
109
110    /**
111     * @return {@link List} &lt; {@link String} > with artifact ids
112     * @throws OverConstrainedVersionException
113     */
114    public List<String> getDependencyTrail()
115        throws OverConstrainedVersionException
116    {
117        List<Artifact> trial = getTrail();
118
119        List<String> ret = new ArrayList<String>( trial.size() );
120
121        for ( Artifact artifact : trial )
122        {
123            ret.add( artifact.getId() );
124        }
125
126        return ret;
127    }
128
129    private List<Artifact> getTrail()
130        throws OverConstrainedVersionException
131    {
132        if ( trail == null )
133        {
134            List<Artifact> ids = new LinkedList<Artifact>();
135            ResolutionNode node = this;
136            while ( node != null )
137            {
138                Artifact artifact = node.getArtifact();
139                if ( artifact.getVersion() == null )
140                {
141                    // set the recommended version
142                    ArtifactVersion selected = artifact.getSelectedVersion();
143                    // MNG-2123: null is a valid response to getSelectedVersion, don't
144                    // assume it won't ever be.
145                    if ( selected != null )
146                    {
147                        artifact.selectVersion( selected.toString() );
148                    }
149                    else
150                    {
151                        throw new OverConstrainedVersionException( "Unable to get a selected Version for "
152                            + artifact.getArtifactId(), artifact );
153                    }
154                }
155
156                ids.add( 0, artifact );
157                node = node.parent;
158            }
159            trail = ids;
160        }
161        return trail;
162    }
163
164    public boolean isResolved()
165    {
166        return children != null;
167    }
168
169    /**
170     * Test whether the node is direct or transitive dependency.
171     */
172    public boolean isChildOfRootNode()
173    {
174        return parent != null && parent.parent == null;
175    }
176
177    public Iterator<ResolutionNode> getChildrenIterator()
178    {
179        return children.iterator();
180    }
181
182    public int getDepth()
183    {
184        return depth;
185    }
186
187    public List<ArtifactRepository> getRemoteRepositories()
188    {
189        return remoteRepositories;
190    }
191
192    public boolean isActive()
193    {
194        return active;
195    }
196
197    public void enable()
198    {
199        active = true;
200
201        // TODO: if it was null, we really need to go find them now... or is this taken care of by the ordering?
202        if ( children != null )
203        {
204            for ( ResolutionNode node : children )
205            {
206                node.enable();
207            }
208        }
209    }
210
211    public void disable()
212    {
213        active = false;
214        if ( children != null )
215        {
216            for ( ResolutionNode node : children )
217            {
218                node.disable();
219            }
220        }
221    }
222
223    public boolean filterTrail( ArtifactFilter filter )
224        throws OverConstrainedVersionException
225    {
226        boolean success = true;
227        if ( filter != null )
228        {
229            for ( Artifact artifact : getTrail() )
230            {
231                if ( !filter.include( artifact ) )
232                {
233                    success = false;
234                }
235            }
236        }
237        return success;
238    }
239
240    @Override
241    public String toString()
242    {
243        return artifact.toString() + " (" + depth + "; " + ( active ? "enabled" : "disabled" ) + ")";
244    }
245
246    public void setArtifact( Artifact artifact )
247    {
248        this.artifact = artifact;
249    }
250
251}