/******************************************************************************* * Copyright (c) 2003, 2005 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(); } }