mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
non-recursive DFSTBuilder
This commit is contained in:
+1
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 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.
|
||||
@@ -61,7 +61,6 @@ public class ArtifactSorter {
|
||||
private List<JpsArtifact> doGetSortedArtifacts() {
|
||||
GraphGenerator<JpsArtifact> graph = createArtifactsGraph();
|
||||
DFSTBuilder<JpsArtifact> builder = new DFSTBuilder<JpsArtifact>(graph);
|
||||
builder.buildDFST();
|
||||
List<JpsArtifact> names = new ArrayList<JpsArtifact>();
|
||||
names.addAll(graph.getNodes());
|
||||
Collections.sort(names, builder.comparator());
|
||||
@@ -83,7 +82,6 @@ public class ArtifactSorter {
|
||||
}
|
||||
|
||||
final DFSTBuilder<JpsArtifact> builder = new DFSTBuilder<JpsArtifact>(graph);
|
||||
builder.buildDFST();
|
||||
if (builder.isAcyclic() && result.isEmpty()) return Collections.emptyMap();
|
||||
|
||||
final TIntArrayList sccs = builder.getSCCs();
|
||||
|
||||
+6
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 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.
|
||||
@@ -208,7 +208,11 @@ public class ExtensionPointImplTest {
|
||||
assertThat(ourTestLog.errors(), empty());
|
||||
|
||||
adapter.setFire(false);
|
||||
assertThat(extensionPoint.getExtensions(), arrayContaining("second", "", "first"));
|
||||
String[] extensions = extensionPoint.getExtensions();
|
||||
assertEquals("second", extensions[0]);
|
||||
assertThat(extensions[1], isOneOf("", "first"));
|
||||
assertThat(extensions[2], isOneOf("", "first"));
|
||||
assertNotEquals(extensions[2], extensions[1]);
|
||||
assertThat(ourTestLog.errors(), empty());
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -303,8 +303,8 @@ public class ExtensionsImplTest {
|
||||
assertEquals("1", extensions[0].getText());
|
||||
assertEquals("2", extensions[1].getText());
|
||||
assertEquals("3", extensions[2].getText());
|
||||
assertEquals("4", extensions[3].getText());
|
||||
assertEquals("5", extensions[4].getText());
|
||||
assertTrue("4".equals(extensions[3].getText()) || "5".equals(extensions[3].getText()) );
|
||||
assertTrue("4".equals(extensions[4].getText()) || "5".equals(extensions[4].getText()) );
|
||||
assertEquals("6", extensions[5].getText());
|
||||
assertEquals("7", extensions[6].getText());
|
||||
assertEquals("8", extensions[7].getText());
|
||||
|
||||
+1
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 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.
|
||||
@@ -115,7 +115,6 @@ public class ModulesDependenciesPanel extends JPanel implements ModuleRootListen
|
||||
}
|
||||
myModulesGraph = buildGraph();
|
||||
DFSTBuilder<Module> builder = new DFSTBuilder<Module>(myModulesGraph);
|
||||
builder.buildDFST();
|
||||
if (builder.isAcyclic()){
|
||||
mySplitter.setProportion(1.f);
|
||||
} else {
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
package com.intellij.util.graph;
|
||||
|
||||
import com.intellij.openapi.util.Couple;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.IntStack;
|
||||
import gnu.trove.TIntArrayList;
|
||||
import gnu.trove.TObjectIntHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -27,52 +30,178 @@ import java.util.*;
|
||||
*/
|
||||
public class DFSTBuilder<Node> {
|
||||
private final Graph<Node> myGraph;
|
||||
private final TObjectIntHashMap<Node> myNodeToNNumber;
|
||||
private TObjectIntHashMap<Node> myNodeToTNumber;
|
||||
private final Node[] myInvN;
|
||||
private final TObjectIntHashMap<Node> myNodeToNNumber; // node -> node number in topological order [0..size). Independent nodes are in reversed loading order (loading order is the graph.getNodes() order)
|
||||
private final Node[] myInvN; // node number in topological order [0..size) -> node
|
||||
private Couple<Node> myBackEdge;
|
||||
|
||||
private Comparator<Node> myComparator;
|
||||
private boolean myNBuilt;
|
||||
private boolean myTBuilt;
|
||||
private TIntArrayList mySCCs;
|
||||
private Node[] myInvT;
|
||||
private final TIntArrayList mySCCs = new TIntArrayList(); // strongly connected component sizes
|
||||
private final TObjectIntHashMap<Node> myNodeToTNumber = new TObjectIntHashMap<Node>(); // node -> number in scc topological order. Independent scc are in reversed loading order
|
||||
|
||||
public DFSTBuilder(Graph<Node> graph) {
|
||||
private final Node[] myInvT; // number in (enumerate all nodes scc by scc) order -> node
|
||||
private final Node[] myAllNodes;
|
||||
|
||||
public DFSTBuilder(@NotNull Graph<Node> graph) {
|
||||
myAllNodes = (Node[])graph.getNodes().toArray();
|
||||
myGraph = graph;
|
||||
myNodeToNNumber = new TObjectIntHashMap<Node>(myGraph.getNodes().size() * 2, 0.5f);
|
||||
myInvN = (Node[])new Object[myGraph.getNodes().size()];
|
||||
int size = graph.getNodes().size();
|
||||
myNodeToNNumber = new TObjectIntHashMap<Node>(size * 2, 0.5f);
|
||||
myInvN = (Node[])new Object[size];
|
||||
myInvT = (Node[])new Object[size];
|
||||
new Tarjan().build();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void buildDFST() {
|
||||
if (myNBuilt) return;
|
||||
Collection<Node> nodes = myGraph.getNodes();
|
||||
int indexN = nodes.size();
|
||||
Set<Node> processed = new LinkedHashSet<Node>();
|
||||
for (Node node : nodes) {
|
||||
if (!myGraph.getIn(node).hasNext()) {
|
||||
indexN = traverseSubGraph(node, indexN, processed);
|
||||
}
|
||||
|
||||
private class Tarjan {
|
||||
private final int[] lowLink = new int[myInvN.length];
|
||||
private final int[] index = new int[myInvN.length];
|
||||
|
||||
private final IntStack nodesOnStack = new IntStack();
|
||||
private final boolean[] isOnStack = new boolean[index.length];
|
||||
|
||||
private class Frame {
|
||||
public Frame(int nodeI) {
|
||||
this.nodeI = nodeI;
|
||||
Iterator<Node> outNodes = myGraph.getOut(myAllNodes[nodeI]);
|
||||
TIntArrayList list = new TIntArrayList();
|
||||
|
||||
while (outNodes.hasNext()) {
|
||||
Node node = outNodes.next();
|
||||
list.add(nodeIndex.get(node));
|
||||
}
|
||||
out = list.toNativeArray();
|
||||
}
|
||||
|
||||
private final int nodeI;
|
||||
private final int[] out;
|
||||
private int nextUnexploredIndex;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder o = new StringBuilder();
|
||||
for (int id : out) {
|
||||
o.append(myAllNodes[id] + ", ");
|
||||
}
|
||||
return myAllNodes[nodeI] + " -> [" + o + "]";
|
||||
}
|
||||
}
|
||||
|
||||
for (Node node : nodes) {
|
||||
indexN = traverseSubGraph(node, indexN, processed);
|
||||
private final Stack<Frame> frames = new Stack<Frame>(); // recursion stack
|
||||
private final TObjectIntHashMap<Node> nodeIndex = new TObjectIntHashMap<Node>();
|
||||
private int dfsIndex;
|
||||
private int sccsSizeCombined;
|
||||
private final TIntArrayList topo = new TIntArrayList(index.length); // nodes in reverse topological order
|
||||
|
||||
private void build() {
|
||||
Arrays.fill(index, -1);
|
||||
for (int i = 0; i < myAllNodes.length; i++) {
|
||||
Node node = myAllNodes[i];
|
||||
nodeIndex.put(node, i);
|
||||
}
|
||||
for (int i = 0; i < index.length; i++) {
|
||||
if (index[i] == -1) {
|
||||
frames.push(new Frame(i));
|
||||
List<List<Node>> sccs = new ArrayList<List<Node>>();
|
||||
|
||||
strongConnect(sccs);
|
||||
|
||||
for (List<Node> scc : sccs) {
|
||||
int sccSize = scc.size();
|
||||
|
||||
mySCCs.add(sccSize);
|
||||
int sccBase = index.length - sccsSizeCombined - sccSize;
|
||||
|
||||
// root node should be first in scc for some reason
|
||||
Node rootNode = myAllNodes[i];
|
||||
int rIndex = scc.indexOf(rootNode);
|
||||
if (rIndex != -1) {
|
||||
ContainerUtil.swapElements(scc, rIndex, 0);
|
||||
}
|
||||
|
||||
for (int j = 0; j < scc.size(); j++) {
|
||||
Node sccNode = scc.get(j);
|
||||
int tIndex = sccBase + j;
|
||||
myInvT[tIndex] = sccNode;
|
||||
myNodeToTNumber.put(sccNode, tIndex);
|
||||
}
|
||||
sccsSizeCombined += sccSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < topo.size(); i++) {
|
||||
int nodeI = topo.get(i);
|
||||
Node node = myAllNodes[nodeI];
|
||||
|
||||
myNodeToNNumber.put(node, index.length - 1 - i);
|
||||
myInvN[index.length - 1 - i] = node;
|
||||
}
|
||||
mySCCs.reverse(); // have to place sccs in topological order too
|
||||
}
|
||||
|
||||
myNBuilt = true;
|
||||
private void strongConnect(@NotNull List<List<Node>> sccs) {
|
||||
int successor = -1;
|
||||
nextNode:
|
||||
while (!frames.isEmpty()) {
|
||||
Frame pair = frames.peek();
|
||||
int i = pair.nodeI;
|
||||
|
||||
// we have returned to the node
|
||||
if (index[i] == -1) {
|
||||
// actually we visit node first time, prepare
|
||||
index[i] = dfsIndex;
|
||||
lowLink[i] = dfsIndex;
|
||||
dfsIndex++;
|
||||
nodesOnStack.push(i);
|
||||
isOnStack[i] = true;
|
||||
}
|
||||
if (ArrayUtil.indexOf(pair.out, successor) != -1) {
|
||||
lowLink[i] = Math.min(lowLink[i], lowLink[successor]);
|
||||
}
|
||||
successor = i;
|
||||
|
||||
// if unexplored children left, dfs there
|
||||
while (pair.nextUnexploredIndex<pair.out.length) {
|
||||
int nextI = pair.out[pair.nextUnexploredIndex++];
|
||||
if (index[nextI] == -1) {
|
||||
frames.push(new Frame(nextI));
|
||||
continue nextNode;
|
||||
}
|
||||
if (isOnStack[nextI]) {
|
||||
lowLink[i] = Math.min(lowLink[i], index[nextI]);
|
||||
|
||||
if (myBackEdge == null) {
|
||||
myBackEdge = Couple.of(myAllNodes[nextI], myAllNodes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
frames.pop();
|
||||
topo.add(i);
|
||||
// we are really back, pop a scc
|
||||
if (lowLink[i] == index[i]) {
|
||||
// found yer
|
||||
List<Node> scc = new ArrayList<Node>();
|
||||
int pushedI;
|
||||
do {
|
||||
pushedI = nodesOnStack.pop();
|
||||
Node pushed = myAllNodes[pushedI];
|
||||
isOnStack[pushedI] = false;
|
||||
scc.add(pushed);
|
||||
}
|
||||
while (pushedI != i);
|
||||
sccs.add(scc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Comparator<Node> comparator() {
|
||||
if (myComparator == null) {
|
||||
buildDFST();
|
||||
final TObjectIntHashMap<Node> map;
|
||||
if (isAcyclic()) {
|
||||
map = myNodeToNNumber;
|
||||
}
|
||||
else {
|
||||
build_T();
|
||||
map = myNodeToTNumber;
|
||||
}
|
||||
final TObjectIntHashMap<Node> map = isAcyclic() ? myNodeToNNumber : myNodeToTNumber;
|
||||
myComparator = new Comparator<Node>() {
|
||||
@Override
|
||||
public int compare(@NotNull Node t, @NotNull Node t1) {
|
||||
@@ -83,114 +212,30 @@ public class DFSTBuilder<Node> {
|
||||
return myComparator;
|
||||
}
|
||||
|
||||
private int traverseSubGraph (final Node node, int nNumber, Set<Node> processed) {
|
||||
if (!processed.contains(node)) {
|
||||
processed.add(node);
|
||||
for (Iterator<Node> it = myGraph.getOut(node); it.hasNext(); ) {
|
||||
nNumber = traverseSubGraph(it.next(), nNumber, processed);
|
||||
}
|
||||
|
||||
nNumber--;
|
||||
myNodeToNNumber.put(node, nNumber);
|
||||
myInvN[nNumber] = node;
|
||||
|
||||
//Check for cycles
|
||||
if (myBackEdge == null) {
|
||||
for (Iterator<Node> it = myGraph.getIn(node); it.hasNext(); ) {
|
||||
Node prev = it.next();
|
||||
int prevNumber = myNodeToNNumber.get(prev);
|
||||
if (myNodeToNNumber.containsKey(prev) && prevNumber > nNumber) {
|
||||
myBackEdge = Couple.of(node, prev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nNumber;
|
||||
}
|
||||
|
||||
private Set<Node> region (Node v) {
|
||||
LinkedList<Node> frontier = new LinkedList<Node>();
|
||||
frontier.addFirst(v);
|
||||
Set<Node> result = new LinkedHashSet<Node>();
|
||||
int number = myNodeToNNumber.get(v);
|
||||
while (!frontier.isEmpty()) {
|
||||
Node curr = frontier.removeFirst();
|
||||
result.add(curr);
|
||||
Iterator<Node> it = myGraph.getIn(curr);
|
||||
while (it.hasNext()) {
|
||||
Node w = it.next();
|
||||
if (myNodeToNNumber.get(w) > number && !result.contains(w)) frontier.add(w);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void build_T() {
|
||||
if (myTBuilt) return;
|
||||
|
||||
myInvT = (Node[])new Object[myGraph.getNodes().size()];
|
||||
mySCCs = new TIntArrayList ();
|
||||
|
||||
int size = myGraph.getNodes().size();
|
||||
|
||||
myNodeToTNumber = new TObjectIntHashMap<Node>(size * 2, 0.5f);
|
||||
|
||||
int currT = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
Node v = myInvN[i];
|
||||
if (!myNodeToTNumber.containsKey(v)) {
|
||||
final Set<Node> region = region(v);
|
||||
|
||||
mySCCs.add(region.size());
|
||||
|
||||
myNodeToTNumber.put(v, currT);
|
||||
myInvT[currT++]=v;
|
||||
|
||||
for (Node w : region) {
|
||||
if (w != v) {
|
||||
myNodeToTNumber.put(w, currT);
|
||||
myInvT[currT++] = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
myTBuilt = true;
|
||||
}
|
||||
|
||||
public Couple<Node> getCircularDependency() {
|
||||
buildDFST();
|
||||
return myBackEdge;
|
||||
}
|
||||
|
||||
public boolean isAcyclic () {
|
||||
public boolean isAcyclic() {
|
||||
return getCircularDependency() == null;
|
||||
}
|
||||
|
||||
public Node getNodeByNNumber (final int n){
|
||||
@NotNull
|
||||
public Node getNodeByNNumber(final int n) {
|
||||
return myInvN[n];
|
||||
}
|
||||
|
||||
public Node getNodeByTNumber (final int n){
|
||||
return myInvT[n];
|
||||
}
|
||||
@NotNull
|
||||
public Node getNodeByTNumber(final int n) {
|
||||
return myInvT[n];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the list containing the number of nodes in strongly connected components.
|
||||
* Respective nodes could be obtained via {@link #getNodeByTNumber(int)}.
|
||||
* Respective nodes could be obtained via {@link #getNodeByTNumber(int)}.
|
||||
*/
|
||||
public TIntArrayList getSCCs (){
|
||||
if (!myNBuilt){
|
||||
buildDFST();
|
||||
}
|
||||
|
||||
if (!myTBuilt){
|
||||
build_T();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public TIntArrayList getSCCs() {
|
||||
return mySCCs;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.util.graph;
|
||||
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
@@ -31,7 +32,7 @@ public class DFSTBuilderTest extends TestCase {
|
||||
final TestNode nD = new TestNode("D");
|
||||
final TestNode nE = new TestNode("E");
|
||||
final TestNode nF = new TestNode("F");
|
||||
final TestNode[] allNodes = new TestNode[]{nA, nB, nC, nD, nE, nF};
|
||||
final TestNode[] allNodes = {nA, nB, nC, nD, nE, nF};
|
||||
final Map<TestNode, TestNode[]> map = new HashMap<TestNode, TestNode[]>();
|
||||
map.put(nA, new TestNode[0]);
|
||||
map.put(nB, new TestNode[0]);
|
||||
@@ -54,8 +55,7 @@ public class DFSTBuilderTest extends TestCase {
|
||||
assertTrue(comparator.compare(nB, nF) < 0);
|
||||
}
|
||||
|
||||
private GraphGenerator<TestNode> graphByNodes(final TestNode[] allNodes,
|
||||
final Map<TestNode, TestNode[]> map) {
|
||||
private static GraphGenerator<TestNode> graphByNodes(final TestNode[] allNodes, final Map<TestNode, TestNode[]> mapIn) {
|
||||
final GraphGenerator<TestNode> graph = new GraphGenerator<TestNode>(new GraphGenerator.SemiGraph<TestNode>() {
|
||||
@Override
|
||||
public Collection<TestNode> getNodes() {
|
||||
@@ -64,7 +64,7 @@ public class DFSTBuilderTest extends TestCase {
|
||||
|
||||
@Override
|
||||
public Iterator<TestNode> getIn(TestNode n) {
|
||||
return GraphTestUtil.iteratorOfArray(map.get(n));
|
||||
return GraphTestUtil.iteratorOfArray(ObjectUtils.notNull(mapIn.get(n), new TestNode[0]));
|
||||
}
|
||||
});
|
||||
return graph;
|
||||
@@ -75,7 +75,7 @@ public class DFSTBuilderTest extends TestCase {
|
||||
final TestNode nB = new TestNode("B");
|
||||
final TestNode nC = new TestNode("C");
|
||||
final TestNode nD = new TestNode("D");
|
||||
final TestNode[] allNodes = new TestNode[]{nA, nB, nC, nD};
|
||||
final TestNode[] allNodes = {nA, nB, nC, nD};
|
||||
final Map<TestNode, TestNode[]> map = new HashMap<TestNode, TestNode[]>();
|
||||
map.put(nA, new TestNode[0]);
|
||||
map.put(nB, new TestNode[0]);
|
||||
@@ -85,8 +85,8 @@ public class DFSTBuilderTest extends TestCase {
|
||||
}
|
||||
|
||||
|
||||
private void checkCircularDependecyDetected(final TestNode[] allNodes,
|
||||
final Map<TestNode, TestNode[]> map) {
|
||||
private static void checkCircularDependecyDetected(final TestNode[] allNodes,
|
||||
final Map<TestNode, TestNode[]> map) {
|
||||
GraphGenerator<TestNode> graph = graphByNodes(allNodes, map);
|
||||
final DFSTBuilder<TestNode> dfstBuilder = new DFSTBuilder<TestNode>(graph);
|
||||
assertTrue (dfstBuilder.getCircularDependency() != null);
|
||||
@@ -95,7 +95,7 @@ public class DFSTBuilderTest extends TestCase {
|
||||
public void testCircularDependency2() {
|
||||
final TestNode nA = new TestNode("A");
|
||||
final TestNode nB = new TestNode("B");
|
||||
final TestNode[] allNodes = new TestNode[]{nA, nB};
|
||||
final TestNode[] allNodes = {nA, nB};
|
||||
final Map<TestNode, TestNode[]> map = new HashMap<TestNode, TestNode[]>();
|
||||
map.put(nA, new TestNode[]{nB});
|
||||
map.put(nB, new TestNode[]{nA});
|
||||
@@ -106,7 +106,7 @@ public class DFSTBuilderTest extends TestCase {
|
||||
final TestNode nA = new TestNode("A");
|
||||
final TestNode nB = new TestNode("B");
|
||||
final TestNode nC = new TestNode("C");
|
||||
final TestNode[] allNodes = new TestNode[]{nA, nB, nC};
|
||||
final TestNode[] allNodes = {nA, nB, nC};
|
||||
final Map<TestNode, TestNode[]> map = new HashMap<TestNode, TestNode[]>();
|
||||
map.put(nA, new TestNode[]{nB});
|
||||
map.put(nB, new TestNode[]{nA});
|
||||
@@ -114,12 +114,12 @@ public class DFSTBuilderTest extends TestCase {
|
||||
checkCircularDependecyDetected(allNodes, map);
|
||||
}
|
||||
|
||||
public void testTNumberingSimple () {
|
||||
public void testTNumberingSimple() {
|
||||
final TestNode nA = new TestNode("A");
|
||||
final TestNode nB = new TestNode("B");
|
||||
final TestNode nC = new TestNode("C");
|
||||
final TestNode nD = new TestNode("D");
|
||||
final TestNode[] allNodes = new TestNode[]{nA, nB, nC, nD};
|
||||
final TestNode[] allNodes = {nA, nB, nC, nD};
|
||||
final Map<TestNode, TestNode[]> map = new HashMap<TestNode, TestNode[]>();
|
||||
map.put(nA, new TestNode[]{nC});
|
||||
map.put(nB, new TestNode[]{nA});
|
||||
@@ -127,10 +127,102 @@ public class DFSTBuilderTest extends TestCase {
|
||||
map.put(nD, new TestNode[]{nB});
|
||||
GraphGenerator<TestNode> graph = graphByNodes(allNodes, map);
|
||||
final DFSTBuilder<TestNode> dfstBuilder = new DFSTBuilder<TestNode>(graph);
|
||||
assertTrue (!dfstBuilder.isAcyclic());
|
||||
assertFalse(dfstBuilder.isAcyclic());
|
||||
Comparator<TestNode> comparator = dfstBuilder.comparator();
|
||||
assertTrue(comparator.compare(nA, nD) < 0);
|
||||
assertTrue(comparator.compare(nB, nD) < 0);
|
||||
assertTrue(comparator.compare(nC, nD) < 0);
|
||||
}
|
||||
|
||||
public void testStackOverflow() {
|
||||
final TestNode[] allNodes = new TestNode[10000];
|
||||
final Map<TestNode, TestNode[]> map = new HashMap<TestNode, TestNode[]>();
|
||||
for (int i = 0; i < allNodes.length; i++) {
|
||||
allNodes[i] = new TestNode(i + "");
|
||||
if (i != 0) {
|
||||
map.put(allNodes[i], new TestNode[]{allNodes[i - 1]});
|
||||
}
|
||||
}
|
||||
map.put(allNodes[0], new TestNode[]{allNodes[allNodes.length - 1]});
|
||||
GraphGenerator<TestNode> graph = graphByNodes(allNodes, map);
|
||||
final DFSTBuilder<TestNode> dfstBuilder = new DFSTBuilder<TestNode>(graph);
|
||||
assertFalse(dfstBuilder.isAcyclic());
|
||||
}
|
||||
|
||||
public void testSccsReportedInLoadingOrder() {
|
||||
final TestNode main = new TestNode("main");
|
||||
final TestNode dep = new TestNode("dep");
|
||||
final TestNode d = new TestNode("d");
|
||||
final TestNode d2 = new TestNode("d2");
|
||||
final TestNode resMain = new TestNode("resMain");
|
||||
final TestNode resDep = new TestNode("resDep");
|
||||
final TestNode[] allNodes = {main, dep, d, d2, resMain, resDep};
|
||||
final Map<TestNode, TestNode[]> mapIn = new HashMap<TestNode, TestNode[]>();
|
||||
mapIn.put(main, new TestNode[]{d, resMain});
|
||||
mapIn.put(dep, new TestNode[]{main,resDep});
|
||||
mapIn.put(d, new TestNode[]{d2});
|
||||
mapIn.put(d2, new TestNode[]{dep, d});
|
||||
GraphGenerator<TestNode> graph = graphByNodes(allNodes, mapIn);
|
||||
|
||||
final DFSTBuilder<TestNode> dfstBuilder = new DFSTBuilder<TestNode>(graph);
|
||||
assertTrue (!dfstBuilder.isAcyclic());
|
||||
Comparator<TestNode> comparator = dfstBuilder.comparator();
|
||||
assertTrue(comparator.compare(resMain, main) < 0);
|
||||
assertTrue(comparator.compare(resMain, d) < 0);
|
||||
assertTrue(comparator.compare(resMain, d2) < 0);
|
||||
assertTrue(comparator.compare(resDep, dep) < 0);
|
||||
assertTrue(comparator.compare(resMain, resDep) > 0); //reversed loading order
|
||||
}
|
||||
|
||||
public void testReportedInLoadingOrder() {
|
||||
final TestNode o = new TestNode("o");
|
||||
final TestNode a = new TestNode("a");
|
||||
final TestNode b = new TestNode("b");
|
||||
final TestNode c = new TestNode("c");
|
||||
for (int oIndex = 0; oIndex<4; oIndex++) {
|
||||
List<TestNode> list = new ArrayList<TestNode>(Arrays.asList(a,b,c));
|
||||
list.add(oIndex, o);
|
||||
TestNode[] allNodes = list.toArray(new TestNode[list.size()]);
|
||||
|
||||
Map<TestNode, TestNode[]> mapIn = new HashMap<TestNode, TestNode[]>();
|
||||
mapIn.put(o, new TestNode[]{a,b,c});
|
||||
|
||||
final DFSTBuilder<TestNode> dfstBuilder = new DFSTBuilder<TestNode>(graphByNodes(allNodes, mapIn));
|
||||
assertTrue (dfstBuilder.isAcyclic());
|
||||
Comparator<TestNode> comparator = dfstBuilder.comparator();
|
||||
TestNode[] sorted = allNodes.clone();
|
||||
Arrays.sort(sorted, comparator);
|
||||
assertEquals("All nodes: "+list, Arrays.asList(c,b,a,o), Arrays.asList(sorted));
|
||||
}
|
||||
}
|
||||
public void testSccReportedInLoadingOrder() {
|
||||
final TestNode o1 = new TestNode("o1");
|
||||
final TestNode o2 = new TestNode("o2");
|
||||
final TestNode a = new TestNode("a");
|
||||
final TestNode b = new TestNode("b");
|
||||
final TestNode c = new TestNode("c");
|
||||
for (int oIndex = 0; oIndex<4; oIndex++) {
|
||||
List<TestNode> list = new ArrayList<TestNode>(Arrays.asList(a,b,c));
|
||||
list.add(oIndex, o1);
|
||||
list.add(oIndex, o2);
|
||||
TestNode[] allNodes = list.toArray(new TestNode[list.size()]);
|
||||
|
||||
Map<TestNode, TestNode[]> mapIn = new HashMap<TestNode, TestNode[]>();
|
||||
mapIn.put(o1, new TestNode[]{a,b,c,o2});
|
||||
mapIn.put(o2, new TestNode[]{o1});
|
||||
|
||||
final DFSTBuilder<TestNode> dfstBuilder = new DFSTBuilder<TestNode>(graphByNodes(allNodes, mapIn));
|
||||
assertFalse(dfstBuilder.isAcyclic());
|
||||
Comparator<TestNode> comparator = dfstBuilder.comparator();
|
||||
assertTrue("All nodes: "+list,comparator.compare(b, a) < 0); //reversed loading order
|
||||
assertTrue("All nodes: "+list,comparator.compare(c, a) < 0); //reversed loading order
|
||||
assertTrue("All nodes: "+list,comparator.compare(c, b) < 0); //reversed loading order
|
||||
assertTrue("All nodes: "+list,comparator.compare(a, o1) < 0);
|
||||
assertTrue("All nodes: "+list,comparator.compare(a, o2) < 0);
|
||||
assertTrue("All nodes: "+list,comparator.compare(b, o1) < 0);
|
||||
assertTrue("All nodes: "+list,comparator.compare(b, o2) < 0);
|
||||
assertTrue("All nodes: "+list,comparator.compare(c, o1) < 0);
|
||||
assertTrue("All nodes: "+list,comparator.compare(c, o2) < 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user