/* * Copyright 2008 The Closure Compiler Authors. * * 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.javascript.jscomp.graph; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; /** * Computes all the reachable nodes. Upon execution of {@link #compute(Object)}, * the graph nodes will be annotated with {@link #REACHABLE} if it is reachable * from the specified entry node. * * @param <N> The type of data that the graph node holds. * @param <E> The type of data that the graph edge holds. * * @see GraphNode#getAnnotation() */ public final class GraphReachability<N, E> implements EdgeCallback<N, E> { // TODO(user): This should work for undirected graphs when // FixedPointGraphTraversal accepts them. private final DiGraph<N, E> graph; private final Predicate<EdgeTuple<N, E>> edgePredicate; public GraphReachability(DiGraph<N, E> graph) { this(graph, null); } /** * @param graph The graph. * @param edgePredicate Given the predecessor P of the a node S and the edge * coming from P to S, this predicate should return true if S is * reachable from P using the edge. */ public GraphReachability(DiGraph<N, E> graph, Predicate<EdgeTuple<N, E>> edgePredicate) { this.graph = graph; this.edgePredicate = edgePredicate; } public void compute(N entry) { graph.clearNodeAnnotations(); graph.getNode(entry).setAnnotation(REACHABLE); FixedPointGraphTraversal.newTraversal(this) .computeFixedPoint(graph, entry); } public void recompute(N reachableNode) { GraphNode<N, E> newReachable = graph.getNode(reachableNode); Preconditions.checkState(newReachable.getAnnotation() != REACHABLE); newReachable.setAnnotation(REACHABLE); FixedPointGraphTraversal.newTraversal(this) .computeFixedPoint(graph, reachableNode); } @Override public boolean traverseEdge(N source, E e, N destination) { if (graph.getNode(source).getAnnotation() == REACHABLE && (edgePredicate == null || edgePredicate.apply(new EdgeTuple<>(source, e, destination)))) { GraphNode<N, E> destNode = graph.getNode(destination); if (destNode.getAnnotation() != REACHABLE) { destNode.setAnnotation(REACHABLE); return true; } } return false; } public static final Annotation REACHABLE = new Annotation() {}; /** * Represents a Source Node and an Edge. */ public static final class EdgeTuple<N, E> { public final N sourceNode; public final E edge; public EdgeTuple(N sourceNode, E edge, N destNode) { this.sourceNode = sourceNode; this.edge = edge; } } }