001 package org.kuali.maven.plugins.graph.dot.edge; 002 003 import java.util.ArrayList; 004 import java.util.HashMap; 005 import java.util.List; 006 import java.util.Map; 007 008 import org.apache.maven.shared.dependency.tree.DependencyNode; 009 import org.kuali.maven.plugins.graph.dot.GraphException; 010 import org.kuali.maven.plugins.graph.pojo.Edge; 011 import org.kuali.maven.plugins.graph.pojo.GraphNode; 012 import org.kuali.maven.plugins.graph.pojo.MavenContext; 013 import org.kuali.maven.plugins.graph.pojo.Scope; 014 import org.kuali.maven.plugins.graph.pojo.State; 015 import org.kuali.maven.plugins.graph.tree.Node; 016 import org.kuali.maven.plugins.graph.tree.TreeHelper; 017 import org.slf4j.Logger; 018 import org.slf4j.LoggerFactory; 019 020 public class SmartEdgeHandler extends FlatEdgeHandler { 021 private static final Logger logger = LoggerFactory.getLogger(SmartEdgeHandler.class); 022 public static final String REPLACEMENT_LABEL = "replacement"; 023 TreeHelper helper = new TreeHelper(); 024 025 Map<String, MavenContext> conflictsMap = new HashMap<String, MavenContext>(); 026 027 @Override 028 public List<Edge> getEdges(Node<MavenContext> node) { 029 List<Edge> edges = new ArrayList<Edge>(); 030 handleNode(node, edges); 031 return edges; 032 } 033 034 protected void handleNode(Node<MavenContext> node, List<Edge> edges) { 035 logger.debug("handling node {}", node.getObject().getId()); 036 switch (node.getObject().getState()) { 037 case INCLUDED: 038 case CYCLIC: 039 case UNKNOWN: 040 // Just draw a line from parent to child 041 // Styling draws attention to CYCLIC and UNKNOWN nodes 042 edges.addAll(super.getEdges(node)); 043 return; 044 case DUPLICATE: 045 // Draw a line from our parent to the included node containing our same artifact 046 handleDuplicate(node, edges); 047 return; 048 case CONFLICT: 049 /** 050 * Draw two lines. One from our parent to the node that has been conflicted out (might be us, might be 051 * another node if the same artifact has been conflicted out twice). Draw a second line from that node to 052 * the node containing the artifact Maven replaced it with. 053 */ 054 handleConflict(node, edges); 055 return; 056 default: 057 throw new GraphException("Unknown state " + node.getObject().getState()); 058 } 059 } 060 061 protected void handleConflict(Node<MavenContext> node, List<Edge> edges) { 062 MavenContext context = node.getObject(); 063 String artifactIdentifier = TreeHelper.getArtifactId(context.getDependencyNode().getRelatedArtifact()); 064 065 // Find the node containing the replacement artifact Maven is actually going to use 066 Node<MavenContext> replacement = findIncludedNode(node.getRoot(), artifactIdentifier); 067 068 // Check to see if we've encountered this same conflict before 069 MavenContext contextToUse = conflictsMap.get(artifactIdentifier); 070 if (contextToUse == null) { 071 contextToUse = context; 072 conflictsMap.put(artifactIdentifier, contextToUse); 073 074 // Draw an edge from contextToUse to the replacement 075 GraphNode parent = contextToUse.getGraphNode(); 076 GraphNode child = replacement.getObject().getGraphNode(); 077 Edge edge = getEdge(parent, child, false, Scope.DEFAULT_SCOPE, State.CONFLICT); 078 edge.setLabel(REPLACEMENT_LABEL); 079 edges.add(edge); 080 } else { 081 // Hide ourself since contextToUse represents the same artifact 082 context.getGraphNode().setHidden(true); 083 } 084 085 // Draw an edge from our parent to contextToUse 086 GraphNode parent = node.getParent().getObject().getGraphNode(); 087 GraphNode child = contextToUse.getGraphNode(); 088 Edge edge = getEdge(parent, child, false, Scope.DEFAULT_SCOPE, State.CONFLICT); 089 090 edges.add(edge); 091 092 } 093 094 protected void handleDuplicate(Node<MavenContext> node, List<Edge> edges) { 095 MavenContext context = node.getObject(); 096 DependencyNode dn = context.getDependencyNode(); 097 098 // Find the node that replaces us 099 Node<MavenContext> replacement = findIncludedNode(node.getRoot(), context.getArtifactIdentifier()); 100 // This is our parent in the tree 101 GraphNode parent = node.getParent().getObject().getGraphNode(); 102 // This is the node that is being used instead of us 103 GraphNode child = replacement.getObject().getGraphNode(); 104 // Use our optional/scope settings 105 boolean optional = dn.getArtifact().isOptional(); 106 Scope scope = Scope.getScope(dn.getArtifact().getScope()); 107 108 // Draw an edge from our parent to the node that replaced us 109 Edge edge = getEdge(parent, child, optional, scope, State.INCLUDED); 110 111 // Add this new edge to the list 112 edges.add(edge); 113 114 // Hide ourself 115 context.getGraphNode().setHidden(true); 116 } 117 118 protected Node<MavenContext> findIncludedNode(Node<MavenContext> root, String artifactId) { 119 List<Node<MavenContext>> nodes = root.getBreadthFirstList(); 120 for (Node<MavenContext> node : nodes) { 121 MavenContext context = node.getObject(); 122 State state = context.getState(); 123 String artifactIdentifier = context.getArtifactIdentifier(); 124 if (state == State.INCLUDED && artifactIdentifier.equals(artifactId)) { 125 return node; 126 } 127 } 128 throw new GraphException("Inconsistent tree. Can't locate " + artifactId); 129 } 130 }