package kr.ac.snu.selab.soot.graph;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import org.apache.log4j.Logger;
public abstract class GraphPathCollector<N extends Node> {
private static Logger log = Logger.getLogger(GraphPathCollector.class);
protected HashMap<String, ArrayList<Path<N>>> pathsMap;
protected HashSet<String> hitSet;
protected N startNode;
protected Graph<N> graph;
private static final int PATH_SET_SIZE_LIMIT = 1000;
public GraphPathCollector(N aStartNode, Graph<N> aGraph) {
pathsMap = new HashMap<String, ArrayList<Path<N>>>();
hitSet = new HashSet<String>();
this.startNode = aStartNode;
this.graph = aGraph;
}
protected boolean isForwardSearch() {
return false;
}
public ArrayList<Path<N>> run() {
log.debug("path collecting start: " + startNode.toString());
long tick1 = System.currentTimeMillis();
pathsMap.clear();
hitSet.clear();
int pathThreshold = getPathThreshold();
ArrayList<Path<N>> paths = new ArrayList<Path<N>>();
int result = findPaths(startNode, graph, paths, pathThreshold);
if (result == CYCLE) {
// throw an exception
return paths;
}
long tick2 = System.currentTimeMillis();
log.debug("Call path collecting finished, " + (tick2 - tick1));
return paths;
}
protected static final int DONE = 0;
protected static final int CYCLE = 1;
protected int findPaths(N aNode, Graph<N> graph, ArrayList<Path<N>> output,
final int pathThreshold) {
if (aNode == null)
return DONE;
String nodeKey = aNode.key();
if (pathsMap.containsKey(nodeKey)) {
output.addAll(pathsMap.get(nodeKey));
return DONE;
}
if (isGoal(aNode)) {
Path<N> path = new Path<N>();
path.addTop(aNode);
output.add(path);
hitSet.add(nodeKey);
pathsMap.put(nodeKey, output);
return DONE;
}
if (hitSet.contains(nodeKey)) {
return CYCLE;
} else {
hitSet.add(nodeKey);
}
Collection<N> nextNodes = getChildren(aNode);
for (N node : nextNodes) {
if (output.size() >= pathThreshold) {
// For performance
break;
}
if (node == null)
continue;
ArrayList<Path<N>> pathSet = new ArrayList<Path<N>>();
int result = findPaths(node, graph, pathSet, pathThreshold);
if (result == CYCLE) {
continue;
} else {
for (Path<N> p : pathSet) {
Path<N> p1 = p.copy();
p1.addTop(aNode);
output.add(p1);
}
}
}
pathsMap.put(nodeKey, output);
return DONE;
}
protected Collection<N> getChildren(N aNode) {
if (isForwardSearch()) {
return graph.targetNodes(aNode);
} else {
return graph.sourceNodes(aNode);
}
}
protected int getPathThreshold() {
return PATH_SET_SIZE_LIMIT;
}
protected abstract boolean isGoal(N aNode);
}