package org.jf.dexlib.Code.Analysis.graphs;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import org.jgrapht.DirectedGraph;
/**
* Provides ways to traverse a directed graph. Currently only depth first search (DFS) is implemented.
*
* @author Juergen Graf <juergen.graf@gmail.com>
*
* @param <V>
* @param <E>
*/
public abstract class GraphWalker<V, E> {
private final DirectedGraph<V, E> graph;
public GraphWalker(DirectedGraph<V, E> graph) {
this.graph = graph;
}
public final DirectedGraph<V, E> getGraph() {
return graph;
}
/*
* So far the recursive solution is more elegant. Better leave this turned off.
*/
private static final boolean NO_RECURSION = true;
public final void traverseDFS(final V start) {
if (NO_RECURSION) {
dfsNoRecrusion(start);
} else {
Set<V> visited = new HashSet<V>();
dfs(start, visited);
}
}
private void dfs(final V node, final Set<V> visited) {
visited.add(node);
discover(node);
for (final E out : graph.outgoingEdgesOf(node)) {
final V succ = graph.getEdgeTarget(out);
if (!visited.contains(succ)) {
dfs(succ, visited);
}
}
finish(node);
}
/**
* Is called when the node is discovered by the walker.
* @param node The node that has been discovered.
*/
public abstract void discover(V node);
/**
* Is called when the node is left (finished) by the walker.
* @param node The node that is left.
*/
public abstract void finish(V node);
/*
* A non-recursive solution of the dfs iteration. Not sure this really works...
*/
private void dfsNoRecrusion(final V entry) {
// iterate dfs finish time
final Set<V> visited = new HashSet<V>(graph.vertexSet().size());
final Stack<V> stack = new Stack<V>();
V current = entry;
while (current != null) {
boolean foundOne = false;
if (!visited.contains(current)) {
visited.add(current);
stack.push(current);
discover(current);
for (E succEdge : graph.outgoingEdgesOf(current)) {
// there may be a problem iff the successor can be the same node twice (or more)
// think of succ normal flow + succ exception flow. But this never happens in the current
// code. Even with empty catch blocks.
final V succ = graph.getEdgeTarget(succEdge);
if (!visited.contains(succ)) {
//XXX this is slow and should be removed with some elegant solution
// so far I propose to use the recursive algorithm.
stack.remove(succ);
if (!foundOne) {
foundOne = true;
current = succ;
} else {
stack.push(succ);
}
}
}
} else {
// finished current node. act.
finish(current);
}
if (!foundOne) {
current = (stack.isEmpty() ? null : stack.pop());
}
}
}
}