1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.g2d.utils;
14 import java.util.ArrayDeque;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.Deque;
19 import java.util.HashSet;
20 import java.util.Queue;
23 import org.simantics.g2d.connection.ConnectionEntity;
24 import org.simantics.g2d.diagram.IDiagram;
25 import org.simantics.g2d.diagram.handler.DataElementMap;
26 import org.simantics.g2d.diagram.handler.RelationshipHandler;
27 import org.simantics.g2d.diagram.handler.RelationshipHandler.Relation;
28 import org.simantics.g2d.diagram.handler.Topology;
29 import org.simantics.g2d.diagram.handler.Topology.Connection;
30 import org.simantics.g2d.diagram.handler.Topology.Terminal;
31 import org.simantics.g2d.element.ElementHints;
32 import org.simantics.g2d.element.IElement;
33 import org.simantics.g2d.element.handler.TerminalTopology;
34 import org.simantics.g2d.elementclass.FlagHandler;
37 * This class tries to expand the selection provided by the specified elements
38 * by a single expansion step. Its purpose is to provide a way for the user to
39 * easily select a larger range of elements based on the diagram connectivity.
40 * This can be useful e.g. when preparing for a copy-paste operation or simply
41 * for visualizing the connectivity of a diagram.</p>
44 * The expansion logic is as follows:
47 * <li>If connections are included in the current selection, make sure that no
48 * connection entity is only partly selected. If only partly selected connection
49 * entities are found, complete those and stop there. Otherwise continue to the
51 * <li>Expand the current selection by one step. For connections this means
52 * selecting all nodes that are attached by the connection but not yet in the
53 * current selection. For nodes this means expanding the selection to all the
54 * connections reachable from that particular node.</li>
57 * @author Tuukka Lehtonen
59 public class TopologicalSelectionExpander {
61 public static final boolean DEBUG = false;
64 Set<IElement> startSelection;
65 Set<IElement> resultSelection;
66 Set<ConnectionEntity> processedConnections = new HashSet<ConnectionEntity>();
71 public static Set<IElement> expandSelection(IDiagram diagram, Set<IElement> elements) {
72 return new TopologicalSelectionExpander(diagram, elements).expanded();
75 public TopologicalSelectionExpander(IDiagram diagram, Set<IElement> startSelection) {
76 assert diagram != null;
78 this.diagram = diagram;
79 this.startSelection = startSelection;
80 this.resultSelection = new HashSet<IElement>(startSelection);
82 this.topology = diagram.getDiagramClass().getAtMostOneItemOfClass(Topology.class);
83 this.dem = diagram.getDiagramClass().getAtMostOneItemOfClass(DataElementMap.class);
87 * @return <code>null</code> if the selection did not change in the
88 * expansion, another set of elements otherwise
90 public Set<IElement> expandedIfChanged() {
91 Set<IElement> result = expanded();
93 System.out.println("result selection: " + result);
94 if (result.equals(startSelection))
97 System.out.println("setting new selection");
104 public Set<IElement> expanded() {
105 if (topology == null || dem == null || startSelection.isEmpty())
106 return startSelection;
109 System.out.println("expand start selection: " + startSelection);
111 Deque<IElement> work = new ArrayDeque<IElement>(startSelection.size() + 4);
112 work.addAll(startSelection);
114 // 1. Iterate the start selection to see if there are any partly
115 // selected connection entities. If so, then only complete the
116 // selection of those entities before expanding the selection in
118 boolean connectionPartsSelected = false;
119 for (IElement e : work) {
120 IElement connection = getConnectionOfConnectionPart(e);
121 if (connection != null) {
122 // There was a mere connection part selection among the selection.
123 Set<IElement> connectionParts = getAllConnectionEntityParts(e);
124 if (!connectionParts.isEmpty()) {
126 System.out.println("\tconnection part selected: " + e + ", replacing with connection " + connection);
127 resultSelection.add(connection);
128 resultSelection.removeAll(connectionParts);
129 connectionPartsSelected = true;
134 if (!connectionPartsSelected) {
135 // No connection entities were partly selected. Go ahead with
136 // the normal selection expansion procedure.
137 while (!work.isEmpty()) {
138 IElement e = work.poll();
140 System.out.println("\texpanding at element: " + e);
141 @SuppressWarnings("unused")
142 boolean expanded = expandConnection(e, work) || expandNode(e, work);
147 System.out.println("expanded selection: " + resultSelection);
148 return resultSelection;
151 boolean expandConnection(IElement connection, Queue<IElement> workQueue) {
152 ConnectionEntity ce = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY);
156 if (!processedConnections.add(ce))
159 // Expand the selection to all the nodes attached to this connection.
161 System.out.println("\texpanding at connection " + ce);
162 Collection<Connection> terminals = new ArrayList<Connection>();
163 ce.getTerminalConnections(terminals);
165 System.out.println("\t\tfound " + terminals.size() + " terminal connections: " + terminals);
166 for (Connection terminal : terminals) {
167 if (resultSelection.add(terminal.node)) {
169 System.out.println("\t\t\tadding node '" + terminal.node + "' at terminal '" + terminal.terminal + "'");
175 boolean expandNode(IElement e, Queue<IElement> workQueue) {
177 TerminalTopology tt = e.getElementClass().getAtMostOneItemOfClass(TerminalTopology.class);
181 System.out.println("\texpanding selection to node terminal connections: " + e);
183 Collection<Terminal> terminals = new ArrayList<Terminal>();
184 tt.getTerminals(e, terminals);
185 Collection<Connection> connections = new ArrayList<Connection>();
186 for (Terminal terminal : terminals) {
187 topology.getConnections(e, terminal, connections);
190 System.out.println("\t\tfound " + connections.size() + " connected terminals: " + connections);
191 for (Connection connection : connections) {
192 IElement conn = getConnectionEntityConnection(connection.edge);
195 System.out.println("\t\t\tadding connection: " + conn);
196 resultSelection.add(conn);
200 boolean expanded = !connections.isEmpty();
203 // * expand selection to monitors and other related "sub-elements" of the selection
205 // * expand selection through flags
206 FlagHandler fh = e.getElementClass().getAtMostOneItemOfClass(FlagHandler.class);
208 RelationshipHandler rh = diagram.getDiagramClass().getAtMostOneItemOfClass(RelationshipHandler.class);
210 for(Relation rel : rh.getRelations(diagram, e, new ArrayList<Relation>())) {
211 if(rel.getSubject() instanceof IElement) {
212 expanded |= resultSelection.add((IElement)rel.getSubject());
214 if(rel.getObject() instanceof IElement) {
215 expanded |= resultSelection.add((IElement)rel.getObject());
224 static IElement getConnectionOfConnectionPart(IElement e) {
225 ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
228 IElement c = ce.getConnection();
234 static IElement getConnectionEntityConnection(IElement e) {
235 ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
238 return ce.getConnection();
241 static Set<IElement> getAllConnectionEntityParts(IElement e) {
242 ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
244 return Collections.emptySet();
245 Set<IElement> result = new HashSet<IElement>();
247 ce.getBranchPoints(result);
248 ce.getSegments(result);