| /******************************************************************************* |
| * 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 org.eclipse.draw2d.geometry.Insets; |
| |
| /** |
| * Converts a compound directed graph into a simple directed graph. |
| * |
| * @author Randy Hudson |
| * @since 2.1.2 |
| */ |
| class ConvertCompoundGraph extends GraphVisitor { |
| |
| private void addContainmentEdges(CompoundDirectedGraph graph) { |
| // For all nested nodes, connect to head and/or tail of containing |
| // subgraph if present |
| for (int i = 0; i < graph.nodes.size(); i++) { |
| Node node = graph.nodes.getNode(i); |
| Subgraph parent = node.getParent(); |
| if (parent == null) |
| continue; |
| if (node instanceof Subgraph) { |
| Subgraph sub = (Subgraph) node; |
| connectHead(graph, sub.head, parent); |
| connectTail(graph, sub.tail, parent); |
| } else { |
| connectHead(graph, node, parent); |
| connectTail(graph, node, parent); |
| } |
| } |
| } |
| |
| int buildNestingTreeIndices(NodeList nodes, int base) { |
| for (int i = 0; i < nodes.size(); i++) { |
| Node node = (Node) nodes.get(i); |
| if (node instanceof Subgraph) { |
| Subgraph s = (Subgraph) node; |
| s.nestingTreeMin = base; |
| base = buildNestingTreeIndices(s.members, base); |
| } |
| node.nestingIndex = base++; |
| } |
| return base++; |
| } |
| |
| private void connectHead(CompoundDirectedGraph graph, Node node, |
| Subgraph parent) { |
| boolean connectHead = true; |
| for (int j = 0; connectHead && j < node.incoming.size(); j++) { |
| Node ancestor = node.incoming.getEdge(j).source; |
| if (parent.isNested(ancestor)) |
| connectHead = false; |
| } |
| if (connectHead) { |
| Edge e = new Edge(parent.head, node); |
| e.weight = 0; |
| graph.edges.add(e); |
| graph.containment.add(e); |
| } |
| } |
| |
| private void connectTail(CompoundDirectedGraph graph, Node node, |
| Subgraph parent) { |
| boolean connectTail = true; |
| for (int j = 0; connectTail && j < node.outgoing.size(); j++) { |
| Node ancestor = node.outgoing.getEdge(j).target; |
| if (parent.isNested(ancestor)) |
| connectTail = false; |
| } |
| if (connectTail) { |
| Edge e = new Edge(node, parent.tail); |
| e.weight = 0; |
| graph.edges.add(e); |
| graph.containment.add(e); |
| } |
| } |
| |
| private void convertSubgraphEndpoints(CompoundDirectedGraph graph) { |
| for (int i = 0; i < graph.edges.size(); i++) { |
| Edge edge = (Edge) graph.edges.get(i); |
| if (edge.source instanceof Subgraph) { |
| Subgraph s = (Subgraph) edge.source; |
| Node newSource; |
| if (s.isNested(edge.target)) |
| newSource = s.head; |
| else |
| newSource = s.tail; |
| // s.outgoing.remove(edge); |
| edge.source = newSource; |
| newSource.outgoing.add(edge); |
| } |
| if (edge.target instanceof Subgraph) { |
| Subgraph s = (Subgraph) edge.target; |
| Node newTarget; |
| if (s.isNested(edge.source)) |
| newTarget = s.tail; |
| else |
| newTarget = s.head; |
| |
| // s.incoming.remove(edge); |
| edge.target = newTarget; |
| newTarget.incoming.add(edge); |
| } |
| } |
| } |
| |
| private void replaceSubgraphsWithBoundaries(CompoundDirectedGraph graph) { |
| for (int i = 0; i < graph.subgraphs.size(); i++) { |
| Subgraph s = (Subgraph) graph.subgraphs.get(i); |
| graph.nodes.add(s.head); |
| graph.nodes.add(s.tail); |
| graph.nodes.remove(s); |
| } |
| } |
| |
| void revisit(DirectedGraph g) { |
| for (int i = 0; i < g.edges.size(); i++) { |
| Edge e = g.edges.getEdge(i); |
| if (e.source instanceof SubgraphBoundary) { |
| e.source.outgoing.remove(e); |
| e.source = e.source.getParent(); |
| } |
| if (e.target instanceof SubgraphBoundary) { |
| e.target.incoming.remove(e); |
| e.target = e.target.getParent(); |
| } |
| } |
| } |
| |
| /** |
| * @see GraphVisitor#visit(org.eclipse.draw2d.graph.DirectedGraph) |
| */ |
| public void visit(DirectedGraph dg) { |
| CompoundDirectedGraph graph = (CompoundDirectedGraph) dg; |
| |
| NodeList roots = new NodeList(); |
| // Find all subgraphs and root subgraphs |
| for (int i = 0; i < graph.nodes.size(); i++) { |
| Object node = graph.nodes.get(i); |
| if (node instanceof Subgraph) { |
| Subgraph s = (Subgraph) node; |
| Insets padding = dg.getPadding(s); |
| s.head = new SubgraphBoundary(s, padding, 0); |
| s.tail = new SubgraphBoundary(s, padding, 2); |
| Edge headToTail = new Edge(s.head, s.tail); |
| headToTail.weight = 10; |
| graph.edges.add(headToTail); |
| graph.containment.add(headToTail); |
| |
| graph.subgraphs.add(s); |
| if (s.getParent() == null) |
| roots.add(s); |
| if (s.members.size() == 2) // The 2 being the head and tail only |
| graph.edges.add(new Edge(s.head, s.tail)); |
| } |
| } |
| |
| buildNestingTreeIndices(roots, 0); |
| convertSubgraphEndpoints(graph); |
| addContainmentEdges(graph); |
| replaceSubgraphsWithBoundaries(graph); |
| } |
| |
| } |