blob: fd530a94d1e4822d40db9449179ed90bc5fb444c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.draw2d.graph;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* Performs a topological sort from left to right of the subgraphs in a compound
* directed graph. This ensures that subgraphs do not intertwine.
*
* @author Randy Hudson
* @since 2.1.2
*/
class SortSubgraphs extends GraphVisitor {
CompoundDirectedGraph g;
NestingTree nestingTrees[];
Set orderingGraphEdges = new HashSet();
Set orderingGraphNodes = new HashSet();
NodePair pair = new NodePair();
private void breakSubgraphCycles() {
// The stack of nodes which have no unmarked incoming edges
List noLefts = new ArrayList();
int index = 1;
// Identify all initial nodes for removal
for (Iterator iter = orderingGraphNodes.iterator(); iter.hasNext();) {
Node node = (Node) iter.next();
if (node.x == 0)
sortedInsert(noLefts, node);
}
Node cycleRoot;
do {
// Remove all leftmost nodes, updating the nodes to their right
while (noLefts.size() > 0) {
Node node = (Node) noLefts.remove(noLefts.size() - 1);
node.sortValue = index++;
orderingGraphNodes.remove(node);
// System.out.println("removed:" + node);
NodeList rightOf = rightOf(node);
if (rightOf == null)
continue;
for (int i = 0; i < rightOf.size(); i++) {
Node right = rightOf.getNode(i);
right.x--;
if (right.x == 0)
sortedInsert(noLefts, right);
}
}
cycleRoot = null;
double min = Double.MAX_VALUE;
for (Iterator iter = orderingGraphNodes.iterator(); iter.hasNext();) {
Node node = (Node) iter.next();
if (node.sortValue < min) {
cycleRoot = node;
min = node.sortValue;
}
}
if (cycleRoot != null) {
// break the cycle;
sortedInsert(noLefts, cycleRoot);
// System.out.println("breaking cycle with:" + cycleRoot);
// Display.getCurrent().beep();
cycleRoot.x = -1; // prevent x from ever reaching 0
} // else if (OGmembers.size() > 0)
//System.out.println("FAILED TO FIND CYCLE ROOT"); //$NON-NLS-1$
} while (cycleRoot != null);
}
private void buildSubgraphOrderingGraph() {
RankList ranks = g.ranks;
nestingTrees = new NestingTree[ranks.size()];
for (int r = 0; r < ranks.size(); r++) {
NestingTree entry = NestingTree.buildNestingTreeForRank(ranks
.getRank(r));
nestingTrees[r] = entry;
entry.calculateSortValues();
entry.recursiveSort(false);
}
for (int i = 0; i < nestingTrees.length; i++) {
NestingTree entry = nestingTrees[i];
buildSubgraphOrderingGraph(entry);
}
}
private void buildSubgraphOrderingGraph(NestingTree entry) {
NodePair pair = new NodePair();
if (entry.isLeaf)
return;
for (int i = 0; i < entry.contents.size(); i++) {
Object right = entry.contents.get(i);
if (right instanceof Node)
pair.n2 = (Node) right;
else {
pair.n2 = ((NestingTree) right).subgraph;
buildSubgraphOrderingGraph((NestingTree) right);
}
if (pair.n1 != null && !orderingGraphEdges.contains(pair)) {
orderingGraphEdges.add(pair);
leftToRight(pair.n1, pair.n2);
orderingGraphNodes.add(pair.n1);
orderingGraphNodes.add(pair.n2);
pair.n2.x++; // Using x field to count predecessors.
pair = new NodePair(pair.n2, null);
} else {
pair.n1 = pair.n2;
}
}
}
/**
* Calculates the average position P for each node and subgraph. The average
* position is stored in the sortValue for each node or subgraph.
*
* Runs in approximately linear time with respect to the number of nodes,
* including virtual nodes.
*/
private void calculateSortValues() {
RankList ranks = g.ranks;
g.subgraphs.resetSortValues();
g.subgraphs.resetIndices();
/*
* For subgraphs, the sum of all positions is kept, along with the
* number of contributions, which is tracked in the subgraph's index
* field.
*/
for (int r = 0; r < ranks.size(); r++) {
Rank rank = ranks.getRank(r);
for (int j = 0; j < rank.count(); j++) {
Node node = rank.getNode(j);
node.sortValue = node.index;
Subgraph parent = node.getParent();
while (parent != null) {
parent.sortValue += node.sortValue;
parent.index++;
parent = parent.getParent();
}
}
}
/*
* For each subgraph, divide the sum of the positions by the number of
* contributions, to give the average position.
*/
for (int i = 0; i < g.subgraphs.size(); i++) {
Subgraph subgraph = (Subgraph) g.subgraphs.get(i);
subgraph.sortValue /= subgraph.index;
}
}
private void repopulateRanks() {
for (int i = 0; i < nestingTrees.length; i++) {
Rank rank = g.ranks.getRank(i);
rank.clear();
nestingTrees[i].repopulateRank(rank);
}
}
private NodeList rightOf(Node left) {
return (NodeList) left.workingData[0];
}
private void leftToRight(Node left, Node right) {
rightOf(left).add(right);
}
void sortedInsert(List list, Node node) {
int insert = 0;
while (insert < list.size()
&& ((Node) list.get(insert)).sortValue > node.sortValue)
insert++;
list.add(insert, node);
}
private void topologicalSort() {
for (int i = 0; i < nestingTrees.length; i++) {
nestingTrees[i].getSortValueFromSubgraph();
nestingTrees[i].recursiveSort(false);
}
}
void init() {
for (int r = 0; r < g.ranks.size(); r++) {
Rank rank = g.ranks.getRank(r);
for (int i = 0; i < rank.count(); i++) {
Node n = (Node) rank.get(i);
n.workingData[0] = new NodeList();
}
}
for (int i = 0; i < g.subgraphs.size(); i++) {
Subgraph s = (Subgraph) g.subgraphs.get(i);
s.workingData[0] = new NodeList();
}
}
public void visit(DirectedGraph dg) {
g = (CompoundDirectedGraph) dg;
init();
buildSubgraphOrderingGraph();
calculateSortValues();
breakSubgraphCycles();
topologicalSort();
repopulateRanks();
}
}