/* This file is part of the db4o object database http://www.db4o.com
Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com
db4o is free software; you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published
by the Free Software Foundation.
db4o is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see http://www.gnu.org/licenses/. */
package EDU.purdue.cs.bloat.util;
import java.util.*;
/**
* Graph represents a graph of nodes with directed edges between them.
* GraphNodes are created and are added to the Graph before the edges can be
* constructed. Each GraphNode as a unique key associated with it. For instance,
* if a Graph represents a control flow graph, each GraphNode would be
* associated with a basic block.
*
* @see #addNode
* @see #addEdge
*
* @see GraphNode
* @see EDU.purdue.cs.bloat.cfg.FlowGraph
*/
public class Graph {
private NodeMap nodes; // The nodes in this Graph
private NodeList preOrder; //
private NodeList postOrder;
private Collection roots; // The root nodes of this Graph (no
// predacessors)
private Collection revRoots; // Reverse root nodes of this Graph (no
// successors)
// These counts are used to determine when certain actions (such as updating
// the
// roots Collection) should or should not be performed.
protected int rootEdgeModCount = 0;
protected int revRootEdgeModCount = 0;
protected int nodeModCount = 0; // Number of nodes that have been modified
protected int edgeModCount = 0; // Number of edges that have been modified
protected int removingNode = 0;
protected int removingEdge = 0;
/**
* Constructor.
*/
public Graph() {
nodes = new NodeMap();
preOrder = null;
postOrder = null;
roots = null;
revRoots = null;
}
/**
* @return The roots of this Graph. That is, the nodes with no predacessors.
*/
public Collection roots() {
if ((roots == null) || (rootEdgeModCount != edgeModCount)) {
rootEdgeModCount = edgeModCount;
roots = new ArrayList();
buildRootList(roots, false);
}
return roots;
}
/**
* @return The reverse roots of this Graph. That is, the nodes with no
* successors.
*/
public Collection reverseRoots() {
if ((roots == null) || (revRootEdgeModCount != edgeModCount)) {
revRootEdgeModCount = edgeModCount;
revRoots = new ArrayList();
buildRootList(revRoots, true);
}
return revRoots;
}
/**
* Compile a collection of root nodes in this Graph. If reverse is true, the
* collection will contain those nodes with no predacessor nodes. If reverse
* is false, the collection will contain those nodes with no sucessor nodes.
*
* @param c
* A Collection that will contain the roots in the graph.
* @param reverse
* Do we make a reverse traversal of the graph?
*/
private void buildRootList(final Collection c, final boolean reverse) {
final HashSet visited = new HashSet(nodes.size() * 2);
final ArrayList stack = new ArrayList();
final Iterator iter = nodes.values().iterator();
while (iter.hasNext()) {
final GraphNode node = (GraphNode) iter.next();
if (!visited.contains(node)) {
visited.add(node);
stack.add(node);
while (!stack.isEmpty()) {
final GraphNode v = (GraphNode) stack
.remove(stack.size() - 1);
boolean pushed = false;
final Iterator preds = reverse ? v.succs.iterator()
: v.preds.iterator();
while (preds.hasNext()) {
final GraphNode w = (GraphNode) preds.next();
if (!visited.contains(w)) {
visited.add(w);
stack.add(w);
pushed = true;
}
}
if (!pushed) {
c.add(v);
}
}
}
}
}
/**
* Return the successors of a given node.
*/
public Collection succs(final GraphNode v) {
return new EdgeSet(v, v.succs);
}
/**
* Returns the predacessors of a given node.
*/
public Collection preds(final GraphNode v) {
return new EdgeSet(v, v.preds);
}
/**
* Determines whether or not a node v is an ancestor (has a lower pre-order
* index and a higher post-order index) of a node w.
*
* @param v
* Candidate ancestor node.
* @param w
* Candidate descendent node.
*
* @return True, if v is an ancestor of w.
*/
public boolean isAncestorToDescendent(final GraphNode v, final GraphNode w) {
return (preOrderIndex(v) <= preOrderIndex(w))
&& (postOrderIndex(w) <= postOrderIndex(v));
}
/**
* Returns the index of a given node in a pre-order ordering of this Graph.
*/
public int preOrderIndex(final GraphNode node) {
if ((preOrder == null) || (edgeModCount != preOrder.edgeModCount)) {
buildLists();
}
return node.preOrderIndex();
}
/**
* Returns the index of a given node in a post-order ordering of this Graph.
*/
public int postOrderIndex(final GraphNode node) {
if ((postOrder == null) || (edgeModCount != postOrder.edgeModCount)) {
buildLists();
}
return node.postOrderIndex();
}
/**
* Returns the nodes in this Graph ordered by their pre-order index.
*/
public List preOrder() {
if ((preOrder == null) || (edgeModCount != preOrder.edgeModCount)) {
buildLists();
}
return preOrder;
}
/**
* Return the nodes in this Graph ordered by their post-order index.
*/
public List postOrder() {
if ((postOrder == null) || (edgeModCount != postOrder.edgeModCount)) {
buildLists();
}
return postOrder;
}
/**
* Constructs lists of nodes in both pre-order and post-order order.
*/
private void buildLists() {
Iterator iter = roots().iterator();
preOrder = new NodeList();
postOrder = new NodeList();
final Set visited = new HashSet();
// Calculate the indices of the nodes.
while (iter.hasNext()) {
final GraphNode root = (GraphNode) iter.next();
Assert.isTrue(nodes.containsValue(root), "Graph does not contain "
+ root);
number(root, visited);
}
// Mark all nodes that were not numbered as having an index of -1. This
// information is used when removing unreachable nodes.
iter = nodes.values().iterator();
while (iter.hasNext()) {
final GraphNode node = (GraphNode) iter.next();
if (!visited.contains(node)) {
node.setPreOrderIndex(-1);
node.setPostOrderIndex(-1);
} else {
Assert.isTrue(node.preOrderIndex() >= 0);
Assert.isTrue(node.postOrderIndex() >= 0);
}
}
}
/**
* Removes all nodes from this Graph that cannot be reached in a pre-order
* traversal of the Graph. These nodes have a pre-order index of -1.
*/
public void removeUnreachable() {
if ((preOrder == null) || (edgeModCount != preOrder.edgeModCount)) {
buildLists();
}
final Iterator iter = nodes.entrySet().iterator();
while (iter.hasNext()) {
final Map.Entry e = (Map.Entry) iter.next();
final GraphNode v = (GraphNode) e.getValue();
if (v.preOrderIndex() == -1) {
iter.remove();
}
}
}
/**
* Sets the pre-order and post-order indices of a node.
*
* @param node
* The node to number.
* @param visited
* The nodes that have been visited already.
*/
private void number(final GraphNode node, final Set visited) {
visited.add(node);
// Visit in pre-order
node.setPreOrderIndex(preOrder.size());
preOrder.addNode(node);
final Iterator iter = succs(node).iterator();
while (iter.hasNext()) {
final GraphNode succ = (GraphNode) iter.next();
if (!visited.contains(succ)) {
number(succ, visited);
}
}
// Visit in post-order
node.setPostOrderIndex(postOrder.size());
postOrder.addNode(node);
}
/**
* Insertes a node (and its associated key) into this Graph.
*
* @param key
* A unique value associated with this node. For instance, if
* this Graph represented a control flow graph, the key would be
* a basic block.
* @param node
* The node to be added.
*/
// This method is NOT guaranteed to be called whenever a node is added.
public void addNode(final Object key, final GraphNode node) {
Assert.isTrue(nodes.get(key) == null);
nodes.putNodeInMap(key, node);
preOrder = null;
postOrder = null;
nodeModCount++;
edgeModCount++;
}
/**
* Returns the node in this Graph with a given key.
*/
public GraphNode getNode(final Object key) {
return (GraphNode) nodes.get(key);
}
/**
* Returns a Set of the keys used to uniquely identify the nodes in this
* Graph.
*/
public Set keySet() {
return nodes.keySet();
}
/**
* Removes a node with a given key from the Graph.
*
* @param key
* The key associated with the node to remove.
*/
// This method is guaranteed to be called whenever a node is deleted.
// If removingNode != 0, the node is NOT deleted when this method returns.
// It is the callers responsibility to delete the node AFTER this method
// is called. An exception will be thrown if the node is not present
// in the graph.
public void removeNode(final Object key) {
final GraphNode node = getNode(key);
Assert.isTrue(node != null, "No node for " + key);
succs(node).clear();
preds(node).clear();
if (removingNode == 0) {
nodes.removeNodeFromMap(key);
} else if (removingNode != 1) {
throw new RuntimeException();
}
// Removing a node invalidates the orderings
preOrder = null;
postOrder = null;
nodeModCount++;
edgeModCount++;
}
/**
* Adds a directed edge from node v to node w.
*
* @param v
* Source node.
* @param w
* Destination node.
*/
// This method is NOT guaranteed to be called whenever an edge is added.
public void addEdge(final GraphNode v, final GraphNode w) {
Assert.isTrue(nodes.containsValue(v), "Graph does not contain " + v);
Assert.isTrue(nodes.containsValue(w), "Graph does not contain " + w);
succs(v).add(w);
edgeModCount++;
}
// This method is guaranteed to be called whenever an edge is deleted.
// If removingEdge != 0, the edge is NOT deleted when this method returns.
// It is the callers responsibility to delete the edge AFTER this method
// is called. An exception will be thrown if the edge is not present
// in the graph.
public void removeEdge(final GraphNode v, final GraphNode w) {
Assert.isTrue(nodes.containsValue(v), "Graph does not contain " + v);
Assert.isTrue(nodes.containsValue(w), "Graph does not contain " + w);
Assert.isTrue(v.succs().contains(w));
if (removingEdge == 0) {
succs(v).remove(w);
} else if (removingEdge != 1) {
throw new RuntimeException();
}
edgeModCount++;
}
public String toString() {
String s = "";
final Iterator iter = nodes.values().iterator();
while (iter.hasNext()) {
final GraphNode node = (GraphNode) iter.next();
s += "[" + node;
s += " succs = " + node.succs();
s += " preds = " + node.preds();
s += "]\n";
}
return s;
}
/**
* Searchs this Graph for a given GraphNode.
*
* @param v
* GraphNode to search for.
*
* @return True, if this Graphs contains v.
*/
public boolean hasNode(final GraphNode v) {
return nodes.containsValue(v);
}
/**
* Searches this Graph for an (directed) edge between two GraphNodes.
*
* @param v
* Source node of desired edge.
* @param w
* Destination node of desired edge.
*
* @return True, if an edge exists between nodes v and w.
*/
public boolean hasEdge(final GraphNode v, final GraphNode w) {
Assert.isTrue(nodes.containsValue(v), "Graph does not contain " + v);
Assert.isTrue(nodes.containsValue(w), "Graph does not contain " + w);
return succs(v).contains(w);
}
/**
* Returns all the nodes in this Graph.
*/
public Collection nodes() {
return nodes.values();
}
/**
* Returns the number of nodes in this Graph.
*/
public int size() {
return nodes.size();
}
/**
* A NodeMap that stores nodes. I guess we use this data structure to make
* it easier to ensure that there are not duplicate nodes in the Graph. A
* HashMap is used as the underlying stored mechanism.
*/
class NodeMap extends AbstractMap {
HashMap map = new HashMap();
void removeNodeFromMap(final Object key) {
map.remove(key);
}
void putNodeInMap(final Object key, final Object value) {
map.put(key, value);
}
public Object remove(final Object key) {
final GraphNode v = (GraphNode) map.get(key);
if (v != null) {
Graph.this.removeNode(v);
}
return v;
}
public Object put(final Object key, final Object value) {
final GraphNode v = (GraphNode) remove(key);
Graph.this.addNode(key, (GraphNode) value);
return v;
}
public void clear() {
final Iterator iter = entrySet().iterator();
while (iter.hasNext()) {
final Map.Entry e = (Map.Entry) iter.next();
removingNode++;
Graph.this.removeNode(e.getKey());
removingNode--;
iter.remove();
}
}
public Set entrySet() /* Modified for final JDK1.2 API */
{
final Collection entries = map.entrySet();
return new AbstractSet() {
public int size() {
return entries.size();
}
public boolean contains(final Object a) {
return entries.contains(a);
}
public boolean remove(final Object a) {
final Map.Entry e = (Map.Entry) a;
removingNode++;
Graph.this.removeNode(e.getKey());
removingNode--;
return entries.remove(a);
}
public void clear() {
final Iterator iter = entries.iterator();
while (iter.hasNext()) {
final Map.Entry e = (Map.Entry) iter.next();
removingNode++;
Graph.this.removeNode(e.getKey());
removingNode--;
iter.remove();
}
}
public Iterator iterator() {
final Iterator iter = entries.iterator();
return new Iterator() {
int nodeModCount = Graph.this.nodeModCount;
Map.Entry last;
public boolean hasNext() {
if (nodeModCount != Graph.this.nodeModCount) {
throw new ConcurrentModificationException();
}
return iter.hasNext();
}
public Object next() {
if (nodeModCount != Graph.this.nodeModCount) {
throw new ConcurrentModificationException();
}
last = (Map.Entry) iter.next();
return last;
}
public void remove() {
if (nodeModCount != Graph.this.nodeModCount) {
throw new ConcurrentModificationException();
}
removingNode++;
Graph.this.removeNode(last.getKey());
removingNode--;
iter.remove();
nodeModCount = Graph.this.nodeModCount;
}
};
}
};
}
}
/**
* NodeList represents a list of nodes. Special provisions must be made for
* methods such as indexOf() and iterator(). A NodeList is used to store the
* pre-order and post-order travsersals of the Graph.
*/
class NodeList extends ArrayList implements List {
int edgeModCount;
NodeList() {
super(Graph.this.size());
edgeModCount = Graph.this.edgeModCount;
}
boolean addNode(final GraphNode a) {
return super.add(a);
}
public void clear() {
throw new UnsupportedOperationException();
}
public boolean add(final Object a) {
throw new UnsupportedOperationException();
}
public boolean remove(final Object a) {
throw new UnsupportedOperationException();
}
// This works only if each node is in the list at most once.
public int indexOf(final Object a) {
if (edgeModCount != Graph.this.edgeModCount) {
throw new ConcurrentModificationException();
}
final GraphNode v = (GraphNode) a;
if (this == Graph.this.preOrder) {
return v.preOrderIndex();
}
if (this == Graph.this.postOrder) {
return v.postOrderIndex();
}
return super.indexOf(a);
}
// This works only if each node is in the list at most once.
public int indexOf(final Object a, final int index) {
final int i = indexOf(a);
if (i >= index) {
return i;
}
return -1;
}
// This works only if each node is in the list at most once.
public int lastIndexOf(final Object a) {
if (edgeModCount != Graph.this.edgeModCount) {
throw new ConcurrentModificationException();
}
final GraphNode v = (GraphNode) a;
if (this == Graph.this.preOrder) {
return v.preOrderIndex();
}
if (this == Graph.this.postOrder) {
return v.postOrderIndex();
}
return super.lastIndexOf(a);
}
// This works only if each node is in the list at most once.
public int lastIndexOf(final Object a, final int index) {
final int i = indexOf(a);
if (i <= index) {
return i;
}
return -1;
}
public Iterator iterator() {
if (Graph.this.edgeModCount != edgeModCount) {
throw new ConcurrentModificationException();
}
final Iterator iter = super.iterator();
return new Iterator() {
int edgeModCount = NodeList.this.edgeModCount;
Object last;
public boolean hasNext() {
if (Graph.this.edgeModCount != edgeModCount) {
throw new ConcurrentModificationException();
}
return iter.hasNext();
}
public Object next() {
if (Graph.this.edgeModCount != edgeModCount) {
throw new ConcurrentModificationException();
}
last = iter.next();
return last;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
/**
* A set of edges. Recall that a Set cannot contain duplicate entries.
*/
class EdgeSet extends AbstractSet {
GraphNode node;
Set set;
int nodeModCount;
/**
*
*/
public EdgeSet(final GraphNode node, final Set set) {
this.node = node;
this.set = set;
this.nodeModCount = Graph.this.nodeModCount;
}
public int size() {
if (nodeModCount != Graph.this.nodeModCount) {
throw new ConcurrentModificationException();
}
return set.size();
}
/**
* Removes all nodes in this set except for those found in collection.
*
* @param c
* Nodes that are to be retained.
*/
public boolean retainAll(final Collection c) {
return super.retainAll(new ArrayList(c));
}
/**
* Removes all of the nodes in this set that are specified in a given
* Collection.
*
* @param c
* The nodes to remove.
*/
public boolean removeAll(final Collection c) {
return super.removeAll(new ArrayList(c));
}
/**
* Adds all of the nodes in a Collection to this set.
*/
public boolean addAll(final Collection c) {
return super.addAll(new ArrayList(c));
}
public boolean add(final Object a) {
if (nodeModCount != Graph.this.nodeModCount) {
throw new ConcurrentModificationException();
}
Assert.isTrue(nodes.containsValue(a));
Assert.isTrue(nodes.containsValue(node));
final GraphNode v = (GraphNode) a;
if (set.add(v)) {
Graph.this.edgeModCount++;
if (set == node.succs) {
v.preds.add(node);
} else {
v.succs.add(node);
}
return true;
}
return false;
}
public boolean remove(final Object a) {
if (nodeModCount != Graph.this.nodeModCount) {
throw new ConcurrentModificationException();
}
final GraphNode v = (GraphNode) a;
if (set.contains(v)) {
Graph.this.edgeModCount++;
if (set == node.succs) {
removingEdge++;
Graph.this.removeEdge(node, v);
removingEdge--;
v.preds.remove(node);
} else {
removingEdge++;
Graph.this.removeEdge(v, node);
removingEdge--;
v.succs.remove(node);
}
set.remove(v);
return true;
}
return false;
}
public boolean contains(final Object a) {
if (nodeModCount != Graph.this.nodeModCount) {
throw new ConcurrentModificationException();
}
Assert.isTrue(nodes.containsValue(a));
Assert.isTrue(nodes.containsValue(node));
if (a instanceof GraphNode) {
return set.contains(a);
}
return false;
}
public void clear() {
if (nodeModCount != Graph.this.nodeModCount) {
throw new ConcurrentModificationException();
}
final Iterator iter = set.iterator();
while (iter.hasNext()) {
final GraphNode v = (GraphNode) iter.next();
if (set == node.succs) {
removingEdge++;
Graph.this.removeEdge(node, v);
removingEdge--;
v.preds.remove(node);
} else {
removingEdge++;
Graph.this.removeEdge(v, node);
removingEdge--;
v.succs.remove(node);
}
}
Graph.this.edgeModCount++;
set.clear();
}
public Iterator iterator() {
if (nodeModCount != Graph.this.nodeModCount) {
throw new ConcurrentModificationException();
}
final Iterator iter = set.iterator();
return new Iterator() {
GraphNode last;
int edgeModCount = Graph.this.edgeModCount;
int nodeModCount = EdgeSet.this.nodeModCount;
public boolean hasNext() {
if (nodeModCount != Graph.this.nodeModCount) {
throw new ConcurrentModificationException();
}
if (edgeModCount != Graph.this.edgeModCount) {
throw new ConcurrentModificationException();
}
return iter.hasNext();
}
public Object next() {
if (nodeModCount != Graph.this.nodeModCount) {
throw new ConcurrentModificationException();
}
if (edgeModCount != Graph.this.edgeModCount) {
throw new ConcurrentModificationException();
}
last = (GraphNode) iter.next();
Assert.isTrue(nodes.containsValue(last), last
+ " not found in graph");
Assert.isTrue(nodes.containsValue(node), node
+ " not found in graph");
return last;
}
public void remove() {
if (nodeModCount != Graph.this.nodeModCount) {
throw new ConcurrentModificationException();
}
if (edgeModCount != Graph.this.edgeModCount) {
throw new ConcurrentModificationException();
}
if (set == node.succs) {
removingEdge++;
Graph.this.removeEdge(node, last);
removingEdge--;
last.preds.remove(node);
} else {
removingEdge++;
Graph.this.removeEdge(last, node);
removingEdge--;
last.succs.remove(node);
}
Graph.this.edgeModCount++;
edgeModCount = Graph.this.edgeModCount;
iter.remove();
}
};
}
}
}