]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/RouteGraphModification.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / synchronization / graph / RouteGraphModification.java
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/RouteGraphModification.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/RouteGraphModification.java
new file mode 100644 (file)
index 0000000..a97975a
--- /dev/null
@@ -0,0 +1,563 @@
+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