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

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

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

* This implementation favors directed graph operations inherited from * DirectedGraph. * Operations from Graph would tends 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) { LinkedDirectedGraphNode src = getNodeOrFail(srcValue); LinkedDirectedGraphNode dest = getNodeOrFail(destValue); LinkedDirectedGraphEdge edge = useEdgeAnnotations ? new AnnotatedLinkedDirectedGraphEdge<>(src, edgeValue, dest) : new LinkedDirectedGraphEdge<>(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) { LinkedDirectedGraphEdge edge = useEdgeAnnotations ? new AnnotatedLinkedDirectedGraphEdge<>(src, edgeValue, dest) : new LinkedDirectedGraphEdge<>(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 connectIfNotConnectedInDirection(N srcValue, E edgeValue, N destValue) { LinkedDirectedGraphNode src = createDirectedGraphNode(srcValue); LinkedDirectedGraphNode dest = createDirectedGraphNode(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) { LinkedDirectedGraphNode src = getNodeOrFail(srcValue); LinkedDirectedGraphNode dest = getNodeOrFail(destValue); for (DiGraphEdge edge : getDirectedGraphEdges(srcValue, destValue)) { src.getOutEdges().remove(edge); dest.getInEdges().remove(edge); } } @Override public Iterable> getDirectedGraphNodes() { return Collections.>unmodifiableCollection( nodes.values()); } @Override public DiGraphNode getDirectedGraphNode(N nodeValue) { return nodes.get(nodeValue); } @Override public GraphNode getNode(N nodeValue) { return getDirectedGraphNode(nodeValue); } @Override public List> getInEdges(N nodeValue) { LinkedDirectedGraphNode node = getNodeOrFail(nodeValue); return Collections.unmodifiableList(node.getInEdges()); } @Override public List> getOutEdges(N nodeValue) { LinkedDirectedGraphNode node = getNodeOrFail(nodeValue); return Collections.unmodifiableList(node.getOutEdges()); } @Override public LinkedDirectedGraphNode createDirectedGraphNode(N nodeValue) { LinkedDirectedGraphNode node = nodes.computeIfAbsent( nodeValue, (N k) -> useNodeAnnotations ? new AnnotatedLinkedDirectedGraphNode(k) : new LinkedDirectedGraphNode(k)); return node; } @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 = getDirectedGraphEdges(n1, n2); List> backwardEdges = getDirectedGraphEdges(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 (DiGraphNode node : nodes.values()) { result.addAll(node.getOutEdges()); } return Collections.unmodifiableList(result); } @Override public GraphEdge getFirstEdge(N n1, N n2) { DiGraphNode dNode1 = getNodeOrFail(n1); DiGraphNode 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 DiGraphNode createNode(N value) { return createDirectedGraphNode(value); } @Override public List> getDirectedGraphEdges(N n1, N n2) { DiGraphNode dNode1 = getNodeOrFail(n1); DiGraphNode dNode2 = getNodeOrFail(n2); List> edges = new ArrayList<>(); for (DiGraphEdge 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. */ @Override public boolean isConnectedInDirection(N n1, N n2) { return isConnectedInDirection(n1, Predicates.alwaysTrue(), n2); } /** * DiGraphNode look ups can be expensive for a large graph operation, prefer the * version below that takes DiGraphNodes, if you have them available. */ @Override public boolean isConnectedInDirection(N n1, E edgeValue, N n2) { return isConnectedInDirection(n1, Predicates.equalTo(edgeValue), n2); } /** * DiGraphNode look ups can be expensive for a large graph operation, prefer this * method if you have the DiGraphNodes available. */ public boolean isConnectedInDirection( DiGraphNode dNode1, Predicate edgeMatcher, DiGraphNode dNode2) { // Verify the nodes. List> outEdges = dNode1.getOutEdges(); int outEdgesLen = outEdges.size(); List> inEdges = dNode2.getInEdges(); int inEdgesLen = inEdges.size(); // It is possible that there is a large assymmetry 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 (int i = 0; i < outEdgesLen; i++) { DiGraphEdge outEdge = outEdges.get(i); if (outEdge.getDestination() == dNode2 && edgeMatcher.apply(outEdge.getValue())) { return true; } } } else { for (int i = 0; i < inEdgesLen; i++) { DiGraphEdge inEdge = inEdges.get(i); if (inEdge.getSource() == dNode1 && edgeMatcher.apply(inEdge.getValue())) { return true; } } } return false; } private boolean isConnectedInDirection(N n1, Predicate edgeMatcher, N n2) { // Verify the nodes. DiGraphNode dNode1 = getNodeOrFail(n1); DiGraphNode 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); List> nodeList = new ArrayList<>(dNode.getInEdges().size()); for (DiGraphEdge edge : dNode.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); List> nodeList = new ArrayList<>(dNode.getOutEdges().size()); for (DiGraphEdge edge : dNode.getOutEdges()) { nodeList.add(edge.getDestination()); } return nodeList; } @Override public List getGraphvizEdges() { List edgeList = new ArrayList<>(); for (LinkedDirectedGraphNode node : nodes.values()) { for (DiGraphEdge edge : node.getOutEdges()) { edgeList.add((LinkedDirectedGraphEdge) edge); } } return edgeList; } @Override public List getGraphvizNodes() { List nodeList = new ArrayList<>(nodes.size()); for (LinkedDirectedGraphNode node : nodes.values()) { nodeList.add(node); } return nodeList; } @Override public String getName() { return "LinkedGraph"; } @Override public boolean isDirected() { return true; } @Override public Collection> getNodes() { return Collections.>unmodifiableCollection(nodes.values()); } @Override public int getNodeCount() { return nodes.size(); } @Override public List> getNeighborNodes(N value) { DiGraphNode node = getDirectedGraphNode(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) { DiGraphNode 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 LinkedDirectedGraphNode implements DiGraphNode, GraphvizNode { List> inEdgeList = new ArrayList<>(); List> outEdgeList = new ArrayList<>(); protected final N value; /** * Constructor * * @param nodeValue Node's value. */ LinkedDirectedGraphNode(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 String.valueOf(value); } @Override public String toString() { return getLabel(); } @Override public List> getInEdges() { return inEdgeList; } @Override public List> getOutEdges() { return outEdgeList; } } /** * A directed graph node with annotations. */ static class AnnotatedLinkedDirectedGraphNode extends LinkedDirectedGraphNode { protected Annotation annotation; /** * @param nodeValue Node's value. */ AnnotatedLinkedDirectedGraphNode(N nodeValue) { super(nodeValue); } @SuppressWarnings("unchecked") @Override public A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data) { annotation = data; } } /** * A directed graph edge that stores the source and destination nodes at each * edge. */ static class LinkedDirectedGraphEdge implements DiGraphEdge, GraphvizEdge { private DiGraphNode sourceNode; private DiGraphNode destNode; protected final E value; /** * Constructor. * * @param edgeValue Edge Value. */ LinkedDirectedGraphEdge(DiGraphNode sourceNode, E edgeValue, DiGraphNode destNode) { this.value = edgeValue; this.sourceNode = sourceNode; this.destNode = destNode; } @Override public DiGraphNode getSource() { return sourceNode; } @Override public DiGraphNode getDestination() { return destNode; } @Override public void setDestination(DiGraphNode node) { destNode = node; } @Override public void setSource(DiGraphNode node) { sourceNode = node; } @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 ((LinkedDirectedGraphNode) sourceNode).getId(); } @Override public String getNode2Id() { return ((LinkedDirectedGraphNode) 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 class AnnotatedLinkedDirectedGraphEdge extends LinkedDirectedGraphEdge { protected Annotation annotation; /** * Constructor. * * @param edgeValue Edge Value. */ AnnotatedLinkedDirectedGraphEdge(DiGraphNode sourceNode, E edgeValue, DiGraphNode 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