/*
* Created on May 8, 2008
*
* Copyright (c) 2008, the JUNG Project and the Regents of the University
* of California
* All rights reserved.
*
* This software is open-source under the BSD license; see either
* "license.txt" or
* http://jung.sourceforge.net/license.txt for a description.
*/
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.CollectionUtils;
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</code> in which each vertex has
* <= k children. The value of 'k' is specified by the constructor
* parameter. A specific child (edge) can be retrieved directly by specifying the
* index at which the child is located. By default, new (child) vertices
* are added at the lowest index available, if no index is specified.
*
*/
@SuppressWarnings("serial")
public class OrderedKAryTree<V, E> extends AbstractTypedGraph<V, E> implements Tree<V, E>
{
protected Map<E, Pair<V>> edge_vpairs;
protected Map<V, VertexData> vertex_data;
protected int height;
protected V root;
protected int order;
/**
* 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 <V,E> Factory<DirectedGraph<V,E>> getFactory(final int order) {
return new Factory<DirectedGraph<V,E>> () {
public DirectedGraph<V,E> create() {
return new OrderedKAryTree<V,E>(order);
}
};
}
/**
* Creates a new instance with the specified order (maximum number of children).
*/
public OrderedKAryTree(int order)
{
super(EdgeType.DIRECTED);
this.order = order;
this.height = -1;
this.edge_vpairs = new HashMap<E, Pair<V>>();
this.vertex_data = new HashMap<V, VertexData>();
}
/**
* Returns the number of children that {@code vertex} has.
* @see edu.uci.ics.jung.graph.Tree#getChildCount(java.lang.Object)
*/
public int getChildCount(V vertex) {
if (!containsVertex(vertex))
return 0;
List<E> edges = vertex_data.get(vertex).child_edges;
if (edges == null)
return 0;
int count = 0;
for (E edge : edges)
count += edge == null ? 0 : 1;
return count;
}
/**
* Returns the child edge of the vertex at index <code>index</code>.
* @param vertex
* @param index
* @return the child edge of the vertex at index <code>index</code>
*/
public E getChildEdge(V vertex, int index)
{
if (!containsVertex(vertex))
return null;
List<E> edges = vertex_data.get(vertex).child_edges;
if (edges == null)
return null;
return edges.get(index);
}
/**
* @see edu.uci.ics.jung.graph.Tree#getChildEdges(java.lang.Object)
*/
public Collection<E> getChildEdges(V vertex)
{
if (!containsVertex(vertex))
return null;
List<E> edges = vertex_data.get(vertex).child_edges;
return edges == null ? Collections.<E>emptySet() :
CollectionUtils.unmodifiableCollection(edges);
}
/**
* Returns an ordered list of {@code vertex}'s child vertices.
* If there is no child in position i, then the list will contain
* {@code null} in position i. If {@code vertex} has no children
* then the empty set will be returned.
* @see edu.uci.ics.jung.graph.Tree#getChildren(java.lang.Object)
*/
public Collection<V> getChildren(V vertex)
{
if (!containsVertex(vertex))
return null;
List<E> edges = vertex_data.get(vertex).child_edges;
if (edges == null)
return Collections.emptySet();
Collection<V> children = new ArrayList<V>(order);
for (E edge : edges)
children.add(this.getOpposite(vertex, edge));
return CollectionUtils.unmodifiableCollection(children);
}
/**
* @see edu.uci.ics.jung.graph.Tree#getDepth(java.lang.Object)
* @return the depth of the vertex in this tree, or -1 if the vertex is
* not present in this tree
*/
public int getDepth(V vertex)
{
if (!containsVertex(vertex))
return -1;
return vertex_data.get(vertex).depth;
}
/**
* Returns the height of the tree, or -1 if the tree is empty.
* @see edu.uci.ics.jung.graph.Tree#getHeight()
*/
public int getHeight()
{
return height;
}
/**
* @see edu.uci.ics.jung.graph.Tree#getParent(java.lang.Object)
*/
public V getParent(V vertex)
{
if (!containsVertex(vertex))
return null;
else if (vertex.equals(root))
return null;
return edge_vpairs.get(vertex_data.get(vertex).parent_edge).getFirst();
}
/**
* @see edu.uci.ics.jung.graph.Tree#getParentEdge(java.lang.Object)
*/
public E getParentEdge(V vertex)
{
if (!containsVertex(vertex))
return null;
return vertex_data.get(vertex).parent_edge;
}
/**
* @see edu.uci.ics.jung.graph.Tree#getRoot()
*/
public V getRoot()
{
return root;
}
/**
* @see edu.uci.ics.jung.graph.Forest#getTrees()
*/
public Collection<Tree<V, E>> getTrees()
{
Collection<Tree<V, E>> forest = new ArrayList<Tree<V, E>>(1);
forest.add(this);
return forest;
}
/**
* Adds the specified {@code child} vertex and edge {@code e} to the graph
* with the specified parent vertex {@code parent}. If {@code index} is
* greater than or equal to 0, then the child is placed at position
* {@code index}; if it is less than 0, the child is placed at the lowest
* available position; if it is greater than or equal to the order of this
* tree, an exception is thrown.
*
* @see edu.uci.ics.jung.graph.Graph#addEdge(java.lang.Object, java.lang.Object, java.lang.Object)
*/
public boolean addEdge(E e, V parent, V child, int index)
{
if (e == null || child == null || parent == null)
throw new IllegalArgumentException("Inputs may not be null");
if (!containsVertex(parent))
throw new IllegalArgumentException("Tree must already " +
"include parent: " + parent);
if (containsVertex(child))
throw new IllegalArgumentException("Tree must not already " +
"include child: " + child);
if (parent.equals(child))
throw new IllegalArgumentException("Input vertices must be distinct");
if (index < 0 || index >= order)
throw new IllegalArgumentException("'index' must be in [0, [order-1]]");
Pair<V> endpoints = new Pair<V>(parent, child);
if (containsEdge(e))
if (!endpoints.equals(edge_vpairs.get(e)))
throw new IllegalArgumentException("Tree already includes edge" +
e + " with different endpoints " + edge_vpairs.get(e));
else
return false;
VertexData parent_data = vertex_data.get(parent);
List<E> outedges = parent_data.child_edges;
if (outedges == null)
outedges = new ArrayList<E>(this.order);
boolean edge_placed = false;
if (index >= 0)
if (outedges.get(index) != null)
throw new IllegalArgumentException("Parent " + parent +
" already has a child at index " + index + " in this tree");
else
outedges.set(index, e);
for (int i = 0; i < order; i++)
{
if (outedges.get(i) == null)
{
outedges.set(i, e);
edge_placed = true;
break;
}
}
if (!edge_placed)
throw new IllegalArgumentException("Parent " + parent + " already" +
" has " + order + " children in this tree");
// initialize VertexData for child; leave child's child_edges null for now
VertexData child_data = new VertexData(e, parent_data.depth + 1);
vertex_data.put(child, child_data);
height = child_data.depth > height ? child_data.depth : height;
edge_vpairs.put(e, endpoints);
return true;
}
/**
* @see edu.uci.ics.jung.graph.Graph#addEdge(java.lang.Object, java.lang.Object, java.lang.Object)
*/
@Override
public boolean addEdge(E e, V parent, V child)
{
return addEdge(e, parent, child, -1);
}
/**
* @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 edge_type)
{
this.validateEdgeType(edge_type);
return addEdge(e, v1, v2);
}
/**
* @see edu.uci.ics.jung.graph.Graph#getDest(java.lang.Object)
*/
public V getDest(E directed_edge)
{
if (!containsEdge(directed_edge))
return null;
return edge_vpairs.get(directed_edge).getSecond();
}
/**
* @see edu.uci.ics.jung.graph.Graph#getEndpoints(java.lang.Object)
*/
public Pair<V> getEndpoints(E edge)
{
if (!containsEdge(edge))
return null;
return edge_vpairs.get(edge);
}
/**
* @see edu.uci.ics.jung.graph.Graph#getInEdges(java.lang.Object)
*/
public Collection<E> getInEdges(V vertex)
{
if (!containsVertex(vertex))
return null;
else if (vertex.equals(root))
return Collections.emptySet();
else
return Collections.singleton(getParentEdge(vertex));
}
/**
* @see edu.uci.ics.jung.graph.Graph#getOpposite(java.lang.Object, java.lang.Object)
*/
@Override
public V getOpposite(V vertex, E edge)
{
if (!containsVertex(vertex) || !containsEdge(edge))
return null;
Pair<V> endpoints = edge_vpairs.get(edge);
V v1 = endpoints.getFirst();
V v2 = endpoints.getSecond();
return v1.equals(vertex) ? v2 : v1;
}
/**
* @see edu.uci.ics.jung.graph.Graph#getOutEdges(java.lang.Object)
*/
public Collection<E> getOutEdges(V vertex)
{
return getChildEdges(vertex);
}
/**
* @see edu.uci.ics.jung.graph.Graph#getPredecessorCount(java.lang.Object)
* @return 0 if <code>vertex</code> is the root, -1 if the vertex is
* not an element of this tree, and 1 otherwise
*/
@Override
public int getPredecessorCount(V vertex)
{
if (!containsVertex(vertex))
return -1;
return vertex.equals(root) ? 0 : 1;
}
/**
* @see edu.uci.ics.jung.graph.Graph#getPredecessors(java.lang.Object)
*/
public Collection<V> getPredecessors(V vertex)
{
if (!containsVertex(vertex))
return null;
if (vertex.equals(root))
return Collections.emptySet();
return Collections.singleton(getParent(vertex));
}
/**
* @see edu.uci.ics.jung.graph.Graph#getSource(java.lang.Object)
*/
public V getSource(E directed_edge)
{
if (!containsEdge(directed_edge))
return null;
return edge_vpairs.get(directed_edge).getSecond();
}
/**
* @see edu.uci.ics.jung.graph.Graph#getSuccessorCount(java.lang.Object)
*/
@Override
public int getSuccessorCount(V vertex)
{
return getChildCount(vertex);
}
/**
* @see edu.uci.ics.jung.graph.Graph#getSuccessors(java.lang.Object)
*/
public Collection<V> getSuccessors(V vertex)
{
return getChildren(vertex);
}
/**
* @see edu.uci.ics.jung.graph.Graph#inDegree(java.lang.Object)
*/
@Override
public int inDegree(V vertex)
{
if (!containsVertex(vertex))
return 0;
if (vertex.equals(root))
return 0;
return 1;
}
/**
* @see edu.uci.ics.jung.graph.Graph#isDest(java.lang.Object, java.lang.Object)
*/
public boolean isDest(V vertex, E edge)
{
if (!containsEdge(edge) || !containsVertex(vertex))
return false;
return edge_vpairs.get(edge).getSecond().equals(vertex);
}
/**
* Returns <code>true</code> if <code>vertex</code> is a leaf of this tree,
* i.e., if it has no children.
* @param vertex the vertex to be queried
* @return <code>true</code> if <code>outDegree(vertex)==0</code>
*/
public boolean isLeaf(V vertex)
{
if (!containsVertex(vertex))
return false;
return outDegree(vertex) == 0;
}
/**
* Returns true iff <code>v1</code> is the parent of <code>v2</code>.
* Note that if <code>v2</code> is the root and <code>v1</code> is <code>null</code>,
* this method returns <code>true</code>.
*
* @see edu.uci.ics.jung.graph.Graph#isPredecessor(java.lang.Object, java.lang.Object)
*/
@Override
public boolean isPredecessor(V v1, V v2)
{
if (!containsVertex(v2))
return false;
return getParent(v2).equals(v1);
}
/**
* Returns <code>true</code> if <code>vertex</code> is a leaf of this tree,
* i.e., if it has no children.
* @param vertex the vertex to be queried
* @return <code>true</code> if <code>outDegree(vertex)==0</code>
*/
public boolean isRoot(V vertex)
{
if (root == null)
return false;
return root.equals(vertex);
}
/**
* @see edu.uci.ics.jung.graph.Graph#isSource(java.lang.Object, java.lang.Object)
*/
public boolean isSource(V vertex, E edge)
{
if (!containsEdge(edge) || !containsVertex(vertex))
return false;
return edge_vpairs.get(edge).getFirst().equals(vertex);
}
/**
* @see edu.uci.ics.jung.graph.Graph#isSuccessor(java.lang.Object, java.lang.Object)
*/
@Override
public boolean isSuccessor(V v1, V v2)
{
if (!containsVertex(v2))
return false;
if (containsVertex(v1))
return getParent(v1).equals(v2);
return isLeaf(v2) && v1 == null;
}
/**
* @see edu.uci.ics.jung.graph.Graph#outDegree(java.lang.Object)
*/
@Override
public int outDegree(V vertex)
{
if (!containsVertex(vertex))
return 0;
List<E> out_edges = vertex_data.get(vertex).child_edges;
if (out_edges == null)
return 0;
int degree = 0;
for (E e : out_edges)
degree += (e == null) ? 0 : 1;
return degree;
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#addEdge(java.lang.Object, java.util.Collection)
*/
@Override
@SuppressWarnings("unchecked")
public boolean addEdge(E edge, Collection<? extends V> vertices, EdgeType edge_type)
{
if (edge == null || vertices == null)
throw new IllegalArgumentException("inputs may not be null");
if (vertices.size() != 2)
throw new IllegalArgumentException("'vertices' must contain " +
"exactly 2 distinct vertices");
this.validateEdgeType(edge_type);
Pair<V> endpoints;
if (vertices instanceof Pair)
endpoints = (Pair<V>)vertices;
else
endpoints = new Pair<V>(vertices);
V v1 = endpoints.getFirst();
V v2 = endpoints.getSecond();
if (v1.equals(v2))
throw new IllegalArgumentException("Input vertices must be distinct");
return addEdge(edge, v1, v2);
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#addVertex(java.lang.Object)
*/
public boolean addVertex(V vertex)
{
if(root == null)
{
this.root = vertex;
vertex_data.put(vertex, new VertexData(null, 0));
this.height = 0;
return true;
}
else
{
throw new UnsupportedOperationException("Unless you are setting " +
"the root, use addEdge() or addChild()");
}
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#isIncident(java.lang.Object, java.lang.Object)
*/
@Override
public boolean isIncident(V vertex, E edge)
{
if (!containsVertex(vertex) || !containsEdge(edge))
return false;
return edge_vpairs.get(edge).contains(vertex);
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#isNeighbor(java.lang.Object, java.lang.Object)
*/
@Override
public boolean isNeighbor(V v1, V v2)
{
if (!containsVertex(v1) || !containsVertex(v2))
return false;
return getNeighbors(v1).contains(v2);
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#containsEdge(java.lang.Object)
*/
public boolean containsEdge(E edge)
{
return edge_vpairs.containsKey(edge);
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#containsVertex(java.lang.Object)
*/
public boolean containsVertex(V vertex)
{
return vertex_data.containsKey(vertex);
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#findEdge(java.lang.Object, java.lang.Object)
*/
@Override
public E findEdge(V v1, V v2)
{
if (!containsVertex(v1) || !containsVertex(v2))
return null;
VertexData v1_data = vertex_data.get(v1);
if (edge_vpairs.get(v1_data.parent_edge).getFirst().equals(v2))
return v1_data.parent_edge;
List<E> edges = v1_data.child_edges;
if (edges == null)
return null;
for (E edge : edges)
if (edge != null && edge_vpairs.get(edge).getSecond().equals(v2))
return edge;
return null;
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#findEdgeSet(java.lang.Object, java.lang.Object)
*/
@Override
public Collection<E> findEdgeSet(V v1, V v2)
{
E edge = findEdge(v1, v2);
if (edge == null)
return Collections.emptySet();
else
return Collections.singleton(edge);
}
/**
* Returns the child of <code>vertex</code> at position <code>index</code>
* in this tree, or <code>null</code> if it has no child at that position.
* @param vertex the vertex to query
* @return the child of <code>vertex</code> at position <code>index</code>
* in this tree, or <code>null</code> if it has no child at that position
* @throws ArrayIndexOutOfBoundsException if <code>index</code> is not in
* the range {@code [0, order-1]}
*/
public V getChild(V vertex, int index)
{
if (index < 0 || index >= order)
throw new ArrayIndexOutOfBoundsException(index + " is not in [0, order-1]");
if (!containsVertex(vertex))
return null;
List<E> edges = vertex_data.get(vertex).child_edges;
if (edges == null)
return null;
E edge = edges.get(index);
return edge == null ? null : edge_vpairs.get(edge).getSecond();
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#getEdgeCount()
*/
public int getEdgeCount()
{
return edge_vpairs.size();
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#getEdges()
*/
public Collection<E> getEdges()
{
return CollectionUtils.unmodifiableCollection(edge_vpairs.keySet());
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#getIncidentCount(java.lang.Object)
*/
@Override
public int getIncidentCount(E edge)
{
return 2; // all tree edges have 2 incident vertices
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#getIncidentEdges(java.lang.Object)
*/
public Collection<E> getIncidentEdges(V vertex)
{
if (!containsVertex(vertex))
return null;
ArrayList<E> edges = new ArrayList<E>(order+1);
VertexData v_data = vertex_data.get(vertex);
if (v_data.parent_edge != null)
edges.add(v_data.parent_edge);
if (v_data.child_edges != null)
{
for (E edge : v_data.child_edges)
if (edge != null)
edges.add(edge);
}
if (edges.isEmpty())
return Collections.emptySet();
return Collections.unmodifiableCollection(edges);
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#getIncidentVertices(java.lang.Object)
*/
@Override
public Collection<V> getIncidentVertices(E edge)
{
return edge_vpairs.get(edge);
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#getNeighborCount(java.lang.Object)
*/
@Override
public int getNeighborCount(V vertex)
{
if (!containsVertex(vertex))
return 0;
return (vertex.equals(root) ? 0 : 1) + this.getChildCount(vertex);
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#getNeighbors(java.lang.Object)
*/
public Collection<V> getNeighbors(V vertex)
{
if (!containsVertex(vertex))
return null;
ArrayList<V> vertices = new ArrayList<V>(order+1);
VertexData v_data = vertex_data.get(vertex);
if (v_data.parent_edge != null)
vertices.add(edge_vpairs.get(v_data.parent_edge).getFirst());
if (v_data.child_edges != null)
{
for (E edge : v_data.child_edges)
if (edge != null)
vertices.add(edge_vpairs.get(edge).getSecond());
}
if (vertices.isEmpty())
return Collections.emptySet();
return Collections.unmodifiableCollection(vertices);
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#getVertexCount()
*/
public int getVertexCount()
{
return vertex_data.size();
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#getVertices()
*/
public Collection<V> getVertices()
{
return CollectionUtils.unmodifiableCollection(vertex_data.keySet());
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#removeEdge(java.lang.Object)
*/
public boolean removeEdge(E edge)
{
if (!containsEdge(edge))
return false;
removeVertex(edge_vpairs.get(edge).getSecond());
edge_vpairs.remove(edge);
return true;
}
/**
* @see edu.uci.ics.jung.graph.Hypergraph#removeVertex(java.lang.Object)
*/
public boolean removeVertex(V vertex)
{
if (!containsVertex(vertex))
return false;
// recursively remove all of vertex's children
for(V v : getChildren(vertex))
removeVertex(v);
E parent_edge = getParentEdge(vertex);
edge_vpairs.remove(parent_edge);
List<E> edges = vertex_data.get(vertex).child_edges;
if (edges != null)
for (E edge : edges)
edge_vpairs.remove(edge);
vertex_data.remove(vertex);
return true;
}
protected class VertexData
{
List<E> child_edges;
E parent_edge;
int depth;
protected VertexData(E parent_edge, int depth)
{
this.parent_edge = parent_edge;
this.depth = depth;
}
}
@Override
public boolean addEdge(E edge, Pair<? extends V> endpoints, EdgeType edgeType)
{
if (edge == null || endpoints == null)
throw new IllegalArgumentException("inputs must not be null");
return addEdge(edge, endpoints.getFirst(), endpoints.getSecond(), edgeType);
}
}