]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/RouteGraphModification.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / synchronization / graph / RouteGraphModification.java
1 package org.simantics.diagram.synchronization.graph;\r
2 \r
3 import gnu.trove.list.array.TIntArrayList;\r
4 import gnu.trove.map.hash.TObjectIntHashMap;\r
5 import gnu.trove.set.hash.THashSet;\r
6 import gnu.trove.set.hash.TIntHashSet;\r
7 \r
8 import java.util.ArrayList;\r
9 import java.util.Arrays;\r
10 import java.util.Collection;\r
11 import java.util.Iterator;\r
12 import java.util.List;\r
13 \r
14 import org.simantics.db.ReadGraph;\r
15 import org.simantics.db.Resource;\r
16 import org.simantics.db.WriteGraph;\r
17 import org.simantics.db.exception.DatabaseException;\r
18 import org.simantics.db.layer0.util.RelativeReference;\r
19 import org.simantics.db.service.SerialisationSupport;\r
20 import org.simantics.diagram.connection.RouteGraph;\r
21 import org.simantics.diagram.connection.RouteLine;\r
22 import org.simantics.diagram.connection.RouteLink;\r
23 import org.simantics.diagram.connection.RouteNode;\r
24 import org.simantics.diagram.connection.RoutePoint;\r
25 import org.simantics.diagram.connection.RouteTerminal;\r
26 import org.simantics.diagram.flag.RouteGraphConnectionSplitter;\r
27 import org.simantics.diagram.stubs.DiagramResource;\r
28 import org.simantics.layer0.Layer0;\r
29 import org.simantics.structural.stubs.StructuralResource2;\r
30 \r
31 /**\r
32  * Encodes modifications to a route graph.\r
33  * @author Hannu Niemistö\r
34  */\r
35 public class RouteGraphModification {\r
36     // Identification information\r
37     int lineCount;\r
38     int terminalCount;\r
39     int[] links;\r
40 \r
41     // Modification information\r
42     ArrayList<Modi> modis = new ArrayList<Modi>(); \r
43 \r
44     // Two different ways to identify\r
45     Resource[] resources;    \r
46     String[] terminalIdentifiers;\r
47 \r
48     // Auxiliary\r
49     TObjectIntHashMap<RouteNode> idMap;\r
50     Resource connection;\r
51 \r
52     public interface Modi {\r
53     }\r
54 \r
55     public static class UpdateLine implements Modi {\r
56         int id;\r
57         double position;\r
58         boolean isHorizontal;\r
59 \r
60         public UpdateLine(int id, double position, boolean isHorizontal) {\r
61             this.id = id;\r
62             this.position = position;\r
63             this.isHorizontal = isHorizontal;\r
64         }\r
65 \r
66         public UpdateLine(String text) {\r
67             String[] parts = text.split("\\$");\r
68             this.id = Integer.parseInt(parts[0]);\r
69             this.position = Double.parseDouble(parts[1]);\r
70             this.isHorizontal = Boolean.parseBoolean(parts[2]);\r
71         }\r
72 \r
73         @Override\r
74         public String toString() {\r
75             return "M" + id + "$" + position + "$" + isHorizontal;\r
76         }\r
77     }\r
78 \r
79     public static class RemoveLink implements Modi {\r
80         int a;\r
81         int b;\r
82 \r
83         public RemoveLink(int a, int b) {\r
84             this.a = a;\r
85             this.b = b;\r
86         }\r
87 \r
88         public RemoveLink(String text) {\r
89             String[] parts = text.split("\\$");\r
90             this.a = Integer.parseInt(parts[0]);\r
91             this.b = Integer.parseInt(parts[1]);\r
92         }\r
93 \r
94         @Override\r
95         public String toString() {\r
96             return "r" + a + "$" + b;\r
97         }\r
98     }\r
99 \r
100     public static class RemoveLine implements Modi {\r
101         int a;\r
102 \r
103         public RemoveLine(int a) {\r
104             this.a = a;\r
105         }\r
106 \r
107         public RemoveLine(String text) {\r
108             this.a = Integer.parseInt(text);\r
109         }\r
110 \r
111         @Override\r
112         public String toString() {\r
113             return "R" + a;\r
114         }\r
115     }\r
116 \r
117     public static class CreateLink implements Modi {\r
118         int a;\r
119         int b;\r
120 \r
121         public CreateLink(int a, int b) {\r
122             this.a = a;\r
123             this.b = b;\r
124         }\r
125 \r
126         public CreateLink(String text) {\r
127             String[] parts = text.split("\\$");\r
128             this.a = Integer.parseInt(parts[0]);\r
129             this.b = Integer.parseInt(parts[1]);\r
130         }\r
131 \r
132         @Override\r
133         public String toString() {\r
134             return "c" + a + "$" + b;\r
135         }\r
136     }\r
137 \r
138     public static class CreateLine implements Modi {\r
139         double position;\r
140         boolean isHorizontal;\r
141 \r
142         public CreateLine(double position, boolean isHorizontal) {\r
143             this.position = position;\r
144             this.isHorizontal = isHorizontal;\r
145         }\r
146 \r
147         public CreateLine(String text) {\r
148             String[] parts = text.split("\\$");\r
149             this.position = Double.parseDouble(parts[0]);\r
150             this.isHorizontal = Boolean.parseBoolean(parts[1]);\r
151         }\r
152 \r
153         @Override\r
154         public String toString() {\r
155             return "C" + position + "$" + isHorizontal;\r
156         }\r
157     }\r
158 \r
159     public static class Split implements Modi {\r
160         int[] interface1;\r
161         int[] interface2; \r
162         int[] lines2; \r
163         int[] terminals1; \r
164         int[] terminals2;\r
165         boolean isHorizontal; \r
166         double isectX;\r
167         double isectY;\r
168         \r
169         public Split(int[] interface1, int[] interface2, int[] lines2,\r
170                 int[] terminals1, int[] terminals2, boolean isHorizontal,\r
171                 double isectX, double isectY) {\r
172             this.interface1 = interface1;\r
173             this.interface2 = interface2;\r
174             this.lines2 = lines2;\r
175             this.terminals1 = terminals1;\r
176             this.terminals2 = terminals2;\r
177             this.isHorizontal = isHorizontal;\r
178             this.isectX = isectX;\r
179             this.isectY = isectY;\r
180         }\r
181 \r
182         public Split(String text) {\r
183             List<String> parts = Arrays.asList(text.split("\\$"));\r
184             Iterator<String> it = parts.iterator();\r
185             this.interface1 = readInts(it);\r
186             this.interface2 = readInts(it);\r
187             this.lines2 = readInts(it);\r
188             this.terminals1 = readInts(it);\r
189             this.terminals2 = readInts(it);\r
190             this.isHorizontal = Boolean.parseBoolean(it.next());\r
191             this.isectX = Double.parseDouble(it.next());\r
192             this.isectY = Double.parseDouble(it.next());\r
193         }\r
194 \r
195         @Override\r
196         public String toString() {\r
197             StringBuilder b = new StringBuilder();\r
198             b.append("S");\r
199             write(b, interface1);\r
200             b.append("$");\r
201             write(b, interface2);\r
202             b.append("$");\r
203             write(b, lines2);\r
204             b.append("$");\r
205             write(b, terminals1);\r
206             b.append("$");\r
207             write(b, terminals2);\r
208             b.append("$");\r
209             b.append(isHorizontal);\r
210             b.append("$");\r
211             b.append(isectX);\r
212             b.append("$");\r
213             b.append(isectY);\r
214             return b.toString();\r
215         }\r
216     }\r
217     \r
218     private static void write(StringBuilder b, int[] ids) {\r
219         b.append(ids.length);\r
220         for(int e : ids) {\r
221             b.append('$');\r
222             b.append(e);\r
223         }\r
224     }\r
225     \r
226     private static int[] readInts(Iterator<String> it) {\r
227         int length = Integer.parseInt(it.next());\r
228         int[] result = new int[length];\r
229         for(int i=0;i<length;++i)\r
230             result[i] = Integer.parseInt(it.next());\r
231         return result;\r
232     }\r
233     \r
234     public void addModi(Modi modi) {\r
235         modis.add(modi);\r
236     }\r
237 \r
238     public RouteGraphModification(String text) {\r
239         String[] parts = text.split(",");\r
240         int pos = 0;\r
241         lineCount = Integer.parseInt(parts[pos++]);\r
242         terminalCount = Integer.parseInt(parts[pos++]);\r
243         terminalIdentifiers = new String[terminalCount];\r
244         for(int i=0;i<terminalCount;++i) {\r
245             terminalIdentifiers[i] = parts[pos++];            \r
246         }\r
247         int linkCount = Integer.parseInt(parts[pos++]);\r
248         links = new int[2*linkCount];\r
249         for(int i=0;i<links.length;++i)\r
250             links[i] = Integer.parseInt(parts[pos++]);\r
251         while(pos < parts.length) {\r
252             String part = parts[pos++];\r
253             char first = part.charAt(0);\r
254             part = part.substring(1);\r
255             switch(first) {\r
256             case 'M': addModi(new UpdateLine(part)); break;\r
257             case 'R': addModi(new RemoveLine(part)); break;\r
258             case 'r': addModi(new RemoveLink(part)); break;\r
259             case 'C': addModi(new CreateLine(part)); break;\r
260             case 'c': addModi(new CreateLink(part)); break;\r
261             case 'S': addModi(new Split(part)); break;\r
262             }\r
263         }\r
264         resources = new Resource[lineCount + terminalCount];\r
265     }\r
266 \r
267     public RouteGraphModification(SerialisationSupport ser, RouteGraph rg) throws DatabaseException {\r
268         Collection<RouteLine> lines = rg.getLines();\r
269         lineCount = lines.size();\r
270         Collection<RouteTerminal> terminals = rg.getTerminals();\r
271         terminalCount = terminals.size();\r
272 \r
273         resources = new Resource[lineCount + terminalCount];\r
274 \r
275         THashSet<RouteLink> linkSet = new THashSet<RouteLink>();\r
276         idMap = new TObjectIntHashMap<RouteNode>(); \r
277 \r
278         int i=0;\r
279         for(RouteLine line : lines) {\r
280             idMap.put(line, i);\r
281             resources[i] = ser.getResource((Long)line.getData());\r
282             for(RoutePoint rp : line.getPoints()) {\r
283                 if(rp instanceof RouteLink) {\r
284                     RouteLink link = (RouteLink)rp;\r
285                     if(!link.getA().isTransient() &&\r
286                             !link.getA().isTransient())\r
287                         linkSet.add(link);\r
288                 }\r
289             }\r
290             ++i;            \r
291         }\r
292         for(RouteTerminal terminal : terminals) {\r
293             idMap.put(terminal, i);\r
294             resources[i] = ser.getResource((Long)terminal.getData());\r
295             ++i;\r
296         }\r
297         if(rg.isSimpleConnection()) {\r
298             links = new int[] {0, 1};\r
299         }\r
300         else {\r
301             links = new int[2*(terminalCount + linkSet.size())];\r
302             i = 0;        \r
303             for(RouteLink link : linkSet) {\r
304                 links[i++] = idMap.get(link.getA());\r
305                 links[i++] = idMap.get(link.getB());\r
306             }\r
307             for(RouteTerminal terminal : terminals) {\r
308                 links[i++] = idMap.get(terminal);\r
309                 links[i++] = idMap.get(terminal.getLine());\r
310             }\r
311         }\r
312     }\r
313 \r
314     public Resource findTerminalIdentifiers(ReadGraph g) throws DatabaseException {\r
315         Resource base = null;\r
316         terminalIdentifiers = new String[terminalCount];\r
317         for(int i=0;i<terminalCount;++i) {\r
318             Resource r = resources[lineCount + i];\r
319             RelativeReference ref = ElementIdentification.getConnectorIdentifier(g, r);\r
320             terminalIdentifiers[i] = ref.path;\r
321             if(ref.base != null)\r
322                 base = ref.base;            \r
323         }\r
324         return base;\r
325     }\r
326 \r
327     @Override\r
328     public String toString() {\r
329         StringBuilder b = new StringBuilder();\r
330         toString(b);\r
331         return b.toString();\r
332     }\r
333 \r
334     public void toString(StringBuilder b) {\r
335         b.append(lineCount);\r
336         b.append(',');\r
337         b.append(terminalCount);\r
338         for(int i=0;i<terminalCount;++i) {\r
339             b.append(',');\r
340             b.append(terminalIdentifiers[i]);\r
341         }\r
342         b.append(',');\r
343         b.append(links.length/2);\r
344         for(int l : links) {\r
345             b.append(',');\r
346             b.append(l);\r
347         }\r
348         for(Modi modi : modis) {\r
349             b.append(',');\r
350             b.append(modi);\r
351         }\r
352     }\r
353 \r
354     public boolean resolveResources(ReadGraph g, Resource model) throws DatabaseException {\r
355         DiagramResource DIA = DiagramResource.getInstance(g);\r
356         StructuralResource2 STR = StructuralResource2.getInstance(g);\r
357 \r
358         // --- Resolve connectors ---------------------------------------------\r
359 \r
360         Resource connection = null;\r
361         {\r
362             ArrayList<List<Resource>> connectorCandidates = new ArrayList<List<Resource>>(terminalCount); \r
363             for(int i=0;i<terminalCount;++i)\r
364                 connectorCandidates.add(ElementIdentification.resolveConnector(g, model, terminalIdentifiers[i]));            \r
365             for(List<Resource> connectors : connectorCandidates) {\r
366                 if(connectors.isEmpty())\r
367                     return false;\r
368                 if(connection == null && connectors.size() == 1) {\r
369                     for(Resource temp : g.getObjects(connectors.get(0), STR.Connects))\r
370                         if(g.isInstanceOf(temp, DIA.Connection)) {\r
371                             connection = temp;\r
372                             break;\r
373                         }\r
374                 }\r
375             }\r
376             if(connection == null)\r
377                 return false;\r
378             loop: for(int i=0;i<terminalCount;++i) {\r
379                 for(Resource connector : connectorCandidates.get(i))\r
380                     if(g.hasStatement(connector, STR.Connects, connection)) {\r
381                         resources[lineCount + i] = connector;\r
382                         continue loop;\r
383                     }\r
384                 return false;\r
385             }\r
386         }\r
387 \r
388         if(lineCount != g.getObjects(connection, DIA.HasInteriorRouteNode).size())\r
389             return false;\r
390         if(terminalCount != g.getObjects(connection, STR.IsConnectedTo).size())\r
391             return false;\r
392 \r
393         if(lineCount == 0)\r
394             return true;\r
395 \r
396         // --- Resolve route lines --------------------------------------------\r
397 \r
398         // Create inverse map for resources\r
399         TObjectIntHashMap<Resource> invResources = new TObjectIntHashMap<Resource>();\r
400         for(int i=0;i<terminalCount;++i) {\r
401             int id = lineCount + i;\r
402             invResources.put(resources[id], id);\r
403         }\r
404 \r
405         // Create neighbor indices\r
406         final TIntHashSet[] neighbors = new TIntHashSet[terminalCount + lineCount];\r
407         for(int i=0;i<neighbors.length;++i)\r
408             neighbors[i] = new TIntHashSet();\r
409         for(int i=0;i<links.length;i+=2) {\r
410             int a = links[i];\r
411             int b = links[i+1];\r
412             if(resources[b] == null)\r
413                 neighbors[a].add(b);\r
414             if(resources[a] == null)\r
415                 neighbors[b].add(a);\r
416         }\r
417 \r
418         // Create stack\r
419         TIntArrayList stack = new TIntArrayList();\r
420         TIntArrayList backlog = new TIntArrayList();\r
421         for(int i=0;i<terminalCount;++i)\r
422             stack.add(lineCount+i);\r
423 \r
424         // Resolve route lines\r
425         int oldResolvedCount = 0;\r
426         while(invResources.size() < resources.length) {\r
427             oldResolvedCount = invResources.size();\r
428             while(!stack.isEmpty()) {\r
429                 int id = stack.removeAt(stack.size()-1);\r
430                 TIntHashSet ns = neighbors[id];\r
431                 for(int n : ns.toArray())\r
432                     if(resources[n] != null)\r
433                         ns.remove(n);\r
434                 if(ns.isEmpty())\r
435                     ;\r
436                 else if(ns.size() == 1) {\r
437                     Resource det = null;\r
438                     for(Resource r : g.getObjects(resources[id], DIA.AreConnected))\r
439                         if(!invResources.containsKey(r)) {\r
440                             if(det == null)\r
441                                 det = r;\r
442                             else\r
443                                 return false;\r
444                         }\r
445                     if(det == null)\r
446                         return false;\r
447                     final int newId = ns.iterator().next();\r
448                     resources[newId] = det;\r
449                     invResources.put(det, newId);\r
450                     stack.add(newId);\r
451                 }\r
452                 else\r
453                     backlog.add(id);\r
454             }\r
455 \r
456             if(oldResolvedCount == invResources.size())\r
457                 return false; // No progress happened\r
458 \r
459             // Reverse backlog and swap stack and backlog\r
460             {\r
461                 backlog.reverse();\r
462 \r
463                 TIntArrayList temp = stack;\r
464                 stack = backlog;                \r
465                 backlog = temp;\r
466             }\r
467         }\r
468 \r
469         return true;\r
470     }\r
471 \r
472     public Resource getConnection(ReadGraph g) throws DatabaseException {        \r
473         if(connection == null) {\r
474             DiagramResource DIA = DiagramResource.getInstance(g);               \r
475             if(lineCount > 0) {\r
476                 connection = g.getSingleObject(resources[0], DIA.HasInteriorRouteNode_Inverse);\r
477             }\r
478             else {\r
479                 StructuralResource2 STR = StructuralResource2.getInstance(g);\r
480                 for(Resource temp : g.getObjects(resources[0], STR.Connects))\r
481                     if(g.isInstanceOf(temp, DIA.Connection)) {\r
482                         connection = temp;\r
483                         break;\r
484                     }\r
485             }\r
486         }\r
487         return connection;\r
488     }\r
489 \r
490     public void runUpdates(WriteGraph g) throws DatabaseException {\r
491         DiagramResource DIA = DiagramResource.getInstance(g);\r
492         Layer0 L0 = Layer0.getInstance(g);\r
493 \r
494         for(Modi modi_ : modis) {\r
495             if(modi_ instanceof UpdateLine) {\r
496                 UpdateLine modi = (UpdateLine)modi_;\r
497                 Resource routeLine = resources[modi.id];\r
498                 g.claimLiteral(routeLine, DIA.HasPosition, modi.position);\r
499                 g.claimLiteral(routeLine, DIA.IsHorizontal, modi.isHorizontal);\r
500             }\r
501             else if(modi_ instanceof RemoveLink) {\r
502                 RemoveLink modi = (RemoveLink)modi_;\r
503                 g.denyStatement(resources[modi.a], DIA.AreConnected, resources[modi.b]);\r
504             }\r
505             else if(modi_ instanceof RemoveLine) {\r
506                 RemoveLine modi = (RemoveLine)modi_;\r
507                 g.deny(resources[modi.a]);\r
508             }\r
509             else if(modi_ instanceof CreateLink) {\r
510                 CreateLink modi = (CreateLink)modi_;\r
511                 g.claim(resources[modi.a], DIA.AreConnected, resources[modi.b]);\r
512             }\r
513             else if(modi_ instanceof CreateLine) {\r
514                 CreateLine modi = (CreateLine)modi_;\r
515                 Resource routeLine = g.newResource();\r
516                 g.claim(routeLine, L0.InstanceOf, DIA.RouteLine);\r
517                 g.claimLiteral(routeLine, DIA.HasPosition, modi.position);\r
518                 g.claimLiteral(routeLine, DIA.IsHorizontal, modi.isHorizontal);                             \r
519                 g.claim(getConnection(g), DIA.HasInteriorRouteNode, routeLine);\r
520                 int id = resources.length;\r
521                 resources = Arrays.copyOf(resources, id+1);\r
522                 resources[id] = routeLine;\r
523             }\r
524             else if(modi_ instanceof Split) {\r
525                 Split modi = (Split)modi_;\r
526                 RouteGraphConnectionSplitter splitter = new RouteGraphConnectionSplitter(g);\r
527                 splitter.doSplit(g, connection,\r
528                         toResources(modi.interface1),\r
529                         toResources(modi.interface2),\r
530                         toResources(modi.lines2),\r
531                         toResources(modi.terminals1),\r
532                         toResources(modi.terminals2),\r
533                         modi.isHorizontal,\r
534                         modi.isectX,\r
535                         modi.isectY\r
536                         );\r
537             }\r
538         }\r
539     }\r
540 \r
541     public TObjectIntHashMap<RouteNode> getIdMap() {\r
542         return idMap;\r
543     }\r
544 \r
545     public int[] toIds(ArrayList<Resource> rs) {        \r
546         TObjectIntHashMap<Resource> rmap = new TObjectIntHashMap<Resource>();\r
547         for(int i=0;i<resources.length;++i)\r
548             rmap.put(resources[i], i);\r
549         \r
550         int[] result = new int[rs.size()];\r
551         for(int i=0;i<rs.size();++i)\r
552             result[i] = rmap.get(rs.get(i));\r
553         \r
554         return result;\r
555     }\r
556     \r
557     public ArrayList<Resource> toResources(int[] ids) {\r
558         ArrayList<Resource> result = new ArrayList<Resource>(ids.length);\r
559         for(int id : ids)\r
560             result.add(resources[id]);\r
561         return result;\r
562     }\r
563 }\r