cdc.graphs.core.GraphPathsExplorer Maven / Gradle / Ivy
package cdc.graphs.core;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import cdc.graphs.EdgeDirection;
import cdc.graphs.EdgeTip;
import cdc.graphs.GraphAdapter;
/**
* Utility used to find paths starting or arriving to a node.
*
* Path that arrive to a sink or start on a source are produced.
*
* @author Damien Carbonne
*
* @param Node type.
* @param Edge type.
*/
public class GraphPathsExplorer extends GraphBase {
private class Visitor {
/**
* Set of currently visited nodes.
* It has the same content as stack but is better for searches.
*/
private final Set visited = new HashSet<>();
/**
* Current stack of edges, that produces a path when arriving on a sink or source.
*/
private final GraphPath.Builder stack = GraphPath.builder();
/**
* The exploration direction.
*/
private final EdgeDirection direction;
/**
* Tip to use to find next node following direction.
*/
private final EdgeTip tip;
public Visitor(EdgeDirection direction) {
this.direction = direction;
this.tip = direction == EdgeDirection.INGOING ? EdgeTip.SOURCE : EdgeTip.TARGET;
}
public void traverse(N node,
Consumer> consumer) {
visited.add(node);
for (final E edge : adapter.getEdges(node, direction)) {
final N succ = adapter.getTip(edge, tip);
stack.push(edge);
if (GraphPathsExplorer.this.adapter.hasEdges(succ, direction)) {
if (visited.contains(succ)) {
// Cycle detected
throw new IllegalArgumentException("Cycle detected: " + stack);
} else {
traverse(succ, consumer);
}
} else {
final GraphPath path = stack.build();
consumer.accept(path);
}
stack.pop();
visited.remove(node);
}
}
}
public GraphPathsExplorer(GraphAdapter adapter) {
super(adapter);
}
/**
* Explores all paths that start on a node and arrive to sinks or sources.
*
* @param from The starting node.
* @param direction The exploration direction.
* OUTGOING leads to sinks, INGOING to sources.
* @param consumer The paths consumer.
* @throws IllegalArgumentException When a cycle is detected.
*/
public void explore(N from,
EdgeDirection direction,
Consumer> consumer) {
final Visitor visitor = new Visitor(direction);
visitor.traverse(from, consumer);
}
}