package edu.uci.ics.jung.graph;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import edu.uci.ics.jung.graph.util.EdgeType;
import edu.uci.ics.jung.graph.util.Pair;
import edu.uci.ics.jung.graph.util.TreeUtils;
/**
* An implementation of <code>Forest<V,E></code> that delegates to a specified
* <code>DirectedGraph</code> instance.
*
* @author Tom Nelson
*
* @param <V>
* the vertex type
* @param <E>
* the edge type
*/
@SuppressWarnings("serial")
public class DelegateForest<V, E> extends GraphDecorator<V, E>
implements Forest<V, E> {
/**
* Creates an instance backed by a new {@code DirectedSparseGraph} instance.
*/
public DelegateForest() {
this(new DirectedSparseGraph<V, E>());
}
/**
* Creates an instance backed by the input {@code DirectedGraph} i
*/
public DelegateForest(DirectedGraph<V, E> delegate) {
super(delegate);
}
/**
* 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) {
if (delegate.getVertices().contains(v1) == false) {
throw new IllegalArgumentException(
"Tree must already contain " + v1);
}
if (delegate.getVertices().contains(v2)) {
throw new IllegalArgumentException(
"Tree must not already contain " + v2);
}
return delegate.addEdge(e, v1, v2, edgeType);
}
/**
* Add vertex as a root of the tree
*
* @param vertex
* the tree root to add
* @return true if this call mutates the underlying graph
* @see edu.uci.ics.jung.graph.Graph#addVertex(java.lang.Object)
*/
@Override
public boolean addVertex(V vertex) {
setRoot(vertex);
return true;
}
/**
* Removes <code>edge</code> from this tree, and the subtree rooted at the
* child vertex incident to <code>edge</code>. (The subtree is removed to
* ensure that the tree in which the edge was found is still a tree rather
* than a forest. To change this behavior so that the
*
* @param edge
* the edge to remove
* @return <code>true</code> iff the tree was modified
* @see edu.uci.ics.jung.graph.Hypergraph#removeEdge(java.lang.Object)
*/
@Override
public boolean removeEdge(E edge) {
return removeEdge(edge, true);
}
/**
* Removes <code>edge</code> from this tree. If <code>remove_subtree</code>
* is <code>true</code>, removes the subtree rooted at the child vertex
* incident to <code>edge</code>. Otherwise, leaves the subtree intact as a
* new component tree of this forest.
*
* @param edge
* the edge to remove
* @param remove_subtree
* if <code>true</code>, remove the subtree
* @return <code>true</code> iff the tree was modified
*/
public boolean removeEdge(E edge, boolean remove_subtree) {
if (!delegate.containsEdge(edge)) {
return false;
}
V child = getDest(edge);
if (remove_subtree) {
return removeVertex(child);
}
delegate.removeEdge(edge);
return false;
}
/**
* Removes <code>vertex</code> from this tree, and the subtree rooted at
* <code>vertex</code>.
*
* @param vertex
* the vertex to remove
* @return <code>true</code> iff the tree was modified
* @see edu.uci.ics.jung.graph.Hypergraph#removeVertex(java.lang.Object)
*/
@Override
public boolean removeVertex(V vertex) {
return removeVertex(vertex, true);
}
/**
* Removes <code>vertex</code> from this tree. If
* <code>remove_subtrees</code> is <code>true</code>, removes the subtrees
* rooted at the children of <code>vertex</code>. Otherwise, leaves these
* subtrees intact as new component trees of this forest.
*
* @param vertex
* the vertex to remove
* @param remove_subtrees
* if <code>true</code>, remove the subtrees rooted at
* <code>vertex</code>'s children
* @return <code>true</code> iff the tree was modified
*/
public boolean removeVertex(V vertex, boolean remove_subtrees) {
if (!delegate.containsVertex(vertex)) {
return false;
}
if (remove_subtrees) {
for (V v : new ArrayList<V>(delegate.getSuccessors(vertex))) {
removeVertex(v, true);
}
}
return delegate.removeVertex(vertex);
}
/**
* returns an ordered list of the nodes beginning at the root and ending at
* the passed child node, including all intermediate nodes.
*
* @param child
* 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 child) {
if (!delegate.containsVertex(child)) {
return null;
}
List<V> list = new ArrayList<V>();
list.add(child);
V parent = getParent(child);
while (parent != null) {
list.add(list.size(), parent);
parent = getParent(parent);
}
return list;
}
@Override
public V getParent(V child) {
if (!delegate.containsVertex(child)) {
return null;
}
Collection<V> parents = delegate.getPredecessors(child);
if (parents.size() > 0) {
return parents.iterator().next();
}
return null;
}
/**
* getter for the root of the tree returns null, as this tree has >1 roots
*
* @return the root
*/
public V getRoot() {
return null;
}
/**
* adds root as a root of the tree
*
* @param root
* the initial tree root
*/
public void setRoot(V root) {
delegate.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.
*/
public int getDepth(V v) {
return getPath(v).size();
}
/**
* computes and returns the height of the tree
*
* @return the height
*/
public int getHeight() {
int height = 0;
for (V v : getVertices()) {
height = Math.max(height, getDepth(v));
}
return height;
}
/**
* computes and returns whether the passed node is neither the root, nor a
* leaf node.
*
* @return <code>true</code> if <code>v</code> is neither a leaf nor a root
*/
public boolean isInternal(V v) {
return isLeaf(v) == false && isRoot(v) == false;
}
/**
* Returns true if {@code v} has no child nodes.
*/
public boolean isLeaf(V v) {
return getChildren(v).size() == 0;
}
/**
* Returns the children of {@code v}.
*/
@Override
public Collection<V> getChildren(V v) {
return delegate.getSuccessors(v);
}
/**
* Returns true if {@code v} has no parent node.
*/
public boolean isRoot(V v) {
return getParent(v) == null;
}
@Override
public int getIncidentCount(E edge) {
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());
}
/**
* Returns the root of each tree of this forest as a {@code Collection}.
*/
public Collection<V> getRoots() {
Collection<V> roots = new HashSet<V>();
for (V v : delegate.getVertices()) {
if (delegate.getPredecessorCount(v) == 0) {
roots.add(v);
}
}
return roots;
}
@Override
public Collection<Tree<V, E>> getTrees() {
Collection<Tree<V, E>> trees = new HashSet<Tree<V, E>>();
for (V v : getRoots()) {
Tree<V, E> tree = new DelegateTree<V, E>();
tree.addVertex(v);
TreeUtils.growSubTree(this, tree, v);
trees.add(tree);
}
return trees;
}
/**
* Adds {@code tree} to this graph as an element of this forest.
*
* @param tree
* the tree to add to this forest as a component
*/
public void addTree(Tree<V, E> tree) {
TreeUtils.addSubTree(this, tree, null, null);
}
@Override
public int getChildCount(V vertex) {
return delegate.getSuccessorCount(vertex);
}
@Override
public Collection<E> getChildEdges(V vertex) {
return delegate.getOutEdges(vertex);
}
@Override
public E getParentEdge(V vertex) {
if (isRoot(vertex)) {
return null;
}
return delegate.getInEdges(vertex).iterator().next();
}
}