Java tutorial
/** * 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; /** * <p> * Various helper methods for working with trees. * </p> * * @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<MavenContext> copy(Node<MavenContext> node) { Node<MavenContext> newNode = new Node<MavenContext>(); MavenContext newMavenContext = copyWithoutEdges(node.getObject()); newNode.setObject(newMavenContext); for (Node<MavenContext> child : node.getChildren()) { Node<MavenContext> 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<MavenContext> findRequiredIncludedNode(Node<MavenContext> root, String artifactId) { List<Node<MavenContext>> nodes = root.getBreadthFirstList(); for (Node<MavenContext> 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); } /** * <p> * Logic for including nodes in a tree only if they match a filter. * </p> * * <p> * Any node where <code>filter.isMatch()</code> returns false, is hidden from view. * </p> * * <p> * The root node is never hidden, even if <code>filter.isMatch()</code> returns false on the root node. * </p> * * <p> * Any node where <code>filter.isMatch()</code> returns true, is displayed. Additionally, any nodes in the path from * that node back to the root node are displayed. * </p> * * @param node * @param filter */ public void include(Node<MavenContext> node, NodeFilter<MavenContext> 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<MavenContext> child : node.getChildren()) { include(child, filter); } } /** * <p> * Logic for excluding nodes from a tree if they match a filter. * </p> * * <p> * Any node where <code>filter.isMatch()</code> returns true, is hidden from view. The entire sub-tree rooted at * that node is also hidden from view. * </p> * * <p> * The root node is never hidden, even if <code>filter.isMatch()</code> returns true on the root node. * </p> * * <p> * Any node where <code>filter.isMatch()</code> returns false, is left untouched. * </p> * * @param node * @param filter */ public void exclude(Node<MavenContext> node, NodeFilter<MavenContext> filter) { if (filter.isMatch(node) && !node.isRoot()) { logger.debug("e:hiding {}", node.getObject().getArtifactIdentifier()); hideTree(node); } for (Node<MavenContext> child : node.getChildren()) { exclude(child, filter); } } /** * <p> * Display every node in the path from this node back to the root. * </p> */ public void showPath(Node<MavenContext> node) { Node<MavenContext>[] path = node.getPath(); List<Node<MavenContext>> pathList = Arrays.asList(path); Collections.reverse(pathList); for (Node<MavenContext> pathNode : pathList) { show(pathNode); } } /** * <p> * Hide this node, and every node in the sub-tree below this node. * </p> * * @param node */ public void hideTree(Node<MavenContext> node) { hide(node); for (Node<MavenContext> child : node.getChildren()) { hideTree(child); } } /** * <p> * Show this node, and every node in the sub-tree below this node. * </p> * * @param node */ public void showTree(Node<MavenContext> node) { show(node); for (Node<MavenContext> child : node.getChildren()) { showTree(child); } } /** * <p> * Convenience method setting the hidden flag to false on the graph node contained inside the * <code>MavenContext</code>. * <p> * * @param node */ public void show(Node<MavenContext> 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); } } /** * <p> * Convenience method setting the hidden flag to true on the graph node contained inside the * <code>MavenContext</code>. * <p> * * @param node */ public void hide(Node<MavenContext> 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); } } /** * <p> * Logic for altering a tree via removal of nodes that do not match the filter criteria. * <p> * * <p> * 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. * </p> * * @param node */ public <T> void prune(Node<T> node, NodeFilter<T> 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<T> child : node.getChildren()) { prune(child, filter); } } } /** * <p> * Convenience method for generating a new MavenContext object from a <code>GrapNode</code> and a * <code>DependencyNode</code>. * </p> * * @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; } /** * <p> * Given a Maven <code>DependencyNode</code> tree return a type safe <code>Node</code> tree. * </p> * * @param dependencyNode */ public Node<MavenContext> getTree(DependencyNode dependencyNode) { GraphNode gn = getGraphNode(dependencyNode); MavenContext context = getMavenContext(gn, dependencyNode); Node<MavenContext> node = new Node<MavenContext>(context); @SuppressWarnings("unchecked") List<DependencyNode> children = dependencyNode.getChildren(); for (DependencyNode child : children) { node.add(getTree(child)); } return node; } /** * <p> * Return true, if and only if, the two artifacts are an exact match of each other. * </p> * * <p> * More precisely, return true if [groupId]:[artfifactId]:[type]:[classifier]:[version] are an exact match, false * otherwise. * </p> * * @param a1 * @param a2 */ public boolean equals(Artifact a1, Artifact a2) { String id1 = getArtifactId(a1); String id2 = getArtifactId(a2); return id1.equals(id2); } /** * <p> * Return true, if and only if, the two artifacts are the same except for version. * </p> * * <p> * More precisely, return true if [groupId]:[artfifactId]:[type]:[classifier] are an exact match, false otherwise * </p> * * @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<Node<MavenContext>> getNodeList(Node<MavenContext> node, State... states) { Assert.notNull(states, "states are required"); List<Node<MavenContext>> contexts = new ArrayList<Node<MavenContext>>(); for (Node<MavenContext> element : node.getBreadthFirstList()) { MavenContext context = element.getObject(); State elementState = context.getState(); if (isMatch(elementState, states)) { contexts.add(element); } } return contexts; } public List<MavenContext> getList(Node<MavenContext> node, State... states) { Assert.notNull(states, "states are required"); List<MavenContext> contexts = new ArrayList<MavenContext>(); for (Node<MavenContext> element : node.getBreadthFirstList()) { MavenContext context = element.getObject(); State elementState = context.getState(); if (isMatch(elementState, states)) { contexts.add(context); } } return contexts; } public Map<String, MavenContext> getMap(List<MavenContext> contexts) { Map<String, MavenContext> map = new HashMap<String, MavenContext>(); for (MavenContext context : contexts) { map.put(context.getArtifactIdentifier(), context); } return map; } public Map<String, MavenContext> getPartialIdMap(List<MavenContext> contexts) { Map<String, MavenContext> map = new HashMap<String, MavenContext>(); for (MavenContext context : contexts) { map.put(getPartialArtifactId(context.getArtifact()), context); } return map; } protected <T> void show(String label, List<T> 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<GraphNode> getGraphNodes(Node<MavenContext> node) { List<GraphNode> nodes = new ArrayList<GraphNode>(); List<Node<MavenContext>> treeNodes = node.getBreadthFirstList(); for (Node<MavenContext> treeNode : treeNodes) { nodes.add(treeNode.getObject().getGraphNode()); } return nodes; } public List<Edge> getEdges(Node<MavenContext> node) { List<Edge> edges = new ArrayList<Edge>(); List<Node<MavenContext>> list = node.getBreadthFirstList(); for (Node<MavenContext> 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<String> labelTokens = new ArrayList<String>(); 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<String> 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<GraphNode> nodes, List<Edge> 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<Edge> 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<GraphNode> 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<Artifact> collector = new VersionFreeArtifactTokenCollector(); return toIdString(collector.getTokens(a)); } /** * [groupId]:[artifactId]:[type]:[classifier]:[version] */ public static String getArtifactId(Artifact a) { TokenCollector<Artifact> 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); } }