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 return VersionComparator.getInstance().compare( version1, version2 );
139 class DistantNodeLocationPredicate
144 public DistantNodeLocationPredicate( int distantCutoff )
146 this.cutoff = distantCutoff;
149 public boolean evaluate( Object object )
151 boolean satisfies = false;
153 if ( object instanceof NodeLocation )
155 NodeLocation nodeloc = (NodeLocation) object;
156 satisfies = ( nodeloc.depth >= this.cutoff );
163 private List<DependencyGraphNode> conflictingArtifacts;
165 private Map<String,NodeLocation> foundNodesMap = new HashMap<String, NodeLocation>();
167 private int currentDepth = 0;
169 private DependencyGraph currentGraph;
171 @SuppressWarnings("unchecked")
172 public RefineConflictsVisitor()
174 conflictingArtifacts = TypedList.decorate( new ArrayList<ArtifactReference>(), ArtifactReference.class );
177 public void discoverGraph( DependencyGraph graph )
179 super.discoverGraph( graph );
180 this.currentGraph = graph;
181 this.foundNodesMap.clear();
184 public void discoverNode( DependencyGraphNode node )
186 super.discoverNode( node );
190 List<DependencyGraphEdge> edgesFrom = currentGraph.getEdgesFrom( node );
191 for ( DependencyGraphEdge edge : edgesFrom )
193 if ( this.conflictingArtifacts.contains( edge.getNodeTo() ) )
195 String nodeKey = DependencyGraphKeys.toKey( edge.getNodeTo() );
196 // Check for existing NodeLocation with same key
197 NodeLocation nodeloc = this.foundNodesMap.get( nodeKey );
199 if ( ( nodeloc == null ) || ( currentDepth < nodeloc.depth ) )
201 nodeloc = new NodeLocation( edge.getNodeTo(), edge, currentDepth );
202 this.foundNodesMap.put( nodeKey, nodeloc );
208 public void finishGraph( DependencyGraph graph )
210 super.finishGraph( graph );
212 if ( MapUtils.isEmpty( this.foundNodesMap ) )
217 // Find winning node.
218 ArtifactReference winningArtifact = findWinningArtifact( this.foundNodesMap.values() );
219 DependencyGraphNode winningNode = graph.getNode( winningArtifact );
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 );
226 // Swing losing nodes to winning node.
227 for ( NodeLocation losingNodeLoc : losingNodes )
229 DependencyGraphNode losingNode = graph.getNode( losingNodeLoc.artifact );
230 DependencyGraphUtils.collapseNodes( graph, losingNode, winningNode );
234 @SuppressWarnings("unchecked")
235 private ArtifactReference findWinningArtifact( Collection<NodeLocation> nodes )
237 List<NodeLocation> remainingNodes = new ArrayList<NodeLocation>();
238 remainingNodes.addAll( nodes );
240 /* .\ Filter by Depth \.____________________________________________________ */
243 Collections.sort( remainingNodes, new DepthComparator() );
245 // Determine 'closest' node depth.
246 NodeLocation nearestNode = remainingNodes.get( 0 );
247 int nearest = nearestNode.depth;
249 // Filter out distant nodes.
250 Predicate distantLocations = new DistantNodeLocationPredicate( nearest );
251 CollectionUtils.filter( remainingNodes, distantLocations );
253 // Do we have 1 node left?
254 if ( remainingNodes.size() == 1 )
257 NodeLocation nodeloc = remainingNodes.get( 0 );
258 return nodeloc.artifact;
261 /* .\ Filter by Newest Version \.___________________________________________ */
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() ) );
267 NodeLocation nodeloc = remainingNodes.get( 0 );
268 return nodeloc.artifact;
271 public void finishNode( DependencyGraphNode node )
273 super.finishNode( node );
277 public List<DependencyGraphNode> getConflictingArtifacts()
279 return conflictingArtifacts;
282 public void addAllConflictingArtifacts( Collection<DependencyGraphNode> nodes )
284 this.conflictingArtifacts.addAll( nodes );
287 public void resetConflictingArtifacts()
289 this.conflictingArtifacts.clear();