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 }