]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/RouteGraphModification.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / synchronization / graph / RouteGraphModification.java
index a97975a7f3fef01743181b351363098fb2b0ea85..a15b99bc45907eeb319cdc7bc5925c0bb1af95be 100644 (file)
-package org.simantics.diagram.synchronization.graph;\r
-\r
-import gnu.trove.list.array.TIntArrayList;\r
-import gnu.trove.map.hash.TObjectIntHashMap;\r
-import gnu.trove.set.hash.THashSet;\r
-import gnu.trove.set.hash.TIntHashSet;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collection;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.layer0.util.RelativeReference;\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.flag.RouteGraphConnectionSplitter;\r
-import org.simantics.diagram.stubs.DiagramResource;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.structural.stubs.StructuralResource2;\r
-\r
-/**\r
- * Encodes modifications to a route graph.\r
- * @author Hannu Niemistö\r
- */\r
-public class RouteGraphModification {\r
-    // Identification information\r
-    int lineCount;\r
-    int terminalCount;\r
-    int[] links;\r
-\r
-    // Modification information\r
-    ArrayList<Modi> modis = new ArrayList<Modi>(); \r
-\r
-    // Two different ways to identify\r
-    Resource[] resources;    \r
-    String[] terminalIdentifiers;\r
-\r
-    // Auxiliary\r
-    TObjectIntHashMap<RouteNode> idMap;\r
-    Resource connection;\r
-\r
-    public interface Modi {\r
-    }\r
-\r
-    public static class UpdateLine implements Modi {\r
-        int id;\r
-        double position;\r
-        boolean isHorizontal;\r
-\r
-        public UpdateLine(int id, double position, boolean isHorizontal) {\r
-            this.id = id;\r
-            this.position = position;\r
-            this.isHorizontal = isHorizontal;\r
-        }\r
-\r
-        public UpdateLine(String text) {\r
-            String[] parts = text.split("\\$");\r
-            this.id = Integer.parseInt(parts[0]);\r
-            this.position = Double.parseDouble(parts[1]);\r
-            this.isHorizontal = Boolean.parseBoolean(parts[2]);\r
-        }\r
-\r
-        @Override\r
-        public String toString() {\r
-            return "M" + id + "$" + position + "$" + isHorizontal;\r
-        }\r
-    }\r
-\r
-    public static class RemoveLink implements Modi {\r
-        int a;\r
-        int b;\r
-\r
-        public RemoveLink(int a, int b) {\r
-            this.a = a;\r
-            this.b = b;\r
-        }\r
-\r
-        public RemoveLink(String text) {\r
-            String[] parts = text.split("\\$");\r
-            this.a = Integer.parseInt(parts[0]);\r
-            this.b = Integer.parseInt(parts[1]);\r
-        }\r
-\r
-        @Override\r
-        public String toString() {\r
-            return "r" + a + "$" + b;\r
-        }\r
-    }\r
-\r
-    public static class RemoveLine implements Modi {\r
-        int a;\r
-\r
-        public RemoveLine(int a) {\r
-            this.a = a;\r
-        }\r
-\r
-        public RemoveLine(String text) {\r
-            this.a = Integer.parseInt(text);\r
-        }\r
-\r
-        @Override\r
-        public String toString() {\r
-            return "R" + a;\r
-        }\r
-    }\r
-\r
-    public static class CreateLink implements Modi {\r
-        int a;\r
-        int b;\r
-\r
-        public CreateLink(int a, int b) {\r
-            this.a = a;\r
-            this.b = b;\r
-        }\r
-\r
-        public CreateLink(String text) {\r
-            String[] parts = text.split("\\$");\r
-            this.a = Integer.parseInt(parts[0]);\r
-            this.b = Integer.parseInt(parts[1]);\r
-        }\r
-\r
-        @Override\r
-        public String toString() {\r
-            return "c" + a + "$" + b;\r
-        }\r
-    }\r
-\r
-    public static class CreateLine implements Modi {\r
-        double position;\r
-        boolean isHorizontal;\r
-\r
-        public CreateLine(double position, boolean isHorizontal) {\r
-            this.position = position;\r
-            this.isHorizontal = isHorizontal;\r
-        }\r
-\r
-        public CreateLine(String text) {\r
-            String[] parts = text.split("\\$");\r
-            this.position = Double.parseDouble(parts[0]);\r
-            this.isHorizontal = Boolean.parseBoolean(parts[1]);\r
-        }\r
-\r
-        @Override\r
-        public String toString() {\r
-            return "C" + position + "$" + isHorizontal;\r
-        }\r
-    }\r
-\r
-    public static class Split implements Modi {\r
-        int[] interface1;\r
-        int[] interface2; \r
-        int[] lines2; \r
-        int[] terminals1; \r
-        int[] terminals2;\r
-        boolean isHorizontal; \r
-        double isectX;\r
-        double isectY;\r
-        \r
-        public Split(int[] interface1, int[] interface2, int[] lines2,\r
-                int[] terminals1, int[] terminals2, boolean isHorizontal,\r
-                double isectX, double isectY) {\r
-            this.interface1 = interface1;\r
-            this.interface2 = interface2;\r
-            this.lines2 = lines2;\r
-            this.terminals1 = terminals1;\r
-            this.terminals2 = terminals2;\r
-            this.isHorizontal = isHorizontal;\r
-            this.isectX = isectX;\r
-            this.isectY = isectY;\r
-        }\r
-\r
-        public Split(String text) {\r
-            List<String> parts = Arrays.asList(text.split("\\$"));\r
-            Iterator<String> it = parts.iterator();\r
-            this.interface1 = readInts(it);\r
-            this.interface2 = readInts(it);\r
-            this.lines2 = readInts(it);\r
-            this.terminals1 = readInts(it);\r
-            this.terminals2 = readInts(it);\r
-            this.isHorizontal = Boolean.parseBoolean(it.next());\r
-            this.isectX = Double.parseDouble(it.next());\r
-            this.isectY = Double.parseDouble(it.next());\r
-        }\r
-\r
-        @Override\r
-        public String toString() {\r
-            StringBuilder b = new StringBuilder();\r
-            b.append("S");\r
-            write(b, interface1);\r
-            b.append("$");\r
-            write(b, interface2);\r
-            b.append("$");\r
-            write(b, lines2);\r
-            b.append("$");\r
-            write(b, terminals1);\r
-            b.append("$");\r
-            write(b, terminals2);\r
-            b.append("$");\r
-            b.append(isHorizontal);\r
-            b.append("$");\r
-            b.append(isectX);\r
-            b.append("$");\r
-            b.append(isectY);\r
-            return b.toString();\r
-        }\r
-    }\r
-    \r
-    private static void write(StringBuilder b, int[] ids) {\r
-        b.append(ids.length);\r
-        for(int e : ids) {\r
-            b.append('$');\r
-            b.append(e);\r
-        }\r
-    }\r
-    \r
-    private static int[] readInts(Iterator<String> it) {\r
-        int length = Integer.parseInt(it.next());\r
-        int[] result = new int[length];\r
-        for(int i=0;i<length;++i)\r
-            result[i] = Integer.parseInt(it.next());\r
-        return result;\r
-    }\r
-    \r
-    public void addModi(Modi modi) {\r
-        modis.add(modi);\r
-    }\r
-\r
-    public RouteGraphModification(String text) {\r
-        String[] parts = text.split(",");\r
-        int pos = 0;\r
-        lineCount = Integer.parseInt(parts[pos++]);\r
-        terminalCount = Integer.parseInt(parts[pos++]);\r
-        terminalIdentifiers = new String[terminalCount];\r
-        for(int i=0;i<terminalCount;++i) {\r
-            terminalIdentifiers[i] = parts[pos++];            \r
-        }\r
-        int linkCount = Integer.parseInt(parts[pos++]);\r
-        links = new int[2*linkCount];\r
-        for(int i=0;i<links.length;++i)\r
-            links[i] = Integer.parseInt(parts[pos++]);\r
-        while(pos < parts.length) {\r
-            String part = parts[pos++];\r
-            char first = part.charAt(0);\r
-            part = part.substring(1);\r
-            switch(first) {\r
-            case 'M': addModi(new UpdateLine(part)); break;\r
-            case 'R': addModi(new RemoveLine(part)); break;\r
-            case 'r': addModi(new RemoveLink(part)); break;\r
-            case 'C': addModi(new CreateLine(part)); break;\r
-            case 'c': addModi(new CreateLink(part)); break;\r
-            case 'S': addModi(new Split(part)); break;\r
-            }\r
-        }\r
-        resources = new Resource[lineCount + terminalCount];\r
-    }\r
-\r
-    public RouteGraphModification(SerialisationSupport ser, RouteGraph rg) throws DatabaseException {\r
-        Collection<RouteLine> lines = rg.getLines();\r
-        lineCount = lines.size();\r
-        Collection<RouteTerminal> terminals = rg.getTerminals();\r
-        terminalCount = terminals.size();\r
-\r
-        resources = new Resource[lineCount + terminalCount];\r
-\r
-        THashSet<RouteLink> linkSet = new THashSet<RouteLink>();\r
-        idMap = new TObjectIntHashMap<RouteNode>(); \r
-\r
-        int i=0;\r
-        for(RouteLine line : lines) {\r
-            idMap.put(line, i);\r
-            resources[i] = ser.getResource((Long)line.getData());\r
-            for(RoutePoint rp : line.getPoints()) {\r
-                if(rp instanceof RouteLink) {\r
-                    RouteLink link = (RouteLink)rp;\r
-                    if(!link.getA().isTransient() &&\r
-                            !link.getA().isTransient())\r
-                        linkSet.add(link);\r
-                }\r
-            }\r
-            ++i;            \r
-        }\r
-        for(RouteTerminal terminal : terminals) {\r
-            idMap.put(terminal, i);\r
-            resources[i] = ser.getResource((Long)terminal.getData());\r
-            ++i;\r
-        }\r
-        if(rg.isSimpleConnection()) {\r
-            links = new int[] {0, 1};\r
-        }\r
-        else {\r
-            links = new int[2*(terminalCount + linkSet.size())];\r
-            i = 0;        \r
-            for(RouteLink link : linkSet) {\r
-                links[i++] = idMap.get(link.getA());\r
-                links[i++] = idMap.get(link.getB());\r
-            }\r
-            for(RouteTerminal terminal : terminals) {\r
-                links[i++] = idMap.get(terminal);\r
-                links[i++] = idMap.get(terminal.getLine());\r
-            }\r
-        }\r
-    }\r
-\r
-    public Resource findTerminalIdentifiers(ReadGraph g) throws DatabaseException {\r
-        Resource base = null;\r
-        terminalIdentifiers = new String[terminalCount];\r
-        for(int i=0;i<terminalCount;++i) {\r
-            Resource r = resources[lineCount + i];\r
-            RelativeReference ref = ElementIdentification.getConnectorIdentifier(g, r);\r
-            terminalIdentifiers[i] = ref.path;\r
-            if(ref.base != null)\r
-                base = ref.base;            \r
-        }\r
-        return base;\r
-    }\r
-\r
-    @Override\r
-    public String toString() {\r
-        StringBuilder b = new StringBuilder();\r
-        toString(b);\r
-        return b.toString();\r
-    }\r
-\r
-    public void toString(StringBuilder b) {\r
-        b.append(lineCount);\r
-        b.append(',');\r
-        b.append(terminalCount);\r
-        for(int i=0;i<terminalCount;++i) {\r
-            b.append(',');\r
-            b.append(terminalIdentifiers[i]);\r
-        }\r
-        b.append(',');\r
-        b.append(links.length/2);\r
-        for(int l : links) {\r
-            b.append(',');\r
-            b.append(l);\r
-        }\r
-        for(Modi modi : modis) {\r
-            b.append(',');\r
-            b.append(modi);\r
-        }\r
-    }\r
-\r
-    public boolean resolveResources(ReadGraph g, Resource model) throws DatabaseException {\r
-        DiagramResource DIA = DiagramResource.getInstance(g);\r
-        StructuralResource2 STR = StructuralResource2.getInstance(g);\r
-\r
-        // --- Resolve connectors ---------------------------------------------\r
-\r
-        Resource connection = null;\r
-        {\r
-            ArrayList<List<Resource>> connectorCandidates = new ArrayList<List<Resource>>(terminalCount); \r
-            for(int i=0;i<terminalCount;++i)\r
-                connectorCandidates.add(ElementIdentification.resolveConnector(g, model, terminalIdentifiers[i]));            \r
-            for(List<Resource> connectors : connectorCandidates) {\r
-                if(connectors.isEmpty())\r
-                    return false;\r
-                if(connection == null && connectors.size() == 1) {\r
-                    for(Resource temp : g.getObjects(connectors.get(0), STR.Connects))\r
-                        if(g.isInstanceOf(temp, DIA.Connection)) {\r
-                            connection = temp;\r
-                            break;\r
-                        }\r
-                }\r
-            }\r
-            if(connection == null)\r
-                return false;\r
-            loop: for(int i=0;i<terminalCount;++i) {\r
-                for(Resource connector : connectorCandidates.get(i))\r
-                    if(g.hasStatement(connector, STR.Connects, connection)) {\r
-                        resources[lineCount + i] = connector;\r
-                        continue loop;\r
-                    }\r
-                return false;\r
-            }\r
-        }\r
-\r
-        if(lineCount != g.getObjects(connection, DIA.HasInteriorRouteNode).size())\r
-            return false;\r
-        if(terminalCount != g.getObjects(connection, STR.IsConnectedTo).size())\r
-            return false;\r
-\r
-        if(lineCount == 0)\r
-            return true;\r
-\r
-        // --- Resolve route lines --------------------------------------------\r
-\r
-        // Create inverse map for resources\r
-        TObjectIntHashMap<Resource> invResources = new TObjectIntHashMap<Resource>();\r
-        for(int i=0;i<terminalCount;++i) {\r
-            int id = lineCount + i;\r
-            invResources.put(resources[id], id);\r
-        }\r
-\r
-        // Create neighbor indices\r
-        final TIntHashSet[] neighbors = new TIntHashSet[terminalCount + lineCount];\r
-        for(int i=0;i<neighbors.length;++i)\r
-            neighbors[i] = new TIntHashSet();\r
-        for(int i=0;i<links.length;i+=2) {\r
-            int a = links[i];\r
-            int b = links[i+1];\r
-            if(resources[b] == null)\r
-                neighbors[a].add(b);\r
-            if(resources[a] == null)\r
-                neighbors[b].add(a);\r
-        }\r
-\r
-        // Create stack\r
-        TIntArrayList stack = new TIntArrayList();\r
-        TIntArrayList backlog = new TIntArrayList();\r
-        for(int i=0;i<terminalCount;++i)\r
-            stack.add(lineCount+i);\r
-\r
-        // Resolve route lines\r
-        int oldResolvedCount = 0;\r
-        while(invResources.size() < resources.length) {\r
-            oldResolvedCount = invResources.size();\r
-            while(!stack.isEmpty()) {\r
-                int id = stack.removeAt(stack.size()-1);\r
-                TIntHashSet ns = neighbors[id];\r
-                for(int n : ns.toArray())\r
-                    if(resources[n] != null)\r
-                        ns.remove(n);\r
-                if(ns.isEmpty())\r
-                    ;\r
-                else if(ns.size() == 1) {\r
-                    Resource det = null;\r
-                    for(Resource r : g.getObjects(resources[id], DIA.AreConnected))\r
-                        if(!invResources.containsKey(r)) {\r
-                            if(det == null)\r
-                                det = r;\r
-                            else\r
-                                return false;\r
-                        }\r
-                    if(det == null)\r
-                        return false;\r
-                    final int newId = ns.iterator().next();\r
-                    resources[newId] = det;\r
-                    invResources.put(det, newId);\r
-                    stack.add(newId);\r
-                }\r
-                else\r
-                    backlog.add(id);\r
-            }\r
-\r
-            if(oldResolvedCount == invResources.size())\r
-                return false; // No progress happened\r
-\r
-            // Reverse backlog and swap stack and backlog\r
-            {\r
-                backlog.reverse();\r
-\r
-                TIntArrayList temp = stack;\r
-                stack = backlog;                \r
-                backlog = temp;\r
-            }\r
-        }\r
-\r
-        return true;\r
-    }\r
-\r
-    public Resource getConnection(ReadGraph g) throws DatabaseException {        \r
-        if(connection == null) {\r
-            DiagramResource DIA = DiagramResource.getInstance(g);              \r
-            if(lineCount > 0) {\r
-                connection = g.getSingleObject(resources[0], DIA.HasInteriorRouteNode_Inverse);\r
-            }\r
-            else {\r
-                StructuralResource2 STR = StructuralResource2.getInstance(g);\r
-                for(Resource temp : g.getObjects(resources[0], STR.Connects))\r
-                    if(g.isInstanceOf(temp, DIA.Connection)) {\r
-                        connection = temp;\r
-                        break;\r
-                    }\r
-            }\r
-        }\r
-        return connection;\r
-    }\r
-\r
-    public void runUpdates(WriteGraph g) throws DatabaseException {\r
-        DiagramResource DIA = DiagramResource.getInstance(g);\r
-        Layer0 L0 = Layer0.getInstance(g);\r
-\r
-        for(Modi modi_ : modis) {\r
-            if(modi_ instanceof UpdateLine) {\r
-                UpdateLine modi = (UpdateLine)modi_;\r
-                Resource routeLine = resources[modi.id];\r
-                g.claimLiteral(routeLine, DIA.HasPosition, modi.position);\r
-                g.claimLiteral(routeLine, DIA.IsHorizontal, modi.isHorizontal);\r
-            }\r
-            else if(modi_ instanceof RemoveLink) {\r
-                RemoveLink modi = (RemoveLink)modi_;\r
-                g.denyStatement(resources[modi.a], DIA.AreConnected, resources[modi.b]);\r
-            }\r
-            else if(modi_ instanceof RemoveLine) {\r
-                RemoveLine modi = (RemoveLine)modi_;\r
-                g.deny(resources[modi.a]);\r
-            }\r
-            else if(modi_ instanceof CreateLink) {\r
-                CreateLink modi = (CreateLink)modi_;\r
-                g.claim(resources[modi.a], DIA.AreConnected, resources[modi.b]);\r
-            }\r
-            else if(modi_ instanceof CreateLine) {\r
-                CreateLine modi = (CreateLine)modi_;\r
-                Resource routeLine = g.newResource();\r
-                g.claim(routeLine, L0.InstanceOf, DIA.RouteLine);\r
-                g.claimLiteral(routeLine, DIA.HasPosition, modi.position);\r
-                g.claimLiteral(routeLine, DIA.IsHorizontal, modi.isHorizontal);                                    \r
-                g.claim(getConnection(g), DIA.HasInteriorRouteNode, routeLine);\r
-                int id = resources.length;\r
-                resources = Arrays.copyOf(resources, id+1);\r
-                resources[id] = routeLine;\r
-            }\r
-            else if(modi_ instanceof Split) {\r
-                Split modi = (Split)modi_;\r
-                RouteGraphConnectionSplitter splitter = new RouteGraphConnectionSplitter(g);\r
-                splitter.doSplit(g, connection,\r
-                        toResources(modi.interface1),\r
-                        toResources(modi.interface2),\r
-                        toResources(modi.lines2),\r
-                        toResources(modi.terminals1),\r
-                        toResources(modi.terminals2),\r
-                        modi.isHorizontal,\r
-                        modi.isectX,\r
-                        modi.isectY\r
-                        );\r
-            }\r
-        }\r
-    }\r
-\r
-    public TObjectIntHashMap<RouteNode> getIdMap() {\r
-        return idMap;\r
-    }\r
-\r
-    public int[] toIds(ArrayList<Resource> rs) {        \r
-        TObjectIntHashMap<Resource> rmap = new TObjectIntHashMap<Resource>();\r
-        for(int i=0;i<resources.length;++i)\r
-            rmap.put(resources[i], i);\r
-        \r
-        int[] result = new int[rs.size()];\r
-        for(int i=0;i<rs.size();++i)\r
-            result[i] = rmap.get(rs.get(i));\r
-        \r
-        return result;\r
-    }\r
-    \r
-    public ArrayList<Resource> toResources(int[] ids) {\r
-        ArrayList<Resource> result = new ArrayList<Resource>(ids.length);\r
-        for(int id : ids)\r
-            result.add(resources[id]);\r
-        return result;\r
-    }\r
-}\r
+package org.simantics.diagram.synchronization.graph;
+
+import gnu.trove.list.array.TIntArrayList;
+import gnu.trove.map.hash.TObjectIntHashMap;
+import gnu.trove.set.hash.THashSet;
+import gnu.trove.set.hash.TIntHashSet;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.util.RelativeReference;
+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.flag.RouteGraphConnectionSplitter;
+import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.layer0.Layer0;
+import org.simantics.structural.stubs.StructuralResource2;
+
+/**
+ * Encodes modifications to a route graph.
+ * @author Hannu Niemist&ouml;
+ */
+public class RouteGraphModification {
+    // Identification information
+    int lineCount;
+    int terminalCount;
+    int[] links;
+
+    // Modification information
+    ArrayList<Modi> modis = new ArrayList<Modi>(); 
+
+    // Two different ways to identify
+    Resource[] resources;    
+    String[] terminalIdentifiers;
+
+    // Auxiliary
+    TObjectIntHashMap<RouteNode> idMap;
+    Resource connection;
+
+    public interface Modi {
+    }
+
+    public static class UpdateLine implements Modi {
+        int id;
+        double position;
+        boolean isHorizontal;
+
+        public UpdateLine(int id, double position, boolean isHorizontal) {
+            this.id = id;
+            this.position = position;
+            this.isHorizontal = isHorizontal;
+        }
+
+        public UpdateLine(String text) {
+            String[] parts = text.split("\\$");
+            this.id = Integer.parseInt(parts[0]);
+            this.position = Double.parseDouble(parts[1]);
+            this.isHorizontal = Boolean.parseBoolean(parts[2]);
+        }
+
+        @Override
+        public String toString() {
+            return "M" + id + "$" + position + "$" + isHorizontal;
+        }
+    }
+
+    public static class RemoveLink implements Modi {
+        int a;
+        int b;
+
+        public RemoveLink(int a, int b) {
+            this.a = a;
+            this.b = b;
+        }
+
+        public RemoveLink(String text) {
+            String[] parts = text.split("\\$");
+            this.a = Integer.parseInt(parts[0]);
+            this.b = Integer.parseInt(parts[1]);
+        }
+
+        @Override
+        public String toString() {
+            return "r" + a + "$" + b;
+        }
+    }
+
+    public static class RemoveLine implements Modi {
+        int a;
+
+        public RemoveLine(int a) {
+            this.a = a;
+        }
+
+        public RemoveLine(String text) {
+            this.a = Integer.parseInt(text);
+        }
+
+        @Override
+        public String toString() {
+            return "R" + a;
+        }
+    }
+
+    public static class CreateLink implements Modi {
+        int a;
+        int b;
+
+        public CreateLink(int a, int b) {
+            this.a = a;
+            this.b = b;
+        }
+
+        public CreateLink(String text) {
+            String[] parts = text.split("\\$");
+            this.a = Integer.parseInt(parts[0]);
+            this.b = Integer.parseInt(parts[1]);
+        }
+
+        @Override
+        public String toString() {
+            return "c" + a + "$" + b;
+        }
+    }
+
+    public static class CreateLine implements Modi {
+        double position;
+        boolean isHorizontal;
+
+        public CreateLine(double position, boolean isHorizontal) {
+            this.position = position;
+            this.isHorizontal = isHorizontal;
+        }
+
+        public CreateLine(String text) {
+            String[] parts = text.split("\\$");
+            this.position = Double.parseDouble(parts[0]);
+            this.isHorizontal = Boolean.parseBoolean(parts[1]);
+        }
+
+        @Override
+        public String toString() {
+            return "C" + position + "$" + isHorizontal;
+        }
+    }
+
+    public static class Split implements Modi {
+        int[] interface1;
+        int[] interface2; 
+        int[] lines2; 
+        int[] terminals1; 
+        int[] terminals2;
+        boolean isHorizontal; 
+        double isectX;
+        double isectY;
+        
+        public Split(int[] interface1, int[] interface2, int[] lines2,
+                int[] terminals1, int[] terminals2, boolean isHorizontal,
+                double isectX, double isectY) {
+            this.interface1 = interface1;
+            this.interface2 = interface2;
+            this.lines2 = lines2;
+            this.terminals1 = terminals1;
+            this.terminals2 = terminals2;
+            this.isHorizontal = isHorizontal;
+            this.isectX = isectX;
+            this.isectY = isectY;
+        }
+
+        public Split(String text) {
+            List<String> parts = Arrays.asList(text.split("\\$"));
+            Iterator<String> it = parts.iterator();
+            this.interface1 = readInts(it);
+            this.interface2 = readInts(it);
+            this.lines2 = readInts(it);
+            this.terminals1 = readInts(it);
+            this.terminals2 = readInts(it);
+            this.isHorizontal = Boolean.parseBoolean(it.next());
+            this.isectX = Double.parseDouble(it.next());
+            this.isectY = Double.parseDouble(it.next());
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder b = new StringBuilder();
+            b.append("S");
+            write(b, interface1);
+            b.append("$");
+            write(b, interface2);
+            b.append("$");
+            write(b, lines2);
+            b.append("$");
+            write(b, terminals1);
+            b.append("$");
+            write(b, terminals2);
+            b.append("$");
+            b.append(isHorizontal);
+            b.append("$");
+            b.append(isectX);
+            b.append("$");
+            b.append(isectY);
+            return b.toString();
+        }
+    }
+    
+    private static void write(StringBuilder b, int[] ids) {
+        b.append(ids.length);
+        for(int e : ids) {
+            b.append('$');
+            b.append(e);
+        }
+    }
+    
+    private static int[] readInts(Iterator<String> it) {
+        int length = Integer.parseInt(it.next());
+        int[] result = new int[length];
+        for(int i=0;i<length;++i)
+            result[i] = Integer.parseInt(it.next());
+        return result;
+    }
+    
+    public void addModi(Modi modi) {
+        modis.add(modi);
+    }
+
+    public RouteGraphModification(String text) {
+        String[] parts = text.split(",");
+        int pos = 0;
+        lineCount = Integer.parseInt(parts[pos++]);
+        terminalCount = Integer.parseInt(parts[pos++]);
+        terminalIdentifiers = new String[terminalCount];
+        for(int i=0;i<terminalCount;++i) {
+            terminalIdentifiers[i] = parts[pos++];            
+        }
+        int linkCount = Integer.parseInt(parts[pos++]);
+        links = new int[2*linkCount];
+        for(int i=0;i<links.length;++i)
+            links[i] = Integer.parseInt(parts[pos++]);
+        while(pos < parts.length) {
+            String part = parts[pos++];
+            char first = part.charAt(0);
+            part = part.substring(1);
+            switch(first) {
+            case 'M': addModi(new UpdateLine(part)); break;
+            case 'R': addModi(new RemoveLine(part)); break;
+            case 'r': addModi(new RemoveLink(part)); break;
+            case 'C': addModi(new CreateLine(part)); break;
+            case 'c': addModi(new CreateLink(part)); break;
+            case 'S': addModi(new Split(part)); break;
+            }
+        }
+        resources = new Resource[lineCount + terminalCount];
+    }
+
+    public RouteGraphModification(SerialisationSupport ser, RouteGraph rg) throws DatabaseException {
+        Collection<RouteLine> lines = rg.getLines();
+        lineCount = lines.size();
+        Collection<RouteTerminal> terminals = rg.getTerminals();
+        terminalCount = terminals.size();
+
+        resources = new Resource[lineCount + terminalCount];
+
+        THashSet<RouteLink> linkSet = new THashSet<RouteLink>();
+        idMap = new TObjectIntHashMap<RouteNode>(); 
+
+        int i=0;
+        for(RouteLine line : lines) {
+            idMap.put(line, i);
+            resources[i] = ser.getResource((Long)line.getData());
+            for(RoutePoint rp : line.getPoints()) {
+                if(rp instanceof RouteLink) {
+                    RouteLink link = (RouteLink)rp;
+                    if(!link.getA().isTransient() &&
+                            !link.getA().isTransient())
+                        linkSet.add(link);
+                }
+            }
+            ++i;            
+        }
+        for(RouteTerminal terminal : terminals) {
+            idMap.put(terminal, i);
+            resources[i] = ser.getResource((Long)terminal.getData());
+            ++i;
+        }
+        if(rg.isSimpleConnection()) {
+            links = new int[] {0, 1};
+        }
+        else {
+            links = new int[2*(terminalCount + linkSet.size())];
+            i = 0;        
+            for(RouteLink link : linkSet) {
+                links[i++] = idMap.get(link.getA());
+                links[i++] = idMap.get(link.getB());
+            }
+            for(RouteTerminal terminal : terminals) {
+                links[i++] = idMap.get(terminal);
+                links[i++] = idMap.get(terminal.getLine());
+            }
+        }
+    }
+
+    public Resource findTerminalIdentifiers(ReadGraph g) throws DatabaseException {
+        Resource base = null;
+        terminalIdentifiers = new String[terminalCount];
+        for(int i=0;i<terminalCount;++i) {
+            Resource r = resources[lineCount + i];
+            RelativeReference ref = ElementIdentification.getConnectorIdentifier(g, r);
+            terminalIdentifiers[i] = ref.path;
+            if(ref.base != null)
+                base = ref.base;            
+        }
+        return base;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder b = new StringBuilder();
+        toString(b);
+        return b.toString();
+    }
+
+    public void toString(StringBuilder b) {
+        b.append(lineCount);
+        b.append(',');
+        b.append(terminalCount);
+        for(int i=0;i<terminalCount;++i) {
+            b.append(',');
+            b.append(terminalIdentifiers[i]);
+        }
+        b.append(',');
+        b.append(links.length/2);
+        for(int l : links) {
+            b.append(',');
+            b.append(l);
+        }
+        for(Modi modi : modis) {
+            b.append(',');
+            b.append(modi);
+        }
+    }
+
+    public boolean resolveResources(ReadGraph g, Resource model) throws DatabaseException {
+        DiagramResource DIA = DiagramResource.getInstance(g);
+        StructuralResource2 STR = StructuralResource2.getInstance(g);
+
+        // --- Resolve connectors ---------------------------------------------
+
+        Resource connection = null;
+        {
+            ArrayList<List<Resource>> connectorCandidates = new ArrayList<List<Resource>>(terminalCount); 
+            for(int i=0;i<terminalCount;++i)
+                connectorCandidates.add(ElementIdentification.resolveConnector(g, model, terminalIdentifiers[i]));            
+            for(List<Resource> connectors : connectorCandidates) {
+                if(connectors.isEmpty())
+                    return false;
+                if(connection == null && connectors.size() == 1) {
+                    for(Resource temp : g.getObjects(connectors.get(0), STR.Connects))
+                        if(g.isInstanceOf(temp, DIA.Connection)) {
+                            connection = temp;
+                            break;
+                        }
+                }
+            }
+            if(connection == null)
+                return false;
+            loop: for(int i=0;i<terminalCount;++i) {
+                for(Resource connector : connectorCandidates.get(i))
+                    if(g.hasStatement(connector, STR.Connects, connection)) {
+                        resources[lineCount + i] = connector;
+                        continue loop;
+                    }
+                return false;
+            }
+        }
+
+        if(lineCount != g.getObjects(connection, DIA.HasInteriorRouteNode).size())
+            return false;
+        if(terminalCount != g.getObjects(connection, STR.IsConnectedTo).size())
+            return false;
+
+        if(lineCount == 0)
+            return true;
+
+        // --- Resolve route lines --------------------------------------------
+
+        // Create inverse map for resources
+        TObjectIntHashMap<Resource> invResources = new TObjectIntHashMap<Resource>();
+        for(int i=0;i<terminalCount;++i) {
+            int id = lineCount + i;
+            invResources.put(resources[id], id);
+        }
+
+        // Create neighbor indices
+        final TIntHashSet[] neighbors = new TIntHashSet[terminalCount + lineCount];
+        for(int i=0;i<neighbors.length;++i)
+            neighbors[i] = new TIntHashSet();
+        for(int i=0;i<links.length;i+=2) {
+            int a = links[i];
+            int b = links[i+1];
+            if(resources[b] == null)
+                neighbors[a].add(b);
+            if(resources[a] == null)
+                neighbors[b].add(a);
+        }
+
+        // Create stack
+        TIntArrayList stack = new TIntArrayList();
+        TIntArrayList backlog = new TIntArrayList();
+        for(int i=0;i<terminalCount;++i)
+            stack.add(lineCount+i);
+
+        // Resolve route lines
+        int oldResolvedCount = 0;
+        while(invResources.size() < resources.length) {
+            oldResolvedCount = invResources.size();
+            while(!stack.isEmpty()) {
+                int id = stack.removeAt(stack.size()-1);
+                TIntHashSet ns = neighbors[id];
+                for(int n : ns.toArray())
+                    if(resources[n] != null)
+                        ns.remove(n);
+                if(ns.isEmpty())
+                    ;
+                else if(ns.size() == 1) {
+                    Resource det = null;
+                    for(Resource r : g.getObjects(resources[id], DIA.AreConnected))
+                        if(!invResources.containsKey(r)) {
+                            if(det == null)
+                                det = r;
+                            else
+                                return false;
+                        }
+                    if(det == null)
+                        return false;
+                    final int newId = ns.iterator().next();
+                    resources[newId] = det;
+                    invResources.put(det, newId);
+                    stack.add(newId);
+                }
+                else
+                    backlog.add(id);
+            }
+
+            if(oldResolvedCount == invResources.size())
+                return false; // No progress happened
+
+            // Reverse backlog and swap stack and backlog
+            {
+                backlog.reverse();
+
+                TIntArrayList temp = stack;
+                stack = backlog;                
+                backlog = temp;
+            }
+        }
+
+        return true;
+    }
+
+    public Resource getConnection(ReadGraph g) throws DatabaseException {        
+        if(connection == null) {
+            DiagramResource DIA = DiagramResource.getInstance(g);              
+            if(lineCount > 0) {
+                connection = g.getSingleObject(resources[0], DIA.HasInteriorRouteNode_Inverse);
+            }
+            else {
+                StructuralResource2 STR = StructuralResource2.getInstance(g);
+                for(Resource temp : g.getObjects(resources[0], STR.Connects))
+                    if(g.isInstanceOf(temp, DIA.Connection)) {
+                        connection = temp;
+                        break;
+                    }
+            }
+        }
+        return connection;
+    }
+
+    public void runUpdates(WriteGraph g) throws DatabaseException {
+        DiagramResource DIA = DiagramResource.getInstance(g);
+        Layer0 L0 = Layer0.getInstance(g);
+
+        for(Modi modi_ : modis) {
+            if(modi_ instanceof UpdateLine) {
+                UpdateLine modi = (UpdateLine)modi_;
+                Resource routeLine = resources[modi.id];
+                g.claimLiteral(routeLine, DIA.HasPosition, modi.position);
+                g.claimLiteral(routeLine, DIA.IsHorizontal, modi.isHorizontal);
+            }
+            else if(modi_ instanceof RemoveLink) {
+                RemoveLink modi = (RemoveLink)modi_;
+                g.denyStatement(resources[modi.a], DIA.AreConnected, resources[modi.b]);
+            }
+            else if(modi_ instanceof RemoveLine) {
+                RemoveLine modi = (RemoveLine)modi_;
+                g.deny(resources[modi.a]);
+            }
+            else if(modi_ instanceof CreateLink) {
+                CreateLink modi = (CreateLink)modi_;
+                g.claim(resources[modi.a], DIA.AreConnected, resources[modi.b]);
+            }
+            else if(modi_ instanceof CreateLine) {
+                CreateLine modi = (CreateLine)modi_;
+                Resource routeLine = g.newResource();
+                g.claim(routeLine, L0.InstanceOf, DIA.RouteLine);
+                g.claimLiteral(routeLine, DIA.HasPosition, modi.position);
+                g.claimLiteral(routeLine, DIA.IsHorizontal, modi.isHorizontal);                                    
+                g.claim(getConnection(g), DIA.HasInteriorRouteNode, routeLine);
+                int id = resources.length;
+                resources = Arrays.copyOf(resources, id+1);
+                resources[id] = routeLine;
+            }
+            else if(modi_ instanceof Split) {
+                Split modi = (Split)modi_;
+                RouteGraphConnectionSplitter splitter = new RouteGraphConnectionSplitter(g);
+                splitter.doSplit(g, connection,
+                        toResources(modi.interface1),
+                        toResources(modi.interface2),
+                        toResources(modi.lines2),
+                        toResources(modi.terminals1),
+                        toResources(modi.terminals2),
+                        modi.isHorizontal,
+                        modi.isectX,
+                        modi.isectY
+                        );
+            }
+        }
+    }
+
+    public TObjectIntHashMap<RouteNode> getIdMap() {
+        return idMap;
+    }
+
+    public int[] toIds(ArrayList<Resource> rs) {        
+        TObjectIntHashMap<Resource> rmap = new TObjectIntHashMap<Resource>();
+        for(int i=0;i<resources.length;++i)
+            rmap.put(resources[i], i);
+        
+        int[] result = new int[rs.size()];
+        for(int i=0;i<rs.size();++i)
+            result[i] = rmap.get(rs.get(i));
+        
+        return result;
+    }
+    
+    public ArrayList<Resource> toResources(int[] ids) {
+        ArrayList<Resource> result = new ArrayList<Resource>(ids.length);
+        for(int id : ids)
+            result.add(resources[id]);
+        return result;
+    }
+}