-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ö
+ */
+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;
+ }
+}