]> source.dussan.org Git - archiva.git/blob
158edaa59f7ab134188bb817ca12c2ef407c4f1d
[archiva.git] /
1 package org.apache.maven.archiva.dependency.graph.tasks;
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.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Comparator;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31
32 import org.apache.commons.collections.CollectionUtils;
33 import org.apache.commons.collections.MapUtils;
34 import org.apache.commons.collections.Predicate;
35 import org.apache.commons.collections.comparators.ReverseComparator;
36 import org.apache.commons.collections.functors.NotPredicate;
37 import org.apache.commons.collections.list.TypedList;
38 import org.apache.maven.archiva.common.utils.VersionComparator;
39 import org.apache.maven.archiva.dependency.graph.DependencyGraph;
40 import org.apache.maven.archiva.dependency.graph.DependencyGraphEdge;
41 import org.apache.maven.archiva.dependency.graph.DependencyGraphKeys;
42 import org.apache.maven.archiva.dependency.graph.DependencyGraphNode;
43 import org.apache.maven.archiva.dependency.graph.DependencyGraphUtils;
44 import org.apache.maven.archiva.dependency.graph.walk.BaseVisitor;
45 import org.apache.maven.archiva.dependency.graph.walk.DependencyGraphVisitor;
46 import org.apache.maven.archiva.model.ArtifactReference;
47
48 /**
49  * RefineConflictsVisitor 
50  *
51  * @version $Id$
52  */
53 public class RefineConflictsVisitor
54     extends BaseVisitor
55     implements DependencyGraphVisitor
56 {
57     class DepthComparator
58         implements Comparator<NodeLocation>
59     {
60         public int compare( NodeLocation obj0, NodeLocation obj1 )
61         {
62             return obj0.depth - obj1.depth;
63         }
64     }
65
66     class NodeLocation
67     {
68         public ArtifactReference artifact;
69
70         public DependencyGraphEdge edge;
71
72         public int depth;
73
74         public NodeLocation( ArtifactReference artifact, DependencyGraphEdge edge, int depth )
75         {
76             this.artifact = artifact;
77             this.edge = edge;
78             this.depth = depth;
79         }
80     }
81
82     class NodeLocationPredicate
83         implements Predicate
84     {
85         private ArtifactReference artifact;
86
87         public NodeLocationPredicate( ArtifactReference artifact )
88         {
89             this.artifact = artifact;
90         }
91
92         public NodeLocationPredicate( DependencyGraphNode node )
93         {
94             this( node.getArtifact() );
95         }
96
97         public boolean evaluate( Object object )
98         {
99             boolean satisfies = false;
100
101             if ( object instanceof NodeLocation )
102             {
103                 NodeLocation nodeloc = (NodeLocation) object;
104                 satisfies = nodeloc.artifact.equals( artifact );
105             }
106
107             return satisfies;
108         }
109
110     }
111
112     class NodeLocationVersionComparator
113         implements Comparator<NodeLocation>
114     {
115         public int compare( NodeLocation o1, NodeLocation o2 )
116         {
117             if ( o1 == null && o2 == null )
118             {
119                 return 0;
120             }
121
122             if ( o1 == null && o2 != null )
123             {
124                 return 1;
125             }
126
127             if ( o1 != null && o2 == null )
128             {
129                 return -1;
130             }
131
132             String version1 = o1.artifact.getVersion();
133             String version2 = o2.artifact.getVersion();
134
135             return VersionComparator.getInstance().compare( version1, version2 );
136         }
137     }
138
139     class DistantNodeLocationPredicate
140         implements Predicate
141     {
142         private int cutoff;
143
144         public DistantNodeLocationPredicate( int distantCutoff )
145         {
146             this.cutoff = distantCutoff;
147         }
148
149         public boolean evaluate( Object object )
150         {
151             boolean satisfies = false;
152
153             if ( object instanceof NodeLocation )
154             {
155                 NodeLocation nodeloc = (NodeLocation) object;
156                 satisfies = ( nodeloc.depth >= this.cutoff );
157             }
158
159             return satisfies;
160         }
161     }
162
163     private List<DependencyGraphNode> conflictingArtifacts;
164
165     private Map<String,NodeLocation> foundNodesMap = new HashMap<String, NodeLocation>();
166
167     private int currentDepth = 0;
168
169     private DependencyGraph currentGraph;
170
171     @SuppressWarnings("unchecked")
172     public RefineConflictsVisitor()
173     {
174         conflictingArtifacts = TypedList.decorate( new ArrayList<ArtifactReference>(), ArtifactReference.class );
175     }
176
177     public void discoverGraph( DependencyGraph graph )
178     {
179         super.discoverGraph( graph );
180         this.currentGraph = graph;
181         this.foundNodesMap.clear();
182     }
183
184     public void discoverNode( DependencyGraphNode node )
185     {
186         super.discoverNode( node );
187
188         currentDepth++;
189
190         List<DependencyGraphEdge> edgesFrom = currentGraph.getEdgesFrom( node );
191         for ( DependencyGraphEdge edge : edgesFrom )
192         {
193             if ( this.conflictingArtifacts.contains( edge.getNodeTo() ) )
194             {
195                 String nodeKey = DependencyGraphKeys.toKey( edge.getNodeTo() );
196                 // Check for existing NodeLocation with same key
197                 NodeLocation nodeloc = this.foundNodesMap.get( nodeKey );
198
199                 if ( ( nodeloc == null ) || ( currentDepth < nodeloc.depth ) )
200                 {
201                     nodeloc = new NodeLocation( edge.getNodeTo(), edge, currentDepth );
202                     this.foundNodesMap.put( nodeKey, nodeloc );
203                 }
204             }
205         }
206     }
207
208     public void finishGraph( DependencyGraph graph )
209     {
210         super.finishGraph( graph );
211
212         if ( MapUtils.isEmpty( this.foundNodesMap ) )
213         {
214             return;
215         }
216
217         // Find winning node.
218         ArtifactReference winningArtifact = findWinningArtifact( this.foundNodesMap.values() );
219         DependencyGraphNode winningNode = graph.getNode( winningArtifact );
220
221         // Gather up Losing Nodes.
222         Set<NodeLocation> losingNodes = new HashSet<NodeLocation>();
223         Predicate losersPredicate = NotPredicate.getInstance( new NodeLocationPredicate( winningArtifact ) );
224         CollectionUtils.select( this.foundNodesMap.values(), losersPredicate, losingNodes );
225
226         // Swing losing nodes to winning node.
227         for ( NodeLocation losingNodeLoc : losingNodes )
228         {
229             DependencyGraphNode losingNode = graph.getNode( losingNodeLoc.artifact );
230             DependencyGraphUtils.collapseNodes( graph, losingNode, winningNode );
231         }
232     }
233
234     @SuppressWarnings("unchecked")
235     private ArtifactReference findWinningArtifact( Collection<NodeLocation> nodes )
236     {
237         List<NodeLocation> remainingNodes = new ArrayList<NodeLocation>();
238         remainingNodes.addAll( nodes );
239
240         /* .\ Filter by Depth \.____________________________________________________ */
241
242         // Sort by depth.
243         Collections.sort( remainingNodes, new DepthComparator() );
244
245         // Determine 'closest' node depth.
246         NodeLocation nearestNode = remainingNodes.get( 0 );
247         int nearest = nearestNode.depth;
248
249         // Filter out distant nodes. 
250         Predicate distantLocations = new DistantNodeLocationPredicate( nearest );
251         CollectionUtils.filter( remainingNodes, distantLocations );
252
253         // Do we have 1 node left?
254         if ( remainingNodes.size() == 1 )
255         {
256             // A winner!
257             NodeLocation nodeloc = remainingNodes.get( 0 );
258             return nodeloc.artifact;
259         }
260
261         /* .\ Filter by Newest Version \.___________________________________________ */
262
263         // We have 2 or more nodes that are equal distance from the root.
264         // Determine which one is 'newest' based on version id.
265         Collections.sort( remainingNodes, new ReverseComparator( new NodeLocationVersionComparator() ) );
266
267         NodeLocation nodeloc = remainingNodes.get( 0 );
268         return nodeloc.artifact;
269     }
270
271     public void finishNode( DependencyGraphNode node )
272     {
273         super.finishNode( node );
274         currentDepth--;
275     }
276
277     public List<DependencyGraphNode> getConflictingArtifacts()
278     {
279         return conflictingArtifacts;
280     }
281
282     public void addAllConflictingArtifacts( Collection<DependencyGraphNode> nodes )
283     {
284         this.conflictingArtifacts.addAll( nodes );
285     }
286
287     public void resetConflictingArtifacts()
288     {
289         this.conflictingArtifacts.clear();
290     }
291 }