]> gerrit.simantics Code Review - simantics/platform.git/blob - 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
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.g2d.utils;\r
13 \r
14 import java.util.ArrayDeque;\r
15 import java.util.ArrayList;\r
16 import java.util.Collection;\r
17 import java.util.Collections;\r
18 import java.util.Deque;\r
19 import java.util.HashSet;\r
20 import java.util.Queue;\r
21 import java.util.Set;\r
22 \r
23 import org.simantics.g2d.connection.ConnectionEntity;\r
24 import org.simantics.g2d.diagram.IDiagram;\r
25 import org.simantics.g2d.diagram.handler.DataElementMap;\r
26 import org.simantics.g2d.diagram.handler.RelationshipHandler;\r
27 import org.simantics.g2d.diagram.handler.RelationshipHandler.Relation;\r
28 import org.simantics.g2d.diagram.handler.Topology;\r
29 import org.simantics.g2d.diagram.handler.Topology.Connection;\r
30 import org.simantics.g2d.diagram.handler.Topology.Terminal;\r
31 import org.simantics.g2d.element.ElementHints;\r
32 import org.simantics.g2d.element.IElement;\r
33 import org.simantics.g2d.element.handler.TerminalTopology;\r
34 import org.simantics.g2d.elementclass.FlagHandler;\r
35 \r
36 /**\r
37  * This class tries to expand the selection provided by the specified elements\r
38  * by a single expansion step. Its purpose is to provide a way for the user to\r
39  * easily select a larger range of elements based on the diagram connectivity.\r
40  * This can be useful e.g. when preparing for a copy-paste operation or simply\r
41  * for visualizing the connectivity of a diagram.</p>\r
42  * \r
43  * <p>\r
44  * The expansion logic is as follows:\r
45  * </p>\r
46  * <ol>\r
47  * <li>If connections are included in the current selection, make sure that no\r
48  * connection entity is only partly selected. If only partly selected connection\r
49  * entities are found, complete those and stop there. Otherwise continue to the\r
50  * next step.</li>\r
51  * <li>Expand the current selection by one step. For connections this means\r
52  * selecting all nodes that are attached by the connection but not yet in the\r
53  * current selection. For nodes this means expanding the selection to all the\r
54  * connections reachable from that particular node.</li>\r
55  * </ol>\r
56  * \r
57  * @author Tuukka Lehtonen\r
58  */\r
59 public class TopologicalSelectionExpander {\r
60 \r
61     public static final boolean DEBUG = false;\r
62 \r
63     IDiagram              diagram;\r
64     Set<IElement>         startSelection;\r
65     Set<IElement>         resultSelection;\r
66     Set<ConnectionEntity> processedConnections = new HashSet<ConnectionEntity>();\r
67 \r
68     Topology              topology;\r
69     DataElementMap        dem;\r
70 \r
71     public static Set<IElement> expandSelection(IDiagram diagram, Set<IElement> elements) {\r
72         return new TopologicalSelectionExpander(diagram, elements).expanded();\r
73     }\r
74 \r
75     public TopologicalSelectionExpander(IDiagram diagram, Set<IElement> startSelection) {\r
76         assert diagram != null;\r
77 \r
78         this.diagram = diagram;\r
79         this.startSelection = startSelection;\r
80         this.resultSelection = new HashSet<IElement>(startSelection);\r
81 \r
82         this.topology = diagram.getDiagramClass().getAtMostOneItemOfClass(Topology.class);\r
83         this.dem = diagram.getDiagramClass().getAtMostOneItemOfClass(DataElementMap.class);\r
84     }\r
85 \r
86     /**\r
87      * @return <code>null</code> if the selection did not change in the\r
88      *         expansion, another set of elements otherwise\r
89      */\r
90     public Set<IElement> expandedIfChanged() {\r
91         Set<IElement> result = expanded();\r
92         if (DEBUG)\r
93             System.out.println("result selection: " + result);\r
94         if (result.equals(startSelection))\r
95             return null;\r
96         if (DEBUG)\r
97             System.out.println("setting new selection");\r
98         return result;\r
99     }\r
100 \r
101     /**\r
102      * @return\r
103      */\r
104     public Set<IElement> expanded() {\r
105         if (topology == null || dem == null || startSelection.isEmpty())\r
106             return startSelection;\r
107 \r
108         if (DEBUG)\r
109             System.out.println("expand start selection: " + startSelection);\r
110 \r
111         Deque<IElement> work = new ArrayDeque<IElement>(startSelection.size() + 4);\r
112         work.addAll(startSelection);\r
113 \r
114         // 1. Iterate the start selection to see if there are any partly\r
115         // selected connection entities. If so, then only complete the\r
116         // selection of those entities before expanding the selection in\r
117         // any other way.\r
118         boolean connectionPartsSelected = false;\r
119         for (IElement e : work) {\r
120             IElement connection = getConnectionOfConnectionPart(e);\r
121             if (connection != null) {\r
122                 // There was a mere connection part selection among the selection.\r
123                 Set<IElement> connectionParts = getAllConnectionEntityParts(e);\r
124                 if (!connectionParts.isEmpty()) {\r
125                     if (DEBUG)\r
126                         System.out.println("\tconnection part selected: " + e + ", replacing with connection " + connection);\r
127                     resultSelection.add(connection);\r
128                     resultSelection.removeAll(connectionParts);\r
129                     connectionPartsSelected = true;\r
130                 }\r
131             }\r
132         }\r
133 \r
134         if (!connectionPartsSelected) {\r
135             // No connection entities were partly selected. Go ahead with\r
136             // the normal selection expansion procedure.\r
137             while (!work.isEmpty()) {\r
138                 IElement e = work.poll();\r
139                 if (DEBUG)\r
140                     System.out.println("\texpanding at element: " + e);\r
141                 @SuppressWarnings("unused")\r
142                 boolean expanded = expandConnection(e, work) || expandNode(e, work);\r
143             }\r
144         }\r
145 \r
146         if (DEBUG)\r
147             System.out.println("expanded selection: " + resultSelection);\r
148         return resultSelection;\r
149     }\r
150 \r
151     boolean expandConnection(IElement connection, Queue<IElement> workQueue) {\r
152         ConnectionEntity ce = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
153         if (ce == null)\r
154             return false;\r
155 \r
156         if (!processedConnections.add(ce))\r
157             return true;\r
158 \r
159         // Expand the selection to all the nodes attached to this connection.\r
160         if (DEBUG)\r
161             System.out.println("\texpanding at connection " + ce);\r
162         Collection<Connection> terminals = new ArrayList<Connection>();\r
163         ce.getTerminalConnections(terminals);\r
164         if (DEBUG)\r
165             System.out.println("\t\tfound " + terminals.size() + " terminal connections: " + terminals);\r
166         for (Connection terminal : terminals) {\r
167             if (resultSelection.add(terminal.node)) {\r
168                 if (DEBUG)\r
169                     System.out.println("\t\t\tadding node '" + terminal.node + "' at terminal '" + terminal.terminal + "'");\r
170             }\r
171         }\r
172         return true;\r
173     }\r
174 \r
175     boolean expandNode(IElement e, Queue<IElement> workQueue) {\r
176         // This is a node.\r
177         TerminalTopology tt = e.getElementClass().getAtMostOneItemOfClass(TerminalTopology.class);\r
178         if (tt == null)\r
179             return false;\r
180         if (DEBUG)\r
181             System.out.println("\texpanding selection to node terminal connections: " + e);\r
182 \r
183         Collection<Terminal> terminals = new ArrayList<Terminal>();\r
184         tt.getTerminals(e, terminals);\r
185         Collection<Connection> connections = new ArrayList<Connection>();\r
186         for (Terminal terminal : terminals) {\r
187             topology.getConnections(e, terminal, connections);\r
188         }\r
189         if (DEBUG)\r
190             System.out.println("\t\tfound " + connections.size() + " connected terminals: " + connections);\r
191         for (Connection connection : connections) {\r
192             IElement conn = getConnectionEntityConnection(connection.edge);\r
193             if (conn != null) {\r
194                 if (DEBUG)\r
195                     System.out.println("\t\t\tadding connection: " + conn);\r
196                 resultSelection.add(conn);\r
197             }\r
198         }\r
199         \r
200         boolean expanded = !connections.isEmpty(); \r
201 \r
202         // We want to:\r
203         // * expand selection to monitors and other related "sub-elements" of the selection\r
204         // We don't want to:\r
205         // * expand selection through flags\r
206         FlagHandler fh = e.getElementClass().getAtMostOneItemOfClass(FlagHandler.class);\r
207         if (fh == null) {\r
208             RelationshipHandler rh = diagram.getDiagramClass().getAtMostOneItemOfClass(RelationshipHandler.class);\r
209             if (rh != null) {\r
210                 for(Relation rel : rh.getRelations(diagram, e, new ArrayList<Relation>())) {\r
211                     if(rel.getSubject() instanceof IElement) {\r
212                         expanded |= resultSelection.add((IElement)rel.getSubject());\r
213                     }\r
214                     if(rel.getObject() instanceof IElement) {\r
215                         expanded |= resultSelection.add((IElement)rel.getObject());\r
216                     }\r
217                 }\r
218             }\r
219         }\r
220 \r
221         return expanded;\r
222     }\r
223 \r
224     static IElement getConnectionOfConnectionPart(IElement e) {\r
225         ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
226         if (ce == null)\r
227             return null;\r
228         IElement c = ce.getConnection();\r
229         if (c == e)\r
230             return null;\r
231         return c;\r
232     }\r
233 \r
234     static IElement getConnectionEntityConnection(IElement e) {\r
235         ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
236         if (ce == null)\r
237             return null;\r
238         return ce.getConnection();\r
239     }\r
240 \r
241     static Set<IElement> getAllConnectionEntityParts(IElement e) {\r
242         ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
243         if (ce == null)\r
244             return Collections.emptySet();\r
245         Set<IElement> result = new HashSet<IElement>();\r
246         result.add(e);\r
247         ce.getBranchPoints(result);\r
248         ce.getSegments(result);\r
249         return result;\r
250     }\r
251 \r
252 }\r