org.kuali.maven.plugins.graph.tree.TreeHelper Maven / Gradle / Ivy
Show all versions of graph-maven-plugin Show documentation
/**
* Copyright 2011-2012 The Kuali Foundation
*
* Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
*
* 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.
*/
package org.kuali.maven.plugins.graph.tree;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.shared.dependency.tree.DependencyNode;
import org.codehaus.plexus.util.StringUtils;
import org.kuali.maven.plugins.graph.collector.ArtifactIdTokenCollector;
import org.kuali.maven.plugins.graph.collector.TokenCollector;
import org.kuali.maven.plugins.graph.collector.VersionFreeArtifactTokenCollector;
import org.kuali.maven.plugins.graph.dot.GraphHelper;
import org.kuali.maven.plugins.graph.filter.NodeFilter;
import org.kuali.maven.plugins.graph.pojo.Edge;
import org.kuali.maven.plugins.graph.pojo.GraphNode;
import org.kuali.maven.plugins.graph.pojo.MavenContext;
import org.kuali.maven.plugins.graph.pojo.Scope;
import org.kuali.maven.plugins.graph.pojo.State;
import org.kuali.maven.plugins.graph.util.Counter;
import org.kuali.maven.plugins.graph.util.Helper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
/**
*
* Various helper methods for working with trees.
*
*
* @author jeffcaddel
*/
public class TreeHelper {
private static final Logger logger = LoggerFactory.getLogger(TreeHelper.class);
public static final String ROOT_FILL_COLOR = "#dddddd";
public static final String OPTIONAL = "optional";
public static final String REQUIRED = "required";
Counter counter = new Counter();
GraphHelper graphHelper = new GraphHelper();
public Node copy(Node node) {
Node newNode = new Node();
MavenContext newMavenContext = copyWithoutEdges(node.getObject());
newNode.setObject(newMavenContext);
for (Node child : node.getChildren()) {
Node newChild = copy(child);
newNode.add(newChild);
}
return newNode;
}
public MavenContext copyWithoutEdges(MavenContext context) {
MavenContext copy = copy(context);
copy.getGraphNode().setEdges(null);
return copy;
}
public MavenContext copy(MavenContext context) {
GraphNode newGraphNode = Helper.copyProperties(GraphNode.class, context.getGraphNode());
MavenContext newContext = Helper.copyProperties(MavenContext.class, context);
newContext.setGraphNode(newGraphNode);
return newContext;
}
public static Node findRequiredIncludedNode(Node root, String artifactId) {
List> nodes = root.getBreadthFirstList();
for (Node node : nodes) {
MavenContext context = node.getObject();
State state = context.getState();
String artifactIdentifier = context.getArtifactIdentifier();
boolean correctState = state == State.INCLUDED;
boolean correctArtifact = artifactId.equals(artifactIdentifier);
if (correctState && correctArtifact) {
return node;
}
}
throw new IllegalStateException("Can't locate " + artifactId);
}
/**
*
* Logic for including nodes in a tree only if they match a filter.
*
*
*
* Any node where filter.isMatch()
returns false, is hidden from view.
*
*
*
* The root node is never hidden, even if filter.isMatch()
returns false on the root node.
*
*
*
* Any node where filter.isMatch()
returns true, is displayed. Additionally, any nodes in the path from
* that node back to the root node are displayed.
*
*
* @param node
* @param filter
*/
public void include(Node node, NodeFilter filter) {
if (!filter.isMatch(node) && !node.isRoot()) {
hide(node);
logger.debug("i:hiding {}", node.getObject().getArtifactIdentifier());
} else {
logger.debug("i:showing path {}", node.getObject().getArtifactIdentifier());
showPath(node);
}
for (Node child : node.getChildren()) {
include(child, filter);
}
}
/**
*
* Logic for excluding nodes from a tree if they match a filter.
*
*
*
* Any node where filter.isMatch()
returns true, is hidden from view. The entire sub-tree rooted at
* that node is also hidden from view.
*
*
*
* The root node is never hidden, even if filter.isMatch()
returns true on the root node.
*
*
*
* Any node where filter.isMatch()
returns false, is left untouched.
*
*
* @param node
* @param filter
*/
public void exclude(Node node, NodeFilter filter) {
if (filter.isMatch(node) && !node.isRoot()) {
logger.debug("e:hiding {}", node.getObject().getArtifactIdentifier());
hideTree(node);
}
for (Node child : node.getChildren()) {
exclude(child, filter);
}
}
/**
*
* Display every node in the path from this node back to the root.
*
*/
public void showPath(Node node) {
Node[] path = node.getPath();
List> pathList = Arrays.asList(path);
Collections.reverse(pathList);
for (Node pathNode : pathList) {
show(pathNode);
}
}
/**
*
* Hide this node, and every node in the sub-tree below this node.
*
*
* @param node
*/
public void hideTree(Node node) {
hide(node);
for (Node child : node.getChildren()) {
hideTree(child);
}
}
/**
*
* Show this node, and every node in the sub-tree below this node.
*
*
* @param node
*/
public void showTree(Node node) {
show(node);
for (Node child : node.getChildren()) {
showTree(child);
}
}
/**
*
* Convenience method setting the hidden flag to false on the graph node contained inside the
* MavenContext
.
*
*
* @param node
*/
public void show(Node node) {
MavenContext context = node.getObject();
GraphNode gn = context.getGraphNode();
if (gn.isHidden()) {
logger.debug("showing node {}: {}", lpad(context.getId(), 4), context.getArtifactIdentifier());
gn.setHidden(false);
}
}
/**
*
* Convenience method setting the hidden flag to true on the graph node contained inside the
* MavenContext
.
*
*
* @param node
*/
public void hide(Node node) {
MavenContext context = node.getObject();
GraphNode gn = node.getObject().getGraphNode();
if (!gn.isHidden()) {
logger.debug(" hiding node {}: {}", lpad(context.getId(), 4), context.getArtifactIdentifier());
gn.setHidden(true);
}
}
/**
*
* Logic for altering a tree via removal of nodes that do not match the filter criteria.
*
*
*
* If the root node does not match the filter criteria, all of it's children are removed. Otherwise, the node and
* all of its children are both removed.
*
*
* @param node
*/
public void prune(Node node, NodeFilter filter) {
if (!filter.isMatch(node)) {
if (node.isRoot()) {
logger.debug("removing all children from root");
node.removeAllChildren();
} else {
logger.debug("removing node at level={}", node.getLevel());
node.removeFromParent();
}
} else {
for (Node child : node.getChildren()) {
prune(child, filter);
}
}
}
/**
*
* Convenience method for generating a new MavenContext object from a GrapNode
and a
* DependencyNode
.
*
*
* @param gn
* @param dn
*/
protected MavenContext getMavenContext(GraphNode gn, DependencyNode dn) {
int id = gn.getId();
String artifactIdentifier = getArtifactId(dn.getArtifact());
MavenContext context = new MavenContext();
context.setId(id);
context.setArtifactIdentifier(artifactIdentifier);
context.setArtifact(dn.getArtifact());
context.setGraphNode(gn);
context.setDependencyNode(dn);
context.setState(State.getState(dn.getState()));
context.setOptional(dn.getArtifact().isOptional());
return context;
}
/**
*
* Given a Maven DependencyNode
tree return a type safe Node
tree.
*
*
* @param dependencyNode
*/
public Node getTree(DependencyNode dependencyNode) {
GraphNode gn = getGraphNode(dependencyNode);
MavenContext context = getMavenContext(gn, dependencyNode);
Node node = new Node(context);
@SuppressWarnings("unchecked")
List children = dependencyNode.getChildren();
for (DependencyNode child : children) {
node.add(getTree(child));
}
return node;
}
/**
*
* Return true, if and only if, the two artifacts are an exact match of each other.
*
*
*
* More precisely, return true if [groupId]:[artfifactId]:[type]:[classifier]:[version] are an exact match, false
* otherwise.
*
*
* @param a1
* @param a2
*/
public boolean equals(Artifact a1, Artifact a2) {
String id1 = getArtifactId(a1);
String id2 = getArtifactId(a2);
return id1.equals(id2);
}
/**
*
* Return true, if and only if, the two artifacts are the same except for version.
*
*
*
* More precisely, return true if [groupId]:[artfifactId]:[type]:[classifier] are an exact match, false otherwise
*
*
* @param a1
* @param a2
*/
public boolean similar(Artifact a1, Artifact a2) {
String n1 = getPartialArtifactId(a1);
String n2 = getPartialArtifactId(a2);
return n1.equals(n2);
}
protected boolean isMatch(State state, State[] states) {
for (State s : states) {
if (state == s) {
return true;
}
}
return false;
}
public List> getNodeList(Node node, State... states) {
Assert.notNull(states, "states are required");
List> contexts = new ArrayList>();
for (Node element : node.getBreadthFirstList()) {
MavenContext context = element.getObject();
State elementState = context.getState();
if (isMatch(elementState, states)) {
contexts.add(element);
}
}
return contexts;
}
public List getList(Node node, State... states) {
Assert.notNull(states, "states are required");
List contexts = new ArrayList();
for (Node element : node.getBreadthFirstList()) {
MavenContext context = element.getObject();
State elementState = context.getState();
if (isMatch(elementState, states)) {
contexts.add(context);
}
}
return contexts;
}
public Map getMap(List contexts) {
Map map = new HashMap();
for (MavenContext context : contexts) {
map.put(context.getArtifactIdentifier(), context);
}
return map;
}
public Map getPartialIdMap(List contexts) {
Map map = new HashMap();
for (MavenContext context : contexts) {
map.put(getPartialArtifactId(context.getArtifact()), context);
}
return map;
}
protected void show(String label, List list) {
logger.info(label);
for (T element : list) {
logger.info(element.toString());
}
}
protected GraphNode getGraphNode(DependencyNode dn) {
Artifact a = dn.getArtifact();
GraphNode n = new GraphNode();
n.setId(counter.increment());
n.setLabel(graphHelper.getLabel(a));
String fillcolor = dn.getParent() == null ? ROOT_FILL_COLOR : n.getFillcolor();
n.setFillcolor(fillcolor);
return n;
}
public List getGraphNodes(Node node) {
List nodes = new ArrayList();
List> treeNodes = node.getBreadthFirstList();
for (Node treeNode : treeNodes) {
nodes.add(treeNode.getObject().getGraphNode());
}
return nodes;
}
public List getEdges(Node node) {
List edges = new ArrayList();
List> list = node.getBreadthFirstList();
for (Node element : list) {
MavenContext context = element.getObject();
GraphNode graphNode = context.getGraphNode();
Helper.addAll(edges, graphNode.getEdges());
}
return edges;
}
// Omit normal info from the label
public static String getRelationshipLabel(Scope scope, boolean optional, State state) {
List labelTokens = new ArrayList();
if (!Scope.DEFAULT_SCOPE.equals(scope)) {
labelTokens.add(scope.name().toLowerCase());
}
if (optional) {
// Using a dotted line for optional makes it clear enough
// labelTokens.add(OPTIONAL);
}
if (!State.INCLUDED.equals(state)) {
labelTokens.add(state.getValue());
}
return toIdString(labelTokens);
}
public static String toIdString(List strings) {
return toIdString(Helper.toArray(strings));
}
public static String toIdString(String... strings) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < strings.length; i++) {
if (i != 0) {
sb.append(":");
}
sb.append(Helper.toEmpty(strings[i]));
}
return sb.toString();
}
public String getProperty(Object bean, String name) {
try {
return BeanUtils.getProperty(bean, name);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
public void copyProperty(Object bean, String name, Object value) {
try {
BeanUtils.copyProperty(bean, name, value);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
public void show(List nodes, List edges) {
int nodeCount = nodes.size();
int edgeCount = edges.size();
int nodeCountShown = getNodeCount(nodes);
int edgeCountShown = getEdgeCount(edges);
int nodeCountHidden = nodeCount - nodeCountShown;
int edgeCountHidden = edgeCount - edgeCountShown;
logger.info("Generated " + nodes.size() + " graph nodes and " + edges.size() + " edges");
logger.info("Showing " + nodeCountShown + " nodes and " + edgeCountShown + " edges");
logger.info("Hiding " + nodeCountHidden + " nodes and " + edgeCountHidden + " edges");
}
protected int getEdgeCount(List edges) {
int count = 0;
for (Edge edge : edges) {
GraphNode parent = edge.getParent();
GraphNode child = edge.getChild();
boolean hidden = parent.isHidden() || child.isHidden();
count = hidden ? count : ++count;
}
return count;
}
protected int getNodeCount(List nodes) {
int count = 0;
for (GraphNode node : nodes) {
count = node.isHidden() ? count : ++count;
}
return count;
}
/**
* [groupId]:[artifactId]:[type]:[classifier]
*/
public static String getPartialArtifactId(Artifact a) {
TokenCollector collector = new VersionFreeArtifactTokenCollector();
return toIdString(collector.getTokens(a));
}
/**
* [groupId]:[artifactId]:[type]:[classifier]:[version]
*/
public static String getArtifactId(Artifact a) {
TokenCollector collector = new ArtifactIdTokenCollector();
return toIdString(collector.getTokens(a));
}
protected String lpad(Object o, int count) {
String s = Helper.toEmpty(o);
return StringUtils.leftPad(s, count);
}
}