cdc.graphs.core.GraphTraverser Maven / Gradle / Ivy
package cdc.graphs.core;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import cdc.graphs.EdgeDirection;
import cdc.graphs.EdgeTip;
import cdc.graphs.GraphAdapter;
import cdc.graphs.TraversalMethod;
import cdc.graphs.TraversalOrder;
import cdc.util.function.Evaluator;
import cdc.util.function.Visitor;
import cdc.util.lang.Checks;
/**
* Class used to visit a graph.
*
* It is possible to choose:
*
* - The traversal method: breadth first or depth first.
*
- The traversal order: pre or post.
*
- The traversal direction: ingoing or outgoing.
*
*
* @author Damien Carbonne
*
* @param Node class
* @param Edge class
*/
public class GraphTraverser extends GraphBase {
static final Logger LOGGER = LogManager.getLogger(GraphTraverser.class);
private static final String DIRECTION = "direction";
private static final String METHOD = "method";
private static final String NODE = "node";
private static final String ORDER = "order";
private static final String VISITOR = "visitor";
public GraphTraverser(GraphAdapter adapter) {
super(adapter);
}
/**
* Most generic traversal function.
*
* @param node The initial node. Must not be null.
* @param method The traversal method. Must not be null.
* @param order The traversal order. Must not be null.
* @param direction The edge direction to follow for traversal. Must not be null.
* @param visitor Visitor that will be invoked for each visited node. Must not be null.
* @param evaluator An optional evaluator used to control traversal
* termination.
*/
public void traverse(N node,
TraversalMethod method,
TraversalOrder order,
EdgeDirection direction,
Visitor visitor,
Evaluator evaluator) {
Checks.isNotNull(node, NODE);
Checks.isNotNull(method, METHOD);
Checks.isNotNull(order, ORDER);
Checks.isNotNull(direction, DIRECTION);
Checks.isNotNull(visitor, VISITOR);
if (method == TraversalMethod.BREADTH_FIRST) {
traverseBreadthFirst(node, order, direction, visitor, evaluator);
} else {
traverseDepthFirst(node, order, direction, visitor, evaluator);
}
}
/**
* Generic traversal, without evaluator.
*
* @param node The initial node. Must not be null.
* @param method The traversal method. Must not be null.
* @param order The traversal order. Must not be null.
* @param direction The edge direction to follow for traversal. Must not be null.
* @param visitor Visitor that will be invoked for each visited node. Must not be null.
*/
public void traverse(N node,
TraversalMethod method,
TraversalOrder order,
EdgeDirection direction,
Visitor visitor) {
traverse(node, method, order, direction, visitor, null);
}
/**
* Depth first traversal.
*
* @param node The initial node. Must not be null.
* @param order The traversal order. Must not be null.
* @param direction The edge direction to follow for traversal. Must not be null.
* @param visitor Visitor that will be invoked for each visited node. Must not be null.
* @param evaluator An optional evaluator used to control traversal
* termination.
*/
public void traverseDepthFirst(N node,
TraversalOrder order,
EdgeDirection direction,
Visitor visitor,
Evaluator evaluator) {
Checks.isNotNull(node, NODE);
Checks.isNotNull(order, ORDER);
Checks.isNotNull(direction, DIRECTION);
Checks.isNotNull(visitor, VISITOR);
final DepthFirstTraverser traverser = new DepthFirstTraverser(direction);
if (order == TraversalOrder.POST_ORDER) {
traverser.traversePost(node, visitor, evaluator);
} else {
traverser.traversePre(node, visitor, evaluator);
}
}
/**
* Depth first traversal without evaluator.
*
* @param node The initial node. Must not be null.
* @param order The traversal order. Must not be null.
* @param direction The edge direction to follow for traversal. Must not be null.
* @param visitor Visitor that will be invoked for each visited node. Must not be null.
*/
public void traverseDepthFirst(N node,
TraversalOrder order,
EdgeDirection direction,
Visitor visitor) {
traverseDepthFirst(node, order, direction, visitor, null);
}
/**
* Depth first pre order traversal.
*
* @param node The initial node. Must not be null.
* @param direction The edge direction to follow for traversal. Must not be null.
* @param visitor Visitor that will be invoked for each visited node. Must not be null.
* @param evaluator An optional evaluator used to control traversal
* termination.
*/
public void traverseDepthFirstPre(N node,
EdgeDirection direction,
Visitor visitor,
Evaluator evaluator) {
Checks.isNotNull(node, NODE);
Checks.isNotNull(direction, DIRECTION);
Checks.isNotNull(visitor, VISITOR);
final DepthFirstTraverser traverser = new DepthFirstTraverser(direction);
traverser.traversePre(node, visitor, evaluator);
}
/**
* Depth first pre order traversal without evaluator.
*
* @param node The initial node. Must not be null.
* @param direction The edge direction to follow for traversal. Must not be null.
* @param visitor Visitor that will be invoked for each visited node. Must not be null.
*/
public void traverseDepthFirstPre(N node,
EdgeDirection direction,
Visitor visitor) {
traverseDepthFirstPre(node, direction, visitor, null);
}
/**
* Depth first post order traversal.
*
* @param node The initial node. Must not be null.
* @param direction The edge direction to follow for traversal. Must not be null.
* @param visitor Visitor that will be invoked for each visited node. Must not be null.
* @param evaluator An optional evaluator used to control traversal
* termination.
*/
public void traverseDepthFirstPost(N node,
EdgeDirection direction,
Visitor visitor,
Evaluator evaluator) {
Checks.isNotNull(node, NODE);
Checks.isNotNull(direction, DIRECTION);
Checks.isNotNull(visitor, VISITOR);
final DepthFirstTraverser traverser = new DepthFirstTraverser(direction);
traverser.traversePost(node, visitor, evaluator);
}
/**
* Depth first post order traversal without evaluator.
*
* @param node The initial node. Must not be null.
* @param direction The edge direction to follow for traversal. Must not be null.
* @param visitor Visitor that will be invoked for each visited node. Must not be null.
*/
public void traverseDepthFirstPost(N node,
EdgeDirection direction,
Visitor visitor) {
traverseDepthFirstPost(node, direction, visitor, null);
}
/**
* Breadth first traversal.
*
* @param node The initial node. Must not be null.
* @param order The traversal order. Must not be null.
* @param direction The edge direction to follow for traversal. Must not be null.
* @param visitor Visitor that will be invoked for each visited node. Must not be null.
* @param evaluator An optional evaluator used to control traversal
* termination.
*/
public void traverseBreadthFirst(N node,
TraversalOrder order,
EdgeDirection direction,
Visitor visitor,
Evaluator evaluator) {
Checks.isNotNull(node, NODE);
Checks.isNotNull(order, ORDER);
Checks.isNotNull(direction, DIRECTION);
Checks.isNotNull(visitor, VISITOR);
final BreadthFirstTraverser traverser = new BreadthFirstTraverser(direction);
if (order == TraversalOrder.POST_ORDER) {
traverser.traversePost(node, visitor, evaluator);
} else {
traverser.traversePre(node, visitor, evaluator);
}
}
/**
* Breadth first traversal without evaluator.
*
* @param node The initial node. Must not be null.
* @param order The traversal order. Must not be null.
* @param direction The edge direction to follow for traversal. Must not be null.
* @param visitor Visitor that will be invoked for each visited node. Must not be null.
*/
public void traverseBreadthFirst(N node,
TraversalOrder order,
EdgeDirection direction,
Visitor visitor) {
traverseBreadthFirst(node, order, direction, visitor, null);
}
/**
* Breadth first pre order traversal.
*
* @param node The initial node. Must not be null.
* @param direction The edge direction to follow for traversal. Must not be null.
* @param visitor Visitor that will be invoked for each visited node. Must not be null.
* @param evaluator An optional evaluator used to control traversal
* termination.
*/
public void traverseBreadthFirstPre(N node,
EdgeDirection direction,
Visitor visitor,
Evaluator evaluator) {
Checks.isNotNull(node, NODE);
Checks.isNotNull(direction, DIRECTION);
Checks.isNotNull(visitor, VISITOR);
final BreadthFirstTraverser traverser = new BreadthFirstTraverser(direction);
traverser.traversePre(node, visitor, evaluator);
}
/**
* Breadth first pre order traversal without evaluator.
*
* @param node The initial node. Must not be null.
* @param direction The edge direction to follow for traversal. Must not be null.
* @param visitor Visitor that will be invoked for each visited node. Must not be null.
*/
public void traverseBreadthFirstPre(N node,
EdgeDirection direction,
Visitor visitor) {
traverseBreadthFirstPre(node, direction, visitor, null);
}
/**
* Breadth first post order traversal.
*
* @param node The initial node. Must not be null.
* @param direction The edge direction to follow for traversal. Must not be null.
* @param visitor Visitor that will be invoked for each visited node. Must not be null.
* @param evaluator An optional evaluator used to control traversal
* termination.
*/
public void traverseBreadthFirstPost(N node,
EdgeDirection direction,
Visitor visitor,
Evaluator evaluator) {
Checks.isNotNull(node, NODE);
Checks.isNotNull(direction, DIRECTION);
Checks.isNotNull(visitor, VISITOR);
final BreadthFirstTraverser traverser = new BreadthFirstTraverser(direction);
traverser.traversePost(node, visitor, evaluator);
}
/**
* Breadth first post order traversal without evaluator.
*
* @param node The initial node. Must not be null.
* @param direction The edge direction to follow for traversal. Must not be null.
* @param visitor Visitor that will be invoked for each visited node. Must not be null.
*/
public void traverseBreadthFirstPost(N node,
EdgeDirection direction,
Visitor visitor) {
traverseBreadthFirstPost(node, direction, visitor, null);
}
/**
* Depth first traversal class.
*
* @author Damien Carbonne
*
*/
private class DepthFirstTraverser {
private final Set visited = new HashSet<>();
private final EdgeDirection direction;
private final EdgeTip tip;
public DepthFirstTraverser(EdgeDirection direction) {
this.direction = direction;
this.tip = direction == EdgeDirection.INGOING ? EdgeTip.SOURCE : EdgeTip.TARGET;
}
void traversePre(N node,
Visitor visitor,
Evaluator evaluator) {
visitor.visit(node);
if (Evaluator.continueTraversal(evaluator, node)) {
visited.add(node);
for (final E edge : adapter.getEdges(node, direction)) {
final N succ = adapter.getTip(edge, tip);
if (!visited.contains(succ)) {
traversePre(succ, visitor, evaluator);
}
}
}
}
void traversePost(N node,
Visitor visitor,
Evaluator evaluator) {
if (Evaluator.continueTraversal(evaluator, node)) {
visited.add(node);
for (final E edge : adapter.getEdges(node, direction)) {
final N succ = adapter.getTip(edge, tip);
if (!visited.contains(succ)) {
traversePost(succ, visitor, evaluator);
}
}
}
visitor.visit(node);
}
}
/**
* Breadth first traversal class.
*
* @author Damien Carbonne
*
*/
private class BreadthFirstTraverser {
private final EdgeDirection direction;
private final EdgeTip tip;
public BreadthFirstTraverser(EdgeDirection direction) {
this.direction = direction;
this.tip = direction == EdgeDirection.INGOING ? EdgeTip.SOURCE : EdgeTip.TARGET;
}
void traversePre(N from,
Visitor visitor,
Evaluator evaluator) {
final Deque q = new ArrayDeque<>();
final Set visited = new HashSet<>();
q.addLast(from);
visited.add(from);
while (!q.isEmpty()) {
final N node = q.pollFirst();
visitor.visit(node);
if (Evaluator.continueTraversal(evaluator, node)) {
for (final E e : adapter.getEdges(node, direction)) {
final N y = adapter.getTip(e, tip);
if (!visited.contains(y)) {
visited.add(y);
q.addLast(y);
}
}
}
}
}
void traversePost(N from,
Visitor visitor,
Evaluator evaluator) {
final List visited = new ArrayList<>();
traversePre(from, visited::add, evaluator);
for (int index = visited.size() - 1; index >= 0; index--) {
final N node = visited.get(index);
visitor.visit(node);
}
}
}
}