[util] easier iteration over DFSTBuilder

This commit is contained in:
Roman Shevchenko
2016-08-24 19:31:09 +03:00
parent d32ac9391d
commit 933a49f8d5
6 changed files with 142 additions and 135 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -20,8 +20,6 @@ import com.intellij.util.graph.CachingSemiGraph;
import com.intellij.util.graph.DFSTBuilder;
import com.intellij.util.graph.GraphGenerator;
import gnu.trove.THashMap;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntProcedure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.builders.*;
import org.jetbrains.jps.incremental.CompileContext;
@@ -83,8 +81,7 @@ public class BuildTargetIndexImpl implements BuildTargetIndex {
myDependencies.put(target, realDependencies);
}
GraphGenerator<BuildTarget<?>>
graph = GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph<BuildTarget<?>>() {
GraphGenerator<BuildTarget<?>> graph = GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph<BuildTarget<?>>() {
@Override
public Collection<BuildTarget<?>> getNodes() {
return realTargets;
@@ -96,24 +93,12 @@ public class BuildTargetIndexImpl implements BuildTargetIndex {
}
}));
final DFSTBuilder<BuildTarget<?>> builder = new DFSTBuilder<BuildTarget<?>>(graph);
final TIntArrayList sccs = builder.getSCCs();
myTargetChunks = new ArrayList<BuildTargetChunk>(sccs.size());
sccs.forEach(new TIntProcedure() {
int myTNumber = 0;
public boolean execute(int size) {
final Set<BuildTarget<?>> chunkNodes = new LinkedHashSet<BuildTarget<?>>();
for (int j = 0; j < size; j++) {
final BuildTarget<?> node = builder.getNodeByTNumber(myTNumber + j);
chunkNodes.add(node);
}
myTargetChunks.add(new BuildTargetChunk(chunkNodes));
myTNumber += size;
return true;
}
});
DFSTBuilder<BuildTarget<?>> builder = new DFSTBuilder<BuildTarget<?>>(graph);
Collection<Collection<BuildTarget<?>>> components = builder.getComponents();
myTargetChunks = new ArrayList<BuildTargetChunk>(components.size());
for (Collection<BuildTarget<?>> component : components) {
myTargetChunks.add(new BuildTargetChunk(ContainerUtil.newLinkedHashSet(component)));
}
}
private static Collection<BuildTarget<?>> includeTransitiveDependenciesOfDummyTargets(Collection<BuildTarget<?>> dependencies,
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -21,8 +21,6 @@ import com.intellij.util.Processor;
import com.intellij.util.graph.CachingSemiGraph;
import com.intellij.util.graph.DFSTBuilder;
import com.intellij.util.graph.GraphGenerator;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntProcedure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.incremental.artifacts.JpsBuilderArtifactService;
import org.jetbrains.jps.model.JpsModel;
@@ -84,20 +82,13 @@ public class ArtifactSorter {
final DFSTBuilder<JpsArtifact> builder = new DFSTBuilder<JpsArtifact>(graph);
if (builder.isAcyclic() && result.isEmpty()) return Collections.emptyMap();
final TIntArrayList sccs = builder.getSCCs();
sccs.forEach(new TIntProcedure() {
int myTNumber = 0;
public boolean execute(int size) {
if (size > 1) {
for (int j = 0; j < size; j++) {
final JpsArtifact artifact = builder.getNodeByTNumber(myTNumber + j);
result.put(artifact, artifact);
}
for (Collection<JpsArtifact> component : builder.getComponents()) {
if (component.size() > 1) {
for (JpsArtifact artifact : component) {
result.put(artifact, artifact);
}
myTNumber += size;
return true;
}
});
}
for (int i = 0; i < graph.getNodes().size(); i++) {
final JpsArtifact artifact = builder.getNodeByTNumber(i);
@@ -45,7 +45,6 @@ import com.intellij.util.xmlb.JDOMXIncluder;
import com.intellij.util.xmlb.XmlSerializationException;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import gnu.trove.TIntProcedure;
import gnu.trove.TObjectIntHashMap;
import org.jdom.Document;
import org.jetbrains.annotations.NotNull;
@@ -529,28 +528,17 @@ public class PluginManagerCore {
final Graph<PluginId> graph = createPluginIdGraph(idToDescriptorMap);
final DFSTBuilder<PluginId> builder = new DFSTBuilder<PluginId>(graph);
if (!builder.isAcyclic()) {
final List<String> cycles = new ArrayList<String>();
builder.getSCCs().forEach(new TIntProcedure() {
private int myTNumber;
@Override
public boolean execute(int size) {
if (size > 1) {
String cycle = "";
for (int j = 0; j < size; j++) {
PluginId id = builder.getNodeByTNumber(myTNumber + j);
idToDescriptorMap.get(id).setEnabled(false);
cycle += id.getIdString() + " ";
}
cycles.add(cycle);
}
myTNumber += size;
return true;
}
});
final String cyclePresentation;
if (ApplicationManager.getApplication().isInternal()) {
cyclePresentation = StringUtil.join(cycles, ";");
final StringBuilder cycles = new StringBuilder();
for (Collection<PluginId> component : builder.getComponents()) {
if (cycles.length() > 0) cycles.append(';');
for (PluginId id : component) {
idToDescriptorMap.get(id).setEnabled(false);
cycles.append(id.getIdString()).append(' ');
}
}
cyclePresentation = cycles.toString();
}
else {
final Couple<PluginId> circularDependency = builder.getCircularDependency();
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.graph.impl;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.graph.Graph;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* User: anna
* Date: Feb 13, 2005
* @author anna
* @since Feb 13, 2005
*/
public class CycleFinder<Node> {
private final Graph<Node> myGraph;
@@ -33,59 +33,39 @@ public class CycleFinder<Node> {
}
@NotNull
public Set<List<Node>> getNodeCycles(final Node node){
public Set<List<Node>> getNodeCycles(final Node node) {
final Set<List<Node>> result = new HashSet<>();
final Graph<Node> graphWithoutNode = new Graph<Node>() {
@Override
public Collection<Node> getNodes() {
final Collection<Node> nodes = myGraph.getNodes();
nodes.remove(node);
return nodes;
}
@Override
public Iterator<Node> getIn(final Node n) {
final HashSet<Node> nodes = new HashSet<>();
final Iterator<Node> in = myGraph.getIn(n);
while (in.hasNext()) {
nodes.add(in.next());
}
final Set<Node> nodes = ContainerUtil.newHashSet(myGraph.getIn(n));
nodes.remove(node);
return nodes.iterator();
}
@Override
public Iterator<Node> getOut(final Node n) {
final HashSet<Node> nodes = new HashSet<>();
final Iterator<Node> out = myGraph.getOut(n);
while (out.hasNext()) {
nodes.add(out.next());
}
final Set<Node> nodes = ContainerUtil.newHashSet(myGraph.getOut(n));
nodes.remove(node);
return nodes.iterator();
}
};
final HashSet<Node> inNodes = new HashSet<>();
final Iterator<Node> in = myGraph.getIn(node);
while (in.hasNext()) {
inNodes.add(in.next());
}
final HashSet<Node> outNodes = new HashSet<>();
final Iterator<Node> out = myGraph.getOut(node);
while (out.hasNext()) {
outNodes.add(out.next());
}
final HashSet<Node> retainNodes = new HashSet<>(inNodes);
final Set<Node> inNodes = ContainerUtil.newHashSet(myGraph.getIn(node));
final Set<Node> outNodes = ContainerUtil.newHashSet(myGraph.getOut(node));
final Set<Node> retainNodes = new HashSet<>(inNodes);
retainNodes.retainAll(outNodes);
for (Node node1 : retainNodes) {
ArrayList<Node> oneNodeCycle = new ArrayList<>();
oneNodeCycle.add(node1);
oneNodeCycle.add(node);
result.add(oneNodeCycle);
result.add(ContainerUtil.newArrayList(node1, node));
}
inNodes.removeAll(retainNodes);
outNodes.removeAll(retainNodes);
@@ -94,13 +74,14 @@ public class CycleFinder<Node> {
for (Node toNode : inNodes) {
final List<Node> shortestPath = finder.findPath(fromNode, toNode);
if (shortestPath != null) {
ArrayList<Node> path = new ArrayList<>();
List<Node> path = new ArrayList<>(shortestPath.size() + 1);
path.addAll(shortestPath);
path.add(node);
result.add(path);
}
}
}
return result;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -18,8 +18,6 @@ package com.intellij.util.graph.impl;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.util.Chunk;
import com.intellij.util.graph.*;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntProcedure;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -69,32 +67,27 @@ public class GraphAlgorithmsImpl extends GraphAlgorithms {
@Override
public <Node> Graph<Chunk<Node>> computeSCCGraph(@NotNull final Graph<Node> graph) {
final DFSTBuilder<Node> builder = new DFSTBuilder<>(graph);
final TIntArrayList sccs = builder.getSCCs();
final List<Chunk<Node>> chunks = new ArrayList<>(sccs.size());
final Collection<Collection<Node>> components = builder.getComponents();
final List<Chunk<Node>> chunks = new ArrayList<>(components.size());
final Map<Node, Chunk<Node>> nodeToChunkMap = new LinkedHashMap<>();
sccs.forEach(new TIntProcedure() {
int myTNumber = 0;
public boolean execute(int size) {
final Set<Node> chunkNodes = new LinkedHashSet<>();
final Chunk<Node> chunk = new Chunk<>(chunkNodes);
chunks.add(chunk);
for (int j = 0; j < size; j++) {
final Node node = builder.getNodeByTNumber(myTNumber + j);
chunkNodes.add(node);
nodeToChunkMap.put(node, chunk);
}
myTNumber += size;
return true;
for (Collection<Node> component : components) {
final Set<Node> chunkNodes = new LinkedHashSet<>();
final Chunk<Node> chunk = new Chunk<>(chunkNodes);
chunks.add(chunk);
for (Node node : component) {
chunkNodes.add(node);
nodeToChunkMap.put(node, chunk);
}
});
}
return GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph<Chunk<Node>>() {
@Override
public Collection<Chunk<Node>> getNodes() {
return chunks;
}
@Override
public Iterator<Chunk<Node>> getIn(Chunk<Node> chunk) {
final Set<Node> chunkNodes = chunk.getNodes();
final Set<Chunk<Node>> ins = new LinkedHashSet<>();
@@ -144,4 +137,4 @@ public class GraphAlgorithmsImpl extends GraphAlgorithms {
private static boolean containsCycle(List<?> path) {
return new HashSet<Object>(path).size() != path.size();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -27,7 +27,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* @author dsl, ven
* @author dsl, ven
*/
public class DFSTBuilder<Node> {
private final Graph<Node> myGraph;
@@ -42,6 +42,7 @@ public class DFSTBuilder<Node> {
private final Node[] myInvT; // number in (enumerate all nodes scc by scc) order -> node
private final Node[] myAllNodes;
@SuppressWarnings("unchecked")
public DFSTBuilder(@NotNull Graph<Node> graph) {
myAllNodes = (Node[])graph.getNodes().toArray();
myGraph = graph;
@@ -52,17 +53,13 @@ public class DFSTBuilder<Node> {
new Tarjan().build();
}
@Deprecated
public void buildDFST() {
}
/**
* Tarjan strong-connect-components search algorithm.
* See e.g. <a href="https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm">https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm</a><br>
* This implementation differs from the canonical one above by<br>
* Tarjan's strongly connected components search algorithm
* (<a href="https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm">Wikipedia article</a>).<br>
* This implementation differs from the canonical one above by:<br>
* <ul>
* <li>- being not recursive</li>
* <li>- computing also topological order during the same single pass</li>
* <li>being non-recursive</li>
* <li>also computing a topological order during the same single pass</li>
* </ul>
*/
private class Tarjan {
@@ -91,11 +88,10 @@ public class DFSTBuilder<Node> {
@Override
public String toString() {
final StringBuilder o = new StringBuilder();
for (int id : out) {
o.append(myAllNodes[id] + ", ");
}
return myAllNodes[nodeI] + " -> [" + o + "]";
StringBuilder o = new StringBuilder();
o.append(myAllNodes[nodeI]).append(" -> [");
for (int id : out) o.append(myAllNodes[id]).append(", ");
return o.append(']').toString();
}
}
@@ -149,7 +145,7 @@ public class DFSTBuilder<Node> {
myNodeToNNumber.put(node, index.length - 1 - i);
myInvN[index.length - 1 - i] = node;
}
mySCCs.reverse(); // have to place sccs in topological order too
mySCCs.reverse(); // have to place SCCs in topological order too
}
private void strongConnect(@NotNull List<List<Node>> sccs) {
@@ -174,7 +170,7 @@ public class DFSTBuilder<Node> {
successor = i;
// if unexplored children left, dfs there
while (pair.nextUnexploredIndex<pair.out.length) {
while (pair.nextUnexploredIndex < pair.out.length) {
int nextI = pair.out[pair.nextUnexploredIndex++];
if (index[nextI] == -1) {
frames.push(new Frame(nextI));
@@ -249,10 +245,83 @@ public class DFSTBuilder<Node> {
return mySCCs;
}
@NotNull
public Collection<Collection<Node>> getComponents() {
final TIntArrayList componentSizes = getSCCs();
if (componentSizes.isEmpty()) return Collections.emptyList();
return new MyCollection<Collection<Node>>(componentSizes.size()) {
@Override
public Iterator<Collection<Node>> iterator() {
return new MyIterator<Collection<Node>>(componentSizes.size()) {
private int offset = 0;
@Override
protected Collection<Node> get(int i) {
final int cSize = componentSizes.get(i), cOffset = offset;
if (cSize == 0) return Collections.emptyList();
offset += cSize;
return new MyCollection<Node>(cSize) {
@Override
public Iterator<Node> iterator() {
return new MyIterator<Node>(cSize) {
@Override
public Node get(int i) {
return getNodeByTNumber(cOffset + i);
}
};
}
};
}
};
}
};
}
private static abstract class MyCollection<T> extends AbstractCollection<T> {
private final int size;
protected MyCollection(int size) {
this.size = size;
}
@Override
public int size() {
return size;
}
}
private static abstract class MyIterator<T> implements Iterator<T> {
private final int size;
private int i = 0;
protected MyIterator(int size) {
this.size = size;
}
@Override
public boolean hasNext() {
return i < size;
}
@Override
public T next() {
if (i == size) throw new NoSuchElementException();
return get(i++);
}
protected abstract T get(int i);
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
@NotNull
public List<Node> getSortedNodes() {
List<Node> result = new ArrayList<Node>(myGraph.getNodes());
Collections.sort(result, comparator());
return result;
}
}
}