| /******************************************************************************* |
| * Copyright (c) 2004, 2006 Sybase, Inc. 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: |
| * Sybase, Inc. - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jst.jsf.facesconfig.ui.pageflow.layout; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.draw2d.geometry.Insets; |
| import org.eclipse.draw2d.geometry.Rectangle; |
| import org.eclipse.draw2d.graph.DirectedGraph; |
| import org.eclipse.draw2d.graph.DirectedGraphLayout; |
| 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.emf.common.util.EList; |
| import org.eclipse.jst.jsf.facesconfig.ui.pageflow.model.Pageflow; |
| import org.eclipse.jst.jsf.facesconfig.ui.pageflow.model.PageflowFactory; |
| import org.eclipse.jst.jsf.facesconfig.ui.pageflow.model.PageflowLink; |
| import org.eclipse.jst.jsf.facesconfig.ui.pageflow.model.PageflowLinkBendpoint; |
| import org.eclipse.jst.jsf.facesconfig.ui.pageflow.model.PageflowNode; |
| import org.eclipse.jst.jsf.facesconfig.ui.pageflow.model.PageflowPage; |
| import org.eclipse.jst.jsf.facesconfig.ui.pageflow.util.PageflowModelManager; |
| |
| /** |
| * This class is a singleton adpater to create the directed graph for the |
| * specified pageflow model or update the location information of pageflow model |
| * according to directed graph layout algorithms. |
| * |
| * @author Xiao-guang Zhang |
| */ |
| public class PageflowLayoutManager { |
| /** node's default size */ |
| private static final int NODE_DEFAULT_WIDTH = 50; |
| |
| private static final int NODE_DEFAULT_HEIGHT = 50; |
| |
| /** |
| * the margin of the top and left , and margin between each connnected |
| * directed graph. |
| */ |
| private static final int X_SPACE = 50; |
| |
| private static final int Y_SPACE = 50; |
| |
| /** node's default padding */ |
| private static final int DEFAULT_PADDING = 30; |
| |
| private static PageflowLayoutManager manager; |
| |
| private Map copiedLinks = null; |
| |
| /** |
| * constructor of PageflowDirectedGraphAdapter |
| * |
| */ |
| private PageflowLayoutManager() { |
| // no external instantiation |
| } |
| |
| /** |
| * return the singleton instance of PageflowDirectedGraphAdapter |
| * |
| * @return - the singleton instance of PageflowDirectedGraphAdapter |
| */ |
| static public PageflowLayoutManager getInstance() { |
| if (manager == null) { |
| manager = new PageflowLayoutManager(); |
| } |
| return manager; |
| } |
| |
| /** |
| * layout pageflow using directed graph layout algorithms |
| * |
| * @param pageflow - |
| * pageflow model |
| */ |
| public void layoutPageflow(Pageflow pageflow) { |
| List selfLoopLinks = removeSelfLoopLinks(pageflow); |
| |
| List lstGraph = createGraphs(pageflow); |
| for (Iterator iter = lstGraph.iterator(); iter.hasNext();) { |
| DirectedGraph graph = (DirectedGraph) iter.next(); |
| new DirectedGraphLayout().visit(graph);// .visit(graph); |
| } |
| updatePageflow(lstGraph); |
| |
| if (selfLoopLinks != null) { |
| addSelfLoopLinks(pageflow, selfLoopLinks); |
| } |
| } |
| |
| /** |
| * remove the self loop links from pageflow, because the layout algorithm |
| * does not allow it. |
| * |
| * @param pageflow |
| * @return |
| */ |
| private List removeSelfLoopLinks(Pageflow pageflow) { |
| List selfLoopLinks = new ArrayList(); |
| |
| if (pageflow.getLinks() != null) { |
| for (Iterator iter = pageflow.getLinks().iterator(); iter.hasNext();) { |
| PageflowLink link = (PageflowLink) iter.next(); |
| |
| if (link.getSource() == link.getTarget()) { |
| iter.remove(); |
| link.getBendPoints().clear(); |
| selfLoopLinks.add(link); |
| } |
| } |
| } |
| return selfLoopLinks; |
| } |
| |
| /** |
| * add back the self loop links with updated bendpoints. |
| * |
| * @param pageflow |
| * @param selfLoopLinks |
| */ |
| private void addSelfLoopLinks(Pageflow pageflow, List selfLoopLinks) { |
| if (pageflow.getLinks() != null && selfLoopLinks != null |
| && selfLoopLinks.size() > 0) { |
| EList links = pageflow.getLinks(); |
| for (Iterator iter = selfLoopLinks.iterator(); iter.hasNext();) { |
| PageflowLink link = (PageflowLink) iter.next(); |
| updateSelfLoopLink(link); |
| links.add(link); |
| } |
| } |
| } |
| |
| /** |
| * Update the bendpoints of the self loop link Follow the following style: |
| * |
| * LeftTop-Top | | Left----Node |
| * |
| * @param selfLoopLink |
| */ |
| public static void updateSelfLoopLink(PageflowLink selfLoopLink) { |
| PageflowNode sourceNode = selfLoopLink.getSource(); |
| Rectangle rectNode = getPageflowNodeBounds(sourceNode); |
| |
| EList outLinks = sourceNode.getOutlinks(); |
| if (outLinks != null && outLinks.size() > 0) { |
| for (Iterator iter = outLinks.iterator(); iter.hasNext();) { |
| PageflowLink anotherSelfLoopLink = (PageflowLink) iter.next(); |
| if (anotherSelfLoopLink != selfLoopLink |
| && anotherSelfLoopLink.getTarget() == sourceNode) { |
| rectNode = getFitnessRectangle(rectNode, |
| anotherSelfLoopLink); |
| } |
| } |
| } |
| |
| PageflowFactory factory = PageflowModelManager.getFactory(); |
| PageflowLinkBendpoint bpTop = factory.createPFLinkBendpoint(); |
| PageflowLinkBendpoint bpLeftTop = factory.createPFLinkBendpoint(); |
| PageflowLinkBendpoint bpLeft = factory.createPFLinkBendpoint(); |
| |
| bpTop.setD1Height(-rectNode.height); |
| bpTop.setD2Height(-rectNode.height); |
| |
| bpLeftTop.setD1Width(-rectNode.width); |
| bpLeftTop.setD1Height(-rectNode.height); |
| bpLeftTop.setD2Width(-rectNode.width); |
| bpLeftTop.setD2Height(-rectNode.height); |
| |
| bpLeft.setD1Width(-rectNode.width); |
| bpLeft.setD2Width(-rectNode.width); |
| |
| selfLoopLink.getBendPoints().add(bpTop); |
| selfLoopLink.getBendPoints().add(bpLeftTop); |
| selfLoopLink.getBendPoints().add(bpLeft); |
| } |
| |
| /** |
| * Calculate the fitness rectangle without conflict with the existing self |
| * loop's rectangle. |
| * |
| * @param rectDefault |
| * @param anotherSelfLoopLink |
| */ |
| private static Rectangle getFitnessRectangle(Rectangle rectDefault, |
| PageflowLink anotherSelfLoopLink) { |
| EList bendPoints = anotherSelfLoopLink.getBendPoints(); |
| if (bendPoints != null && bendPoints.size() > 0) { |
| for (Iterator iterBendPoint = bendPoints.iterator(); iterBendPoint |
| .hasNext();) { |
| PageflowLinkBendpoint bendPoint = (PageflowLinkBendpoint) iterBendPoint |
| .next(); |
| if (bendPoint.getD1Width() == -rectDefault.width |
| && bendPoint.getD1Height() == -rectDefault.height) { |
| rectDefault = new Rectangle(0, 0, rectDefault.width |
| + DEFAULT_PADDING, rectDefault.height |
| + DEFAULT_PADDING); |
| break; |
| } |
| } |
| } |
| return rectDefault; |
| } |
| |
| /** |
| * get the pageflow node's bounds, the orginal point is (0,0) |
| * |
| * @param pfNode |
| * @return |
| */ |
| private static Rectangle getPageflowNodeBounds(PageflowNode pfNode) { |
| return new Rectangle(0, 0, 64, 36); |
| } |
| |
| /** |
| * get the pageflow node's border rectangle |
| * |
| * @param pfNode |
| * @return |
| */ |
| private static Rectangle getPageflowNodeRectangle(PageflowNode pfNode) { |
| Rectangle rectNode = null; |
| |
| Rectangle bounds = getPageflowNodeBounds(pfNode); |
| |
| rectNode = new Rectangle(pfNode.getX(), pfNode.getY(), bounds.width, |
| bounds.height); |
| |
| return rectNode; |
| } |
| |
| /** |
| * create the connected subgraphs for the pageflow model, because there |
| * maybe more than one connected graph in one pageflow definition. |
| * |
| * @param pageflow - |
| * Pageflow model |
| * @return - the connected subgraphs |
| */ |
| private List createGraphs(Pageflow pageflow) { |
| /** the connected subgraphs */ |
| List lstGraph = null; |
| |
| if (pageflow != null) { |
| // Graph is not connected totally. |
| DirectedGraph graph = null; |
| HashMap nodesMap = new HashMap(); |
| |
| NodeList nodes = new NodeList(); |
| EdgeList edges = new EdgeList(); |
| |
| // get all nodes in the pageflow |
| List pfNodes = pageflow.getNodes(); |
| if (pfNodes != null) { |
| for (Iterator iter = pfNodes.iterator(); iter.hasNext();) { |
| PageflowNode pfNode = (PageflowNode) iter.next(); |
| Node node = new Node(pfNode); |
| Rectangle rectNode = null; |
| rectNode = getPageflowNodeBounds(pfNode); |
| if (rectNode != null) { |
| node.width = rectNode.width * 2; |
| node.height = rectNode.height * 2; |
| node.setPadding(new Insets(node.height, node.width, |
| node.height, node.width)); |
| } else { |
| node.width = NODE_DEFAULT_WIDTH; |
| node.height = NODE_DEFAULT_HEIGHT; |
| node.setPadding(new Insets(DEFAULT_PADDING)); |
| } |
| nodesMap.put(pfNode, node); |
| nodes.add(node); |
| } |
| } |
| // get all edges in the pageflow |
| List pfLinks = pageflow.getLinks(); |
| for (Iterator iter = pfLinks.iterator(); iter.hasNext();) { |
| PageflowLink link = (PageflowLink) iter.next(); |
| PageflowNode source = link.getSource(); |
| PageflowNode target = link.getTarget(); |
| Node sourceNode = (Node) nodesMap.get(source); |
| Node targetNode = (Node) nodesMap.get(target); |
| |
| if (sourceNode != null && targetNode != null) { |
| Edge edge = new Edge(sourceNode, targetNode); |
| edges.add(edge); |
| } |
| } |
| |
| graph = new DirectedGraph(); |
| |
| graph.nodes = nodes; |
| graph.edges = edges; |
| |
| // get the connected subgraphs. |
| lstGraph = new ArrayList(); |
| lstGraph.add(graph);// ..getConnectedSubGraphs(); |
| } |
| return lstGraph; |
| } |
| |
| /** |
| * update the pageflow according to layout results which are stored in the |
| * connected subgraphs |
| * |
| * @param lstGraph - |
| * connected subgraphs |
| * |
| */ |
| private void updatePageflow(List lstGraph) { |
| // y coordiantion of the subgraph's start point |
| int topSubGraph = 0; |
| |
| for (Iterator iter = lstGraph.iterator(); iter.hasNext();) { |
| // sub graph's bottom |
| int bottomSubGraph = 0; |
| DirectedGraph graph = (DirectedGraph) iter.next(); |
| for (int i = 0; i < graph.nodes.size(); i++) { |
| Node node = graph.nodes.getNode(i); |
| if (!(node.data instanceof PageflowNode)) { |
| continue; |
| } |
| PageflowNode pfNode = (PageflowNode) node.data; |
| pfNode.setX(X_SPACE + node.y); |
| |
| pfNode.setY(Y_SPACE + node.x + topSubGraph); |
| |
| if ((Y_SPACE + node.x + topSubGraph) > bottomSubGraph) { |
| bottomSubGraph = Y_SPACE + node.x + topSubGraph; |
| } |
| } |
| topSubGraph = bottomSubGraph + Y_SPACE; |
| } |
| } |
| |
| /** |
| * update new pageflow's layout using the existing one. |
| * @param newPageflow |
| * @param oldPageflow |
| */ |
| public void updatePageflowLayout(Pageflow newPageflow, Pageflow oldPageflow) { |
| List notUpdatedNodes = new ArrayList(); |
| |
| copiedLinks = new HashMap(); |
| |
| if (oldPageflow.getNodes().size() > 0) { |
| for (Iterator iter = newPageflow.getNodes().iterator(); iter |
| .hasNext();) { |
| PageflowNode newNode = (PageflowNode) iter.next(); |
| |
| if (!updatePageflowNode(newNode, oldPageflow)) { |
| notUpdatedNodes.add(newNode); |
| } |
| } |
| } else { |
| notUpdatedNodes.addAll(newPageflow.getNodes()); |
| } |
| |
| if (oldPageflow.getLinks().size() > 0) { |
| for (Iterator iter = newPageflow.getLinks().iterator(); iter |
| .hasNext();) { |
| PageflowLink newLink = (PageflowLink) iter.next(); |
| |
| updatePFLink(newLink, oldPageflow); |
| } |
| } |
| |
| // if there are still some nodes which are not updated, |
| // they should be check whether there are some layout conflict. |
| if (notUpdatedNodes.size() > 0) { |
| resolveConflict(newPageflow, notUpdatedNodes); |
| } |
| |
| } |
| |
| /** |
| * Update pageflow node using the same node |
| * |
| * @param object |
| * @return - the old pageflow node. |
| */ |
| private boolean updatePageflowNode(PageflowNode newNode, |
| Pageflow oldPageflow) { |
| for (Iterator iter = oldPageflow.getNodes().iterator(); iter.hasNext();) { |
| PageflowNode oldNode = (PageflowNode) iter.next(); |
| |
| if (oldNode instanceof PageflowPage |
| && newNode instanceof PageflowPage) { |
| if (((PageflowPage) oldNode).getPath().trim().equals( |
| ((PageflowPage) newNode).getPath().trim())) { |
| updatePageflowNode(newNode, oldNode); |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Update pageflow node using the same node |
| * |
| * @param object |
| */ |
| private void updatePageflowNode(PageflowNode newNode, PageflowNode node) { |
| newNode.setX(node.getX()); |
| newNode.setY(node.getY()); |
| } |
| |
| /** |
| * If there are conflict for the nodes, it will resolve it. |
| * |
| * @param newPageflow |
| * @param notUpdatedNodes |
| */ |
| private void resolveConflict(Pageflow newPageflow, List notUpdatedNodes) { |
| for (Iterator iter = notUpdatedNodes.iterator(); iter.hasNext();) { |
| resolveConflict(newPageflow, (PageflowNode) iter.next()); |
| } |
| } |
| |
| /** |
| * Resolve the layout conflict |
| * |
| * @param newPageflow |
| * @param node |
| */ |
| private void resolveConflict(Pageflow newPageflow, PageflowNode node) { |
| Rectangle nodeRect = getPageflowNodeRectangle(node); |
| boolean bModified = false; |
| for (int i = 0, size = newPageflow.getNodes().size(); i < size; i++) { |
| PageflowNode fixedNode = (PageflowNode) newPageflow.getNodes().get( |
| i); |
| if (node == fixedNode) { |
| continue; |
| } |
| Rectangle fixedNodeRect = getPageflowNodeRectangle(fixedNode); |
| if (fixedNodeRect.intersects(nodeRect)) { |
| nodeRect.x += fixedNodeRect.width + X_SPACE; |
| nodeRect.y += fixedNodeRect.height + Y_SPACE; |
| bModified = true; |
| } |
| } |
| if (bModified) { |
| node.setX(nodeRect.x); |
| node.setY(nodeRect.y); |
| } |
| } |
| |
| /** |
| * Update the link using the same link in the old pageflow |
| * |
| * @param newLink |
| * @param oldPageflow |
| */ |
| private void updatePFLink(PageflowLink newLink, Pageflow oldPageflow) { |
| for (Iterator iter = oldPageflow.getLinks().iterator(); iter.hasNext();) { |
| PageflowLink oldLink = (PageflowLink) iter.next(); |
| |
| if (copiedLinks.get(oldLink) != null) { |
| continue; |
| } |
| |
| if (isSameLink(newLink, oldLink)) { |
| updatePFLink(newLink, oldLink); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Check whether this two links in differnet pageflow are same or not. |
| * |
| * @param newLink |
| * @param oldLink |
| * @return |
| */ |
| private boolean isSameLink(PageflowLink newLink, PageflowLink oldLink) { |
| PageflowNode newSource = newLink.getSource(); |
| PageflowNode newTarget = newLink.getTarget(); |
| |
| PageflowNode oldSource = oldLink.getSource(); |
| PageflowNode oldTarget = oldLink.getTarget(); |
| |
| // Page-Page |
| if (newSource instanceof PageflowPage |
| && oldSource instanceof PageflowPage |
| && newTarget instanceof PageflowPage |
| && oldTarget instanceof PageflowPage) { |
| if (((PageflowPage) newSource).getPath().trim().equalsIgnoreCase( |
| ((PageflowPage) oldSource).getPath().trim()) |
| && ((PageflowPage) newTarget) |
| .getPath() |
| .trim() |
| .equalsIgnoreCase( |
| ((PageflowPage) oldTarget).getPath().trim())) { |
| if ((newLink.getOutcome() == null && oldLink.getOutcome() == null) |
| || (newLink.getOutcome() != null |
| && oldLink.getOutcome() != null && newLink |
| .getOutcome().trim().equals( |
| oldLink.getOutcome().trim()))) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Update the link using the same link |
| * |
| * @param nodesMap |
| * @param object |
| * @return |
| */ |
| private PageflowLink updatePFLink(PageflowLink newLink, PageflowLink link) { |
| PageflowFactory factory = PageflowModelManager.getFactory(); |
| |
| newLink.getBendPoints().clear(); |
| |
| for (Iterator iter = link.getBendPoints().iterator(); iter.hasNext();) { |
| PageflowLinkBendpoint bendPoint = (PageflowLinkBendpoint) iter |
| .next(); |
| PageflowLinkBendpoint newBendPoint = factory |
| .createPFLinkBendpoint(); |
| newBendPoint.setD1Height(bendPoint.getD1Height()); |
| newBendPoint.setD1Width(bendPoint.getD1Width()); |
| newBendPoint.setD2Height(bendPoint.getD2Height()); |
| newBendPoint.setD2Width(bendPoint.getD2Width()); |
| newLink.getBendPoints().add(newBendPoint); |
| } |
| |
| copiedLinks.put(link, link); |
| return newLink; |
| } |
| |
| } |