blob: b6b6ae00dc143f3d4bd52c7bf4e370d82c08c677 [file] [log] [blame]
/*
* Copyright 2000-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 com.intellij.util.graph;
import com.intellij.openapi.util.Couple;
import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* @author dsl, ven
*/
public class DFSTBuilder<Node> {
private final Graph<Node> myGraph;
private final Map<Node, Integer> myNodeToNNumber;
private Map<Node, Integer> myNodeToTNumber;
private final Node[] myInvN;
private Couple<Node> myBackEdge = null;
private Comparator<Node> myComparator = null;
private boolean myNBuilt = false;
private boolean myTBuilt = false;
private TIntArrayList mySCCs = null;
private Node[] myInvT;
public DFSTBuilder(Graph<Node> graph) {
myGraph = graph;
myNodeToNNumber = new LinkedHashMap<Node, Integer>(myGraph.getNodes().size() * 2, 0.5f);
myInvN = (Node[])new Object[myGraph.getNodes().size()];
}
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);
}
}
for (Node node : nodes) {
indexN = traverseSubGraph(node, indexN, processed);
}
myNBuilt = true;
}
public Comparator<Node> comparator() {
if (myComparator == null) {
buildDFST();
final Map<Node, Integer> map;
if (isAcyclic()) {
map = myNodeToNNumber;
}
else {
build_T();
map = myNodeToTNumber;
}
myComparator = new Comparator<Node>() {
@Override
public int compare(Node t, Node t1) {
return map.get(t).compareTo(map.get(t1));
}
};
}
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, Integer.valueOf(nNumber));
myInvN[nNumber] = node;
//Check for cycles
if (myBackEdge == null) {
for (Iterator<Node> it = myGraph.getIn(node); it.hasNext(); ) {
Node prev = it.next();
Integer prevNumber = myNodeToNNumber.get(prev);
if (prevNumber != null && prevNumber.intValue() > 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).intValue();
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).intValue() > 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 LinkedHashMap<Node, Integer>(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, Integer.valueOf(currT));
myInvT[currT++]=v;
for (Node w : region) {
if (w != v) {
myNodeToTNumber.put(w, Integer.valueOf(currT));
myInvT[currT++] = w;
}
}
}
}
myTBuilt = true;
}
public Couple<Node> getCircularDependency() {
buildDFST();
return myBackEdge;
}
public boolean isAcyclic () {
return getCircularDependency() == null;
}
public Node getNodeByNNumber (final int n){
return myInvN[n];
}
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)}.
*/
public TIntArrayList getSCCs (){
if (!myNBuilt){
buildDFST();
}
if (!myTBuilt){
build_T();
}
return mySCCs;
}
@NotNull
public List<Node> getSortedNodes() {
List<Node> result = new ArrayList<Node>(myGraph.getNodes());
Collections.sort(result, comparator());
return result;
}
}