/* Modified code from
* ==========================================
* JGraphT : a free Java graph-theory library
* ==========================================
*
* Project Info: http://jgrapht.sourceforge.net/
* Project Creator: Barak Naveh (http://sourceforge.net/users/barak_naveh)
*
* (C) Copyright 2003-2007, by Barak Naveh and Contributors.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
package com.jopdesign.common.graphutils;
import org.jgrapht.DirectedGraph;
import org.jgrapht.traverse.DepthFirstIterator;
import java.util.ArrayList;
import java.util.List;
/**
* Modified cycle detector, which returns the cycle actually found, if there is one in the
* given directed graph. The standard cycle detector of JGraphT doesn't report the cycle
* itself, only the vertices involved - this makes debugging rather tricky.
*
* @author Benedikt Huber (benedikt.huber@gmail.com)
* @author Barak Naveh and Contributors (jgrapht)
*/
public class DirectedCycleDetector {
@SuppressWarnings({"unchecked"})
public static <V, E> Pair<List<V>, List<V>> findCycle(DirectedGraph<V, E> g, V start) {
ProbeIterator pi = new ProbeIterator<V, E>(g, start);
try {
while (pi.hasNext()) pi.next();
return null;
} catch (CycleDetectedException ex) {
return new Pair<List<V>, List<V>>(ex.getPrefix(), ex.getCyclePath());
}
}
@SuppressWarnings({"unchecked", "UncheckedExceptionClass"})
/* exception classes may not be generic */
private static class CycleDetectedException extends Error {
private static final long serialVersionUID = 1L;
private List prefix;
private List cyclePath;
public CycleDetectedException(List prefix, List path) {
super("CycleDetected: " + path + "reachable via " + prefix);
this.prefix = prefix;
this.cyclePath = path;
}
public List getPrefix() {
return prefix;
}
public List getCyclePath() {
return cyclePath;
}
}
/**
* Version of DFS which maintains a backtracking path used to probe for
* cycles (Modified from JGraphT)
*/
private static class ProbeIterator<V, E>
extends DepthFirstIterator<V, E> {
private List<V> path;
private DirectedGraph<V, E> graph;
ProbeIterator(DirectedGraph<V, E> graph, V startVertex) {
super(graph, startVertex);
this.graph = graph;
path = new ArrayList<V>();
}
protected void encounterVertexAgain(V vertex, E edge) {
super.encounterVertexAgain(vertex, edge);
int i = path.indexOf(vertex);
if (i > -1) {
List<V> prefix;
if (i > 0) prefix = new ArrayList<V>(path.subList(0, i));
else prefix = new ArrayList<V>();
List<V> subPath = path.subList(i, path.size());
subPath.add(vertex);
throw new CycleDetectedException(prefix, subPath);
}
}
protected V provideNextVertex() {
V v = super.provideNextVertex();
// backtrack
for (int i = path.size() - 1; i >= 0; --i) {
if (graph.containsEdge(path.get(i), v)) {
break;
}
path.remove(i);
}
path.add(v);
return v;
}
}
}