All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.graph.Graph Maven / Gradle / Ivy

/*
 * 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.checkNotNull;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.List;

/**
 * The base generic class for graph-like data structure and algorithms in the compiler.
 *
 * 

Nodes and edges in the graph can store a piece of data that this graph is used to represent. * For example, a variable interference graph might store a variable in the node. This piece of data * can be accessed with {@link GraphNode#getValue} and {@link GraphEdge#getValue}. However, in some * cases wrapping the nodes and/or edges in a custom class may be preferrable, as this additional * piece of data must be explicitly cast down from {@link Annotation} to its true type. * *

Algorithms and analysis can annotate information on the nodes and edges using {@link * GraphNode#getValue} and {@link GraphEdge#getValue}. For example, a graph coloring algorithm can * store the color as an annotation. If multiple analyses are required, it is up to the user of the * analysis to save the annotated solution between passes. * *

We implemented our own graph data structure (as opposed to using com.google.common.graph * ) for three reasons. First, aside from the node's label value, we would like to annotate * information on the nodes and edges. Using a map to annotate would introduce too much overhead * during fix point analysis. Also, com.google.common.graph does not support labeling * of edges. Second, avoiding using an external package limits our dependencies. Third, * com.google.common.graph uses WeakRefs which J2CL does not support. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public abstract class Graph implements AdjacencyGraph { /** * Pseudo typedef for a pair of annotations. Record of an object's * annotation at some state. */ private static final class AnnotationState { private final Annotatable first; private final Annotation second; public AnnotationState(Annotatable annotatable, Annotation annotation) { this.first = annotatable; this.second = annotation; } } /** * Pseudo typedef for {@code ArrayList}. Record of a collection of * objects' annotations at some state. */ private static class GraphAnnotationState extends ArrayList { private static final long serialVersionUID = 1L; public GraphAnnotationState(int size) { super(size); } } /** * Used by {@link #pushNodeAnnotations()} and {@link #popNodeAnnotations()}. */ private Deque nodeAnnotationStack; /** * Used by {@link #pushEdgeAnnotations()} and {@link #popEdgeAnnotations()}. */ private Deque edgeAnnotationStack; /** * Connects two nodes in the graph with an edge. * * @param n1 First node. * @param edge The edge. * @param n2 Second node. */ public abstract void connect(N n1, E edge, N n2); /** * Disconnects two nodes in the graph by removing all edges between them. * * @param n1 First node. * @param n2 Second node. */ public abstract void disconnect(N n1, N n2); /** * Connects two nodes in the graph with an edge if such edge does not already * exists between the nodes. * * @param n1 First node. * @param edge The edge. * @param n2 Second node. */ public final void connectIfNotFound(N n1, E edge, N n2) { if (!isConnected(n1, edge, n2)) { connect(n1, edge, n2); } } /** * Gets a node from the graph given a value. New nodes are created if that * value has not been assigned a graph node. Values equality are compared * using Object.equals. * * @param value The node's value. * @return The corresponding node in the graph. */ public abstract GraphNode createNode(N value); /** Gets an immutable list of all nodes. */ @Override public abstract Collection> getNodes(); @Override public abstract int getNodeCount(); /** Gets an immutable list of all edges. */ public abstract List> getEdges(); /** * Retrieves an edge from the graph. * * @param n1 Node one. * @param n2 Node two. * @return The list of edges between those two values in the graph. */ public abstract List> getEdges(N n1, N n2); /** * Gets the degree of a node. * * @param value The node's value. * @return The degree of the node. */ public abstract int getNodeDegree(N value); @Override public int getWeight(N value) { return getNodeDegree(value); } /** * Gets the neighboring nodes. * * @param value The node's value. * @return A list of neighboring nodes. */ public abstract List> getNeighborNodes(N value); /** * Retrieves any edge from the graph. * * @param n1 Node one. * @param n2 Node two. * @return The first edges between those two values in the graph. null if * there are none. */ public abstract GraphEdge getFirstEdge(N n1, N n2); /** * Checks whether the node exists in the graph ({@link #createNode(Object)} * has been called with that value). * * @param n Node. * @return true if it exist. */ public final boolean hasNode(N n) { return getNode(n) != null; } /** * Checks whether two nodes in the graph are connected. * * @param n1 Node 1. * @param n2 Node 2. * @return true if the two nodes are connected. */ public abstract boolean isConnected(N n1, N n2); /** * Checks whether two nodes in the graph are connected by the given * edge type. * * @param n1 Node 1. * @param e The edge type. * @param n2 Node 2. */ public abstract boolean isConnected(N n1, E e, N n2); /** * Gets the node of the specified type, or throws an * IllegalArgumentException. */ @SuppressWarnings("unchecked") > T getNodeOrFail(N val) { T node = (T) getNode(val); if (node == null) { throw new IllegalArgumentException(val + " does not exist in graph"); } return node; } @Override public final void clearNodeAnnotations() { for (GraphNode n : getNodes()) { n.setAnnotation(null); } } /** Makes each edge's annotation null. */ public final void clearEdgeAnnotations() { for (GraphEdge e : getEdges()) { e.setAnnotation(null); } } /** * Pushes nodes' annotation values. Restored with * {@link #popNodeAnnotations()}. Nodes' annotation values are cleared. */ public final void pushNodeAnnotations() { if (nodeAnnotationStack == null) { nodeAnnotationStack = new ArrayDeque<>(); } pushAnnotations(nodeAnnotationStack, getNodes()); } /** * Restores nodes' annotation values to state before last * {@link #pushNodeAnnotations()}. */ public final void popNodeAnnotations() { checkNotNull(nodeAnnotationStack, "Popping node annotations without pushing."); popAnnotations(nodeAnnotationStack); } /** * Pushes edges' annotation values. Restored with * {@link #popEdgeAnnotations()}. Edges' annotation values are cleared. */ public final void pushEdgeAnnotations() { if (edgeAnnotationStack == null) { edgeAnnotationStack = new ArrayDeque<>(); } pushAnnotations(edgeAnnotationStack, getEdges()); } /** * Restores edges' annotation values to state before last * {@link #pushEdgeAnnotations()}. */ public final void popEdgeAnnotations() { checkNotNull(edgeAnnotationStack, "Popping edge annotations without pushing."); popAnnotations(edgeAnnotationStack); } /** * A generic edge. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public interface GraphEdge extends Annotatable { /** * Retrieves the edge's value. * * @return The value. */ E getValue(); GraphNode getNodeA(); GraphNode getNodeB(); } /** * A simple implementation of SubGraph that calculates adjacency by iterating * over a node's neighbors. */ static class SimpleSubGraph implements SubGraph { private final Graph graph; private final List> nodes = new ArrayList<>(); SimpleSubGraph(Graph graph) { this.graph = graph; } @Override public boolean isIndependentOf(N value) { GraphNode node = graph.getNode(value); for (GraphNode n : nodes) { if (graph.getNeighborNodes(n.getValue()).contains(node)) { return false; } } return true; } @Override public void addNode(N value) { nodes.add(graph.getNodeOrFail(value)); } } /** * Pushes a new list on stack and stores nodes annotations in the new list. * Clears objects' annotations as well. */ private static void pushAnnotations( Deque stack, Collection haveAnnotations) { stack.push(new GraphAnnotationState(haveAnnotations.size())); for (Annotatable h : haveAnnotations) { stack.peek().add(new AnnotationState(h, h.getAnnotation())); h.setAnnotation(null); } } /** * Restores the node annotations on the top of stack and pops stack. */ private static void popAnnotations(Deque stack) { for (AnnotationState as : stack.pop()) { as.first.setAnnotation(as.second); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy