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