]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling/src/org/simantics/modeling/flags/MergeFlags.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / flags / MergeFlags.java
1 /*******************************************************************************\r
2  * Copyright (c) 2012 Association for Decentralized Information Management in\r
3  * 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.modeling.flags;\r
13 \r
14 import gnu.trove.map.hash.THashMap;\r
15 import gnu.trove.set.hash.THashSet;\r
16 \r
17 import java.util.ArrayList;\r
18 import java.util.Iterator;\r
19 import java.util.List;\r
20 \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
36 \r
37 public class MergeFlags {\r
38 \r
39     private static class CP {\r
40         public final Resource component;\r
41         public final Resource connectionPoint;\r
42         \r
43         public CP(Resource component, Resource connectionPoint) {\r
44             super();\r
45             this.component = component;\r
46             this.connectionPoint = connectionPoint;\r
47         }\r
48 \r
49         @Override\r
50         public int hashCode() {\r
51             return component.hashCode() + 31 * connectionPoint.hashCode();\r
52         }\r
53 \r
54         @Override\r
55         public boolean equals(Object obj) {\r
56             if (this == obj)\r
57                 return true;\r
58             if (obj == null)\r
59                 return false;\r
60             if (getClass() != obj.getClass())\r
61                 return false;\r
62             CP other = (CP) obj;\r
63             return component.equals(other.component) \r
64                     && connectionPoint.equals(other.connectionPoint);\r
65         }\r
66     }\r
67     \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
72         \r
73         DiagramResource DIA = DiagramResource.getInstance(g);\r
74         StructuralResource2 STR = StructuralResource2.getInstance(g);\r
75         \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
79 \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
84         }\r
85         \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
90         }\r
91         \r
92         THashSet<Resource> uniqueConnections = new THashSet<Resource>(connections.size());\r
93         for(Resource connection : connections) {\r
94             uniqueConnections.add(connection);\r
95         }\r
96         \r
97         if(uniqueConnections.size() == 1)\r
98             return null;\r
99                 \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
104             if(cps.isEmpty())\r
105                 return "Flags are not connected to a common terminal.";\r
106         }\r
107         \r
108         return null;\r
109     }\r
110     \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
115         return result;\r
116     }\r
117     \r
118     /**\r
119      * Merges all flags in the given list to one or two flags.\r
120      * @param g\r
121      * @param flags to join\r
122      * @return Error message or empty string if the operation succeeded.\r
123      */\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
126     }\r
127     \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
131         \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
139                     connection = temp;\r
140                     break;\r
141                 }\r
142             if(connection == null)\r
143                 continue;\r
144             \r
145             Resource flagType = g.getSingleObject(flag, DIA.HasFlagType);\r
146             Resource connectionType = g.getPossibleObject(connection, STR.HasConnectionType);\r
147             \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
153             }\r
154             group.add(flag);\r
155         }\r
156     \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
161                 if(temp != null)\r
162                     errorMessage = temp;\r
163             }\r
164         }\r
165         return errorMessage;\r
166     }\r
167     \r
168     /**\r
169      * Merges all flags in the given list to the first flag of the list.\r
170      * @param g\r
171      * @param flags to join\r
172      * @return Error message or null if the operation succeeded.\r
173      */\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
177         \r
178         DiagramResource DIA = DiagramResource.getInstance(g);\r
179         StructuralResource2 STR = StructuralResource2.getInstance(g);\r
180         \r
181         // Find connectors\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
186         }        \r
187         \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
193         }\r
194         \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
198         \r
199         // Do the merging\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
203             \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
210             }\r
211             \r
212             // Remove flag and its connector\r
213             removeElement(g, flag);\r
214             g.deny(connectors.get(i));\r
215         }\r
216         \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
221         \r
222         for(Resource connection : uniqueConnections)\r
223             cleanUpConnection(g, connection);\r
224         \r
225         return null;\r
226     }\r
227 \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
232         \r
233         Resource diagramConnection = g.getSingleObject(connection, MOD.ConnectionToDiagramConnection);\r
234         \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
243         }\r
244         // Otherwise just remove degenerated route nodes\r
245         else {\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
251                         g.deny(routeNode);\r
252                         modifiedSomething = true;\r
253                     }\r
254             }\r
255         }\r
256     }\r
257     \r
258     private static void removeElement(WriteGraph g, Resource element) throws DatabaseException {\r
259         OrderedSetUtils.remove(g, OrderedSetUtils.getSingleOwnerList(g, element), element);\r
260         g.deny(element);\r
261     }\r
262 \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
267         return result;\r
268     }\r
269     \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
276             }\r
277         }\r
278         return null;\r
279     }\r
280     \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
285         return result;\r
286     }\r
287 \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
291                                 \r
292                 THashSet<Resource> connectionSet = new THashSet<Resource>();\r
293                 \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
299                 }\r
300                 \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
310                 }\r
311                 \r
312                 THashSet<Resource> visited = new THashSet<Resource>(flags);\r
313                                                 \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
320                                                 flags.add(flag);\r
321                 }                                       \r
322         }   \r
323         \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
328         \r
329         Resource diagram = g.getPossibleObject(composite, MOD.CompositeToDiagram);\r
330         if(diagram == null)\r
331             return;\r
332         \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
337         \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
341                 group.add(flag);\r
342                 expandFlagSet(g, group);\r
343                 flags.removeAll(group);\r
344                 if(group.size() > 1)\r
345                     groups.add(group);\r
346             }\r
347         }\r
348         \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
352         }\r
353 \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
362     }\r
363     \r
364     public static String mergeFlags (WriteGraph graph, Resource diagram ) throws DatabaseException {\r
365         \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
370         }\r
371 \r
372                 return "Merged flags in diagram resource: " + diagram.toString() + " and name: " + NameUtils.getSafeName(graph, diagram);\r
373     }\r
374     \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
380         }\r
381     }    \r
382 }\r