/* * Copyright (C) Yutaka Matsuno 2010-2012 All rights reserved. */ package net.dependableos.dcase.diagram.editor.layout; import java.util.Collections; import java.util.Stack; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.draw2d.graph.Edge; import org.eclipse.draw2d.graph.EdgeList; import org.eclipse.draw2d.graph.Node; import org.eclipse.draw2d.graph.NodeList; import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeEditPart; /** * A class that represents a node with information for locating. */ public class NodeEx { /** * the node. */ private Node node = null; /** * the size of the node. */ private Rectangle bounds = null; /** * the depth. */ private int depth = 0; /** * the looped link flag. */ private boolean looped = false; /** * the stack to check the looped link. */ private Stack<NodeEx> stack = new Stack<NodeEx>(); /** * Create an instance and initializes it. * * @param node the node. */ public NodeEx(Node node) { this.node = node; Dimension dim = ((ShapeEditPart) node.data).getSize(); node.width = dim.width; node.height = dim.height; bounds = new Rectangle(0, 0, node.width, node.height); } /** * Returns the node. * * @return the node. */ public Node getNode() { return node; } /** * Returns the bounds to lay out the node and its children. * * @return the bounds to lay out the node and its children. */ public Rectangle getBlockBounds() { return bounds; } /** * Sets the bounds to lay out the node and its children. * * * @param rect the bounds to lay out the node and its children. */ public void setBlockBounds(Rectangle rect) { this.bounds = rect; } /** * Returns the depth. * * @return the depth. */ public int getDepth() { return depth; } /** * Sets the depth. * * @param depth the depth. */ public void setDepth(int depth) { this.depth = depth; } /** * Returns whether the node is contained in a looped link. * * @return true if and only if the node is contained in a looped link;false otherwise. */ public boolean getLooped() { return looped; } /** * Sets whether the node is contained in a looped link. * * @param looped true if and only if the node is contained in a looped link;false otherwise. */ public void setLooped(boolean looped) { this.looped = looped; } /** * Returns the count of the parents. * * @return the count of the parents. */ public int getParentCount() { return node.incoming.size(); } /** * Returns the count of the children. * * @return the count of the children. */ public int getChildCount() { return node.outgoing.size(); } /** * Returns the count of the children those should be locate to the south. * * @return the count of the children those should be locate to the south. */ public int getSouthCount() { int count = 0; for (Object obj : node.outgoing) { if (!DcaseDirectedGraph.isEast((Node) ((Edge) obj).target)) { count++; } } return count; } /** * Returns the count of the children those should be locate to the east. * * @return the count of the children those should be locate to the east. */ public int getEastCount() { int count = 0; for (Object obj : node.outgoing) { if (DcaseDirectedGraph.isEast((Node) ((Edge) obj).target)) { count++; } } return count; } /** * Returns the parents. * * @return the parents. */ @SuppressWarnings("unchecked") public NodeList getParent() { NodeList nodeList = new NodeList(); for (Object obj : node.incoming) { nodeList.add((Node) ((Edge) obj).source); } return nodeList; } /** * Returns the children. * * @return Returns the children. */ @SuppressWarnings("unchecked") public NodeList getChildren() { NodeList nodeList = new NodeList(); for (Object obj : node.outgoing) { nodeList.add((Node) ((Edge) obj).target); } return nodeList; } /** * Returns the children those should be locate to the south. * * @return the children those should be locate to the south. */ @SuppressWarnings("unchecked") public NodeList getSouthNodes() { EdgeList edgeList = new EdgeList(); NodeList nodeList = new NodeList(); for (Object obj : node.outgoing) { if (!DcaseDirectedGraph.isEast((Node) ((Edge) obj).target)) { edgeList.add(obj); } } // sorts by the sibling order. if (edgeList.size() > 1) { Collections.sort(edgeList, new DcaseEditPartLayoutComparator()); } for (int i = 0; i < edgeList.size(); i++) { nodeList.add(((Edge) edgeList.get(i)).target); } return nodeList; } /** * Returns the children those should be locate to the east. * * @return the children those should be locate to the east. */ @SuppressWarnings("unchecked") public NodeList getEastNodes() { EdgeList edgeList = new EdgeList(); NodeList nodeList = new NodeList(); for (Object obj : node.outgoing) { if (DcaseDirectedGraph.isEast((Node) ((Edge) obj).target)) { edgeList.add(obj); } } // sorts by the sibling order. if (edgeList.size() > 1) { Collections.sort(edgeList, new DcaseEditPartLayoutComparator()); } for (int i = 0; i < edgeList.size(); i++) { nodeList.add(((Edge) edgeList.get(i)).target); } return nodeList; } /** * Returns the size to lay out children to the south. * * @param gap the horizontal space between nodes. * @param flag true if and only if the last node should be except;false otherwise. * @return the size to lay out children to the south. */ public Dimension getSouthSize(int gap, boolean flag) { int height = 0; int width = 0; int count = 0; NodeList list = getSouthNodes(); if (ArrangeAngle.createInstance().getAngle() == ArrangeAngle.Direction.Vertical) { // top down layout for (int i = 0; i < list.size(); i++) { if (flag && i == list.size() - 1) { continue; } Node childNode = (Node) list.get(i); NodeEx nodeEx = DcaseDirectedGraph.getNodeEx(childNode); if (this.getDepth() < nodeEx.getDepth()) { Dimension dim = nodeEx.calcBlockSize(flag); width += dim.width; height = Math.max(height, dim.height); count++; } } if (count > 1) { width += (gap * (count - 1)); } return new Dimension(width, height); } else { // left to right layout for (int i = 0; i < list.size(); i++) { if (flag && i == list.size() - 1) { continue; } Node childNode = (Node) list.get(i); NodeEx nodeEx = DcaseDirectedGraph.getNodeEx(childNode); if (this.getDepth() < nodeEx.getDepth()) { Dimension dim = nodeEx.calcBlockSize(flag); width = Math.max(width, dim.width); height += dim.height; count++; } } if (count > 1) { height += (gap * (count - 1)); } return new Dimension(width, height); } } /** * Returns the size to lay out children to the east. * * @param gap the vertical space between nodes. * @param flag true if and only if the last node should be except;false otherwise. * @return the size to lay out children to the east. */ public Dimension getEastSize(int gap, boolean flag) { int width = 0; int height = 0; int count = 0; NodeList list = getEastNodes(); if (ArrangeAngle.createInstance().getAngle() == ArrangeAngle.Direction.Vertical) { // top down layout for (int i = 0; i < list.size(); i++) { if (flag && i == list.size() - 1) { continue; } Node childNode = (Node) list.get(i); NodeEx nodeEx = DcaseDirectedGraph.getNodeEx(childNode); if (this.getDepth() < nodeEx.getDepth()) { Dimension dim = nodeEx.calcBlockSize(flag); width = Math.max(width, dim.width); height += dim.height; count++; } } if (count > 1) { height += (gap * (count - 1)); } return new Dimension(width, height); } else { // left to right layout for (int i = 0; i < list.size(); i++) { if (flag && i == list.size() - 1) { continue; } Node childNode = (Node) list.get(i); NodeEx nodeEx = DcaseDirectedGraph.getNodeEx(childNode); if (this.getDepth() < nodeEx.getDepth()) { Dimension dim = nodeEx.calcBlockSize(flag); width += dim.width; height = Math.max(height, dim.height); count++; } } if (count > 1) { width += (gap * (count - 1)); } return new Dimension(width, height); } } /** * Returns the size to lay out the node and its children. * * @return the size to lay out the node and its children. */ public Dimension calcBlockSize() { return calcBlockSize(false); } /** * Returns the size to lay out the node and its children. * * @param flag true if and only if the last node should be except;false otherwise. * @return the size to lay out the node and its children. */ public Dimension calcBlockSize(boolean flag) { if (stack.contains(this)) { return new Dimension(); } Dimension dim = null; stack.push(this); if (ArrangeAngle.createInstance().getAngle() == ArrangeAngle.Direction.Vertical) { dim = calcBlockSizeByVertical(flag); } else { dim = calcBlockSizeByHorizontal(flag); } stack.pop(); return dim; } /** * Returns the size to vertical layout the node and its children. * * @param flag true if and only if the last node should be except;false otherwise. * @return the size to lay out the node and its children. */ private Dimension calcBlockSizeByVertical(boolean flag) { int width = 0; int height = 0; Dimension dimBottom = getSouthSize(DcaseGraphVisitor.HORIZONTAL_SPACING, flag); Dimension dimRight = null; if (flag) { dimRight = new Dimension(); } else { dimRight = getEastSize(DcaseGraphVisitor.VERTICAL_SPACING, flag); } width = Math.max(node.width, dimBottom.width); if (dimRight.width > 0) { width = Math.max(width / 2 + node.width / 2 + DcaseGraphVisitor.VERTICAL_CONTEXT_SPACING + dimRight.width, width); } height = Math.max(node.height, dimRight.height); if (dimBottom.height > 0) { height += (DcaseGraphVisitor.VERTICAL_SPACING + dimBottom.height); } return new Dimension(width, height); } /** * Returns the size to horizontal layout the node and its children. * * @param flag true if and only if the last node should be except;false otherwise. * @return the size to lay out the node and its children. */ private Dimension calcBlockSizeByHorizontal(boolean flag) { int width = 0; int height = 0; Dimension dimBottom = null; Dimension dimRight = getSouthSize(DcaseGraphVisitor.VERTICAL_SPACING, flag); if (flag) { dimBottom = new Dimension(); } else { dimBottom = getEastSize(DcaseGraphVisitor.HORIZONTAL_SPACING, flag); } height = Math.max(node.height, dimRight.height); if (dimBottom.height > 0) { height = Math.max(height / 2 + node.height / 2 + DcaseGraphVisitor.HORIZONTAL_SPACING + dimBottom.height, height); } width = Math.max(node.width, dimBottom.width); if (dimRight.width > 0) { width += (DcaseGraphVisitor.HORIZONTAL_SPACING + dimRight.width); } return new Dimension(width, height); } /** * Returns the bounds of the node. * * @return the bounds of the node. */ public Rectangle getNodeBounds() { return new Rectangle(node.x, node.y, node.width, node.height); } }