/* * Copyright 2003-2014 JetBrains s.r.o. * * 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 jetbrains.mps.classloading; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; public class GraphHolder<V> { private static final Logger LOG = LogManager.getLogger(GraphHolder.class); private final Graph<V> myGraph; private final Graph<V> myConjugateGraph; // transposed graph public GraphHolder() { myGraph = new Graph<V>(); myConjugateGraph = new Graph<V>(); } public int getEdgesCount() { checkGraphsCorrectness(); return myGraph.getEdgesCount(); } public int getVerticesCount() { checkGraphsCorrectness(); return myGraph.getVerticesCount(); } public void checkGraphsCorrectness() { if (myGraph.getVerticesCount() != myConjugateGraph.getVerticesCount()) { throw new GraphsInconsistencyException("Difference in vertices' count"); } if (myGraph.getEdgesCount() != myConjugateGraph.getEdgesCount()) { throw new GraphsInconsistencyException("Difference in edges' count"); } } public Collection<V> getVertices() { checkGraphsCorrectness(); return Collections.unmodifiableCollection(myGraph.getVertices()); } public void add(V v) { if (getVertices().contains(v)) { LOG.debug("Already watching vertex " + v); return; } myGraph.addVertex(v); myConjugateGraph.addVertex(v); } /** * removes vertex with all its outs and ins * also updates its disposedDeps cache */ public void remove(V v) { Collection<? extends V> outs = myGraph.getOuts(v); Collection<? extends V> backOuts = myConjugateGraph.getOuts(v); myGraph.removeVertex(v); myConjugateGraph.removeVertex(v); for (V v1 : outs) myConjugateGraph.removeEdge(v1, v); for (V v1 : backOuts) myGraph.removeEdge(v1, v); } public boolean addEdge(V v1, V v2) { boolean edgeAdded = myGraph.addEdge(v1, v2); myConjugateGraph.addEdge(v2, v1); return edgeAdded; } public boolean removeEdge(V v1, V v2) { boolean edgeRemoved = myGraph.removeEdge(v1, v2); myConjugateGraph.removeEdge(v2, v1); return edgeRemoved; } public Graph<V> getGraph() { return myGraph; } public Graph<V> getConjugateGraph() { return myConjugateGraph; } public boolean contains(V v) { checkGraphsCorrectness(); return getVertices().contains(v); } public Collection<? extends V> getOutgoingEdges(V v) { checkGraphsCorrectness(); return myGraph.getOuts(v); } // TODO : merge with jetbrains.mps.util.Graph (mps.util.Graph needs to be modified for a bit) static class Graph<V> { private final Map<V, Set<V>> myOuts = new LinkedHashMap<V, Set<V>>(); private int myEdgesCount; public int getEdgesCount() { return myEdgesCount; } public int getVerticesCount() { return myOuts.keySet().size(); } private boolean containsVertex(V v) { return myOuts.containsKey(v); } public boolean addVertex(V v) { if (containsVertex(v)) return false; myOuts.put(v, new LinkedHashSet<V>()); return true; } public boolean removeVertex(V v) { Set<V> removed = myOuts.remove(v); if (removed != null) { myEdgesCount -= removed.size(); } return removed != null; } public boolean addEdge(V v1, V v2) { if (!containsVertex(v1) || !containsVertex(v2)) { throw new IllegalArgumentException("Trying to add an edge between nonexistent vertices"); } Collection<V> vs = myOuts.get(v1); assert vs != null; if (vs.add(v2)) { ++myEdgesCount; return true; } return false; } public boolean removeEdge(V v1, V v2) { Collection<V> vs = myOuts.get(v1); if (vs == null) return false; if (vs.remove(v2)) { --myEdgesCount; return true; } return false; } @NotNull public Collection<? extends V> getOuts(V v) { return myOuts.get(v); } public void dfs(Iterable<? extends V> starts, VertexVisitor<V> visitor) { new DfsTraversal<V>(this, starts, visitor).dfs(); } public Collection<V> getVertices() { return myOuts.keySet(); } private static class DfsTraversal<V> { private final Graph<V> myGraph; private final Set<V> myVisited = new HashSet<V>(); private final Iterable<? extends V> myStartVs; private final VertexVisitor<V> myVisitor; public DfsTraversal(Graph<V> graph, Iterable<? extends V> startVs, VertexVisitor<V> visitor) { myGraph = graph; myStartVs = startVs; myVisitor = visitor; } public void dfs() { for (V v : myStartVs) { if (myVisited.contains(v)) continue; dfs0(v); } } private void dfs0(V v) { assert myGraph.containsVertex(v) : "Graph does not contain vertex " + v; myVisited.add(v); myVisitor.visit(v); for (V vOut : myGraph.getOuts(v)) { if (myVisited.contains(vOut)) continue; dfs0(vOut); } } } public interface VertexVisitor<V> { void visit(V v); } } private static class GraphsInconsistencyException extends RuntimeException { public GraphsInconsistencyException(String msg) { super(msg); } } }