--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2011 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.diagram.synchronization.graph;\r
+\r
+import gnu.trove.map.hash.TObjectIntHashMap;\r
+import gnu.trove.procedure.TObjectIntProcedure;\r
+import gnu.trove.set.hash.THashSet;\r
+\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.ServiceLocator;\r
+import org.simantics.db.Statement;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.CommandMetadata;\r
+import org.simantics.db.common.CommentMetadata;\r
+import org.simantics.db.common.request.IndexRoot;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.common.utils.NameUtils;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.request.Write;\r
+import org.simantics.db.service.SerialisationSupport;\r
+import org.simantics.diagram.connection.RouteGraph;\r
+import org.simantics.diagram.connection.RouteLine;\r
+import org.simantics.diagram.connection.RouteLink;\r
+import org.simantics.diagram.connection.RouteNode;\r
+import org.simantics.diagram.connection.RoutePoint;\r
+import org.simantics.diagram.connection.RouteTerminal;\r
+import org.simantics.diagram.connection.delta.RouteGraphDelta;\r
+import org.simantics.diagram.connection.delta.RouteGraphDelta.RouteLinePair;\r
+import org.simantics.diagram.connection.delta.RouteGraphDelta.RouteTerminalPair;\r
+import org.simantics.diagram.content.ConnectionUtil;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent;\r
+import org.simantics.scl.commands.Commands;\r
+import org.simantics.scl.compiler.top.ValueNotFound;\r
+import org.simantics.scl.osgi.SCLOsgi;\r
+import org.simantics.scl.runtime.SCLContext;\r
+import org.simantics.scl.runtime.function.Function;\r
+import org.simantics.structural.stubs.StructuralResource2;\r
+import org.simantics.utils.datastructures.Pair;\r
+\r
+/**\r
+ * FIXME: Adding new route lines does not reconnect the new route lines to existing terminals/other route lines\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ */\r
+@SuppressWarnings("rawtypes")\r
+public class RouteGraphConnection {\r
+\r
+ private static final boolean DEBUG_SYNC = false;\r
+ private static final boolean USE_COMMAND_BASED_SYNCHRONIZATION = true;\r
+\r
+ public static Write synchronizer(final Resource connection, final RouteGraphChangeEvent event) {\r
+ return new WriteRequest() {\r
+ @Override\r
+ public void perform(WriteGraph graph) throws DatabaseException {\r
+ RouteGraphConnection rgc = new RouteGraphConnection(graph, connection);\r
+ rgc.synchronize(graph, event.before, event.after, event.delta);\r
+ CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
+ graph.addMetadata(cm.add("Modified connection route"));\r
+ graph.markUndoPoint();\r
+ }\r
+ };\r
+ }\r
+\r
+ private Layer0 L0;\r
+ private DiagramResource DIA;\r
+ private ConnectionUtil cu;\r
+ private Resource connection;\r
+\r
+ public RouteGraphConnection(WriteGraph graph, Resource connection) {\r
+ this.L0 = Layer0.getInstance(graph);\r
+ this.DIA = DiagramResource.getInstance(graph);\r
+ this.cu = new ConnectionUtil(graph);\r
+ this.connection = connection;\r
+ }\r
+ \r
+ private static Function ConnectionPoint;\r
+ private static Function Element;\r
+ private static Function RouteGraphStructure;\r
+ private static Function UpdateLine;\r
+ private static Function RemoveNode;\r
+ private static Function RemoveLink;\r
+ private static Function CreateLine;\r
+ private static Function CreateLink;\r
+\r
+ private static boolean initialized = false;\r
+ private static void initialize(ReadGraph graph) {\r
+ if(!initialized) {\r
+ Object oldGraph = SCLContext.getCurrent().put("graph", graph);\r
+ try {\r
+ ConnectionPoint = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/ConnectionPoint");\r
+ Element = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/Element");\r
+ RouteGraphStructure = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/RouteGraphStructure");\r
+ UpdateLine = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/UpdateLine");\r
+ RemoveNode = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/RemoveNode");\r
+ RemoveLink = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/RemoveLink");\r
+ CreateLine = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/CreateLine");\r
+ CreateLink = (Function)SCLOsgi.MODULE_REPOSITORY.getValue("Simantics/RouteGraph/CreateLink");\r
+ initialized = true;\r
+ } catch(ValueNotFound e) {\r
+ e.printStackTrace();\r
+ } finally {\r
+ SCLContext.getCurrent().put("graph", oldGraph);\r
+ }\r
+ }\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ private static Object getConnectorIdentifier(ReadGraph graph, Resource connector) throws DatabaseException {\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ DiagramResource DIA = DiagramResource.getInstance(graph);\r
+ StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
+ ModelingResources MOD = ModelingResources.getInstance(graph);\r
+ for(Statement stat : graph.getStatements(connector, STR.Connects)) {\r
+ Resource pred = stat.getPredicate();\r
+ Resource element = stat.getObject();\r
+ if(!graph.isSubrelationOf(pred, DIA.IsConnectorOf)) {\r
+ Resource component = graph.getPossibleObject(element, MOD.ElementToComponent);\r
+ if(component != null) {\r
+ String componentName = graph.getRelatedValue(component, L0.HasName);\r
+ String relationName = graph.getRelatedValue(graph.getInverse(pred), L0.HasName);\r
+ return ConnectionPoint.apply(componentName, relationName);\r
+ }\r
+ else /* element is flag or reference element */ {\r
+ String elementName = graph.getRelatedValue(element, L0.HasName);\r
+ return Element.apply(elementName);\r
+ }\r
+ }\r
+ }\r
+ throw new DatabaseException("Didn't find indentification for " + connector + ".");\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ public void synchronize(\r
+ WriteGraph graph,\r
+ RouteGraph before,\r
+ RouteGraph after,\r
+ RouteGraphDelta delta) throws DatabaseException {\r
+ \r
+ if (DEBUG_SYNC)\r
+ delta.print();\r
+ \r
+ if(USE_COMMAND_BASED_SYNCHRONIZATION) {\r
+ initialize(graph); \r
+ \r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ ModelingResources MOD = ModelingResources.getInstance(graph);\r
+ \r
+ Resource diagram = graph.getSingleObject(connection, L0.PartOf);\r
+ Resource composite = graph.getSingleObject(diagram, MOD.DiagramToComposite);\r
+ \r
+ // Structure\r
+ Object structure;\r
+ TObjectIntHashMap<RouteNode> routeNodeMap = new TObjectIntHashMap<RouteNode>() {\r
+ @Override\r
+ protected int hash(Object notnull) {\r
+ RouteNode node = (RouteNode)notnull;\r
+ Object data = node.getData();\r
+ if(data != null)\r
+ return data.hashCode();\r
+ else\r
+ return node.hashCode();\r
+ }\r
+ \r
+ @Override\r
+ protected boolean equals(Object notnull, Object two) {\r
+ RouteNode node1 = (RouteNode)notnull;\r
+ RouteNode node2 = (RouteNode)two;\r
+ Object data1 = node1.getData();\r
+ if(data1 == null)\r
+ return node1 == node2;\r
+ Object data2 = node2.getData();\r
+ return data1.equals(data2);\r
+ }\r
+ };\r
+ \r
+ { \r
+ ArrayList<Object> connectorIdentifiers = new ArrayList<Object>();\r
+ \r
+ for(RouteTerminal terminal : before.getTerminals()) {\r
+ Resource connector = deserialize(graph, terminal.getData());\r
+ connectorIdentifiers.add(getConnectorIdentifier(graph, connector));\r
+ routeNodeMap.put(terminal, routeNodeMap.size());\r
+ }\r
+ \r
+ ArrayList<Integer> routeLinks;\r
+ if(before.isSimpleConnection()) {\r
+ routeLinks = new ArrayList<Integer>(2);\r
+ routeLinks.add(0);\r
+ routeLinks.add(1);\r
+ }\r
+ else {\r
+ THashSet<RouteLink> linkSet = new THashSet<RouteLink>();\r
+ for(RouteLine line : before.getLines()) {\r
+ int id = routeNodeMap.size();\r
+ routeNodeMap.put(line, id);\r
+ for(RoutePoint rp : line.getPoints())\r
+ if(rp instanceof RouteLink) {\r
+ RouteLink link = (RouteLink)rp;\r
+ if(!link.getA().isTransient() &&\r
+ !link.getB().isTransient())\r
+ linkSet.add(link);\r
+ }\r
+ }\r
+ \r
+ routeLinks = new ArrayList<Integer>(linkSet.size()*2+before.getTerminals().size());\r
+ for(RouteTerminal terminal : before.getTerminals()) {\r
+ routeLinks.add(routeNodeMap.get(terminal));\r
+ routeLinks.add(routeNodeMap.get(terminal.getLine()));\r
+ }\r
+ for(RouteLink link : linkSet) {\r
+ routeLinks.add(routeNodeMap.get(link.getA()));\r
+ routeLinks.add(routeNodeMap.get(link.getB()));\r
+ }\r
+ }\r
+ \r
+ structure = RouteGraphStructure.apply(composite, \r
+ connectorIdentifiers, routeNodeMap.size()-connectorIdentifiers.size(),\r
+ routeLinks\r
+ );\r
+ }\r
+ \r
+ // Process modifications\r
+ ArrayList<Object> modifications = new ArrayList<Object>();\r
+ ArrayList<Pair<RouteLine,Integer>> newRouteLines = new ArrayList<Pair<RouteLine,Integer>>(); \r
+ {\r
+ // Make direct changes to connection components\r
+ for (RouteLinePair pair : delta.getLinesThatDiffer())\r
+ modifications.add(UpdateLine.apply(routeNodeMap.get(pair.left), \r
+ pair.right.getPosition(), pair.right.isHorizontal()));\r
+ \r
+ // Write new components into connection\r
+ for (RouteLine added : delta.getLinesOnlyInRight()) {\r
+ modifications.add(CreateLine.apply(added.getPosition(), added.isHorizontal()));\r
+ newRouteLines.add(Pair.make(added, routeNodeMap.size()));\r
+ routeNodeMap.put(added, routeNodeMap.size());\r
+ }\r
+ \r
+ // Remove all removed links from connection\r
+ for (RouteLink removed : delta.getLinksOnlyInLeft())\r
+ modifications.add(RemoveLink.apply(\r
+ routeNodeMap.get(removed.getA()), \r
+ routeNodeMap.get(removed.getB())));\r
+ \r
+ // Write modifications to connectivity between terminals and interior\r
+ // route nodes.\r
+ for (RouteLink added : delta.getLinksOnlyInRight())\r
+ modifications.add(CreateLink.apply(\r
+ routeNodeMap.get(added.getA()), \r
+ routeNodeMap.get(added.getB())));\r
+ \r
+ // Update modifications to terminals\r
+ if(before.isSimpleConnection()) {\r
+ if(!after.isSimpleConnection()) {\r
+ modifications.add(RemoveLink.apply(0, 1));\r
+ for(RouteTerminal terminal : after.getTerminals())\r
+ modifications.add(CreateLink.apply(\r
+ routeNodeMap.get(terminal),\r
+ routeNodeMap.get(terminal.getLine())\r
+ ));\r
+ }\r
+ }\r
+ else if(after.isSimpleConnection()) {\r
+ for(RouteTerminal terminal : before.getTerminals())\r
+ modifications.add(RemoveLink.apply(\r
+ routeNodeMap.get(terminal),\r
+ routeNodeMap.get(terminal.getLine())\r
+ ));\r
+ modifications.add(CreateLink.apply(0, 1));\r
+ } else {\r
+ for (RouteTerminalPair pair : delta.getTerminalsThatDiffer()) {\r
+ // If terminals are connected to different route lines,\r
+ // disconnect left terminal connector from its connected counterpart\r
+ // and connect the same terminal connector to the connected counterpart\r
+ // listed in the right terminal.\r
+ \r
+ int terminalId = routeNodeMap.get(pair.left);\r
+ int leftLine = routeNodeMap.get(pair.left.getLine());\r
+ int rightLine = routeNodeMap.get(pair.right.getLine());\r
+ \r
+ if(leftLine != rightLine) {\r
+ modifications.add(RemoveLink.apply(terminalId, leftLine));\r
+ modifications.add(CreateLink.apply(terminalId, rightLine));\r
+ }\r
+ }\r
+ }\r
+ \r
+ // Disconnect and remove removed terminal connectors\r
+ for(RouteTerminal removedTerminal : delta.getTerminalsOnlyInLeft())\r
+ modifications.add(RemoveNode.apply(routeNodeMap.get(removedTerminal)));\r
+ \r
+ // Finally delete removed route lines\r
+ for(RouteLine removed : delta.getLinesOnlyInLeft())\r
+ modifications.add(RemoveNode.apply(routeNodeMap.get(removed)));\r
+ }\r
+ \r
+ // Execute command\r
+ if(!modifications.isEmpty()) {\r
+ /*System.out.println("--");\r
+ System.out.println(structure);\r
+ System.out.println(modifications);*/\r
+ final List<Resource> allRouteNodes = (List<Resource>)\r
+ Commands.get(graph, "Simantics/RouteGraph/modifyRouteGraph")\r
+ .execute(graph, graph.syncRequest(new IndexRoot(connection)), \r
+ structure, modifications);\r
+ for(Pair<RouteLine,Integer> pair : newRouteLines)\r
+ pair.first.setData(allRouteNodes.get(pair.second).getResourceId());\r
+ }\r
+ }\r
+ else {\r
+\r
+ Map<RouteNode, Resource> newComponents = new HashMap<RouteNode, Resource>();\r
+\r
+ CommentMetadata comments = graph.getMetadata(CommentMetadata.class);\r
+ comments.add("Modified connection " + NameUtils.getSafeLabel(graph, connection));\r
+\r
+ writeCommandMetadata(graph, before, delta, after);\r
+\r
+ // Make direct changes to connection components\r
+ for (RouteLinePair pair : delta.getLinesThatDiffer()) {\r
+ Resource line = deserialize(graph, pair.right.getData());\r
+ if (DEBUG_SYNC) {\r
+ System.out.print("synchronizing existing route line: " + NameUtils.getSafeLabel(graph, line) + " - ");\r
+ pair.right.print(System.out);\r
+ }\r
+ writeLine(graph, line, pair.right);\r
+ comments.add("Synchronized existing route line: " + NameUtils.getSafeLabel(graph, line));\r
+ }\r
+\r
+ // Write new components into connection\r
+ for (RouteLine added : delta.getLinesOnlyInRight()) {\r
+ Resource line = tryDeserialize(graph, added.getData());\r
+ if (DEBUG_SYNC) {\r
+ System.out.print("adding new route line (" + line + "): ");\r
+ added.print(System.out);\r
+ }\r
+ newComponents.put( added, line = writeLine(graph, line, added) );\r
+\r
+ comments.add("Added new route line " + NameUtils.getSafeLabel(graph, line));\r
+ }\r
+\r
+ // Remove all removed links from connection\r
+ for (RouteLink removed : delta.getLinksOnlyInLeft()) {\r
+ Resource a = deserialize(graph, removed.getA().getData());\r
+ Resource b = deserialize(graph, removed.getB().getData());\r
+ if (DEBUG_SYNC)\r
+ System.out.println("removing link: " + NameUtils.getSafeLabel(graph, a) + " <> " + NameUtils.getSafeLabel(graph, b));\r
+ cu.disconnect(a, b);\r
+ comments.add("Removed link: " + NameUtils.getSafeLabel(graph, a) + " <> " + NameUtils.getSafeLabel(graph, b));\r
+ }\r
+\r
+ // Write modifications to connectivity between terminals and interior\r
+ // route nodes.\r
+ for (RouteLink added : delta.getLinksOnlyInRight()) {\r
+ if (DEBUG_SYNC) {\r
+ System.out.println("processing added link:");\r
+ added.getA().print(System.out);\r
+ added.getB().print(System.out);\r
+ }\r
+ Resource a = deserialize(graph, added.getA().getData());\r
+ Resource b = deserialize(graph, added.getB().getData());\r
+ if (DEBUG_SYNC)\r
+ System.out.println("adding link between "\r
+ + NameUtils.getSafeLabel(graph, a) + " <> "\r
+ + NameUtils.getSafeLabel(graph, b));\r
+ cu.connect(a, b);\r
+ comments.add("Added link between "\r
+ + NameUtils.getSafeLabel(graph, a) + " <> "\r
+ + NameUtils.getSafeLabel(graph, b));\r
+ }\r
+\r
+ Set<Resource> looseConnectors = new HashSet<Resource>();\r
+\r
+ // Handle terminals that differ\r
+ for (RouteTerminalPair pair : delta.getTerminalsThatDiffer()) {\r
+ if (DEBUG_SYNC) {\r
+ System.out.println("processing differing terminal:");\r
+ pair.left.print(System.out);\r
+ pair.right.print(System.out);\r
+ }\r
+\r
+ // If terminals are connected to different route lines,\r
+ // disconnect left terminal connector from its connected counterpart\r
+ // and connect the same terminal connector to the connected counterpart\r
+ // listed in the right terminal.\r
+\r
+ if (pair.left.getLine() != pair.right.getLine()) {\r
+ Resource connector = deserialize( graph, pair.left.getData() );\r
+\r
+ // Case 1:\r
+ // - two terminals connected directly to each other.\r
+ // - terminals moved so that a transient route line is added but\r
+ // nothing should be written into the graph.\r
+ // Case recognition logic:\r
+ // left.line == null\r
+ // && right.line != null\r
+ // && right.line.data == null\r
+ if (pair.left.getLine() == null\r
+ && pair.right.getLine() != null\r
+ && pair.right.getLine().getData() == null)\r
+ continue;\r
+\r
+ // Case 2a:\r
+ // - terminal previously connected to either another terminal or a persistent route line\r
+ // Case 2b:\r
+ // - terminal previously connected to another terminal through a transient route line\r
+ // Case 2c:\r
+ // - terminal previously connected to another terminal\r
+ if (pair.left.getLine() != null && pair.left.getLine().getData() != null) {\r
+ Resource line = deserialize(graph, pair.left.getLine().getData());\r
+ // Case 2a\r
+ if (DEBUG_SYNC)\r
+ System.out.println("removing link between terminal "\r
+ + NameUtils.getSafeLabel(graph, connector) + " and route line "\r
+ + NameUtils.getSafeLabel(graph, line));\r
+ cu.disconnect(connector, line);\r
+ comments.add("Removed link between terminal "\r
+ + NameUtils.getSafeLabel(graph, connector) + " and route line "\r
+ + NameUtils.getSafeLabel(graph, line));\r
+ } else {\r
+ // Case 2b & 2c\r
+ if (DEBUG_SYNC)\r
+ System.out.println("removing link between terminal "\r
+ + NameUtils.getSafeLabel(graph, connector) + " and other terminals ");\r
+ cu.disconnectFromAllRouteNodes(connector);\r
+ comments.add("Removed link between terminal "\r
+ + NameUtils.getSafeLabel(graph, connector) + " and other terminals ");\r
+ }\r
+\r
+ // Case 3a:\r
+ // - terminal is now connected to a persistent route line\r
+ // Case 3b:\r
+ // - terminal is now connected to another terminal\r
+ if (pair.right.getLine() != null && pair.right.getLine().getData() != null) {\r
+ // Case 3a\r
+ Resource line = deserialize(graph, pair.right.getLine().getData());\r
+ if (DEBUG_SYNC)\r
+ System.out.println("adding link between terminal "\r
+ + NameUtils.getSafeLabel(graph, connector) + " and route line "\r
+ + NameUtils.getSafeLabel(graph, line));\r
+ cu.connect(connector, line);\r
+ comments.add("Added link between terminal "\r
+ + NameUtils.getSafeLabel(graph, connector) + " and route line "\r
+ + NameUtils.getSafeLabel(graph, line));\r
+ } else {\r
+ // Case 3b\r
+ // Connector was disconnected from route line.\r
+ // This can currently only mean one thing:\r
+ // there are no more route lines in the connection.\r
+ // If there are still connectors in the connection, the\r
+ // only possible assumption is that there are two\r
+ // connectors and they need to be connected together\r
+ looseConnectors.add(connector);\r
+ }\r
+ }\r
+ }\r
+ if (looseConnectors.size() == 2) {\r
+ Resource[] cns = looseConnectors.toArray(Resource.NONE);\r
+ if (DEBUG_SYNC)\r
+ System.out.println("linking two loose terminal connectors "\r
+ + NameUtils.getSafeLabel(graph, cns[0]) + " and "\r
+ + NameUtils.getSafeLabel(graph, cns[1]));\r
+ cu.connect(cns[0], cns[1]);\r
+ comments.add("Linking two loose terminal connectors "\r
+ + NameUtils.getSafeLabel(graph, cns[0]) + " and "\r
+ + NameUtils.getSafeLabel(graph, cns[1]));\r
+ } else if (!looseConnectors.isEmpty()) {\r
+ System.err.println("BUG: there can only be 0 or 2 loose terminal connectors in any connection. Found " + looseConnectors.size() + ":");\r
+ for (Resource cn : looseConnectors)\r
+ System.err.println(NameUtils.getSafeLabel(graph, cn));\r
+ System.err.flush();\r
+ }\r
+\r
+ // Disconnect and remove removed terminal connectors\r
+ for (RouteTerminal removedTerminal : delta.getTerminalsOnlyInLeft()) {\r
+ Resource connector = deserialize(graph, removedTerminal.getData());\r
+ if (DEBUG_SYNC)\r
+ System.out.println("removing route terminal: " + NameUtils.getSafeLabel(graph, connector));\r
+ cu.removeConnectionPart(connector);\r
+ comments.add("Removed route terminal " + NameUtils.getSafeLabel(graph, connector));\r
+ }\r
+\r
+ // Finally delete removed route lines\r
+ for (RouteLine removed : delta.getLinesOnlyInLeft()) {\r
+ Resource line = deserialize(graph, removed.getData());\r
+ if (DEBUG_SYNC)\r
+ System.out.println("removing route line: " + NameUtils.getSafeLabel(graph, line));\r
+ cu.removeConnectionPart(line);\r
+ comments.add("Removed route line " + NameUtils.getSafeLabel(graph, line));\r
+ }\r
+\r
+ graph.addMetadata(comments);\r
+ }\r
+ }\r
+\r
+ private void writeCommandMetadata(WriteGraph graph, RouteGraph left, RouteGraphDelta delta, RouteGraph right) {\r
+ try {\r
+ if(!delta.getTerminalsOnlyInLeft().isEmpty() || !delta.getTerminalsOnlyInRight().isEmpty())\r
+ return; // These are not changes layout organizer may do.\r
+ \r
+ RouteGraphModification proxy = new RouteGraphModification(graph.getService(SerialisationSupport.class), left);\r
+ TObjectIntHashMap<RouteNode> idMap = proxy.getIdMap();\r
+ TObjectIntHashMap<RouteNode> rightIdMap = new TObjectIntHashMap<RouteNode>();\r
+ {\r
+ final TObjectIntHashMap<Object> keyToId = new TObjectIntHashMap<Object>();\r
+ idMap.forEachEntry(new TObjectIntProcedure<RouteNode>() { \r
+ @Override\r
+ public boolean execute(RouteNode a, int b) {\r
+ Object data = a.getData();\r
+ if(data != null)\r
+ keyToId.put(data, b);\r
+ return true;\r
+ }\r
+ });\r
+ for(RouteLine line : right.getLines()) {\r
+ Object data = line.getData();\r
+ if(keyToId.containsKey(data))\r
+ rightIdMap.put(line, keyToId.get(data));\r
+ }\r
+ for(RouteTerminal terminal : right.getTerminals()) {\r
+ Object data = terminal.getData();\r
+ if(keyToId.containsKey(data))\r
+ rightIdMap.put(terminal, keyToId.get(data));\r
+ }\r
+ }\r
+ int id = idMap.size();\r
+ for(RouteLink link : delta.getLinksOnlyInLeft()) {\r
+ proxy.addModi(new RouteGraphModification.RemoveLink(idMap.get(link.getA()), idMap.get(link.getB())));\r
+ }\r
+ for(RouteLine line : delta.getLinesOnlyInLeft()) {\r
+ proxy.addModi(new RouteGraphModification.RemoveLine(idMap.get(line)));\r
+ } \r
+ for(RouteLinePair pair : delta.getLinesThatDiffer()) {\r
+ proxy.addModi(new RouteGraphModification.UpdateLine(idMap.get(pair.left), pair.right.getPosition(), pair.right.isHorizontal()));\r
+ }\r
+ for(RouteLine line : delta.getLinesOnlyInRight()) {\r
+ rightIdMap.put(line, id++);\r
+ proxy.addModi(new RouteGraphModification.CreateLine(line.getPosition(), line.isHorizontal()));\r
+ }\r
+ if(left.isSimpleConnection() && !right.isSimpleConnection())\r
+ proxy.addModi(new RouteGraphModification.RemoveLink(0, 1));\r
+ for(RouteTerminalPair pair : delta.getTerminalsThatDiffer())\r
+ if(pair.left.getLine() != pair.right.getLine()) {\r
+ if(pair.left.getLine() != null)\r
+ proxy.addModi(new RouteGraphModification.RemoveLink(idMap.get(pair.left), idMap.get(pair.left.getLine())));\r
+ if(pair.right.getLine() != null)\r
+ proxy.addModi(new RouteGraphModification.CreateLink(idMap.get(pair.left), rightIdMap.get(pair.right.getLine())));\r
+ }\r
+ if(!left.isSimpleConnection() && right.isSimpleConnection() && right.getTerminals().size() == 2) {\r
+ Iterator<RouteTerminal> it = right.getTerminals().iterator();\r
+ proxy.addModi(new RouteGraphModification.CreateLink(\r
+ rightIdMap.get(it.next()), rightIdMap.get(it.next())));\r
+ }\r
+ for(RouteLink link : delta.getLinksOnlyInRight()) {\r
+ proxy.addModi(new RouteGraphModification.CreateLink(rightIdMap.get(link.getA()), rightIdMap.get(link.getB())));\r
+ }\r
+ Resource model = proxy.findTerminalIdentifiers(graph);\r
+ StringBuilder b = new StringBuilder();\r
+ b.append("modifyConnection \"");\r
+ proxy.toString(b);\r
+ b.append("\"");\r
+ if (model != null)\r
+ CommandMetadata.add(graph, model.getResourceId(), b.toString());\r
+ } catch(DatabaseException e) {\r
+ // Failure in command writing must not cancel the write transaction\r
+ e.printStackTrace();\r
+ } catch(NullPointerException e) {\r
+ // Failure in command writing must not cancel the write transaction \r
+ e.printStackTrace();\r
+ }\r
+ }\r
+\r
+ public Resource writeLine(WriteGraph graph, Resource line, RouteLine routeLine) throws DatabaseException {\r
+ if (line == null) {\r
+ line = graph.newResource();\r
+ graph.claim(line, L0.InstanceOf, null, DIA.RouteLine);\r
+ graph.claim(connection, DIA.HasInteriorRouteNode, line);\r
+ }\r
+ graph.claimLiteral(line, DIA.IsHorizontal, routeLine.isHorizontal());\r
+ graph.claimLiteral(line, DIA.HasPosition, routeLine.getPosition());\r
+ routeLine.setData( serialize(graph, line) );\r
+ if (DEBUG_SYNC) {\r
+ System.out.print("wrote route line: ");\r
+ routeLine.print(System.out);\r
+ }\r
+ return line;\r
+ }\r
+\r
+ public static Object serialize(ServiceLocator locator, Resource r) throws DatabaseException {\r
+ SerialisationSupport ss = locator.getService(SerialisationSupport.class);\r
+ return ss.getRandomAccessId(r); \r
+ }\r
+\r
+ public static Resource deserialize(ServiceLocator locator, Object o) throws DatabaseException {\r
+ Resource r = tryDeserialize(locator, o);\r
+ if (r != null)\r
+ return r;\r
+ throw new IllegalArgumentException("unrecognized object: " + o);\r
+ }\r
+\r
+ public static Resource tryDeserialize(ServiceLocator locator, Object o) throws DatabaseException {\r
+ if (o instanceof Long)\r
+ return locator.getService(SerialisationSupport.class).getResource((Long) o);\r
+ return null;\r
+ }\r
+\r
+}\r