-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.g2d.utils;\r
-\r
-import java.util.ArrayDeque;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.Deque;\r
-import java.util.HashSet;\r
-import java.util.Queue;\r
-import java.util.Set;\r
-\r
-import org.simantics.g2d.connection.ConnectionEntity;\r
-import org.simantics.g2d.diagram.IDiagram;\r
-import org.simantics.g2d.diagram.handler.DataElementMap;\r
-import org.simantics.g2d.diagram.handler.RelationshipHandler;\r
-import org.simantics.g2d.diagram.handler.RelationshipHandler.Relation;\r
-import org.simantics.g2d.diagram.handler.Topology;\r
-import org.simantics.g2d.diagram.handler.Topology.Connection;\r
-import org.simantics.g2d.diagram.handler.Topology.Terminal;\r
-import org.simantics.g2d.element.ElementHints;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.element.handler.TerminalTopology;\r
-import org.simantics.g2d.elementclass.FlagHandler;\r
-\r
-/**\r
- * This class tries to expand the selection provided by the specified elements\r
- * by a single expansion step. Its purpose is to provide a way for the user to\r
- * easily select a larger range of elements based on the diagram connectivity.\r
- * This can be useful e.g. when preparing for a copy-paste operation or simply\r
- * for visualizing the connectivity of a diagram.</p>\r
- * \r
- * <p>\r
- * The expansion logic is as follows:\r
- * </p>\r
- * <ol>\r
- * <li>If connections are included in the current selection, make sure that no\r
- * connection entity is only partly selected. If only partly selected connection\r
- * entities are found, complete those and stop there. Otherwise continue to the\r
- * next step.</li>\r
- * <li>Expand the current selection by one step. For connections this means\r
- * selecting all nodes that are attached by the connection but not yet in the\r
- * current selection. For nodes this means expanding the selection to all the\r
- * connections reachable from that particular node.</li>\r
- * </ol>\r
- * \r
- * @author Tuukka Lehtonen\r
- */\r
-public class TopologicalSelectionExpander {\r
-\r
- public static final boolean DEBUG = false;\r
-\r
- IDiagram diagram;\r
- Set<IElement> startSelection;\r
- Set<IElement> resultSelection;\r
- Set<ConnectionEntity> processedConnections = new HashSet<ConnectionEntity>();\r
-\r
- Topology topology;\r
- DataElementMap dem;\r
-\r
- public static Set<IElement> expandSelection(IDiagram diagram, Set<IElement> elements) {\r
- return new TopologicalSelectionExpander(diagram, elements).expanded();\r
- }\r
-\r
- public TopologicalSelectionExpander(IDiagram diagram, Set<IElement> startSelection) {\r
- assert diagram != null;\r
-\r
- this.diagram = diagram;\r
- this.startSelection = startSelection;\r
- this.resultSelection = new HashSet<IElement>(startSelection);\r
-\r
- this.topology = diagram.getDiagramClass().getAtMostOneItemOfClass(Topology.class);\r
- this.dem = diagram.getDiagramClass().getAtMostOneItemOfClass(DataElementMap.class);\r
- }\r
-\r
- /**\r
- * @return <code>null</code> if the selection did not change in the\r
- * expansion, another set of elements otherwise\r
- */\r
- public Set<IElement> expandedIfChanged() {\r
- Set<IElement> result = expanded();\r
- if (DEBUG)\r
- System.out.println("result selection: " + result);\r
- if (result.equals(startSelection))\r
- return null;\r
- if (DEBUG)\r
- System.out.println("setting new selection");\r
- return result;\r
- }\r
-\r
- /**\r
- * @return\r
- */\r
- public Set<IElement> expanded() {\r
- if (topology == null || dem == null || startSelection.isEmpty())\r
- return startSelection;\r
-\r
- if (DEBUG)\r
- System.out.println("expand start selection: " + startSelection);\r
-\r
- Deque<IElement> work = new ArrayDeque<IElement>(startSelection.size() + 4);\r
- work.addAll(startSelection);\r
-\r
- // 1. Iterate the start selection to see if there are any partly\r
- // selected connection entities. If so, then only complete the\r
- // selection of those entities before expanding the selection in\r
- // any other way.\r
- boolean connectionPartsSelected = false;\r
- for (IElement e : work) {\r
- IElement connection = getConnectionOfConnectionPart(e);\r
- if (connection != null) {\r
- // There was a mere connection part selection among the selection.\r
- Set<IElement> connectionParts = getAllConnectionEntityParts(e);\r
- if (!connectionParts.isEmpty()) {\r
- if (DEBUG)\r
- System.out.println("\tconnection part selected: " + e + ", replacing with connection " + connection);\r
- resultSelection.add(connection);\r
- resultSelection.removeAll(connectionParts);\r
- connectionPartsSelected = true;\r
- }\r
- }\r
- }\r
-\r
- if (!connectionPartsSelected) {\r
- // No connection entities were partly selected. Go ahead with\r
- // the normal selection expansion procedure.\r
- while (!work.isEmpty()) {\r
- IElement e = work.poll();\r
- if (DEBUG)\r
- System.out.println("\texpanding at element: " + e);\r
- @SuppressWarnings("unused")\r
- boolean expanded = expandConnection(e, work) || expandNode(e, work);\r
- }\r
- }\r
-\r
- if (DEBUG)\r
- System.out.println("expanded selection: " + resultSelection);\r
- return resultSelection;\r
- }\r
-\r
- boolean expandConnection(IElement connection, Queue<IElement> workQueue) {\r
- ConnectionEntity ce = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
- if (ce == null)\r
- return false;\r
-\r
- if (!processedConnections.add(ce))\r
- return true;\r
-\r
- // Expand the selection to all the nodes attached to this connection.\r
- if (DEBUG)\r
- System.out.println("\texpanding at connection " + ce);\r
- Collection<Connection> terminals = new ArrayList<Connection>();\r
- ce.getTerminalConnections(terminals);\r
- if (DEBUG)\r
- System.out.println("\t\tfound " + terminals.size() + " terminal connections: " + terminals);\r
- for (Connection terminal : terminals) {\r
- if (resultSelection.add(terminal.node)) {\r
- if (DEBUG)\r
- System.out.println("\t\t\tadding node '" + terminal.node + "' at terminal '" + terminal.terminal + "'");\r
- }\r
- }\r
- return true;\r
- }\r
-\r
- boolean expandNode(IElement e, Queue<IElement> workQueue) {\r
- // This is a node.\r
- TerminalTopology tt = e.getElementClass().getAtMostOneItemOfClass(TerminalTopology.class);\r
- if (tt == null)\r
- return false;\r
- if (DEBUG)\r
- System.out.println("\texpanding selection to node terminal connections: " + e);\r
-\r
- Collection<Terminal> terminals = new ArrayList<Terminal>();\r
- tt.getTerminals(e, terminals);\r
- Collection<Connection> connections = new ArrayList<Connection>();\r
- for (Terminal terminal : terminals) {\r
- topology.getConnections(e, terminal, connections);\r
- }\r
- if (DEBUG)\r
- System.out.println("\t\tfound " + connections.size() + " connected terminals: " + connections);\r
- for (Connection connection : connections) {\r
- IElement conn = getConnectionEntityConnection(connection.edge);\r
- if (conn != null) {\r
- if (DEBUG)\r
- System.out.println("\t\t\tadding connection: " + conn);\r
- resultSelection.add(conn);\r
- }\r
- }\r
- \r
- boolean expanded = !connections.isEmpty(); \r
-\r
- // We want to:\r
- // * expand selection to monitors and other related "sub-elements" of the selection\r
- // We don't want to:\r
- // * expand selection through flags\r
- FlagHandler fh = e.getElementClass().getAtMostOneItemOfClass(FlagHandler.class);\r
- if (fh == null) {\r
- RelationshipHandler rh = diagram.getDiagramClass().getAtMostOneItemOfClass(RelationshipHandler.class);\r
- if (rh != null) {\r
- for(Relation rel : rh.getRelations(diagram, e, new ArrayList<Relation>())) {\r
- if(rel.getSubject() instanceof IElement) {\r
- expanded |= resultSelection.add((IElement)rel.getSubject());\r
- }\r
- if(rel.getObject() instanceof IElement) {\r
- expanded |= resultSelection.add((IElement)rel.getObject());\r
- }\r
- }\r
- }\r
- }\r
-\r
- return expanded;\r
- }\r
-\r
- static IElement getConnectionOfConnectionPart(IElement e) {\r
- ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
- if (ce == null)\r
- return null;\r
- IElement c = ce.getConnection();\r
- if (c == e)\r
- return null;\r
- return c;\r
- }\r
-\r
- static IElement getConnectionEntityConnection(IElement e) {\r
- ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
- if (ce == null)\r
- return null;\r
- return ce.getConnection();\r
- }\r
-\r
- static Set<IElement> getAllConnectionEntityParts(IElement e) {\r
- ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
- if (ce == null)\r
- return Collections.emptySet();\r
- Set<IElement> result = new HashSet<IElement>();\r
- result.add(e);\r
- ce.getBranchPoints(result);\r
- ce.getSegments(result);\r
- return result;\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * 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:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.g2d.utils;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.Queue;
+import java.util.Set;
+
+import org.simantics.g2d.connection.ConnectionEntity;
+import org.simantics.g2d.diagram.IDiagram;
+import org.simantics.g2d.diagram.handler.DataElementMap;
+import org.simantics.g2d.diagram.handler.RelationshipHandler;
+import org.simantics.g2d.diagram.handler.RelationshipHandler.Relation;
+import org.simantics.g2d.diagram.handler.Topology;
+import org.simantics.g2d.diagram.handler.Topology.Connection;
+import org.simantics.g2d.diagram.handler.Topology.Terminal;
+import org.simantics.g2d.element.ElementHints;
+import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.element.handler.TerminalTopology;
+import org.simantics.g2d.elementclass.FlagHandler;
+
+/**
+ * This class tries to expand the selection provided by the specified elements
+ * by a single expansion step. Its purpose is to provide a way for the user to
+ * easily select a larger range of elements based on the diagram connectivity.
+ * This can be useful e.g. when preparing for a copy-paste operation or simply
+ * for visualizing the connectivity of a diagram.</p>
+ *
+ * <p>
+ * The expansion logic is as follows:
+ * </p>
+ * <ol>
+ * <li>If connections are included in the current selection, make sure that no
+ * connection entity is only partly selected. If only partly selected connection
+ * entities are found, complete those and stop there. Otherwise continue to the
+ * next step.</li>
+ * <li>Expand the current selection by one step. For connections this means
+ * selecting all nodes that are attached by the connection but not yet in the
+ * current selection. For nodes this means expanding the selection to all the
+ * connections reachable from that particular node.</li>
+ * </ol>
+ *
+ * @author Tuukka Lehtonen
+ */
+public class TopologicalSelectionExpander {
+
+ public static final boolean DEBUG = false;
+
+ IDiagram diagram;
+ Set<IElement> startSelection;
+ Set<IElement> resultSelection;
+ Set<ConnectionEntity> processedConnections = new HashSet<ConnectionEntity>();
+
+ Topology topology;
+ DataElementMap dem;
+
+ public static Set<IElement> expandSelection(IDiagram diagram, Set<IElement> elements) {
+ return new TopologicalSelectionExpander(diagram, elements).expanded();
+ }
+
+ public TopologicalSelectionExpander(IDiagram diagram, Set<IElement> startSelection) {
+ assert diagram != null;
+
+ this.diagram = diagram;
+ this.startSelection = startSelection;
+ this.resultSelection = new HashSet<IElement>(startSelection);
+
+ this.topology = diagram.getDiagramClass().getAtMostOneItemOfClass(Topology.class);
+ this.dem = diagram.getDiagramClass().getAtMostOneItemOfClass(DataElementMap.class);
+ }
+
+ /**
+ * @return <code>null</code> if the selection did not change in the
+ * expansion, another set of elements otherwise
+ */
+ public Set<IElement> expandedIfChanged() {
+ Set<IElement> result = expanded();
+ if (DEBUG)
+ System.out.println("result selection: " + result);
+ if (result.equals(startSelection))
+ return null;
+ if (DEBUG)
+ System.out.println("setting new selection");
+ return result;
+ }
+
+ /**
+ * @return
+ */
+ public Set<IElement> expanded() {
+ if (topology == null || dem == null || startSelection.isEmpty())
+ return startSelection;
+
+ if (DEBUG)
+ System.out.println("expand start selection: " + startSelection);
+
+ Deque<IElement> work = new ArrayDeque<IElement>(startSelection.size() + 4);
+ work.addAll(startSelection);
+
+ // 1. Iterate the start selection to see if there are any partly
+ // selected connection entities. If so, then only complete the
+ // selection of those entities before expanding the selection in
+ // any other way.
+ boolean connectionPartsSelected = false;
+ for (IElement e : work) {
+ IElement connection = getConnectionOfConnectionPart(e);
+ if (connection != null) {
+ // There was a mere connection part selection among the selection.
+ Set<IElement> connectionParts = getAllConnectionEntityParts(e);
+ if (!connectionParts.isEmpty()) {
+ if (DEBUG)
+ System.out.println("\tconnection part selected: " + e + ", replacing with connection " + connection);
+ resultSelection.add(connection);
+ resultSelection.removeAll(connectionParts);
+ connectionPartsSelected = true;
+ }
+ }
+ }
+
+ if (!connectionPartsSelected) {
+ // No connection entities were partly selected. Go ahead with
+ // the normal selection expansion procedure.
+ while (!work.isEmpty()) {
+ IElement e = work.poll();
+ if (DEBUG)
+ System.out.println("\texpanding at element: " + e);
+ @SuppressWarnings("unused")
+ boolean expanded = expandConnection(e, work) || expandNode(e, work);
+ }
+ }
+
+ if (DEBUG)
+ System.out.println("expanded selection: " + resultSelection);
+ return resultSelection;
+ }
+
+ boolean expandConnection(IElement connection, Queue<IElement> workQueue) {
+ ConnectionEntity ce = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY);
+ if (ce == null)
+ return false;
+
+ if (!processedConnections.add(ce))
+ return true;
+
+ // Expand the selection to all the nodes attached to this connection.
+ if (DEBUG)
+ System.out.println("\texpanding at connection " + ce);
+ Collection<Connection> terminals = new ArrayList<Connection>();
+ ce.getTerminalConnections(terminals);
+ if (DEBUG)
+ System.out.println("\t\tfound " + terminals.size() + " terminal connections: " + terminals);
+ for (Connection terminal : terminals) {
+ if (resultSelection.add(terminal.node)) {
+ if (DEBUG)
+ System.out.println("\t\t\tadding node '" + terminal.node + "' at terminal '" + terminal.terminal + "'");
+ }
+ }
+ return true;
+ }
+
+ boolean expandNode(IElement e, Queue<IElement> workQueue) {
+ // This is a node.
+ TerminalTopology tt = e.getElementClass().getAtMostOneItemOfClass(TerminalTopology.class);
+ if (tt == null)
+ return false;
+ if (DEBUG)
+ System.out.println("\texpanding selection to node terminal connections: " + e);
+
+ Collection<Terminal> terminals = new ArrayList<Terminal>();
+ tt.getTerminals(e, terminals);
+ Collection<Connection> connections = new ArrayList<Connection>();
+ for (Terminal terminal : terminals) {
+ topology.getConnections(e, terminal, connections);
+ }
+ if (DEBUG)
+ System.out.println("\t\tfound " + connections.size() + " connected terminals: " + connections);
+ for (Connection connection : connections) {
+ IElement conn = getConnectionEntityConnection(connection.edge);
+ if (conn != null) {
+ if (DEBUG)
+ System.out.println("\t\t\tadding connection: " + conn);
+ resultSelection.add(conn);
+ }
+ }
+
+ boolean expanded = !connections.isEmpty();
+
+ // We want to:
+ // * expand selection to monitors and other related "sub-elements" of the selection
+ // We don't want to:
+ // * expand selection through flags
+ FlagHandler fh = e.getElementClass().getAtMostOneItemOfClass(FlagHandler.class);
+ if (fh == null) {
+ RelationshipHandler rh = diagram.getDiagramClass().getAtMostOneItemOfClass(RelationshipHandler.class);
+ if (rh != null) {
+ for(Relation rel : rh.getRelations(diagram, e, new ArrayList<Relation>())) {
+ if(rel.getSubject() instanceof IElement) {
+ expanded |= resultSelection.add((IElement)rel.getSubject());
+ }
+ if(rel.getObject() instanceof IElement) {
+ expanded |= resultSelection.add((IElement)rel.getObject());
+ }
+ }
+ }
+ }
+
+ return expanded;
+ }
+
+ static IElement getConnectionOfConnectionPart(IElement e) {
+ ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
+ if (ce == null)
+ return null;
+ IElement c = ce.getConnection();
+ if (c == e)
+ return null;
+ return c;
+ }
+
+ static IElement getConnectionEntityConnection(IElement e) {
+ ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
+ if (ce == null)
+ return null;
+ return ce.getConnection();
+ }
+
+ static Set<IElement> getAllConnectionEntityParts(IElement e) {
+ ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
+ if (ce == null)
+ return Collections.emptySet();
+ Set<IElement> result = new HashSet<IElement>();
+ result.add(e);
+ ce.getBranchPoints(result);
+ ce.getSegments(result);
+ return result;
+ }
+
+}