com.google.javascript.jscomp.graph.LinkedUndirectedGraph Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of closure-compiler-unshaded Show documentation
Show all versions of closure-compiler-unshaded Show documentation
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.
The 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 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;
import org.jspecify.nullness.Nullable;
/**
* 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 {
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;
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) {
return nodes.computeIfAbsent(
nodeValue,
(N k) ->
useNodeAnnotations
? new AnnotatedLinkedUndirectedGraphNode(k)
: new LinkedUndirectedGraphNode(k));
}
@Override
public List> getNeighborNodes(N value) {
UndiGraphNode uNode = getUndirectedGraphNode(value);
return ((LinkedUndirectedGraphNode) uNode).neighborList();
}
@SuppressWarnings("unchecked")
@Override
public @Nullable 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 @Nullable 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;
}
}
}