]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.g2d/src/org/simantics/g2d/utils/TopologicalSelectionExpander.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / utils / TopologicalSelectionExpander.java
diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/utils/TopologicalSelectionExpander.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/utils/TopologicalSelectionExpander.java
new file mode 100644 (file)
index 0000000..55f2ce1
--- /dev/null
@@ -0,0 +1,252 @@
+/*******************************************************************************\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