1 package org.apache.maven.archiva.dependency.graph.tasks;
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
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;
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;
49 * RefineConflictsVisitor
53 public class RefineConflictsVisitor
55 implements DependencyGraphVisitor
58 implements Comparator<NodeLocation>
60 public int compare( NodeLocation obj0, NodeLocation obj1 )
62 return obj0.depth - obj1.depth;
68 public ArtifactReference artifact;
70 public DependencyGraphEdge edge;
74 public NodeLocation( ArtifactReference artifact, DependencyGraphEdge edge, int depth )
76 this.artifact = artifact;
82 class NodeLocationPredicate
85 private ArtifactReference artifact;
87 public NodeLocationPredicate( ArtifactReference artifact )
89 this.artifact = artifact;
92 public NodeLocationPredicate( DependencyGraphNode node )
94 this( node.getArtifact() );
97 public boolean evaluate( Object object )
99 boolean satisfies = false;
101 if ( object instanceof NodeLocation )
103 NodeLocation nodeloc = (NodeLocation) object;
104 satisfies = nodeloc.artifact.equals( artifact );
112 class NodeLocationVersionComparator
113 implements Comparator<NodeLocation>
115 public int compare( NodeLocation o1, NodeLocation o2 )
117 if ( o1 == null && o2 == null )
122 if ( o1 == null && o2 != null )
127 if ( o1 != null && o2 == null )
132 // String version1 = o1.artifact.getVersion();
133 // String version2 = o2.artifact.getVersion();
135 // VersionComparator.getInstance().compare( version1, version2 );
141 class DistantNodeLocationPredicate
146 public DistantNodeLocationPredicate( int distantCutoff )
148 this.cutoff = distantCutoff;
151 public boolean evaluate( Object object )
153 boolean satisfies = false;
155 if ( object instanceof NodeLocation )
157 NodeLocation nodeloc = (NodeLocation) object;
158 satisfies = ( nodeloc.depth >= this.cutoff );
165 private List<DependencyGraphNode> conflictingArtifacts;
167 private Map<String,NodeLocation> foundNodesMap = new HashMap<String, NodeLocation>();
169 private int currentDepth = 0;
171 private DependencyGraph currentGraph;
173 @SuppressWarnings("unchecked")
174 public RefineConflictsVisitor()
176 conflictingArtifacts = TypedList.decorate( new ArrayList<ArtifactReference>(), ArtifactReference.class );
179 public void discoverGraph( DependencyGraph graph )
181 super.discoverGraph( graph );
182 this.currentGraph = graph;
183 this.foundNodesMap.clear();
186 public void discoverNode( DependencyGraphNode node )
188 super.discoverNode( node );
192 List<DependencyGraphEdge> edgesFrom = currentGraph.getEdgesFrom( node );
193 for ( DependencyGraphEdge edge : edgesFrom )
195 if ( this.conflictingArtifacts.contains( edge.getNodeTo() ) )
197 String nodeKey = DependencyGraphKeys.toKey( edge.getNodeTo() );
198 // Check for existing NodeLocation with same key
199 NodeLocation nodeloc = this.foundNodesMap.get( nodeKey );
201 if ( ( nodeloc == null ) || ( currentDepth < nodeloc.depth ) )
203 nodeloc = new NodeLocation( edge.getNodeTo(), edge, currentDepth );
204 this.foundNodesMap.put( nodeKey, nodeloc );
210 public void finishGraph( DependencyGraph graph )
212 super.finishGraph( graph );
214 if ( MapUtils.isEmpty( this.foundNodesMap ) )
219 // Find winning node.
220 ArtifactReference winningArtifact = findWinningArtifact( this.foundNodesMap.values() );
221 DependencyGraphNode winningNode = graph.getNode( winningArtifact );
223 // Gather up Losing Nodes.
224 Set<NodeLocation> losingNodes = new HashSet<NodeLocation>();
225 Predicate losersPredicate = NotPredicate.getInstance( new NodeLocationPredicate( winningArtifact ) );
226 CollectionUtils.select( this.foundNodesMap.values(), losersPredicate, losingNodes );
228 // Swing losing nodes to winning node.
229 for ( NodeLocation losingNodeLoc : losingNodes )
231 DependencyGraphNode losingNode = graph.getNode( losingNodeLoc.artifact );
232 DependencyGraphUtils.collapseNodes( graph, losingNode, winningNode );
236 @SuppressWarnings("unchecked")
237 private ArtifactReference findWinningArtifact( Collection<NodeLocation> nodes )
239 List<NodeLocation> remainingNodes = new ArrayList<NodeLocation>();
240 remainingNodes.addAll( nodes );
242 /* .\ Filter by Depth \.____________________________________________________ */
245 Collections.sort( remainingNodes, new DepthComparator() );
247 // Determine 'closest' node depth.
248 NodeLocation nearestNode = remainingNodes.get( 0 );
249 int nearest = nearestNode.depth;
251 // Filter out distant nodes.
252 Predicate distantLocations = new DistantNodeLocationPredicate( nearest );
253 CollectionUtils.filter( remainingNodes, distantLocations );
255 // Do we have 1 node left?
256 if ( remainingNodes.size() == 1 )
259 NodeLocation nodeloc = remainingNodes.get( 0 );
260 return nodeloc.artifact;
263 /* .\ Filter by Newest Version \.___________________________________________ */
265 // We have 2 or more nodes that are equal distance from the root.
266 // Determine which one is 'newest' based on version id.
267 Collections.sort( remainingNodes, new ReverseComparator( new NodeLocationVersionComparator() ) );
269 NodeLocation nodeloc = remainingNodes.get( 0 );
270 return nodeloc.artifact;
273 public void finishNode( DependencyGraphNode node )
275 super.finishNode( node );
279 public List<DependencyGraphNode> getConflictingArtifacts()
281 return conflictingArtifacts;
284 public void addAllConflictingArtifacts( Collection<DependencyGraphNode> nodes )
286 this.conflictingArtifacts.addAll( nodes );
289 public void resetConflictingArtifacts()
291 this.conflictingArtifacts.clear();