/*******************************************************************************
* 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.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import com.ibm.wala.util.Predicate;
import com.ibm.wala.util.collections.FilterIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.graph.NumberedGraph;
/**
* utilities related to depth-first search.
*/
public class DFS {
/**
* Perform a DFS starting with a particular node and return the set of all nodes visited.
*
* @param C collection of nodes to start from
* @param filter only traverse nodes that need this filter
* @throws IllegalArgumentException if C is null
*/
@SuppressWarnings("serial")
public static <T> Collection<T> getReachableNodes(final Graph<T> G, Collection<? extends T> C, @SuppressWarnings("rawtypes") final Predicate filter) {
if (C == null) {
throw new IllegalArgumentException("C is null");
}
Iterator<T> dfs = new SlowDFSFinishTimeIterator<T>(G, C.iterator()) {
@Override
protected Iterator<T> getConnected(T n) {
return new FilterIterator<T>(G.getSuccNodes(n), filter);
}
};
return Iterator2Collection.toSet(dfs);
}
/**
* Perform a DFS starting with a particular node set and return the set of all nodes visited.
*
* @param G the graph containing n
* @return Set
* @throws IllegalArgumentException if C is null
*/
public static <T> Set<T> getReachableNodes(Graph<T> G, Collection<? extends T> C) {
if (C == null) {
throw new IllegalArgumentException("C is null");
}
HashSet<T> result = HashSetFactory.make();
Iterator<T> dfs = iterateFinishTime(G, C.iterator());
while (dfs.hasNext()) {
result.add(dfs.next());
}
return result;
}
/**
* Perform a DFS and return the set of all nodes visited.
*
* @param G the graph containing n
* @return Set
* @throws IllegalArgumentException if G == null
*/
public static <T> Set<T> getReachableNodes(Graph<T> G) throws IllegalArgumentException {
if (G == null) {
throw new IllegalArgumentException("G == null");
}
HashSet<T> result = HashSetFactory.make();
Iterator<T> dfs = iterateFinishTime(G);
while (dfs.hasNext()) {
result.add(dfs.next());
}
return result;
}
/**
* Perform a DFS of a graph starting with a specified node and return a sorted list of nodes. The nodes are sorted by depth first
* order.
*
* @param G a graph
* @param n the initial node
* @return a sorted set of nodes in the graph in depth first order
*/
public static <T> SortedSet<T> sortByDepthFirstOrder(Graph<T> G, T n) {
Map<T, Integer> order = HashMapFactory.make();
TreeSet<T> result = new TreeSet<T>(new DFSComparator<T>(order));
Iterator<T> dfs = iterateFinishTime(G, new NonNullSingletonIterator<T>(n));
int i = 0;
while (dfs.hasNext()) {
T nxt = dfs.next();
order.put(nxt, new Integer(i++));
result.add(nxt);
}
return result;
}
/**
* Comparator class to order the nodes in the DFS according to the depth first order
*/
static class DFSComparator<T> implements Comparator<T> {
final private Map<T, Integer> order;
DFSComparator(Map<T, Integer> order) {
this.order = order;
}
@Override
public int compare(T o1, T o2) {
// throws an exception if either argument is not a Node object
if (o1 == o2) {
return 0;
}
Integer t1 = order.get(o1);
Integer t2 = order.get(o2);
// throws an exception if either node has not been ordered
return (t1.intValue() - t2.intValue());
}
}
/**
* @param G
* @return iterator of nodes of G in order of DFS discover time
*/
public static <T> DFSDiscoverTimeIterator<T> iterateDiscoverTime(Graph<T> G) {
if (G instanceof NumberedGraph) {
return new NumberedDFSDiscoverTimeIterator<T>((NumberedGraph<T>) G);
} else {
return new SlowDFSDiscoverTimeIterator<T>(G);
}
}
/**
* @param roots roots of traversal, in order to visit in outermost loop of DFS
* @return iterator of nodes of G in order of DFS discover time
* @throws IllegalArgumentException if roots == null
*/
public static <T> Iterator<T> iterateDiscoverTime(Graph<T> G, Iterator<T> roots) throws IllegalArgumentException {
if (roots == null) {
throw new IllegalArgumentException("roots == null");
}
if (G instanceof NumberedGraph) {
return new NumberedDFSDiscoverTimeIterator<T>((NumberedGraph<T>) G, roots);
} else {
return new SlowDFSDiscoverTimeIterator<T>(G, roots);
}
}
/**
* @param N root of traversal
* @return iterator of nodes of G in order of DFS discover time
*/
public static <T> DFSDiscoverTimeIterator<T> iterateDiscoverTime(Graph<T> G, T N) {
if (G == null) {
throw new IllegalArgumentException("G == null");
}
if (G instanceof NumberedGraph) {
return new NumberedDFSDiscoverTimeIterator<T>((NumberedGraph<T>) G, N);
} else {
return new SlowDFSDiscoverTimeIterator<T>(G, N);
}
}
/**
* @param G a graph
* @return iterator of nodes of G in order of DFS finish time
* @throws IllegalArgumentException if G == null
*/
public static <T> DFSFinishTimeIterator<T> iterateFinishTime(Graph<T> G) throws IllegalArgumentException {
if (G == null) {
throw new IllegalArgumentException("G == null");
}
if (G instanceof NumberedGraph) {
return new NumberedDFSFinishTimeIterator<T>((NumberedGraph<T>) G);
} else {
return new SlowDFSFinishTimeIterator<T>(G);
}
}
/**
* @param G a graph
* @param ie roots of traversal, in order to visit in outermost loop of DFS
* @return iterator of nodes of G in order of DFS finish time
*/
public static <T> DFSFinishTimeIterator<T> iterateFinishTime(Graph<T> G, Iterator<? extends T> ie) {
if (ie == null) {
throw new IllegalArgumentException("null ie");
}
if (G instanceof NumberedGraph) {
return new NumberedDFSFinishTimeIterator<T>((NumberedGraph<T>) G, ie);
} else {
return new SlowDFSFinishTimeIterator<T>(G, ie);
}
}
}