com.google.javascript.jscomp.graph.FixedPointGraphTraversal Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.frontend.js.minifier
Show all versions of com.liferay.frontend.js.minifier
Liferay Frontend JS Minifier
/*
* 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 static com.google.common.base.Preconditions.checkState;
import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge;
import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* A utility class for doing fixed-point computations. We traverse
* the edges over the given directed graph until the graph reaches
* a steady state.
*
* @author [email protected] (Nick Santos)
*
* @param Value type that the graph node stores.
* @param Value type that the graph edge stores.
*/
public final class FixedPointGraphTraversal {
// TODO(nicksantos): Generalize the algorithm for undirected graphs, if we
// need it.
private final EdgeCallback callback;
public static final String NON_HALTING_ERROR_MSG =
"Fixed point computation not halting";
/**
* Create a new traversal.
* @param callback A callback for updating the state of the graph each
* time an edge is traversed.
*/
public FixedPointGraphTraversal(EdgeCallback callback) {
this.callback = callback;
}
/**
* Helper method for creating new traversals.
*/
public static FixedPointGraphTraversal newTraversal(
EdgeCallback callback) {
return new FixedPointGraphTraversal<>(callback);
}
/**
* Compute a fixed point for the given graph.
* @param graph The graph to traverse.
*/
public void computeFixedPoint(DiGraph graph) {
Set nodes = new LinkedHashSet<>();
for (DiGraphNode node : graph.getDirectedGraphNodes()) {
nodes.add(node.getValue());
}
computeFixedPoint(graph, nodes);
}
/**
* Compute a fixed point for the given graph, entering from the given node.
* @param graph The graph to traverse.
* @param entry The node to begin traversing from.
*/
public void computeFixedPoint(DiGraph graph, N entry) {
Set entrySet = new LinkedHashSet<>();
entrySet.add(entry);
computeFixedPoint(graph, entrySet);
}
/**
* Compute a fixed point for the given graph, entering from the given nodes.
* @param graph The graph to traverse.
* @param entrySet The nodes to begin traversing from.
*/
public void computeFixedPoint(DiGraph graph, Set entrySet) {
int cycleCount = 0;
long nodeCount = graph.getNodeCount();
// Choose a bail-out heuristically in case the computation
// doesn't converge.
long maxIterations = Math.max(nodeCount * nodeCount * nodeCount, 100);
// Use a LinkedHashSet, so that the traversal is deterministic.
LinkedHashSet> workSet = new LinkedHashSet<>();
for (N n : entrySet) {
workSet.add(graph.getDirectedGraphNode(n));
}
for (; !workSet.isEmpty() && cycleCount < maxIterations; cycleCount++) {
// For every out edge in the workSet, traverse that edge. If that
// edge updates the state of the graph, then add the destination
// node to the resultSet, so that we can update all of its out edges
// on the next iteration.
DiGraphNode source = workSet.iterator().next();
N sourceValue = source.getValue();
workSet.remove(source);
List> outEdges = source.getOutEdges();
for (DiGraphEdge edge : outEdges) {
N destNode = edge.getDestination().getValue();
if (callback.traverseEdge(sourceValue, edge.getValue(), destNode)) {
workSet.add(edge.getDestination());
}
}
}
checkState(cycleCount != maxIterations, NON_HALTING_ERROR_MSG);
}
/** Edge callback */
public static interface EdgeCallback {
/**
* Update the state of the destination node when the given edge
* is traversed. For the fixed-point computation to work, only the
* destination node may be modified. The source node and the edge must
* not be modified.
*
* @param source The start node.
* @param e The edge.
* @param destination The end node.
* @return Whether the state of the destination node changed.
*/
boolean traverseEdge(Node source, Edge e, Node destination);
}
}