View Javadoc
1   package org.eclipse.aether.internal.impl.collect;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   * 
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   * 
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.Arrays;
23  import java.util.Collections;
24  import java.util.List;
25  
26  import org.eclipse.aether.artifact.Artifact;
27  import org.eclipse.aether.graph.Dependency;
28  import org.eclipse.aether.graph.DependencyCycle;
29  import org.eclipse.aether.graph.DependencyNode;
30  import org.eclipse.aether.util.artifact.ArtifactIdUtils;
31  
32  /**
33   * Default implementation of {@link DependencyCycle}.
34   * Internal helper class for collector implementations.
35   */
36  public final class DefaultDependencyCycle
37      implements DependencyCycle
38  {
39      private final List<Dependency> dependencies;
40  
41      private final int cycleEntry;
42  
43      public DefaultDependencyCycle( List<DependencyNode> nodes, int cycleEntry, Dependency dependency )
44      {
45          // skip root node unless it actually has a dependency or is considered the cycle entry (due to its label)
46          int offset = ( cycleEntry > 0 && nodes.get( 0 ).getDependency() == null ) ? 1 : 0;
47          Dependency[] dependencies = new Dependency[nodes.size() - offset + 1];
48          for ( int i = 0, n = dependencies.length - 1; i < n; i++ )
49          {
50              DependencyNode node = nodes.get( i + offset );
51              dependencies[i] = node.getDependency();
52              // when cycle starts at root artifact as opposed to root dependency, synthesize a dependency
53              if ( dependencies[i] == null )
54              {
55                  dependencies[i] = new Dependency( node.getArtifact(), null );
56              }
57          }
58          dependencies[dependencies.length - 1] = dependency;
59          this.dependencies = Collections.unmodifiableList( Arrays.asList( dependencies ) );
60          this.cycleEntry = cycleEntry;
61      }
62  
63      @Override
64      public List<Dependency> getPrecedingDependencies()
65      {
66          return dependencies.subList( 0, cycleEntry );
67      }
68  
69      @Override
70      public List<Dependency> getCyclicDependencies()
71      {
72          return dependencies.subList( cycleEntry, dependencies.size() );
73      }
74  
75      /**
76       * Searches for a node associated with the given artifact. A version of the artifact is not considered during the
77       * search.
78       *
79       * @param nodes a list representing single path in the dependency graph. First element is the root.
80       * @param artifact to find among the parent nodes.
81       * @return the index of the node furthest from the root and associated with the given artifact, or {@literal -1} if
82       * there is no such node.
83       */
84      public static int find( List<DependencyNode> nodes, Artifact artifact )
85      {
86  
87          for ( int i = nodes.size() - 1; i >= 0; i-- )
88          {
89              DependencyNode node = nodes.get( i );
90  
91              Artifact a = node.getArtifact();
92              if ( a == null )
93              {
94                  break;
95              }
96  
97              if ( !a.getArtifactId().equals( artifact.getArtifactId() ) )
98              {
99                  continue;
100             }
101             if ( !a.getGroupId().equals( artifact.getGroupId() ) )
102             {
103                 continue;
104             }
105             if ( !a.getExtension().equals( artifact.getExtension() ) )
106             {
107                 continue;
108             }
109             if ( !a.getClassifier().equals( artifact.getClassifier() ) )
110             {
111                 continue;
112             }
113             /*
114              * NOTE: While a:1 and a:2 are technically different artifacts, we want to consider the path a:2 -> b:2 ->
115              * a:1 a cycle in the current context. The artifacts themselves might not form a cycle but their producing
116              * projects surely do. Furthermore, conflict resolution will always have to consider a:1 a loser (otherwise
117              * its ancestor a:2 would get pruned and so would a:1) so there is no point in building the sub graph of
118              * a:1.
119              */
120 
121             return i;
122         }
123 
124         return -1;
125     }
126 
127     @Override
128     public String toString()
129     {
130         StringBuilder buffer = new StringBuilder( 256 );
131         int i = 0;
132         for ( Dependency dependency : dependencies )
133         {
134             if ( i++ > 0 )
135             {
136                 buffer.append( " -> " );
137             }
138             buffer.append( ArtifactIdUtils.toVersionlessId( dependency.getArtifact() ) );
139         }
140         return buffer.toString();
141     }
142 
143 }