com.google.javascript.jscomp.graph.LinkedUndirectedGraph 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 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.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* An undirected graph using linked list within nodes to store edge
* information.
*
*
* @param Value type that the graph node stores.
* @param Value type that the graph edge stores.
*/
public final class LinkedUndirectedGraph
extends UndiGraph implements GraphvizGraph {
protected final Map> nodes = new LinkedHashMap<>();
@Override
public SubGraph newSubGraph() {
return new SimpleSubGraph<>(this);
}
public static LinkedUndirectedGraph create() {
return new LinkedUndirectedGraph<>(true, true);
}
private final boolean useNodeAnnotations;
private final boolean useEdgeAnnotations;
protected LinkedUndirectedGraph(
boolean useNodeAnnotations, boolean useEdgeAnnotations) {
this.useNodeAnnotations = useNodeAnnotations;
this.useEdgeAnnotations = useEdgeAnnotations;
}
@Override
public void connect(N srcValue, E edgeValue, N destValue) {
LinkedUndirectedGraphNode src = getNodeOrFail(srcValue);
LinkedUndirectedGraphNode dest = getNodeOrFail(destValue);
LinkedUndirectedGraphEdge edge =
useEdgeAnnotations ?
new AnnotatedLinkedUndirectedGraphEdge<>(src, edgeValue, dest) :
new LinkedUndirectedGraphEdge<>(src, edgeValue, dest);
src.getNeighborEdges().add(edge);
dest.getNeighborEdges().add(edge);
}
@Override
public void disconnect(N srcValue, N destValue) {
LinkedUndirectedGraphNode src = getNodeOrFail(srcValue);
LinkedUndirectedGraphNode dest = getNodeOrFail(destValue);
for (UndiGraphEdge edge :
getUndirectedGraphEdges(srcValue, destValue)) {
src.getNeighborEdges().remove(edge);
dest.getNeighborEdges().remove(edge);
}
}
@Override
public UndiGraphNode createUndirectedGraphNode(
N nodeValue) {
LinkedUndirectedGraphNode node =
nodes.computeIfAbsent(
nodeValue,
(N k) ->
useNodeAnnotations
? new AnnotatedLinkedUndirectedGraphNode(k)
: new LinkedUndirectedGraphNode(k));
return node;
}
@Override
public List> getNeighborNodes(N value) {
UndiGraphNode uNode = getUndirectedGraphNode(value);
return ((LinkedUndirectedGraphNode) uNode).neighborList();
}
@SuppressWarnings("unchecked")
@Override
public List> getUndirectedGraphEdges(N n1, N n2) {
UndiGraphNode dNode1 = nodes.get(n1);
if (dNode1 == null) {
return null;
}
UndiGraphNode dNode2 = nodes.get(n2);
if (dNode2 == null) {
return null;
}
List> edges = new ArrayList<>();
for (UndiGraphEdge outEdge : dNode1.getNeighborEdges()) {
if (outEdge.getNodeA() == dNode2 || outEdge.getNodeB() == dNode2) {
edges.add(outEdge);
}
}
return edges;
}
@Override
public UndiGraphNode getUndirectedGraphNode(N nodeValue) {
return nodes.get(nodeValue);
}
@Override
public Collection> getUndirectedGraphNodes() {
return Collections.unmodifiableCollection(nodes.values());
}
@Override
public GraphNode createNode(N value) {
return createUndirectedGraphNode(value);
}
@Override
public List> getEdges(N n1, N n2) {
return Collections.unmodifiableList(getUndirectedGraphEdges(n1, n2));
}
@SuppressWarnings("unchecked")
@Override
public List> getEdges() {
List> result = new ArrayList<>();
for (LinkedUndirectedGraphNode node : nodes.values()) {
for (UndiGraphEdge edge : node.getNeighborEdges()) {
if (edge.getNodeA() == node) {
result.add(edge);
}
}
}
return result;
}
@Override
public GraphEdge getFirstEdge(N n1, N n2) {
UndiGraphNode dNode1 = getNodeOrFail(n1);
UndiGraphNode dNode2 = getNodeOrFail(n2);
for (UndiGraphEdge outEdge : dNode1.getNeighborEdges()) {
if (outEdge.getNodeA() == dNode2 || outEdge.getNodeB() == dNode2) {
return outEdge;
}
}
return null;
}
@Override
public GraphNode getNode(N value) {
return getUndirectedGraphNode(value);
}
@Override
public boolean isConnected(N n1, N n2) {
return isConnected(n1, Predicates.alwaysTrue(), n2);
}
@Override
public boolean isConnected(N n1, E e, N n2) {
return isConnected(n1, Predicates.equalTo(e), n2);
}
private boolean isConnected(N n1, Predicate edgePredicate, N n2) {
UndiGraphNode dNode1 = nodes.get(n1);
if (dNode1 == null) {
return false;
}
UndiGraphNode dNode2 = nodes.get(n2);
if (dNode2 == null) {
return false;
}
for (UndiGraphEdge outEdge : dNode1.getNeighborEdges()) {
if ((outEdge.getNodeA() == dNode1 && outEdge.getNodeB() == dNode2) ||
(outEdge.getNodeA() == dNode2 && outEdge.getNodeB() == dNode1)) {
if (edgePredicate.apply(outEdge.getValue())) {
return true;
}
}
}
return false;
}
@Override
public List getGraphvizEdges() {
List edgeList = new ArrayList<>();
for (LinkedUndirectedGraphNode node : nodes.values()) {
for (UndiGraphEdge edge : node.getNeighborEdges()) {
if (edge.getNodeA() == node) {
edgeList.add((GraphvizEdge) edge);
}
}
}
return edgeList;
}
@Override
public String getName() {
return "LinkedUndirectedGraph";
}
@Override
public List getGraphvizNodes() {
List nodeList = new ArrayList<>(nodes.size());
for (LinkedUndirectedGraphNode node : nodes.values()) {
nodeList.add(node);
}
return nodeList;
}
@Override
public boolean isDirected() {
return false;
}
@Override
public Collection> getNodes() {
return Collections.unmodifiableCollection(nodes.values());
}
@Override
public int getNodeCount() {
return nodes.size();
}
@Override
public int getNodeDegree(N value) {
UndiGraphNode uNode = getUndirectedGraphNode(value);
if (uNode == null) {
throw new IllegalArgumentException(value + " not found in graph");
}
return uNode.getNeighborEdges().size();
}
/**
* An undirected graph node that stores outgoing edges and incoming edges as
* an list within the node itself.
*/
static class LinkedUndirectedGraphNode implements UndiGraphNode,
GraphvizNode {
private final List> neighborEdges = new ArrayList<>();
private final N value;
LinkedUndirectedGraphNode(N nodeValue) {
this.value = nodeValue;
}
@Override
public List> getNeighborEdges() {
return neighborEdges;
}
@Override
public Iterator> getNeighborEdgesIterator() {
return neighborEdges.iterator();
}
@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 N getValue() {
return value;
}
@Override
public String getColor() {
return "white";
}
@Override
public String getId() {
return "LDN" + hashCode();
}
@Override
public String getLabel() {
return String.valueOf(value);
}
private List> neighborList() {
List> result = new ArrayList<>(neighborEdges.size());
for (UndiGraphEdge edge : neighborEdges) {
if (edge.getNodeA() == this) {
result.add(edge.getNodeB());
} else {
result.add(edge.getNodeA());
}
}
return result;
}
}
/**
* An undirected graph node with annotations.
*/
static class AnnotatedLinkedUndirectedGraphNode
extends LinkedUndirectedGraphNode {
protected Annotation annotation;
AnnotatedLinkedUndirectedGraphNode(N nodeValue) {
super(nodeValue);
}
@SuppressWarnings("unchecked")
@Override
public A getAnnotation() {
return (A) annotation;
}
@Override
public void setAnnotation(Annotation data) {
annotation = data;
}
}
/**
* An undirected graph edge that stores two nodes at each edge.
*/
static class LinkedUndirectedGraphEdge implements UndiGraphEdge,
GraphvizEdge {
private final UndiGraphNode nodeA;
private final UndiGraphNode nodeB;
protected final E value;
LinkedUndirectedGraphEdge(UndiGraphNode nodeA, E edgeValue,
UndiGraphNode nodeB) {
this.value = edgeValue;
this.nodeA = nodeA;
this.nodeB = nodeB;
}
@Override
public E getValue() {
return value;
}
@Override
public GraphNode getNodeA() {
return nodeA;
}
@Override
public GraphNode getNodeB() {
return nodeB;
}
@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);
}
@SuppressWarnings("unchecked")
@Override
public String getNode1Id() {
return ((LinkedUndirectedGraphNode) nodeA).getId();
}
@SuppressWarnings("unchecked")
@Override
public String getNode2Id() {
return ((LinkedUndirectedGraphNode) nodeB).getId();
}
@Override
public String toString() {
return nodeA + " -- " + nodeB;
}
}
/**
* An annotated undirected graph edge..
*/
static class AnnotatedLinkedUndirectedGraphEdge
extends LinkedUndirectedGraphEdge {
protected Annotation annotation;
AnnotatedLinkedUndirectedGraphEdge(
UndiGraphNode nodeA, E edgeValue,
UndiGraphNode nodeB) {
super(nodeA, edgeValue, nodeB);
}
@SuppressWarnings("unchecked")
@Override
public A getAnnotation() {
return (A) annotation;
}
@Override
public void setAnnotation(Annotation data) {
annotation = data;
}
}
}