package edu.uci.ics.jung.graph; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.collections15.Factory; import edu.uci.ics.jung.graph.util.EdgeType; import edu.uci.ics.jung.graph.util.Pair; /** * An implementation of <code>Tree<V,E></code> that delegates to a specified * instance of <code>DirectedGraph<V,E></code>. * * @author Tom Nelson * * @param <V> * the vertex type * @param <E> * the edge type */ @SuppressWarnings("serial") public class DelegateTree<V, E> extends GraphDecorator<V, E> implements Tree<V, E> { /** * Returns a {@code Factory} that creates an instance of this graph type. * * @param <V> * the vertex type for the graph factory * @param <E> * the edge type for the graph factory */ public static final <V, E> Factory<Tree<V, E>> getFactory() { return new Factory<Tree<V, E>>() { @Override public Tree<V, E> create() { return new DelegateTree<V, E>( new DirectedSparseMultigraph<V, E>()); } }; } protected V root; protected Map<V, Integer> vertex_depths; /** * Creates an instance. */ public DelegateTree() { this(DirectedSparseMultigraph.<V, E> getFactory()); } /** * create an instance with passed values. * * @param graphFactory * must create a DirectedGraph to use as a delegate */ public DelegateTree(Factory<DirectedGraph<V, E>> graphFactory) { super(graphFactory.create()); this.vertex_depths = new HashMap<V, Integer>(); } /** * Creates a new <code>DelegateTree</code> which delegates to * <code>graph</code>. Assumes that <code>graph</code> is already a tree; if * it's not, future behavior of this instance is undefined. */ public DelegateTree(DirectedGraph<V, E> graph) { super(graph); // if(graph.getVertexCount() != 0) throw new IllegalArgumentException( // "Passed DirectedGraph must be empty"); this.vertex_depths = new HashMap<V, Integer>(); } /** * Add an edge to the tree, connecting v1, the parent and v2, the child. v1 * must already exist in the tree, and v2 must not already exist the passed * edge must be unique in the tree. Passing an edgeType other than * EdgeType.DIRECTED may cause an illegal argument exception in the delegate * graph. * * @param e * a unique edge to add * @param v1 * the parent node * @param v2 * the child node * @param edgeType * should be EdgeType.DIRECTED * @return true if this call mutates the underlying graph * @see edu.uci.ics.jung.graph.Graph#addEdge(java.lang.Object, * java.lang.Object, java.lang.Object, * edu.uci.ics.jung.graph.util.EdgeType) */ @Override public boolean addEdge(E e, V v1, V v2, EdgeType edgeType) { return addChild(e, v1, v2, edgeType); } /** * Add an edge to the tree, connecting v1, the parent and v2, the child. v1 * must already exist in the tree, and v2 must not already exist the passed * edge must be unique in the tree. * * @param e * a unique edge to add * @param v1 * the parent node * @param v2 * the child node * @return true if this call mutates the underlying graph * @see edu.uci.ics.jung.graph.Graph#addEdge(java.lang.Object, * java.lang.Object, java.lang.Object) */ @Override public boolean addEdge(E e, V v1, V v2) { return addChild(e, v1, v2); } /** * Will set the root of the Tree, only if the Tree is empty and the root is * currently unset. * * @param vertex * the tree root to set * @return true if this call mutates the underlying graph * @see edu.uci.ics.jung.graph.Graph#addVertex(java.lang.Object) * @throws UnsupportedOperationException * if the root was previously set */ @Override public boolean addVertex(V vertex) { if (root == null) { this.root = vertex; vertex_depths.put(vertex, 0); return delegate.addVertex(vertex); } throw new UnsupportedOperationException( "Unless you are setting the root, use addChild()"); } /** * remove the passed node, and all nodes that are descendants of the passed * node. * * @param vertex * @return <code>true</code> iff the tree was modified * @see edu.uci.ics.jung.graph.Graph#removeVertex(java.lang.Object) */ @Override public boolean removeVertex(V vertex) { if (!delegate.containsVertex(vertex)) { return false; } for (V v : getChildren(vertex)) { removeVertex(v); vertex_depths.remove(v); } // recalculate height vertex_depths.remove(vertex); return delegate.removeVertex(vertex); } /** * add the passed child node as a child of parent. parent must exist in the * tree, and child must not already exist. * * @param edge * the unique edge to connect the parent and child nodes * @param parent * the existing parent to attach the child to * @param child * the new child to add to the tree as a child of parent * @param edgeType * must be EdgeType.DIRECTED or the underlying graph may throw an * exception * @return whether this call mutates the underlying graph */ public boolean addChild(E edge, V parent, V child, EdgeType edgeType) { Collection<V> vertices = delegate.getVertices(); if (vertices.contains(parent) == false) { throw new IllegalArgumentException( "Tree must already contain parent " + parent); } if (vertices.contains(child)) { throw new IllegalArgumentException( "Tree must not already contain child " + child); } vertex_depths.put(child, vertex_depths.get(parent) + 1); return delegate.addEdge(edge, parent, child, edgeType); } /** * add the passed child node as a child of parent. parent must exist in the * tree, and child must not already exist * * @param edge * the unique edge to connect the parent and child nodes * @param parent * the existing parent to attach the child to * @param child * the new child to add to the tree as a child of parent * @return whether this call mutates the underlying graph */ public boolean addChild(E edge, V parent, V child) { Collection<V> vertices = delegate.getVertices(); if (vertices.contains(parent) == false) { throw new IllegalArgumentException( "Tree must already contain parent " + parent); } if (vertices.contains(child)) { throw new IllegalArgumentException( "Tree must not already contain child " + child); } vertex_depths.put(child, vertex_depths.get(parent) + 1); return delegate.addEdge(edge, parent, child); } /** * get the number of children of the passed parent node */ @Override public int getChildCount(V parent) { if (!delegate.containsVertex(parent)) { return 0; } return getChildren(parent).size(); } /** * get the immediate children nodes of the passed parent */ @Override public Collection<V> getChildren(V parent) { if (!delegate.containsVertex(parent)) { return null; } return delegate.getSuccessors(parent); } /** * get the single parent node of the passed child */ @Override public V getParent(V child) { if (!delegate.containsVertex(child)) { return null; } Collection<V> predecessors = delegate.getPredecessors(child); if (predecessors.size() == 0) { return null; } return predecessors.iterator().next(); } /** * Returns an ordered list of the nodes beginning at the root and ending at * {@code vertex}, including all intermediate nodes. * * @param vertex * the last node in the path from the root * @return an ordered list of the nodes from root to child */ public List<V> getPath(V vertex) { if (!delegate.containsVertex(vertex)) { return null; } List<V> vertex_to_root = new ArrayList<V>(); vertex_to_root.add(vertex); V parent = getParent(vertex); while (parent != null) { vertex_to_root.add(parent); parent = getParent(parent); } // reverse list so that it goes from root to child List<V> root_to_vertex = new ArrayList<V>(vertex_to_root.size()); for (int i = vertex_to_root.size() - 1; i >= 0; i--) { root_to_vertex.add(vertex_to_root.get(i)); } return root_to_vertex; } /** * getter for the root of the tree * * @return the root */ @Override public V getRoot() { return root; } /** * sets the root to the passed value, only if the root is previously unset * * @param root * the initial tree root */ public void setRoot(V root) { addVertex(root); } /** * removes a node from the tree, causing all descendants of the removed node * also to be removed * * @param orphan * the node to remove * @return whether this call mutates the underlying graph */ public boolean removeChild(V orphan) { return removeVertex(orphan); } /** * computes and returns the depth of the tree from the root to the passed * vertex * * @param v * the node who's depth is computed * @return the depth to the passed node. */ @Override public int getDepth(V v) { return this.vertex_depths.get(v); } /** * Computes and returns the height of the tree. * * @return the height */ @Override public int getHeight() { int height = 0; for (V v : getVertices()) { height = Math.max(height, getDepth(v)); } return height; } /** * Returns <code>true</code> if <code>v</code> is neither a leaf nor the * root of this tree. * * @return <code>true</code> if <code>v</code> is neither a leaf nor the * root of this tree */ public boolean isInternal(V v) { if (!delegate.containsVertex(v)) { return false; } return isLeaf(v) == false && isRoot(v) == false; } /** * Returns <code>true</code> if the passed node has no children. * * @return <code>true</code> if the passed node has no children */ public boolean isLeaf(V v) { if (!delegate.containsVertex(v)) { return false; } return getChildren(v).size() == 0; } /** * computes whether the passed node is a root node (has no children) */ public boolean isRoot(V v) { if (!delegate.containsVertex(v)) { return false; } return getParent(v) == null; } @Override public int getIncidentCount(E edge) { if (!delegate.containsEdge(edge)) { return 0; } // all edges in a tree connect exactly 2 vertices return 2; } @SuppressWarnings("unchecked") @Override public boolean addEdge(E edge, Collection<? extends V> vertices) { Pair<V> pair = null; if (vertices instanceof Pair) { pair = (Pair<V>) vertices; } else { pair = new Pair<V>(vertices); } return addEdge(edge, pair.getFirst(), pair.getSecond()); } @Override public String toString() { return "Tree of " + delegate.toString(); } @Override public Collection<Tree<V, E>> getTrees() { return Collections.<Tree<V, E>> singleton(this); } @Override public Collection<E> getChildEdges(V vertex) { return getOutEdges(vertex); } @Override public E getParentEdge(V vertex) { return getInEdges(vertex).iterator().next(); } }