]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling/src/org/simantics/modeling/flags/MergeFlags.java
Find SCL references in SCLModuleEditor with Ctrl+Shift+G
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / flags / MergeFlags.java
1 /*******************************************************************************
2  * Copyright (c) 2012 Association for Decentralized Information Management in
3  * Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.modeling.flags;
13
14 import gnu.trove.map.hash.THashMap;
15 import gnu.trove.set.hash.THashSet;
16
17 import java.util.ArrayList;
18 import java.util.Iterator;
19 import java.util.List;
20
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;
36
37 public class MergeFlags {
38
39     private static class CP {
40         public final Resource component;
41         public final Resource connectionPoint;
42         
43         public CP(Resource component, Resource connectionPoint) {
44             super();
45             this.component = component;
46             this.connectionPoint = connectionPoint;
47         }
48
49         @Override
50         public int hashCode() {
51             return component.hashCode() + 31 * connectionPoint.hashCode();
52         }
53
54         @Override
55         public boolean equals(Object obj) {
56             if (this == obj)
57                 return true;
58             if (obj == null)
59                 return false;
60             if (getClass() != obj.getClass())
61                 return false;
62             CP other = (CP) obj;
63             return component.equals(other.component) 
64                     && connectionPoint.equals(other.connectionPoint);
65         }
66     }
67     
68     public static String validateForMerge(ReadGraph g,
69             List<Resource> flags) throws DatabaseException {
70         if(flags.size() <= 1)
71             return "At least two flags must be chosen.";
72         
73         DiagramResource DIA = DiagramResource.getInstance(g);
74         StructuralResource2 STR = StructuralResource2.getInstance(g);
75         
76         for(Resource flag : flags)
77             if(!g.hasStatement(flag, DIA.FlagIsJoinedBy))
78                 return "All flags are not joined to other flags.";
79
80         List<Resource> connectors = getPossibleRelated(g, flags, DIA.Flag_ConnectionPoint);
81         for(Resource connector : connectors) {
82             if(connector == null)
83                 return "All flags are not connected";
84         }
85         
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.";
90         }
91         
92         THashSet<Resource> uniqueConnections = new THashSet<Resource>(connections.size());
93         for(Resource connection : connections) {
94             uniqueConnections.add(connection);
95         }
96         
97         if(uniqueConnections.size() == 1)
98             return null;
99                 
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()));
104             if(cps.isEmpty())
105                 return "Flags are not connected to a common terminal.";
106         }
107         
108         return null;
109     }
110     
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())));
115         return result;
116     }
117     
118     /**
119      * Merges all flags in the given list to one or two flags.
120      * @param g
121      * @param flags to join
122      * @return Error message or empty string if the operation succeeded.
123      */
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);
126     }
127     
128     public static String mergeWithoutMetadata(WriteGraph g, List<Resource> flags) throws DatabaseException {
129         THashMap<Tuple2, ArrayList<Resource>> groups = 
130                 new THashMap<Tuple2, ArrayList<Resource>>();
131         
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)) {
139                     connection = temp;
140                     break;
141                 }
142             if(connection == null)
143                 continue;
144             
145             Resource flagType = g.getSingleObject(flag, DIA.HasFlagType);
146             Resource connectionType = g.getPossibleObject(connection, STR.HasConnectionType);
147             
148             Tuple2 tuple = new Tuple2(flagType, connectionType);
149             ArrayList<Resource> group = groups.get(tuple);
150             if(group == null) {
151                 group = new ArrayList<Resource>();
152                 groups.put(tuple, group);
153             }
154             group.add(flag);
155         }
156     
157         String errorMessage = "";
158         for(ArrayList<Resource> group : groups.values()) {
159             if(group.size() > 1) {
160                 String temp = mergeAux(g, group);
161                 if(temp != null)
162                     errorMessage = temp;
163             }
164         }
165         return errorMessage;
166     }
167     
168     /**
169      * Merges all flags in the given list to the first flag of the list.
170      * @param g
171      * @param flags to join
172      * @return Error message or null if the operation succeeded.
173      */
174     private static String mergeAux(WriteGraph g, List<Resource> flags) throws DatabaseException {
175         if(flags.size() <= 1)
176             return null; // Nothing to do
177         
178         DiagramResource DIA = DiagramResource.getInstance(g);
179         StructuralResource2 STR = StructuralResource2.getInstance(g);
180         
181         // Find connectors
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";
186         }        
187         
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.";
193         }
194         
195         // Choose canonical flag and connection where other flags are merged to
196         Resource canonicalFlag = flags.get(0);
197         Resource canonicalConnection = connections.get(0);
198         
199         // Do the merging
200         for(int i=1;i<flags.size();++i) {
201             Resource flag = flags.get(i);
202             Resource connection = connections.get(i);
203             
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);
210             }
211             
212             // Remove flag and its connector
213             removeElement(g, flag);
214             g.deny(connectors.get(i));
215         }
216         
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));
221         
222         for(Resource connection : uniqueConnections)
223             cleanUpConnection(g, connection);
224         
225         return null;
226     }
227
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);
232         
233         Resource diagramConnection = g.getSingleObject(connection, MOD.ConnectionToDiagramConnection);
234         
235         // If connection is degenerated, remove it
236         if(g.getObjects(connection, STR.IsJoinedBy).size() == 0 &&  
237                 g.getObjects(connection, STR.Connects).size() <= 1) {
238             g.deny(connection);
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);
243         }
244         // Otherwise just remove degenerated route nodes
245         else {
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) {
251                         g.deny(routeNode);
252                         modifiedSomething = true;
253                     }
254             }
255         }
256     }
257     
258     private static void removeElement(WriteGraph g, Resource element) throws DatabaseException {
259         OrderedSetUtils.remove(g, OrderedSetUtils.getSingleOwnerList(g, element), element);
260         g.deny(element);
261     }
262
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)));
267         return result;
268     }
269     
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);
276             }
277         }
278         return null;
279     }
280     
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));
285         return result;
286     }
287
288         public static void expandFlagSet(ReadGraph graph, List<Resource> flags) throws DatabaseException {
289                 DiagramResource DIA = DiagramResource.getInstance(graph);
290                 StructuralResource2 STR = StructuralResource2.getInstance(graph);
291                                 
292                 THashSet<Resource> connectionSet = new THashSet<Resource>();
293                 
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);
299                 }
300                 
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);
310                 }
311                 
312                 THashSet<Resource> visited = new THashSet<Resource>(flags);
313                                                 
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))
320                                                 flags.add(flag);
321                 }                                       
322         }   
323         
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);
328         
329         Resource diagram = g.getPossibleObject(composite, MOD.CompositeToDiagram);
330         if(diagram == null)
331             return;
332         
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))
336                 flags.add(element);
337         
338         for(Resource flag : flags.toArray(new Resource[flags.size()])) 
339             if(flags.contains(flag)) {
340                 ArrayList<Resource> group = new ArrayList<Resource>();
341                 group.add(flag);
342                 expandFlagSet(g, group);
343                 flags.removeAll(group);
344                 if(group.size() > 1)
345                     groups.add(group);
346             }
347         }
348         
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);
352         }
353
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);
362     }
363     
364     public static String mergeFlags (WriteGraph graph, Resource diagram ) throws DatabaseException {
365         
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);
370         }
371
372                 return "Merged flags in diagram resource: " + diagram.toString() + " and name: " + NameUtils.getSafeName(graph, diagram);
373     }
374     
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);
380         }
381     }    
382 }