package org.apache.maven.archiva.dependency.graph;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.functors.AndPredicate;
import org.apache.commons.collections.functors.NotPredicate;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.archiva.dependency.graph.functors.NodePredicate;
import org.apache.maven.archiva.dependency.graph.functors.OrphanedNodePredicate;
import org.apache.maven.archiva.model.ArchivaProjectModel;
import org.apache.maven.archiva.model.ArtifactReference;
import org.apache.maven.archiva.model.Dependency;
import org.apache.maven.archiva.model.DependencyScope;
import org.apache.maven.archiva.model.Exclusion;
import org.apache.maven.archiva.model.VersionedReference;
/**
* Utilities for manipulating the DependencyGraph.
*
* @version $Id$
*/
public class DependencyGraphUtils
{
/**
* Standard way to add a model to the graph.
*
* NOTE: Used by archiva-repository-layer runtime and archiva-dependency-graph tests.
*
* @param model the model to add
* @param graph the graph to add it to
* @param fromNode the node to add it from.
*/
public static void addNodeFromModel( ArchivaProjectModel model, DependencyGraph graph, DependencyGraphNode fromNode )
{
if ( model == null )
{
throw new IllegalStateException( "Unable to add null model for "
+ DependencyGraphKeys.toKey( fromNode.getArtifact() ) );
}
if ( model.getRelocation() != null )
{
// We need to CHANGE this node.
ArtifactReference refTO = new ArtifactReference();
refTO.setGroupId( fromNode.getArtifact().getGroupId() );
refTO.setArtifactId( fromNode.getArtifact().getArtifactId() );
refTO.setVersion( fromNode.getArtifact().getVersion() );
refTO.setClassifier( fromNode.getArtifact().getClassifier() );
refTO.setType( fromNode.getArtifact().getType() );
VersionedReference relocation = model.getRelocation();
if ( StringUtils.isNotBlank( relocation.getGroupId() ) )
{
refTO.setGroupId( relocation.getGroupId() );
}
if ( StringUtils.isNotBlank( relocation.getArtifactId() ) )
{
refTO.setArtifactId( relocation.getArtifactId() );
}
if ( StringUtils.isNotBlank( relocation.getVersion() ) )
{
refTO.setVersion( relocation.getVersion() );
}
DependencyGraphNode nodeTO = new DependencyGraphNode( refTO );
graph.addNode( nodeTO );
collapseNodes( graph, fromNode, nodeTO );
return;
}
boolean isRootNode = graph.getRootNode().equals( fromNode );
if ( CollectionUtils.isNotEmpty( model.getDependencyManagement() ) )
{
for ( Dependency dependency : model.getDependencyManagement() )
{
fromNode.addDependencyManagement( dependency );
}
}
if ( CollectionUtils.isNotEmpty( model.getDependencies() ) )
{
for ( Dependency dependency : model.getDependencies() )
{
String scope = dependency.getScope();
// Test scopes *NOT* from root node can be skipped.
if ( DependencyScope.TEST.equals( scope ) && !isRootNode )
{
// skip add of test scope
continue;
}
ArtifactReference artifactRef = new ArtifactReference();
artifactRef.setGroupId( dependency.getGroupId() );
artifactRef.setArtifactId( dependency.getArtifactId() );
artifactRef.setVersion( dependency.getVersion() );
artifactRef.setClassifier( dependency.getClassifier() );
artifactRef.setType( dependency.getType() );
DependencyGraphNode toNode = new DependencyGraphNode( artifactRef );
if ( CollectionUtils.isNotEmpty( dependency.getExclusions() ) )
{
for ( Exclusion exclusion : dependency.getExclusions() )
{
toNode.addExclude( exclusion );
}
}
if ( dependency.isFromParent() )
{
toNode.setFromParent( true );
}
// Add node (to)
graph.addNode( toNode );
DependencyGraphEdge edge = new DependencyGraphEdge( fromNode.getArtifact(), toNode.getArtifact() );
edge.setScope( StringUtils.defaultIfEmpty( dependency.getScope(), DependencyScope.COMPILE ) );
if ( dependency.isOptional() )
{
edge.setDisabled( true );
edge.setDisabledType( DependencyGraph.DISABLED_OPTIONAL );
edge.setDisabledReason( "Optional Dependency" );
}
graph.addEdge( edge );
}
}
fromNode.setResolved( true );
graph.addNode( fromNode );
}
/**
* Clean out any nodes that may have become orphaned in the graph.
*
* @param graph the graph to check.
*/
public static void cleanupOrphanedNodes( DependencyGraph graph )
{
boolean done = false;
Predicate orphanedNodePredicate = new OrphanedNodePredicate( graph );
Predicate notRootNode = NotPredicate.getInstance( new NodePredicate( graph.getRootNode().getArtifact() ) );
Predicate orphanedChildNodePredicate = AndPredicate.getInstance( notRootNode, orphanedNodePredicate );
while ( !done )
{
// Find orphaned node.
DependencyGraphNode orphanedNode = (DependencyGraphNode) CollectionUtils.find( graph.getNodes(),
orphanedChildNodePredicate );
if ( orphanedNode == null )
{
done = true;
break;
}
// Remove edges FROM orphaned node.
for ( DependencyGraphEdge edge : graph.getEdgesFrom( orphanedNode ) )
{
graph.removeEdge( edge );
}
// Remove orphaned node.
graph.removeNode( orphanedNode );
}
}
/**
* Functionaly similar to {@link #collapseVersions(DependencyGraph, ArtifactReference, String, String)}, but
* in a new, easier to use, format.
*
* 1) Removes the FROM edges connected to the FROM node
* 2) Moves the TO edges connected to the FROM node to the TO node.
* 3) Removes the FROM node (which is now orphaned)
*
* @param graph the graph to perform operation on
* @param nodeFrom the node to collapse from
* @param nodeTo the node to collapse to
*/
public static void collapseNodes( DependencyGraph graph, DependencyGraphNode nodeFROM, DependencyGraphNode nodeTO )
{
Set<DependencyGraphEdge> edgesToRemove = new HashSet<DependencyGraphEdge>();
// 1) Remove all of the edge.from references from nodeFROM
List<DependencyGraphEdge> fromEdges = graph.getEdgesFrom( nodeFROM );
if ( CollectionUtils.isNotEmpty( fromEdges ) )
{
edgesToRemove.addAll( fromEdges );
}
// 2) Swing all of the edge.to references from nodeFROM to nodeTO.
// System.out.println( "Swinging incoming edges from " + nodeFROM );
// System.out.println( " to " + nodeTO );
List<DependencyGraphEdge> toEdges = graph.getEdgesTo( nodeFROM );
for ( DependencyGraphEdge edge : toEdges )
{
// Identify old edge to remove.
edgesToRemove.add( edge );
// Clone edge, set edge.to and add to graph.
DependencyGraphEdge newedge = clone( edge );
newedge.setNodeTo( nodeTO );
// System.out.println( " edge from: " + edge );
// System.out.println( " to: " + newedge );
graph.addEdge( newedge );
}
// Actually remove the old edges.
for ( DependencyGraphEdge edge : edgesToRemove )
{
graph.removeEdge( edge );
}
// 3) Remove the nodeFROM
graph.removeNode( nodeFROM );
}
/**
* Create a clone of an edge.
*
* @param edge the edge to clone.
* @return the cloned edge.
*/
public static DependencyGraphEdge clone( DependencyGraphEdge edge )
{
DependencyGraphEdge cloned = new DependencyGraphEdge( edge.getNodeFrom(), edge.getNodeTo() );
cloned.setDisabled( edge.isDisabled() );
cloned.setDisabledReason( edge.getDisabledReason() );
cloned.setDisabledType( edge.getDisabledType() );
cloned.setScope( edge.getScope() );
return cloned;
}
}