package jenkins.util;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
/**
* A possible cyclic directed graph.
*
* This class defines various algorithms on a directed graph that's not necessarily acyclic.
*
* @author Kohsuke Kawaguchi
*/
public abstract class DirectedGraph<N> {
/**
* All the vertices of the nodes.
*/
protected abstract Collection<N> nodes();
/**
* Forward traversal of the edges.
*/
protected abstract Collection<N> forward(N node);
/**
* Strongly connected component (SCC) of a graph.
*/
public static class SCC<N> extends AbstractSet<N> {
/**
* The Tarjan's algorithm is such that this index constitutes
* the reverse topological order of the topological sort of the SCC DAG.
*
* <p>
* That is, if you think about a derived graph where nodes are SCCs of the original directed graph,
* it will always form a DAG even when the original graph has cycles.
*
* Smallest SCC# means it's more of a sink, and larger SCC# means it's more of a source.
*/
public final int index;
private final List<N> members = new ArrayList<N>();
public SCC(int index) {
this.index = index;
}
@Override
public Iterator<N> iterator() {
return members.iterator();
}
@Override
public int size() {
return members.size();
}
}
/**
* Node of the cyclic graph, which is primarily {@link N} but with additional
* data structures needed for the Tarjan's algorithm.
*/
class Node {
final N n;
/**
* DFS visit order.
*/
int index = -1;
/**
* The smallest index of any nodes reachable from this node transitively.
*/
int lowlink;
SCC scc;
Node(N n) {
this.n = n;
}
Collection<N> edges() {
return forward(n);
}
}
/**
* Performs the Tarjan's algorithm and computes strongly-connected components from the
* sink to source order.
*
* See http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
*/
public List<SCC<N>> getStronglyConnectedComponents() {
final Map<N, Node> nodes = new HashMap<N, Node>();
for (N n : nodes()) {
nodes.put(n,new Node(n));
}
final List<SCC<N>> sccs = new ArrayList<SCC<N>>();
class Tarjan {
int index = 0;
int sccIndex = 0;
/**
* Nodes not yet classified for the strongly connected components
*/
Stack<Node> pending = new Stack<Node>();
void traverse() {
for (Node n : nodes.values()) {
if (n.index==-1)
visit(n);
}
}
void visit(Node v) {
v.index = v.lowlink = index++;
pending.push(v);
for (N q : v.edges()) {
Node w = nodes.get(q);
if (w.index==-1) {
visit(w);
v.lowlink = Math.min(v.lowlink,w.lowlink);
} else
if (pending.contains(w)) {
v.lowlink = Math.min(v.lowlink,w.index);
}
}
if (v.lowlink==v.index) {
// found a new SCC
SCC<N> scc = new SCC<N>(sccIndex++);
sccs.add(scc);
Node w;
do {
w = pending.pop();
w.scc = scc;
scc.members.add(w.n);
} while(w!=v);
}
}
}
new Tarjan().traverse();
Collections.reverse(sccs);
return sccs;
}
}