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

com.google.javascript.jscomp.graph.LinkedDirectedGraph 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.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