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