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

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

Go to download

Closure Compiler is a JavaScript optimizing compiler. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls. It is used in many of Google's JavaScript apps, including Gmail, Google Web Search, Google Maps, and Google Docs.

There is a newer version: v20240317
Show newest version
/*
 * 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.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge;
import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode;
import com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizEdge;
import com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizNode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.jspecify.nullness.Nullable;

/**
 * A directed graph using ArrayLists within nodes to store edge information.
 *
 * 

This implementation favors directed graph operations inherited from * DirectedGraph. Operations from Graph would tend to be slower. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public class LinkedDirectedGraph extends DiGraph implements GraphvizGraph { protected final Map> nodes = new LinkedHashMap<>(); @Override public SubGraph newSubGraph() { return new SimpleSubGraph<>(this); } public static LinkedDirectedGraph createWithoutAnnotations() { return new LinkedDirectedGraph<>(false, false); } public static LinkedDirectedGraph create() { return new LinkedDirectedGraph<>(true, true); } private final boolean useNodeAnnotations; private final boolean useEdgeAnnotations; protected LinkedDirectedGraph( boolean useNodeAnnotations, boolean useEdgeAnnotations) { this.useNodeAnnotations = useNodeAnnotations; this.useEdgeAnnotations = useEdgeAnnotations; } @Override public void connect(N srcValue, E edgeValue, N destValue) { LinkedDiGraphNode src = getNodeOrFail(srcValue); LinkedDiGraphNode dest = getNodeOrFail(destValue); LinkedDiGraphEdge edge = useEdgeAnnotations ? new AnnotatedLinkedDiGraphEdge<>(src, edgeValue, dest) : new LinkedDiGraphEdge<>(src, edgeValue, dest); src.getOutEdges().add(edge); dest.getInEdges().add(edge); } // TODO(johnlenz): make this part of the general graph interface. /** * DiGraphNode look ups can be expensive for a large graph operation, prefer this method if you * have the DiGraphNode available. */ public void connect(DiGraphNode src, E edgeValue, DiGraphNode dest) { LinkedDiGraphNode lSrc = (LinkedDiGraphNode) src; LinkedDiGraphNode lDest = (LinkedDiGraphNode) dest; LinkedDiGraphEdge edge = useEdgeAnnotations ? new AnnotatedLinkedDiGraphEdge<>(lSrc, edgeValue, lDest) : new LinkedDiGraphEdge<>(lSrc, edgeValue, lDest); lSrc.getOutEdges().add(edge); lDest.getInEdges().add(edge); } // TODO(johnlenz): make this part of the general graph interface. /** * DiGraphNode look ups can be expensive for a large graph operation, prefer this * method if you have the DiGraphNode available. */ public void connectIfNotConnectedInDirection(N srcValue, E edgeValue, N destValue) { LinkedDiGraphNode src = createNode(srcValue); LinkedDiGraphNode dest = createNode(destValue); if (!this.isConnectedInDirection(src, Predicates.equalTo(edgeValue), dest)) { this.connect(src, edgeValue, dest); } } @Override public void disconnect(N n1, N n2) { disconnectInDirection(n1, n2); disconnectInDirection(n2, n1); } @Override public void disconnectInDirection(N srcValue, N destValue) { LinkedDiGraphNode src = getNodeOrFail(srcValue); LinkedDiGraphNode dest = getNodeOrFail(destValue); for (DiGraphEdge edge : getEdgesInDirection(srcValue, destValue)) { src.getOutEdges().remove(edge); dest.getInEdges().remove(edge); } } @Override public Collection> getNodes() { return Collections.unmodifiableCollection(nodes.values()); } @Override public LinkedDiGraphNode getNode(N nodeValue) { return nodes.get(nodeValue); } @Override public List> getInEdges(N nodeValue) { LinkedDiGraphNode node = getNodeOrFail(nodeValue); return Collections.unmodifiableList(node.getInEdges()); } @Override public List> getOutEdges(N nodeValue) { LinkedDiGraphNode node = getNodeOrFail(nodeValue); return Collections.unmodifiableList(node.getOutEdges()); } @Override public LinkedDiGraphNode createNode(N nodeValue) { return nodes.computeIfAbsent( nodeValue, (N k) -> useNodeAnnotations ? new AnnotatedLinkedDiGraphNode(k) : new LinkedDiGraphNode(k)); } @Override public List> getEdges(N n1, N n2) { // Since this is a method from a generic graph, edges from both // directions must be added to the returning list. List> forwardEdges = getEdgesInDirection(n1, n2); List> backwardEdges = getEdgesInDirection(n2, n1); int totalSize = forwardEdges.size() + backwardEdges.size(); List> edges = new ArrayList<>(totalSize); edges.addAll(forwardEdges); edges.addAll(backwardEdges); return edges; } @Override public List> getEdges() { List> result = new ArrayList<>(); for (LinkedDiGraphNode node : nodes.values()) { result.addAll(node.getOutEdges()); } return Collections.unmodifiableList(result); } @Override public @Nullable GraphEdge getFirstEdge(N n1, N n2) { LinkedDiGraphNode dNode1 = getNodeOrFail(n1); LinkedDiGraphNode dNode2 = getNodeOrFail(n2); for (DiGraphEdge outEdge : dNode1.getOutEdges()) { if (outEdge.getDestination() == dNode2) { return outEdge; } } for (DiGraphEdge outEdge : dNode2.getOutEdges()) { if (outEdge.getDestination() == dNode1) { return outEdge; } } return null; } @Override public List> getEdgesInDirection(N n1, N n2) { LinkedDiGraphNode dNode1 = getNodeOrFail(n1); LinkedDiGraphNode dNode2 = getNodeOrFail(n2); List> edges = new ArrayList<>(); for (LinkedDiGraphEdge outEdge : dNode1.getOutEdges()) { if (outEdge.getDestination() == dNode2) { edges.add(outEdge); } } return edges; } /** * DiGraphNode look ups can be expensive for a large graph operation, prefer the version below * that takes DiGraphNodes, if you have them available. * * @param source the source node from which we traverse outwards */ @Override public boolean isConnectedInDirection(N source, N dest) { return isConnectedInDirection(source, Predicates.alwaysTrue(), dest); } /** * DiGraphNode look ups can be expensive for a large graph operation, prefer the version below * that takes DiGraphNodes, if you have them available. * * @param source the source node from which we traverse outwards * @param edgeValue only edges equal to the given value will be traversed */ @Override public boolean isConnectedInDirection(N source, E edgeValue, N dest) { return isConnectedInDirection(source, Predicates.equalTo(edgeValue), dest); } /** * DiGraphNode look ups can be expensive for a large graph operation, prefer this method if you * have the DiGraphNodes available. * * @param source the source node from which we traverse outwards * @param edgeFilter only edges matching this filter will be traversed * @param dest the destination node */ public boolean isConnectedInDirection( LinkedDiGraphNode source, Predicate edgeFilter, LinkedDiGraphNode dest) { List> outEdges = source.getOutEdges(); int outEdgesLen = outEdges.size(); List> inEdges = dest.getInEdges(); int inEdgesLen = inEdges.size(); // It is possible that there is a large asymmetry between the nodes, so pick the direction // to search based on the shorter list since the edge lists should be symmetric. if (outEdgesLen < inEdgesLen) { for (DiGraphEdge outEdge : outEdges) { if (outEdge.getDestination() == dest && edgeFilter.apply(outEdge.getValue())) { return true; } } } else { for (DiGraphEdge inEdge : inEdges) { if (inEdge.getSource() == source && edgeFilter.apply(inEdge.getValue())) { return true; } } } return false; } private boolean isConnectedInDirection(N n1, Predicate edgeMatcher, N n2) { // Verify the nodes. LinkedDiGraphNode dNode1 = getNodeOrFail(n1); LinkedDiGraphNode dNode2 = getNodeOrFail(n2); return isConnectedInDirection(dNode1, edgeMatcher, dNode2); } @Override public List> getDirectedPredNodes(N nodeValue) { return getDirectedPredNodes(nodes.get(nodeValue)); } @Override public List> getDirectedPredNodes(DiGraphNode dNode) { checkNotNull(dNode); LinkedDiGraphNode lNode = (LinkedDiGraphNode) dNode; List> nodeList = new ArrayList<>(lNode.getInEdges().size()); for (LinkedDiGraphEdge edge : lNode.getInEdges()) { nodeList.add(edge.getSource()); } return nodeList; } @Override public List> getDirectedSuccNodes(N nodeValue) { return getDirectedSuccNodes(nodes.get(nodeValue)); } @Override public List> getDirectedSuccNodes(DiGraphNode dNode) { checkNotNull(dNode); LinkedDiGraphNode lNode = (LinkedDiGraphNode) dNode; List> nodeList = new ArrayList<>(lNode.getOutEdges().size()); for (LinkedDiGraphEdge edge : lNode.getOutEdges()) { nodeList.add(edge.getDestination()); } return nodeList; } @Override public List getGraphvizEdges() { List edgeList = new ArrayList<>(); for (LinkedDiGraphNode node : nodes.values()) { for (DiGraphEdge edge : node.getOutEdges()) { edgeList.add((LinkedDiGraphEdge) edge); } } return edgeList; } @Override public List getGraphvizNodes() { List nodeList = new ArrayList<>(nodes.size()); for (LinkedDiGraphNode node : nodes.values()) { nodeList.add(node); } return nodeList; } @Override public String getName() { return "LinkedGraph"; } @Override public boolean isDirected() { return true; } @Override public int getNodeCount() { return nodes.size(); } @Override public List> getNeighborNodes(N value) { LinkedDiGraphNode node = getNode(value); List> result = new ArrayList<>( node.getInEdges().size() + node.getOutEdges().size()); for (DiGraphEdge inEdge : node.getInEdges()) { result.add(inEdge.getSource()); } for (DiGraphEdge outEdge : node.getOutEdges()) { result.add(outEdge.getDestination()); } return result; } @Override public int getNodeDegree(N value) { LinkedDiGraphNode node = getNodeOrFail(value); return node.getInEdges().size() + node.getOutEdges().size(); } /** * A directed graph node that stores outgoing edges and incoming edges as an list within the node * itself. */ public static class LinkedDiGraphNode implements DiGraphNode, GraphvizNode { // The overwhelming majority of nodes have in/out degree == 1. Initialize our lists to account // for that. private final List> inEdgeList = new ArrayList<>(/* initialCapacity= */ 1); private final List> outEdgeList = new ArrayList<>(/* initialCapacity= */ 1); protected final N value; private int priority = -1; /** * Constructor * * @param nodeValue Node's value. */ private LinkedDiGraphNode(N nodeValue) { this.value = nodeValue; } @Override public N getValue() { return value; } @Override public A getAnnotation() { throw new UnsupportedOperationException( "Graph initialized with node annotations turned off"); } @Override public void setAnnotation(Annotation data) { throw new UnsupportedOperationException( "Graph initialized with node annotations turned off"); } @Override public String getColor() { return "white"; } @Override public String getId() { return "LDN" + hashCode(); } @Override public String getLabel() { return this.toString(); } @Override public String toString() { return String.valueOf(value); } @Override public List> getInEdges() { return inEdgeList; } @Override public List> getOutEdges() { return outEdgeList; } @Override public boolean hasPriority() { return this.priority >= 0; } @Override public int getPriority() { checkState(this.priority >= 0, "priority not set"); return this.priority; } @Override public void setPriority(int priority) { checkArgument(priority >= 0, "priorities must be non-negative"); this.priority = priority; } } /** A directed graph node with annotations. */ static final class AnnotatedLinkedDiGraphNode extends LinkedDiGraphNode { Annotation annotation; /** @param nodeValue Node's value. */ private AnnotatedLinkedDiGraphNode(N nodeValue) { super(nodeValue); } @SuppressWarnings("unchecked") @Override public A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data) { annotation = data; } @Override public String getLabel() { String result = this.toString(); if (this.annotation != null) { result += "\n" + this.annotation; } return result; } } /** A directed graph edge that stores the source and destination nodes at each edge. */ public static class LinkedDiGraphEdge implements DiGraphEdge, GraphvizEdge { private final LinkedDiGraphNode sourceNode; private final LinkedDiGraphNode destNode; protected final E value; /** * Constructor. * * @param edgeValue Edge Value. */ private LinkedDiGraphEdge( LinkedDiGraphNode sourceNode, E edgeValue, LinkedDiGraphNode destNode) { this.value = edgeValue; this.sourceNode = sourceNode; this.destNode = destNode; } @Override public LinkedDiGraphNode getSource() { return sourceNode; } @Override public LinkedDiGraphNode getDestination() { return destNode; } @Override public E getValue() { return value; } @Override public A getAnnotation() { throw new UnsupportedOperationException( "Graph initialized with edge annotations turned off"); } @Override public void setAnnotation(Annotation data) { throw new UnsupportedOperationException( "Graph initialized with edge annotations turned off"); } @Override public String getColor() { return "black"; } @Override public String getLabel() { return String.valueOf(value); } @Override public String getNode1Id() { return sourceNode.getId(); } @Override public String getNode2Id() { return destNode.getId(); } @Override public String toString() { return sourceNode + " -> " + destNode; } @Override public GraphNode getNodeA() { return sourceNode; } @Override public GraphNode getNodeB() { return destNode; } } /** A directed graph edge that stores the source and destination nodes at each edge. */ static final class AnnotatedLinkedDiGraphEdge extends LinkedDiGraphEdge { Annotation annotation; /** * Constructor. * * @param edgeValue Edge Value. */ private AnnotatedLinkedDiGraphEdge( LinkedDiGraphNode sourceNode, E edgeValue, LinkedDiGraphNode destNode) { super(sourceNode, edgeValue, destNode); } @SuppressWarnings("unchecked") @Override public A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data) { annotation = data; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy