com.google.javascript.jscomp.graph.LinkedDirectedGraph Maven / Gradle / Ivy
Show all versions of closure-compiler Show documentation
/*
* 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 com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
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 =
Maps.newHashMap();
public SubGraph newSubGraph() {
return new SimpleSubGraph(this);
}
public static LinkedDirectedGraph createWithoutAnnotations() {
return new LinkedDirectedGraph(false, false);
}
public static LinkedDirectedGraph createWithNodeAnnotations() {
return new LinkedDirectedGraph(true, false);
}
public static LinkedDirectedGraph createWithEdgeAnnotations() {
return new LinkedDirectedGraph(false, true);
}
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);
}
@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 DiGraphNode createDirectedGraphNode(N nodeValue) {
LinkedDirectedGraphNode node = nodes.get(nodeValue);
if (node == null) {
node = useNodeAnnotations ?
new AnnotatedLinkedDirectedGraphNode(nodeValue) :
new LinkedDirectedGraphNode(nodeValue);
nodes.put(nodeValue, node);
}
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 = Lists.newArrayListWithCapacity(totalSize);
edges.addAll(forwardEdges);
edges.addAll(backwardEdges);
return edges;
}
@Override
public GraphNode createNode(N value) {
return createDirectedGraphNode(value);
}
@Override
public List> getDirectedGraphEdges(N n1, N n2) {
DiGraphNode dNode1 = getNodeOrFail(n1);
DiGraphNode dNode2 = getNodeOrFail(n2);
List> edges = Lists.newArrayList();
for (DiGraphEdge outEdge : dNode1.getOutEdges()) {
if (outEdge.getDestination() == dNode2) {
edges.add(outEdge);
}
}
return edges;
}
@Override
public boolean isConnectedInDirection(N n1, N n2) {
return isConnectedInDirection(n1, Predicates.alwaysTrue(), n2);
}
@Override
public boolean isConnectedInDirection(N n1, E edgeValue, N n2) {
return isConnectedInDirection(n1, Predicates.equalTo(edgeValue), n2);
}
private boolean isConnectedInDirection(N n1, Predicate edgeMatcher, N n2) {
// Verify the nodes.
DiGraphNode dNode1 = getNodeOrFail(n1);
DiGraphNode dNode2 = getNodeOrFail(n2);
for (DiGraphEdge outEdge : dNode1.getOutEdges()) {
if (outEdge.getDestination() == dNode2 &&
edgeMatcher.apply(outEdge.getValue())) {
return true;
}
}
return false;
}
@Override
public List> getDirectedPredNodes(N nodeValue) {
return getDirectedPredNodes(nodes.get(nodeValue));
}
@Override
public List> getDirectedSuccNodes(N nodeValue) {
return getDirectedSuccNodes(nodes.get(nodeValue));
}
@Override
public List> getDirectedPredNodes(
DiGraphNode dNode) {
if (dNode == null) {
throw new IllegalArgumentException(dNode + " is null");
}
List> nodeList = Lists.newArrayList();
for (DiGraphEdge edge : dNode.getInEdges()) {
nodeList.add(edge.getSource());
}
return nodeList;
}
@Override
public List> getDirectedSuccNodes(
DiGraphNode dNode) {
if (dNode == null) {
throw new IllegalArgumentException(dNode + " is null");
}
List> nodeList = Lists.newArrayList();
for (DiGraphEdge edge : dNode.getOutEdges()) {
nodeList.add(edge.getDestination());
}
return nodeList;
}
@Override
public List getGraphvizEdges() {
List edgeList = Lists.newArrayList();
for (LinkedDirectedGraphNode node : nodes.values()) {
for (DiGraphEdge edge : node.getOutEdges()) {
edgeList.add((LinkedDirectedGraphEdge) edge);
}
}
return edgeList;
}
@Override
public List getGraphvizNodes() {
List nodeList =
Lists.newArrayListWithCapacity(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 List> getNeighborNodes(N value) {
DiGraphNode node = getDirectedGraphNode(value);
return getNeighborNodes(node);
}
public List> getNeighborNodes(DiGraphNode node) {
List> result = Lists.newArrayList();
for (Iterator> i =
((LinkedDirectedGraphNode) node).neighborIterator();i.hasNext();) {
result.add(i.next());
}
return result;
}
@Override
public Iterator> getNeighborNodesIterator(N value) {
LinkedDirectedGraphNode node = nodes.get(value);
Preconditions.checkNotNull(node);
return node.neighborIterator();
}
@Override
public List> getEdges() {
List> result = Lists.newArrayList();
for (DiGraphNode node : nodes.values()) {
for (DiGraphEdge edge : node.getOutEdges()) {
result.add(edge);
}
}
return Collections.unmodifiableList(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.
*/
static class LinkedDirectedGraphNode implements DiGraphNode,
GraphvizNode {
List> inEdgeList = Lists.newArrayList();
List> outEdgeList =
Lists.newArrayList();
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 value != null ? value.toString() : "null";
}
@Override
public String toString() {
return getLabel();
}
@Override
public List> getInEdges() {
return inEdgeList;
}
@Override
public List> getOutEdges() {
return outEdgeList;
}
private Iterator> neighborIterator() {
return new NeighborIterator();
}
private class NeighborIterator implements Iterator> {
private final Iterator> in = inEdgeList.iterator();
private final Iterator> out = outEdgeList.iterator();
@Override
public boolean hasNext() {
return in.hasNext() || out.hasNext();
}
@Override
public GraphNode next() {
boolean isOut = !in.hasNext();
Iterator> curIterator = isOut ? out : in;
DiGraphEdge s = curIterator.next();
return isOut ? s.getDestination() : s.getSource();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Remove not supported.");
}
}
}
/**
* 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 value != null ? value.toString() : "null";
}
@Override
public String getNode1Id() {
return ((LinkedDirectedGraphNode) sourceNode).getId();
}
@Override
public String getNode2Id() {
return ((LinkedDirectedGraphNode) destNode).getId();
}
@Override
public String toString() {
return sourceNode.toString() + " -> " + destNode.toString();
}
@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;
}
}
}