1 /*******************************************************************************
\r
2 * Copyright (c) 2012 Association for Decentralized Information Management in
\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.modeling.flags;
\r
14 import gnu.trove.map.hash.THashMap;
\r
15 import gnu.trove.set.hash.THashSet;
\r
17 import java.util.ArrayList;
\r
18 import java.util.Iterator;
\r
19 import java.util.List;
\r
21 import org.simantics.db.ReadGraph;
\r
22 import org.simantics.db.Resource;
\r
23 import org.simantics.db.Statement;
\r
24 import org.simantics.db.WriteGraph;
\r
25 import org.simantics.db.common.request.IndexRoot;
\r
26 import org.simantics.db.common.utils.NameUtils;
\r
27 import org.simantics.db.common.utils.OrderedSetUtils;
\r
28 import org.simantics.db.exception.DatabaseException;
\r
29 import org.simantics.diagram.content.ConnectionUtil;
\r
30 import org.simantics.diagram.stubs.DiagramResource;
\r
31 import org.simantics.layer0.Layer0;
\r
32 import org.simantics.modeling.ModelingResources;
\r
33 import org.simantics.scl.commands.Commands;
\r
34 import org.simantics.scl.runtime.tuple.Tuple2;
\r
35 import org.simantics.structural.stubs.StructuralResource2;
\r
37 public class MergeFlags {
\r
39 private static class CP {
\r
40 public final Resource component;
\r
41 public final Resource connectionPoint;
\r
43 public CP(Resource component, Resource connectionPoint) {
\r
45 this.component = component;
\r
46 this.connectionPoint = connectionPoint;
\r
50 public int hashCode() {
\r
51 return component.hashCode() + 31 * connectionPoint.hashCode();
\r
55 public boolean equals(Object obj) {
\r
60 if (getClass() != obj.getClass())
\r
62 CP other = (CP) obj;
\r
63 return component.equals(other.component)
\r
64 && connectionPoint.equals(other.connectionPoint);
\r
68 public static String validateForMerge(ReadGraph g,
\r
69 List<Resource> flags) throws DatabaseException {
\r
70 if(flags.size() <= 1)
\r
71 return "At least two flags must be chosen.";
\r
73 DiagramResource DIA = DiagramResource.getInstance(g);
\r
74 StructuralResource2 STR = StructuralResource2.getInstance(g);
\r
76 for(Resource flag : flags)
\r
77 if(!g.hasStatement(flag, DIA.FlagIsJoinedBy))
\r
78 return "All flags are not joined to other flags.";
\r
80 List<Resource> connectors = getPossibleRelated(g, flags, DIA.Flag_ConnectionPoint);
\r
81 for(Resource connector : connectors) {
\r
82 if(connector == null)
\r
83 return "All flags are not connected";
\r
86 List<Resource> connections = getConnection(g, flags, connectors);
\r
87 for(Resource connection : connections) {
\r
88 if(connection == null)
\r
89 return "Invalid flag. Didn't find configuration connection.";
\r
92 THashSet<Resource> uniqueConnections = new THashSet<Resource>(connections.size());
\r
93 for(Resource connection : connections) {
\r
94 uniqueConnections.add(connection);
\r
97 if(uniqueConnections.size() == 1)
\r
100 Iterator<Resource> it = uniqueConnections.iterator();
\r
101 THashSet<CP> cps = getConnectionPoints(g, STR, it.next());
\r
102 while(it.hasNext()) {
\r
103 cps.retainAll(getConnectionPoints(g, STR, it.next()));
\r
105 return "Flags are not connected to a common terminal.";
\r
111 private static THashSet<CP> getConnectionPoints(ReadGraph g, StructuralResource2 STR, Resource connection) throws DatabaseException {
\r
112 THashSet<CP> result = new THashSet<CP>();
\r
113 for(Statement stat : g.getStatements(connection, STR.Connects))
\r
114 result.add(new CP(stat.getObject(), g.getInverse(stat.getPredicate())));
\r
119 * Merges all flags in the given list to one or two flags.
\r
121 * @param flags to join
\r
122 * @return Error message or empty string if the operation succeeded.
\r
124 public static String merge(WriteGraph g, List<Resource> flags) throws DatabaseException {
\r
125 return (String)Commands.get(g, "Simantics/Flag/mergeFlags").execute(g, g.syncRequest(new IndexRoot(flags.get(0))), flags);
\r
128 public static String mergeWithoutMetadata(WriteGraph g, List<Resource> flags) throws DatabaseException {
\r
129 THashMap<Tuple2, ArrayList<Resource>> groups =
\r
130 new THashMap<Tuple2, ArrayList<Resource>>();
\r
132 DiagramResource DIA = DiagramResource.getInstance(g);
\r
133 StructuralResource2 STR = StructuralResource2.getInstance(g);
\r
134 for(Resource flag : flags) {
\r
135 Resource connector = g.getSingleObject(flag, DIA.Flag_ConnectionPoint);
\r
136 Resource connection = null;
\r
137 for(Resource temp : g.getObjects(connector, STR.Connects))
\r
138 if(!temp.equals(flag)) {
\r
142 if(connection == null)
\r
145 Resource flagType = g.getSingleObject(flag, DIA.HasFlagType);
\r
146 Resource connectionType = g.getPossibleObject(connection, STR.HasConnectionType);
\r
148 Tuple2 tuple = new Tuple2(flagType, connectionType);
\r
149 ArrayList<Resource> group = groups.get(tuple);
\r
150 if(group == null) {
\r
151 group = new ArrayList<Resource>();
\r
152 groups.put(tuple, group);
\r
157 String errorMessage = "";
\r
158 for(ArrayList<Resource> group : groups.values()) {
\r
159 if(group.size() > 1) {
\r
160 String temp = mergeAux(g, group);
\r
162 errorMessage = temp;
\r
165 return errorMessage;
\r
169 * Merges all flags in the given list to the first flag of the list.
\r
171 * @param flags to join
\r
172 * @return Error message or null if the operation succeeded.
\r
174 private static String mergeAux(WriteGraph g, List<Resource> flags) throws DatabaseException {
\r
175 if(flags.size() <= 1)
\r
176 return null; // Nothing to do
\r
178 DiagramResource DIA = DiagramResource.getInstance(g);
\r
179 StructuralResource2 STR = StructuralResource2.getInstance(g);
\r
182 List<Resource> connectors = getPossibleRelated(g, flags, DIA.Flag_ConnectionPoint);
\r
183 for(Resource connector : connectors) {
\r
184 if(connector == null)
\r
185 return "All flags are not connected";
\r
188 // Find configuration connections
\r
189 List<Resource> connections = getConnection(g, flags, connectors);
\r
190 for(Resource connection : connections) {
\r
191 if(connection == null)
\r
192 return "Invalid flag. Didn't find configuration connection.";
\r
195 // Choose canonical flag and connection where other flags are merged to
\r
196 Resource canonicalFlag = flags.get(0);
\r
197 Resource canonicalConnection = connections.get(0);
\r
200 for(int i=1;i<flags.size();++i) {
\r
201 Resource flag = flags.get(i);
\r
202 Resource connection = connections.get(i);
\r
204 // Replace references in joins to the canonical flag and connection
\r
205 for(Resource join : g.getObjects(flag, DIA.FlagIsJoinedBy)) {
\r
206 g.denyStatement(flag, DIA.FlagIsJoinedBy, join);
\r
207 g.claim(canonicalFlag, DIA.FlagIsJoinedBy, join);
\r
208 g.denyStatement(join, STR.Joins, connection);
\r
209 g.claim(join, STR.Joins, canonicalConnection);
\r
212 // Remove flag and its connector
\r
213 removeElement(g, flag);
\r
214 g.deny(connectors.get(i));
\r
217 // Clean up connections (remove extra route lines and complete connections)
\r
218 THashSet<Resource> uniqueConnections = new THashSet<Resource>(connections.size());
\r
219 for(int i=1;i<connections.size();++i)
\r
220 uniqueConnections.add(connections.get(i));
\r
222 for(Resource connection : uniqueConnections)
\r
223 cleanUpConnection(g, connection);
\r
228 private static void cleanUpConnection(WriteGraph g, Resource connection) throws DatabaseException {
\r
229 DiagramResource DIA = DiagramResource.getInstance(g);
\r
230 StructuralResource2 STR = StructuralResource2.getInstance(g);
\r
231 ModelingResources MOD = ModelingResources.getInstance(g);
\r
233 Resource diagramConnection = g.getSingleObject(connection, MOD.ConnectionToDiagramConnection);
\r
235 // If connection is degenerated, remove it
\r
236 if(g.getObjects(connection, STR.IsJoinedBy).size() == 0 &&
\r
237 g.getObjects(connection, STR.Connects).size() <= 1) {
\r
238 g.deny(connection);
\r
239 // Garbage collection removes interior route nodes etc. stuff
\r
240 //removeElement(g, diagramConnection);
\r
241 // platform #4473: Remove connection completely to not leave behind stray connectors.
\r
242 new ConnectionUtil(g).removeConnection(diagramConnection);
\r
244 // Otherwise just remove degenerated route nodes
\r
246 boolean modifiedSomething = true;
\r
247 while(modifiedSomething) { // loop until no modification are made (O(n^2) algorithm)
\r
248 modifiedSomething = false;
\r
249 for(Resource routeNode : g.getObjects(diagramConnection, DIA.HasInteriorRouteNode))
\r
250 if(g.getObjects(routeNode, DIA.AreConnected).size() <= 1) {
\r
252 modifiedSomething = true;
\r
258 private static void removeElement(WriteGraph g, Resource element) throws DatabaseException {
\r
259 OrderedSetUtils.remove(g, OrderedSetUtils.getSingleOwnerList(g, element), element);
\r
263 private static List<Resource> getConnection(ReadGraph g, List<Resource> flags, List<Resource> connectors) throws DatabaseException {
\r
264 ArrayList<Resource> result = new ArrayList<Resource>(flags.size());
\r
265 for(int i=0;i<flags.size();++i)
\r
266 result.add(getConnection(g, flags.get(i), connectors.get(i)));
\r
270 private static Resource getConnection(ReadGraph g, Resource flag, Resource connector) throws DatabaseException {
\r
271 StructuralResource2 STR = StructuralResource2.getInstance(g);
\r
272 ModelingResources MOD = ModelingResources.getInstance(g);
\r
273 for(Resource diagramConnection : g.getObjects(connector, STR.Connects)) {
\r
274 if(!flag.equals(diagramConnection)) {
\r
275 return g.getPossibleObject(diagramConnection, MOD.DiagramConnectionToConnection);
\r
281 private static List<Resource> getPossibleRelated(ReadGraph g, List<Resource> subjects, Resource relation) throws DatabaseException {
\r
282 ArrayList<Resource> result = new ArrayList<Resource>(subjects.size());
\r
283 for(int i=0;i<subjects.size();++i)
\r
284 result.add(g.getPossibleObject(subjects.get(i), relation));
\r
288 public static void expandFlagSet(ReadGraph graph, List<Resource> flags) throws DatabaseException {
\r
289 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
290 StructuralResource2 STR = StructuralResource2.getInstance(graph);
\r
292 THashSet<Resource> connectionSet = new THashSet<Resource>();
\r
294 for(Resource flag : flags) {
\r
295 for(Resource connector : graph.getObjects(flag, STR.IsConnectedTo))
\r
296 for(Resource connection : graph.getObjects(connector, STR.Connects))
\r
297 if(!connection.equals(flag))
\r
298 connectionSet.add(connection);
\r
301 for(Resource connection : connectionSet.toArray(new Resource[connectionSet.size()])) {
\r
302 for(Resource connector : graph.getObjects(connection, STR.IsConnectedTo))
\r
303 for(Statement stat : graph.getStatements(connector, STR.Connects))
\r
304 if(!stat.getObject().equals(connection))
\r
305 for(Resource connector2 : graph.getObjects(stat.getObject(), graph.getInverse(stat.getPredicate())))
\r
306 if(!connector2.equals(connector))
\r
307 for(Resource connection2 : graph.getObjects(connector2, STR.Connects))
\r
308 if(graph.isInstanceOf(connection2, DIA.Connection))
\r
309 connectionSet.add(connection2);
\r
312 THashSet<Resource> visited = new THashSet<Resource>(flags);
\r
314 for(Resource connection : connectionSet) {
\r
315 visited.add(connection);
\r
316 for(Resource connector : graph.getObjects(connection, STR.IsConnectedTo))
\r
317 for(Resource flag : graph.getObjects(connector, STR.Connects))
\r
318 if(visited.add(flag) && graph.isInstanceOf(flag, DIA.Flag)
\r
319 && graph.hasStatement(flag, DIA.FlagIsJoinedBy))
\r
324 public static void collectFlagGroupsInComposite(ReadGraph g, Resource composite, ArrayList<ArrayList<Resource>> groups) throws DatabaseException {
\r
325 DiagramResource DIA = DiagramResource.getInstance(g);
\r
326 ModelingResources MOD = ModelingResources.getInstance(g);
\r
327 Layer0 L0 = Layer0.getInstance(g);
\r
329 Resource diagram = g.getPossibleObject(composite, MOD.CompositeToDiagram);
\r
330 if(diagram == null)
\r
333 THashSet<Resource> flags = new THashSet<Resource>();
\r
334 for(Resource element : g.getObjects(diagram, L0.ConsistsOf))
\r
335 if(g.isInstanceOf(element, DIA.Flag) && g.hasStatement(element, DIA.FlagIsJoinedBy))
\r
336 flags.add(element);
\r
338 for(Resource flag : flags.toArray(new Resource[flags.size()]))
\r
339 if(flags.contains(flag)) {
\r
340 ArrayList<Resource> group = new ArrayList<Resource>();
\r
342 expandFlagSet(g, group);
\r
343 flags.removeAll(group);
\r
344 if(group.size() > 1)
\r
349 public static void expandCompositeSet(ReadGraph g, THashSet<Resource> composites) throws DatabaseException {
\r
350 for(Resource composite : composites.toArray(new Resource[composites.size()]))
\r
351 expandCompositeSet(g, composite, composites);
\r
354 private static void expandCompositeSet(ReadGraph g, Resource composite,
\r
355 THashSet<Resource> composites) throws DatabaseException {
\r
356 Layer0 L0 = Layer0.getInstance(g);
\r
357 StructuralResource2 STR = StructuralResource2.getInstance(g);
\r
358 for(Resource child : g.getObjects(composite, L0.ConsistsOf))
\r
359 if(g.isInstanceOf(child, STR.Composite))
\r
360 if(composites.add(child))
\r
361 expandCompositeSet(g, child, composites);
\r
364 public static String mergeFlags (WriteGraph graph, Resource diagram ) throws DatabaseException {
\r
366 ArrayList<ArrayList<Resource>> groups = new ArrayList<ArrayList<Resource>>();
\r
367 MergeFlags.collectFlagGroupsInComposite(graph, diagram, groups);
\r
368 for(ArrayList<Resource> group : groups) {
\r
369 MergeFlags.merge(graph, group);
\r
372 return "Merged flags in diagram resource: " + diagram.toString() + " and name: " + NameUtils.getSafeName(graph, diagram);
\r
375 public static void expandFlags(WriteGraph graph, Resource diagram) throws DatabaseException {
\r
376 ArrayList<Resource> groups = new ArrayList<Resource>();
\r
377 ExpandFlags.collectGroupedFlags(graph, diagram, groups);
\r
378 for(Resource group : groups) {
\r
379 ExpandFlags.expandFlag(graph, group);
\r