WALA/com.ibm.wala.util/src/com/ibm/wala/util/graph/traverse/DFSPathFinder.java

212 lines
5.6 KiB
Java

/*******************************************************************************
* Copyright (c) 2002 - 2006 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.wala.util.graph.traverse;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import com.ibm.wala.util.Predicate;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.graph.Graph;
/**
* This class searches depth-first search for node that matches some criteria. If found, it reports a path to the first node found.
*
* This class follows the outNodes of the graph nodes to define the graph, but this behavior can be changed by overriding the
* getConnected method.
*/
public class DFSPathFinder<T> extends Stack<T> {
public static final long serialVersionUID = 9939900773328288L;
/**
* The graph to search
*/
protected final Graph<T> G;
/**
* The Filter which defines the target set of nodes to find
*/
final private Predicate<T> filter;
/**
* an enumeration of all nodes to search from
*/
final private Iterator<T> roots;
/**
* An iterator of child nodes for each node being searched
*/
final protected Map<Object, Iterator<? extends T>> pendingChildren = HashMapFactory.make(25);
/**
* Flag recording whether initialization has happened.
*/
private boolean initialized = false;
/**
* Construct a depth-first enumerator starting with a particular node in a directed graph.
*
* @param G the graph whose nodes to enumerate
* @throws IllegalArgumentException if G is null
*/
public DFSPathFinder(Graph<T> G, T N, Predicate<T> f) throws IllegalArgumentException {
if (G == null) {
throw new IllegalArgumentException("G is null");
}
if (!G.containsNode(N)) {
throw new IllegalArgumentException("source node not in graph: " + N);
}
this.G = G;
this.roots = new NonNullSingletonIterator<T>(N);
this.filter = f;
}
/**
* Construct a depth-first enumerator across the (possibly improper) subset of nodes reachable from the nodes in the given
* enumeration.
*
* @param nodes the set of nodes from which to start searching
*/
public DFSPathFinder(Graph<T> G, Iterator<T> nodes, Predicate<T> f) {
this.G = G;
this.roots = nodes;
this.filter = f;
if (G == null) {
throw new IllegalArgumentException("G is null");
}
if (roots == null) {
throw new IllegalArgumentException("roots is null");
}
if (filter == null) {
throw new IllegalArgumentException("filter is null");
}
}
private void init() {
initialized = true;
if (roots.hasNext()) {
T n = roots.next();
push(n);
setPendingChildren(n, getConnected(n));
}
}
/**
* @return a List of nodes that specifies the first path found from a root to a node accepted by the filter. Returns null if no
* path found.
*/
public List<T> find() {
if (!initialized) {
init();
}
while (hasNext()) {
T n = peek();
if (filter.test(n)) {
List<T> path = currentPath();
advance();
return path;
}
advance();
}
return null;
}
protected List<T> currentPath() {
ArrayList<T> result = new ArrayList<T>();
for (Iterator<T> path = iterator(); path.hasNext();) {
result.add(0, path.next());
}
return result;
}
/**
* Return whether there are any more nodes left to enumerate.
*
* @return true if there nodes left to enumerate.
*/
public boolean hasNext() {
return (!empty());
}
/**
* Method getPendingChildren.
*
* @return Object
*/
protected Iterator<? extends T> getPendingChildren(T n) {
return pendingChildren.get(n);
}
/**
* Method setPendingChildren.
*
* @param v
* @param iterator
*/
protected void setPendingChildren(T v, Iterator<? extends T> iterator) {
pendingChildren.put(v, iterator);
}
/**
* Advance to the next graph node in discover time order.
*/
private void advance() {
// we always return the top node on the stack.
T currentNode = peek();
// compute the next node to return.
assert getPendingChildren(currentNode) != null;
do {
T stackTop = peek();
for (Iterator<? extends T> it = getPendingChildren(stackTop); it.hasNext();) {
T child = it.next();
if (getPendingChildren(child) == null) {
// found a new child.
push(child);
setPendingChildren(child, getConnected(child));
return;
}
}
// didn't find any new children. pop the stack and try again.
pop();
} while (!empty());
// search for the next unvisited root.
while (roots.hasNext()) {
T nextRoot = roots.next();
if (getPendingChildren(nextRoot) == null) {
push(nextRoot);
setPendingChildren(nextRoot, getConnected(nextRoot));
}
}
return;
}
/**
* get the out edges of a given node
*
* @param n the node of which to get the out edges
* @return the out edges
*
*/
protected Iterator<? extends T> getConnected(T n) {
return G.getSuccNodes(n);
}
}