/** * This file is protected by Copyright. * Please refer to the COPYRIGHT file distributed with this source distribution. * * This file is part of REDHAWK IDE. * * 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. * */ package gov.redhawk.diagram.providers; import gov.redhawk.diagram.part.PartitioningVisualIDRegistry; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.draw2d.graph.EdgeList; import org.eclipse.draw2d.graph.Node; import org.eclipse.gef.ConnectionEditPart; import org.eclipse.gef.EditPart; import org.eclipse.gef.GraphicalEditPart; import org.eclipse.gmf.runtime.common.core.service.IOperation; import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.ListItemEditPart; import org.eclipse.gmf.runtime.diagram.ui.providers.CompositeLeftRightProvider; import org.eclipse.gmf.runtime.diagram.ui.services.layout.ILayoutNode; import org.eclipse.gmf.runtime.diagram.ui.services.layout.ILayoutNodeOperation; import org.eclipse.gmf.runtime.diagram.ui.services.layout.LayoutType; import org.eclipse.gmf.runtime.draw2d.ui.graph.ConstrainedEdge; import org.eclipse.gmf.runtime.notation.Diagram; import org.eclipse.gmf.runtime.notation.View; /** * @since 3.0 * */ public class PartitioningLayoutProvider extends CompositeLeftRightProvider { private final PartitioningVisualIDRegistry visualIdRegistry; public PartitioningLayoutProvider(final PartitioningVisualIDRegistry visualIdRegistry) { this.visualIdRegistry = visualIdRegistry; } @Override public boolean provides(final IOperation operation) { // enable this provider only on our diagrams if (operation instanceof ILayoutNodeOperation) { final Iterator< ? > nodes = ((ILayoutNodeOperation) operation).getLayoutNodes().listIterator(); if (nodes.hasNext()) { final View node = ((ILayoutNode) nodes.next()).getNode(); final Diagram container = node.getDiagram(); if (container == null || !(container.getType().equals(this.visualIdRegistry.getModelID()))) { return false; } } } else { return false; } final IAdaptable layoutHint = ((ILayoutNodeOperation) operation).getLayoutHint(); final String layoutType = (String) layoutHint.getAdapter(String.class); return LayoutType.DEFAULT.equals(layoutType); } @Override protected boolean shouldHandleConnectableListItems() { return true; } @Override protected List< ? > getRelevantConnections(@SuppressWarnings("rawtypes") final Hashtable editPartToNodeDict) { final Enumeration< ? > enumeration = editPartToNodeDict.keys(); final List< ? > connectionsToMove = new ArrayList<Object>(); while (enumeration.hasMoreElements()) { final Object e = enumeration.nextElement(); final GraphicalEditPart shapeEP = (GraphicalEditPart) e; final DiagramEditPart diagram = (DiagramEditPart) shapeEP.getViewer().getContents(); return diagram.getConnections(); } return connectionsToMove; } @Override @SuppressWarnings({ "rawtypes", "unchecked", "restriction" }) protected EdgeList buildEdges(final List selectedObjects, final Map editPartToNodeDict) { final EdgeList edges = new EdgeList(); // Do "topdown" relationships first. Since they traditionally // go upward on a diagram, they are reversed when placed in the graph // for // layout. Also, layout traverses the arcs from each node in the order // of their insertion when finding a spanning tree for its constructed // hierarchy. Therefore, arcs added early are less likely to be // reversed. // In fact, since there are no cycles in these arcs, adding // them to the graph first should guarantee that they are never // reversed, // i.e., the inheritance hierarchy is preserved graphically. final ArrayList<ConnectionEditPart> objects = new ArrayList<ConnectionEditPart>(); objects.addAll(selectedObjects); ListIterator<ConnectionEditPart> li = objects.listIterator(); final ArrayList<ConnectionEditPart> notTopDownEdges = new ArrayList<ConnectionEditPart>(); final boolean shouldHandleListItems = shouldHandleConnectableListItems(); while (li.hasNext()) { final EditPart gep = li.next(); if (gep instanceof ConnectionEditPart) { final ConnectionEditPart poly = (ConnectionEditPart) gep; if (layoutTopDown(poly)) { EditPart from = poly.getSource(); EditPart to = poly.getTarget(); if (from instanceof IBorderItemEditPart && !editPartToNodeDict.containsKey(from)) { from = from.getParent(); } else if (shouldHandleListItems && from instanceof ListItemEditPart) { from = getFirstAnscestorinNodesMap(from, editPartToNodeDict); } if (to instanceof IBorderItemEditPart && !editPartToNodeDict.containsKey(to)) { to = to.getParent(); } else if (shouldHandleListItems && to instanceof ListItemEditPart) { to = getFirstAnscestorinNodesMap(to, editPartToNodeDict); } final Node fromNode = getNode(from, editPartToNodeDict); final Node toNode = getNode(to, editPartToNodeDict); if (fromNode != null && toNode != null && !checkSelfEdge(from, to, editPartToNodeDict)) { addEdge(edges, poly, toNode, fromNode); } } else { notTopDownEdges.add(poly); } } } // third pass for all other connections li = notTopDownEdges.listIterator(); while (li.hasNext()) { final ConnectionEditPart poly = li.next(); EditPart from = poly.getSource(); EditPart to = poly.getTarget(); if (from instanceof IBorderItemEditPart && !editPartToNodeDict.containsKey(from)) { from = from.getParent(); } else if (shouldHandleListItems && from instanceof ListItemEditPart) { from = getFirstAnscestorinNodesMap(from, editPartToNodeDict); } if (to instanceof IBorderItemEditPart && !editPartToNodeDict.containsKey(to)) { to = to.getParent(); } else if (shouldHandleListItems && to instanceof ListItemEditPart) { to = getFirstAnscestorinNodesMap(to, editPartToNodeDict); } final Node fromNode = getNode(from, editPartToNodeDict); final Node toNode = getNode(to, editPartToNodeDict); if (fromNode != null && toNode != null && !checkSelfEdge(from, to, editPartToNodeDict)) { addEdge(edges, poly, fromNode, toNode); } } return edges; } private Node getNode(final EditPart editPart, @SuppressWarnings("rawtypes") final Map editPartToNodeDict) { if (editPart == null) { return null; } final Object retVal = editPartToNodeDict.get(editPart); if (retVal == null) { return getNode(editPart.getParent(), editPartToNodeDict); } return (Node) retVal; } private EditPart getFirstAnscestorinNodesMap(final EditPart editPart, @SuppressWarnings("rawtypes") final Map editPartToNodeDict) { EditPart ancestor = editPart; while (ancestor != null) { if (editPartToNodeDict.get(ancestor) != null) { return ancestor; } ancestor = ancestor.getParent(); } return null; } private boolean checkSelfEdge(final EditPart from, final EditPart to, @SuppressWarnings("rawtypes") final Map dictionary) { final Node graphSource = (from instanceof IBorderItemEditPart) ? (Node) dictionary.get(from.getParent()) : (Node) dictionary.get(from); final Node graphTarget = (to instanceof IBorderItemEditPart) ? (Node) dictionary.get(to.getParent()) : (Node) dictionary.get(to); // Fixes #163 If we are colocating a single component with feedback onto itself we will have a null graphSource & graphTarget but equal EditParts. if (graphSource == null || graphTarget == null) { return from.equals(to); } return graphSource.equals(graphTarget); } @SuppressWarnings({ "restriction", "unchecked" }) private void addEdge(final EdgeList edges, final ConnectionEditPart connectionEP, final Node fromNode, final Node toNode) { final ConstrainedEdge edge = new ConstrainedEdge(connectionEP, fromNode, toNode); initializeEdge(connectionEP, edge); edges.add(edge); } }