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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.g2d.utils;
\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
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
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
44 * The expansion logic is as follows:
\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
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
57 * @author Tuukka Lehtonen
\r
59 public class TopologicalSelectionExpander {
\r
61 public static final boolean DEBUG = false;
\r
64 Set<IElement> startSelection;
\r
65 Set<IElement> resultSelection;
\r
66 Set<ConnectionEntity> processedConnections = new HashSet<ConnectionEntity>();
\r
71 public static Set<IElement> expandSelection(IDiagram diagram, Set<IElement> elements) {
\r
72 return new TopologicalSelectionExpander(diagram, elements).expanded();
\r
75 public TopologicalSelectionExpander(IDiagram diagram, Set<IElement> startSelection) {
\r
76 assert diagram != null;
\r
78 this.diagram = diagram;
\r
79 this.startSelection = startSelection;
\r
80 this.resultSelection = new HashSet<IElement>(startSelection);
\r
82 this.topology = diagram.getDiagramClass().getAtMostOneItemOfClass(Topology.class);
\r
83 this.dem = diagram.getDiagramClass().getAtMostOneItemOfClass(DataElementMap.class);
\r
87 * @return <code>null</code> if the selection did not change in the
\r
88 * expansion, another set of elements otherwise
\r
90 public Set<IElement> expandedIfChanged() {
\r
91 Set<IElement> result = expanded();
\r
93 System.out.println("result selection: " + result);
\r
94 if (result.equals(startSelection))
\r
97 System.out.println("setting new selection");
\r
104 public Set<IElement> expanded() {
\r
105 if (topology == null || dem == null || startSelection.isEmpty())
\r
106 return startSelection;
\r
109 System.out.println("expand start selection: " + startSelection);
\r
111 Deque<IElement> work = new ArrayDeque<IElement>(startSelection.size() + 4);
\r
112 work.addAll(startSelection);
\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
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
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
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
140 System.out.println("\texpanding at element: " + e);
\r
141 @SuppressWarnings("unused")
\r
142 boolean expanded = expandConnection(e, work) || expandNode(e, work);
\r
147 System.out.println("expanded selection: " + resultSelection);
\r
148 return resultSelection;
\r
151 boolean expandConnection(IElement connection, Queue<IElement> workQueue) {
\r
152 ConnectionEntity ce = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY);
\r
156 if (!processedConnections.add(ce))
\r
159 // Expand the selection to all the nodes attached to this connection.
\r
161 System.out.println("\texpanding at connection " + ce);
\r
162 Collection<Connection> terminals = new ArrayList<Connection>();
\r
163 ce.getTerminalConnections(terminals);
\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
169 System.out.println("\t\t\tadding node '" + terminal.node + "' at terminal '" + terminal.terminal + "'");
\r
175 boolean expandNode(IElement e, Queue<IElement> workQueue) {
\r
177 TerminalTopology tt = e.getElementClass().getAtMostOneItemOfClass(TerminalTopology.class);
\r
181 System.out.println("\texpanding selection to node terminal connections: " + e);
\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
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
195 System.out.println("\t\t\tadding connection: " + conn);
\r
196 resultSelection.add(conn);
\r
200 boolean expanded = !connections.isEmpty();
\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
208 RelationshipHandler rh = diagram.getDiagramClass().getAtMostOneItemOfClass(RelationshipHandler.class);
\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
214 if(rel.getObject() instanceof IElement) {
\r
215 expanded |= resultSelection.add((IElement)rel.getObject());
\r
224 static IElement getConnectionOfConnectionPart(IElement e) {
\r
225 ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
\r
228 IElement c = ce.getConnection();
\r
234 static IElement getConnectionEntityConnection(IElement e) {
\r
235 ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
\r
238 return ce.getConnection();
\r
241 static Set<IElement> getAllConnectionEntityParts(IElement e) {
\r
242 ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
\r
244 return Collections.emptySet();
\r
245 Set<IElement> result = new HashSet<IElement>();
\r
247 ce.getBranchPoints(result);
\r
248 ce.getSegments(result);
\r