// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.graph; import com.google.common.collect.Ordering; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; /** * <p> The DFS class encapsulates a depth-first search visitation, including * the order in which nodes are to be visited relative to their successors * (PREORDER/POSTORDER), whether the forward or transposed graph is to be * used, and which nodes have been seen already. </p> * * <p> A variety of common uses of DFS are offered through methods of * Digraph; however clients can use this class directly for maximum * flexibility. See the implementation of * Digraph.getStronglyConnectedComponents() for an example. </p> * * <p> Clients should not modify the enclosing Digraph instance of a DFS * while a traversal is in progress. </p> */ public class DFS<T> { // (Preferred over a boolean to avoid parameter confusion.) public enum Order { PREORDER, POSTORDER } private final Order order; // = (PREORDER|POSTORDER) private final Comparator<Node<T>> edgeOrder; private final boolean transpose; private final Set<Node<T>> marked = new HashSet<>(); /** * Constructs a DFS instance for searching over the enclosing Digraph * instance, using the specified visitation parameters. * * @param order PREORDER or POSTORDER, determines node visitation order * @param edgeOrder an ordering in which the edges originating from the same * node should be visited (if null, the order is unspecified) * @param transpose iff true, the graph is implicitly transposed during * visitation. */ public DFS(Order order, final Comparator<? super T> edgeOrder, boolean transpose) { this.order = order; this.transpose = transpose; if (edgeOrder == null) { this.edgeOrder = null; } else { this.edgeOrder = new Comparator<Node<T>>() { @Override public int compare(Node<T> o1, Node<T> o2) { return edgeOrder.compare(o1.getLabel(), o2.getLabel()); } }; } } public DFS(Order order, boolean transpose) { this(order, null, transpose); } /** * Returns the (immutable) set of nodes visited so far. */ public Set<Node<T>> getMarked() { return Collections.unmodifiableSet(marked); } public void visit(Node<T> node, GraphVisitor<T> visitor) { if (!marked.add(node)) { return; } if (order == Order.PREORDER) { visitor.visitNode(node); } Collection<Node<T>> edgeTargets = transpose ? node.getPredecessors() : node.getSuccessors(); if (edgeOrder != null) { List<Node<T>> mutableNodeList = Ordering.from(edgeOrder).sortedCopy(edgeTargets); edgeTargets = mutableNodeList; } for (Node<T> v: edgeTargets) { visit(v, visitor); } if (order == Order.POSTORDER) { visitor.visitNode(node); } } }