package ch.akuhn.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
/**
*
* @author Adrian Kuhn
*
* @param <E>
*/
public abstract class CycleDetector<E> {
@SuppressWarnings("serial")
private static class CycleFound extends Exception {
}
private class Node {
public int color = 0;
public final E payload;
public Node(E payload) {
this.payload = payload;
}
private final void beBlack() {
color = 2;
}
private final void beGray() {
color = 1;
}
private final Collection<Node> children() {
Collection<E> es = getChildren(payload);
Collection<Node> ns = new ArrayList<Node>(es.size());
for (E e : es) {
Node n = map.get(e);
assert n != null;
ns.add(n);
}
return ns;
}
private final void cycle() throws CycleFound {
if (!isWhite()) return;
beGray();
path.push(this.payload);
for (Node n : children()) {
if (n.isGray()) throw new CycleFound();
if (n.isWhite()) n.cycle();
}
path.pop();
beBlack();
}
private final boolean isGray() {
return color == 1;
}
private final boolean isWhite() {
return color == 0;
}
}
private Map<E,Node> map = new HashMap<E,Node>();
private Stack<E> path = new Stack<E>();
public CycleDetector() {
}
public CycleDetector(Collection<E>... ess) {
for (Collection<E> es : ess)
for (E e : es)
put(e);
}
public abstract Collection<E> getChildren(E payload);
public List<E> getCycle() {
try {
for (Node n : map.values())
n.cycle();
} catch (CycleFound ex) {
return path;
}
return null;
}
public boolean hasCycle() {
return getCycle() != null;
}
public CycleDetector<E> put(E e) {
map.put(e, new Node(e));
return this;
}
}