]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/RouteGraphConnection.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / synchronization / graph / RouteGraphConnection.java
1 /*******************************************************************************\r
2  * Copyright (c) 2011 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.diagram.synchronization.graph;\r
13 \r
14 import gnu.trove.map.hash.TObjectIntHashMap;\r
15 import gnu.trove.procedure.TObjectIntProcedure;\r
16 import gnu.trove.set.hash.THashSet;\r
17 \r
18 import java.util.ArrayList;\r
19 import java.util.HashMap;\r
20 import java.util.HashSet;\r
21 import java.util.Iterator;\r
22 import java.util.List;\r
23 import java.util.Map;\r
24 import java.util.Set;\r
25 \r
26 import org.simantics.db.ReadGraph;\r
27 import org.simantics.db.Resource;\r
28 import org.simantics.db.ServiceLocator;\r
29 import org.simantics.db.Statement;\r
30 import org.simantics.db.WriteGraph;\r
31 import org.simantics.db.common.CommandMetadata;\r
32 import org.simantics.db.common.CommentMetadata;\r
33 import org.simantics.db.common.request.IndexRoot;\r
34 import org.simantics.db.common.request.WriteRequest;\r
35 import org.simantics.db.common.utils.NameUtils;\r
36 import org.simantics.db.exception.DatabaseException;\r
37 import org.simantics.db.request.Write;\r
38 import org.simantics.db.service.SerialisationSupport;\r
39 import org.simantics.diagram.connection.RouteGraph;\r
40 import org.simantics.diagram.connection.RouteLine;\r
41 import org.simantics.diagram.connection.RouteLink;\r
42 import org.simantics.diagram.connection.RouteNode;\r
43 import org.simantics.diagram.connection.RoutePoint;\r
44 import org.simantics.diagram.connection.RouteTerminal;\r
45 import org.simantics.diagram.connection.delta.RouteGraphDelta;\r
46 import org.simantics.diagram.connection.delta.RouteGraphDelta.RouteLinePair;\r
47 import org.simantics.diagram.connection.delta.RouteGraphDelta.RouteTerminalPair;\r
48 import org.simantics.diagram.content.ConnectionUtil;\r
49 import org.simantics.diagram.stubs.DiagramResource;\r
50 import org.simantics.layer0.Layer0;\r
51 import org.simantics.modeling.ModelingResources;\r
52 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent;\r
53 import org.simantics.scl.commands.Commands;\r
54 import org.simantics.scl.compiler.top.ValueNotFound;\r
55 import org.simantics.scl.osgi.SCLOsgi;\r
56 import org.simantics.scl.runtime.SCLContext;\r
57 import org.simantics.scl.runtime.function.Function;\r
58 import org.simantics.structural.stubs.StructuralResource2;\r
59 import org.simantics.utils.datastructures.Pair;\r
60 \r
61 /**\r
62  * FIXME: Adding new route lines does not reconnect the new route lines to existing terminals/other route lines\r
63  * \r
64  * @author Tuukka Lehtonen\r
65  */\r
66 @SuppressWarnings("rawtypes")\r
67 public class RouteGraphConnection {\r
68 \r
69     private static final boolean DEBUG_SYNC = false;\r
70     private static final boolean USE_COMMAND_BASED_SYNCHRONIZATION = true;\r
71 \r
72     public static Write synchronizer(final Resource connection, final RouteGraphChangeEvent event) {\r
73         return new WriteRequest() {\r
74             @Override\r
75             public void perform(WriteGraph graph) throws DatabaseException {\r
76                 RouteGraphConnection rgc = new RouteGraphConnection(graph, connection);\r
77                 rgc.synchronize(graph, event.before, event.after, event.delta);\r
78                 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
79                 graph.addMetadata(cm.add("Modified connection route"));\r
80                 graph.markUndoPoint();\r
81             }\r
82         };\r
83     }\r
84 \r
85     private Layer0 L0;\r
86     private DiagramResource DIA;\r
87     private ConnectionUtil cu;\r
88     private Resource connection;\r
89 \r
90     public RouteGraphConnection(WriteGraph graph, Resource connection) {\r
91         this.L0 = Layer0.getInstance(graph);\r
92         this.DIA = DiagramResource.getInstance(graph);\r
93         this.cu = new ConnectionUtil(graph);\r
94         this.connection = connection;\r
95     }\r
96     \r
97     private static Function ConnectionPoint;\r
98     private static Function Element;\r
99     private static Function RouteGraphStructure;\r
100     private static Function UpdateLine;\r
101     private static Function RemoveNode;\r
102     private static Function RemoveLink;\r
103     private static Function CreateLine;\r
104     private static Function CreateLink;\r
105 \r
106     private static boolean initialized = false;\r
107     private static void initialize(ReadGraph graph) {\r
108         if(!initialized) {\r
109             Object oldGraph = SCLContext.getCurrent().put("graph", graph);\r
110             try {\r
111                 ConnectionPoint = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/ConnectionPoint");\r
112                 Element = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/Element");\r
113                 RouteGraphStructure = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/RouteGraphStructure");\r
114                 UpdateLine = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/UpdateLine");\r
115                 RemoveNode = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/RemoveNode");\r
116                 RemoveLink = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/RemoveLink");\r
117                 CreateLine = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/CreateLine");\r
118                 CreateLink = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/CreateLink");\r
119                 initialized = true;\r
120             } catch(ValueNotFound e) {\r
121                 e.printStackTrace();\r
122             } finally {\r
123                 SCLContext.getCurrent().put("graph", oldGraph);\r
124             }\r
125         }\r
126     }\r
127     \r
128     @SuppressWarnings("unchecked")\r
129     private static Object getConnectorIdentifier(ReadGraph graph, Resource connector) throws DatabaseException {\r
130         Layer0 L0 = Layer0.getInstance(graph);\r
131         DiagramResource DIA = DiagramResource.getInstance(graph);\r
132         StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
133         ModelingResources MOD = ModelingResources.getInstance(graph);\r
134         for(Statement stat : graph.getStatements(connector, STR.Connects)) {\r
135             Resource pred = stat.getPredicate();\r
136             Resource element = stat.getObject();\r
137             if(!graph.isSubrelationOf(pred, DIA.IsConnectorOf)) {\r
138                 Resource component = graph.getPossibleObject(element, MOD.ElementToComponent);\r
139                 if(component != null) {\r
140                     String componentName = graph.getRelatedValue(component, L0.HasName);\r
141                     String relationName = graph.getRelatedValue(graph.getInverse(pred), L0.HasName);\r
142                     return ConnectionPoint.apply(componentName, relationName);\r
143                 }\r
144                 else /* element is flag or reference element */ {\r
145                     String elementName = graph.getRelatedValue(element, L0.HasName);\r
146                     return Element.apply(elementName);\r
147                 }\r
148             }\r
149         }\r
150         throw new DatabaseException("Didn't find indentification for " + connector + ".");\r
151     }\r
152     \r
153     @SuppressWarnings("unchecked")\r
154     public void synchronize(\r
155             WriteGraph graph,\r
156             RouteGraph before,\r
157             RouteGraph after,\r
158             RouteGraphDelta delta) throws DatabaseException {\r
159         \r
160         if (DEBUG_SYNC)\r
161             delta.print();\r
162         \r
163         if(USE_COMMAND_BASED_SYNCHRONIZATION) {\r
164             initialize(graph);        \r
165         \r
166                 Layer0 L0 = Layer0.getInstance(graph);\r
167             ModelingResources MOD = ModelingResources.getInstance(graph);\r
168             \r
169             Resource diagram = graph.getSingleObject(connection, L0.PartOf);\r
170             Resource composite = graph.getSingleObject(diagram, MOD.DiagramToComposite);\r
171             \r
172             // Structure\r
173             Object structure;\r
174             TObjectIntHashMap<RouteNode> routeNodeMap = new TObjectIntHashMap<RouteNode>() {\r
175                 @Override\r
176                 protected int hash(Object notnull) {\r
177                     RouteNode node = (RouteNode)notnull;\r
178                     Object data = node.getData();\r
179                     if(data != null)\r
180                         return data.hashCode();\r
181                     else\r
182                         return node.hashCode();\r
183                 }\r
184                 \r
185                 @Override\r
186                 protected boolean equals(Object notnull, Object two) {\r
187                     RouteNode node1 = (RouteNode)notnull;\r
188                     RouteNode node2 = (RouteNode)two;\r
189                     Object data1 = node1.getData();\r
190                     if(data1 == null)\r
191                         return node1 == node2;\r
192                     Object data2 = node2.getData();\r
193                     return data1.equals(data2);\r
194                 }\r
195             };\r
196             \r
197             {   \r
198                 ArrayList<Object> connectorIdentifiers = new ArrayList<Object>();\r
199                 \r
200                 for(RouteTerminal terminal : before.getTerminals()) {\r
201                     Resource connector = deserialize(graph, terminal.getData());\r
202                     connectorIdentifiers.add(getConnectorIdentifier(graph, connector));\r
203                     routeNodeMap.put(terminal, routeNodeMap.size());\r
204                 }\r
205                 \r
206                 ArrayList<Integer> routeLinks;\r
207                 if(before.isSimpleConnection()) {\r
208                     routeLinks = new ArrayList<Integer>(2);\r
209                     routeLinks.add(0);\r
210                     routeLinks.add(1);\r
211                 }\r
212                 else {\r
213                     THashSet<RouteLink> linkSet = new THashSet<RouteLink>();\r
214                     for(RouteLine line : before.getLines()) {\r
215                         int id = routeNodeMap.size();\r
216                         routeNodeMap.put(line, id);\r
217                         for(RoutePoint rp : line.getPoints())\r
218                             if(rp instanceof RouteLink) {\r
219                                 RouteLink link = (RouteLink)rp;\r
220                                 if(!link.getA().isTransient() &&\r
221                                         !link.getB().isTransient())\r
222                                     linkSet.add(link);\r
223                             }\r
224                     }\r
225                     \r
226                     routeLinks = new ArrayList<Integer>(linkSet.size()*2+before.getTerminals().size());\r
227                     for(RouteTerminal terminal : before.getTerminals()) {\r
228                         routeLinks.add(routeNodeMap.get(terminal));\r
229                         routeLinks.add(routeNodeMap.get(terminal.getLine()));\r
230                     }\r
231                     for(RouteLink link : linkSet) {\r
232                         routeLinks.add(routeNodeMap.get(link.getA()));\r
233                         routeLinks.add(routeNodeMap.get(link.getB()));\r
234                     }\r
235                 }\r
236                 \r
237                 structure = RouteGraphStructure.apply(composite, \r
238                          connectorIdentifiers, routeNodeMap.size()-connectorIdentifiers.size(),\r
239                          routeLinks\r
240                          );\r
241             }\r
242             \r
243             // Process modifications\r
244             ArrayList<Object> modifications = new ArrayList<Object>();\r
245             ArrayList<Pair<RouteLine,Integer>> newRouteLines = new ArrayList<Pair<RouteLine,Integer>>(); \r
246             {\r
247                 // Make direct changes to connection components\r
248                 for (RouteLinePair pair : delta.getLinesThatDiffer())\r
249                     modifications.add(UpdateLine.apply(routeNodeMap.get(pair.left), \r
250                             pair.right.getPosition(), pair.right.isHorizontal()));\r
251                 \r
252                 // Write new components into connection\r
253                 for (RouteLine added : delta.getLinesOnlyInRight()) {\r
254                     modifications.add(CreateLine.apply(added.getPosition(), added.isHorizontal()));\r
255                     newRouteLines.add(Pair.make(added, routeNodeMap.size()));\r
256                     routeNodeMap.put(added, routeNodeMap.size());\r
257                 }\r
258                 \r
259                 // Remove all removed links from connection\r
260                 for (RouteLink removed : delta.getLinksOnlyInLeft())\r
261                     modifications.add(RemoveLink.apply(\r
262                             routeNodeMap.get(removed.getA()), \r
263                             routeNodeMap.get(removed.getB())));\r
264                 \r
265                 // Write modifications to connectivity between terminals and interior\r
266                 // route nodes.\r
267                 for (RouteLink added : delta.getLinksOnlyInRight())\r
268                     modifications.add(CreateLink.apply(\r
269                             routeNodeMap.get(added.getA()), \r
270                             routeNodeMap.get(added.getB())));\r
271     \r
272                 // Update modifications to terminals\r
273                 if(before.isSimpleConnection()) {\r
274                     if(!after.isSimpleConnection()) {\r
275                         modifications.add(RemoveLink.apply(0, 1));\r
276                         for(RouteTerminal terminal : after.getTerminals())\r
277                             modifications.add(CreateLink.apply(\r
278                                     routeNodeMap.get(terminal),\r
279                                     routeNodeMap.get(terminal.getLine())\r
280                                     ));\r
281                     }\r
282                 }\r
283                 else if(after.isSimpleConnection()) {\r
284                     for(RouteTerminal terminal : before.getTerminals())\r
285                         modifications.add(RemoveLink.apply(\r
286                                 routeNodeMap.get(terminal),\r
287                                 routeNodeMap.get(terminal.getLine())\r
288                                 ));\r
289                     modifications.add(CreateLink.apply(0, 1));\r
290                 } else {\r
291                     for (RouteTerminalPair pair : delta.getTerminalsThatDiffer()) {\r
292                         // If terminals are connected to different route lines,\r
293                         // disconnect left terminal connector from its connected counterpart\r
294                         // and connect the same terminal connector to the connected counterpart\r
295                         // listed in the right terminal.\r
296     \r
297                         int terminalId = routeNodeMap.get(pair.left);\r
298                         int leftLine = routeNodeMap.get(pair.left.getLine());\r
299                         int rightLine = routeNodeMap.get(pair.right.getLine());\r
300                         \r
301                         if(leftLine != rightLine) {\r
302                             modifications.add(RemoveLink.apply(terminalId, leftLine));\r
303                             modifications.add(CreateLink.apply(terminalId, rightLine));\r
304                         }\r
305                     }\r
306                 }\r
307                 \r
308                 // Disconnect and remove removed terminal connectors\r
309                 for(RouteTerminal removedTerminal : delta.getTerminalsOnlyInLeft())\r
310                     modifications.add(RemoveNode.apply(routeNodeMap.get(removedTerminal)));\r
311                 \r
312                 // Finally delete removed route lines\r
313                 for(RouteLine removed : delta.getLinesOnlyInLeft())\r
314                     modifications.add(RemoveNode.apply(routeNodeMap.get(removed)));\r
315             }\r
316             \r
317             // Execute command\r
318             if(!modifications.isEmpty()) {\r
319                 /*System.out.println("--");\r
320                 System.out.println(structure);\r
321                 System.out.println(modifications);*/\r
322                 final List<Resource> allRouteNodes = (List<Resource>)\r
323                         Commands.get(graph, "Simantics/RouteGraph/modifyRouteGraph")\r
324                         .execute(graph, graph.syncRequest(new IndexRoot(connection)), \r
325                                 structure, modifications);\r
326                 for(Pair<RouteLine,Integer> pair : newRouteLines)\r
327                     pair.first.setData(allRouteNodes.get(pair.second).getResourceId());\r
328             }\r
329         }\r
330         else {\r
331 \r
332             Map<RouteNode, Resource> newComponents = new HashMap<RouteNode, Resource>();\r
333 \r
334             CommentMetadata comments = graph.getMetadata(CommentMetadata.class);\r
335             comments.add("Modified connection " + NameUtils.getSafeLabel(graph, connection));\r
336 \r
337             writeCommandMetadata(graph, before, delta, after);\r
338 \r
339             // Make direct changes to connection components\r
340             for (RouteLinePair pair : delta.getLinesThatDiffer()) {\r
341                 Resource line = deserialize(graph, pair.right.getData());\r
342                 if (DEBUG_SYNC) {\r
343                     System.out.print("synchronizing existing route line: " + NameUtils.getSafeLabel(graph, line) + " - ");\r
344                     pair.right.print(System.out);\r
345                 }\r
346                 writeLine(graph, line, pair.right);\r
347                 comments.add("Synchronized existing route line: " + NameUtils.getSafeLabel(graph, line));\r
348             }\r
349 \r
350             // Write new components into connection\r
351             for (RouteLine added : delta.getLinesOnlyInRight()) {\r
352                 Resource line = tryDeserialize(graph, added.getData());\r
353                 if (DEBUG_SYNC) {\r
354                     System.out.print("adding new route line (" + line + "): ");\r
355                     added.print(System.out);\r
356                 }\r
357                 newComponents.put( added, line = writeLine(graph, line, added) );\r
358 \r
359                 comments.add("Added new route line " + NameUtils.getSafeLabel(graph, line));\r
360             }\r
361 \r
362             // Remove all removed links from connection\r
363             for (RouteLink removed : delta.getLinksOnlyInLeft()) {\r
364                 Resource a = deserialize(graph, removed.getA().getData());\r
365                 Resource b = deserialize(graph, removed.getB().getData());\r
366                 if (DEBUG_SYNC)\r
367                     System.out.println("removing link: " + NameUtils.getSafeLabel(graph, a) + " <> " + NameUtils.getSafeLabel(graph, b));\r
368                 cu.disconnect(a, b);\r
369                 comments.add("Removed link: " + NameUtils.getSafeLabel(graph, a) + " <> " + NameUtils.getSafeLabel(graph, b));\r
370             }\r
371 \r
372             // Write modifications to connectivity between terminals and interior\r
373             // route nodes.\r
374             for (RouteLink added : delta.getLinksOnlyInRight()) {\r
375                 if (DEBUG_SYNC) {\r
376                     System.out.println("processing added link:");\r
377                     added.getA().print(System.out);\r
378                     added.getB().print(System.out);\r
379                 }\r
380                 Resource a = deserialize(graph, added.getA().getData());\r
381                 Resource b = deserialize(graph, added.getB().getData());\r
382                 if (DEBUG_SYNC)\r
383                     System.out.println("adding link between "\r
384                             + NameUtils.getSafeLabel(graph, a) + " <> "\r
385                             + NameUtils.getSafeLabel(graph, b));\r
386                 cu.connect(a, b);\r
387                 comments.add("Added link between "\r
388                         + NameUtils.getSafeLabel(graph, a) + " <> "\r
389                         + NameUtils.getSafeLabel(graph, b));\r
390             }\r
391 \r
392             Set<Resource> looseConnectors = new HashSet<Resource>();\r
393 \r
394             // Handle terminals that differ\r
395             for (RouteTerminalPair pair : delta.getTerminalsThatDiffer()) {\r
396                 if (DEBUG_SYNC) {\r
397                     System.out.println("processing differing terminal:");\r
398                     pair.left.print(System.out);\r
399                     pair.right.print(System.out);\r
400                 }\r
401 \r
402                 // If terminals are connected to different route lines,\r
403                 // disconnect left terminal connector from its connected counterpart\r
404                 // and connect the same terminal connector to the connected counterpart\r
405                 // listed in the right terminal.\r
406 \r
407                 if (pair.left.getLine() != pair.right.getLine()) {\r
408                     Resource connector = deserialize( graph, pair.left.getData() );\r
409 \r
410                     // Case 1:\r
411                     // - two terminals connected directly to each other.\r
412                     // - terminals moved so that a transient route line is added but\r
413                     //   nothing should be written into the graph.\r
414                     // Case recognition logic:\r
415                     //      left.line == null\r
416                     //   && right.line != null\r
417                     //   && right.line.data == null\r
418                     if (pair.left.getLine() == null\r
419                             && pair.right.getLine() != null\r
420                             && pair.right.getLine().getData() == null)\r
421                         continue;\r
422 \r
423                     // Case 2a:\r
424                     // - terminal previously connected to either another terminal or a persistent route line\r
425                     // Case 2b:\r
426                     // - terminal previously connected to another terminal through a transient route line\r
427                     // Case 2c:\r
428                     // - terminal previously connected to another terminal\r
429                     if (pair.left.getLine() != null && pair.left.getLine().getData() != null) {\r
430                         Resource line = deserialize(graph, pair.left.getLine().getData());\r
431                         // Case 2a\r
432                         if (DEBUG_SYNC)\r
433                             System.out.println("removing link between terminal "\r
434                                     + NameUtils.getSafeLabel(graph, connector) + " and route line "\r
435                                     + NameUtils.getSafeLabel(graph, line));\r
436                         cu.disconnect(connector, line);\r
437                         comments.add("Removed link between terminal "\r
438                                 + NameUtils.getSafeLabel(graph, connector) + " and route line "\r
439                                 + NameUtils.getSafeLabel(graph, line));\r
440                     } else {\r
441                         // Case 2b & 2c\r
442                         if (DEBUG_SYNC)\r
443                             System.out.println("removing link between terminal "\r
444                                     + NameUtils.getSafeLabel(graph, connector) + " and other terminals ");\r
445                         cu.disconnectFromAllRouteNodes(connector);\r
446                         comments.add("Removed link between terminal "\r
447                                 + NameUtils.getSafeLabel(graph, connector) + " and other terminals ");\r
448                     }\r
449 \r
450                     // Case 3a:\r
451                     // - terminal is now connected to a persistent route line\r
452                     // Case 3b:\r
453                     // - terminal is now connected to another terminal\r
454                     if (pair.right.getLine() != null && pair.right.getLine().getData() != null) {\r
455                         // Case 3a\r
456                         Resource line = deserialize(graph, pair.right.getLine().getData());\r
457                         if (DEBUG_SYNC)\r
458                             System.out.println("adding link between terminal "\r
459                                     + NameUtils.getSafeLabel(graph, connector) + " and route line "\r
460                                     + NameUtils.getSafeLabel(graph, line));\r
461                         cu.connect(connector, line);\r
462                         comments.add("Added link between terminal "\r
463                                 + NameUtils.getSafeLabel(graph, connector) + " and route line "\r
464                                 + NameUtils.getSafeLabel(graph, line));\r
465                     } else {\r
466                         // Case 3b\r
467                         // Connector was disconnected from route line.\r
468                         // This can currently only mean one thing:\r
469                         // there are no more route lines in the connection.\r
470                         // If there are still connectors in the connection, the\r
471                         // only possible assumption is that there are two\r
472                         // connectors and they need to be connected together\r
473                         looseConnectors.add(connector);\r
474                     }\r
475                 }\r
476             }\r
477             if (looseConnectors.size() == 2) {\r
478                 Resource[] cns = looseConnectors.toArray(Resource.NONE);\r
479                 if (DEBUG_SYNC)\r
480                     System.out.println("linking two loose terminal connectors "\r
481                             + NameUtils.getSafeLabel(graph, cns[0]) + " and "\r
482                             + NameUtils.getSafeLabel(graph, cns[1]));\r
483                 cu.connect(cns[0], cns[1]);\r
484                 comments.add("Linking two loose terminal connectors "\r
485                         + NameUtils.getSafeLabel(graph, cns[0]) + " and "\r
486                         + NameUtils.getSafeLabel(graph, cns[1]));\r
487             } else if (!looseConnectors.isEmpty()) {\r
488                 System.err.println("BUG: there can only be 0 or 2 loose terminal connectors in any connection. Found " + looseConnectors.size() + ":");\r
489                 for (Resource cn : looseConnectors)\r
490                     System.err.println(NameUtils.getSafeLabel(graph, cn));\r
491                 System.err.flush();\r
492             }\r
493 \r
494             // Disconnect and remove removed terminal connectors\r
495             for (RouteTerminal removedTerminal : delta.getTerminalsOnlyInLeft()) {\r
496                 Resource connector = deserialize(graph, removedTerminal.getData());\r
497                 if (DEBUG_SYNC)\r
498                     System.out.println("removing route terminal: " + NameUtils.getSafeLabel(graph, connector));\r
499                 cu.removeConnectionPart(connector);\r
500                 comments.add("Removed route terminal " + NameUtils.getSafeLabel(graph, connector));\r
501             }\r
502 \r
503             // Finally delete removed route lines\r
504             for (RouteLine removed : delta.getLinesOnlyInLeft()) {\r
505                 Resource line = deserialize(graph, removed.getData());\r
506                 if (DEBUG_SYNC)\r
507                     System.out.println("removing route line: " + NameUtils.getSafeLabel(graph, line));\r
508                 cu.removeConnectionPart(line);\r
509                 comments.add("Removed route line " + NameUtils.getSafeLabel(graph, line));\r
510             }\r
511 \r
512             graph.addMetadata(comments);\r
513         }\r
514     }\r
515 \r
516     private void writeCommandMetadata(WriteGraph graph, RouteGraph left, RouteGraphDelta delta, RouteGraph right) {\r
517         try {\r
518             if(!delta.getTerminalsOnlyInLeft().isEmpty() || !delta.getTerminalsOnlyInRight().isEmpty())\r
519                 return; // These are not changes layout organizer may do.\r
520             \r
521             RouteGraphModification proxy = new RouteGraphModification(graph.getService(SerialisationSupport.class), left);\r
522             TObjectIntHashMap<RouteNode> idMap = proxy.getIdMap();\r
523             TObjectIntHashMap<RouteNode> rightIdMap = new TObjectIntHashMap<RouteNode>();\r
524             {\r
525                 final TObjectIntHashMap<Object> keyToId = new TObjectIntHashMap<Object>();\r
526                 idMap.forEachEntry(new TObjectIntProcedure<RouteNode>() {                               \r
527                                 @Override\r
528                                 public boolean execute(RouteNode a, int b) {\r
529                                         Object data = a.getData();\r
530                                         if(data != null)\r
531                                                 keyToId.put(data, b);\r
532                                         return true;\r
533                                 }\r
534                         });\r
535                 for(RouteLine line : right.getLines()) {\r
536                         Object data = line.getData();\r
537                         if(keyToId.containsKey(data))\r
538                                 rightIdMap.put(line, keyToId.get(data));\r
539                 }\r
540                 for(RouteTerminal terminal : right.getTerminals()) {\r
541                         Object data = terminal.getData();\r
542                         if(keyToId.containsKey(data))\r
543                                 rightIdMap.put(terminal, keyToId.get(data));\r
544                 }\r
545             }\r
546             int id = idMap.size();\r
547             for(RouteLink link : delta.getLinksOnlyInLeft()) {\r
548                 proxy.addModi(new RouteGraphModification.RemoveLink(idMap.get(link.getA()), idMap.get(link.getB())));\r
549             }\r
550             for(RouteLine line : delta.getLinesOnlyInLeft()) {\r
551                 proxy.addModi(new RouteGraphModification.RemoveLine(idMap.get(line)));\r
552             }        \r
553             for(RouteLinePair pair : delta.getLinesThatDiffer()) {\r
554                 proxy.addModi(new RouteGraphModification.UpdateLine(idMap.get(pair.left), pair.right.getPosition(), pair.right.isHorizontal()));\r
555             }\r
556             for(RouteLine line : delta.getLinesOnlyInRight()) {\r
557                 rightIdMap.put(line, id++);\r
558                 proxy.addModi(new RouteGraphModification.CreateLine(line.getPosition(), line.isHorizontal()));\r
559             }\r
560             if(left.isSimpleConnection() && !right.isSimpleConnection())\r
561                 proxy.addModi(new RouteGraphModification.RemoveLink(0, 1));\r
562             for(RouteTerminalPair pair : delta.getTerminalsThatDiffer())\r
563                 if(pair.left.getLine() != pair.right.getLine()) {\r
564                     if(pair.left.getLine() != null)\r
565                         proxy.addModi(new RouteGraphModification.RemoveLink(idMap.get(pair.left), idMap.get(pair.left.getLine())));\r
566                     if(pair.right.getLine() != null)\r
567                         proxy.addModi(new RouteGraphModification.CreateLink(idMap.get(pair.left), rightIdMap.get(pair.right.getLine())));\r
568                 }\r
569             if(!left.isSimpleConnection() && right.isSimpleConnection() && right.getTerminals().size() == 2) {\r
570                 Iterator<RouteTerminal> it = right.getTerminals().iterator();\r
571                 proxy.addModi(new RouteGraphModification.CreateLink(\r
572                         rightIdMap.get(it.next()), rightIdMap.get(it.next())));\r
573             }\r
574             for(RouteLink link : delta.getLinksOnlyInRight()) {\r
575                 proxy.addModi(new RouteGraphModification.CreateLink(rightIdMap.get(link.getA()), rightIdMap.get(link.getB())));\r
576             }\r
577             Resource model = proxy.findTerminalIdentifiers(graph);\r
578             StringBuilder b = new StringBuilder();\r
579             b.append("modifyConnection \"");\r
580             proxy.toString(b);\r
581             b.append("\"");\r
582             if (model != null)\r
583                 CommandMetadata.add(graph, model.getResourceId(), b.toString());\r
584         } catch(DatabaseException e) {\r
585             // Failure in command writing must not cancel the write transaction\r
586             e.printStackTrace();\r
587         } catch(NullPointerException e) {\r
588             // Failure in command writing must not cancel the write transaction            \r
589             e.printStackTrace();\r
590         }\r
591     }\r
592 \r
593     public Resource writeLine(WriteGraph graph, Resource line, RouteLine routeLine) throws DatabaseException {\r
594         if (line == null) {\r
595             line = graph.newResource();\r
596             graph.claim(line, L0.InstanceOf, null, DIA.RouteLine);\r
597             graph.claim(connection, DIA.HasInteriorRouteNode, line);\r
598         }\r
599         graph.claimLiteral(line, DIA.IsHorizontal, routeLine.isHorizontal());\r
600         graph.claimLiteral(line, DIA.HasPosition, routeLine.getPosition());\r
601         routeLine.setData( serialize(graph, line) );\r
602         if (DEBUG_SYNC) {\r
603             System.out.print("wrote route line: ");\r
604             routeLine.print(System.out);\r
605         }\r
606         return line;\r
607     }\r
608 \r
609     public static Object serialize(ServiceLocator locator, Resource r) throws DatabaseException {\r
610         SerialisationSupport ss = locator.getService(SerialisationSupport.class);\r
611         return ss.getRandomAccessId(r); \r
612     }\r
613 \r
614     public static Resource deserialize(ServiceLocator locator, Object o) throws DatabaseException {\r
615         Resource r = tryDeserialize(locator, o);\r
616         if (r != null)\r
617             return r;\r
618         throw new IllegalArgumentException("unrecognized object: " + o);\r
619     }\r
620 \r
621     public static Resource tryDeserialize(ServiceLocator locator, Object o) throws DatabaseException {\r
622         if (o instanceof Long)\r
623             return locator.getService(SerialisationSupport.class).getResource((Long) o);\r
624         return null;\r
625     }\r
626 \r
627 }\r