1 package org.simantics.diagram.synchronization.graph;
3 import gnu.trove.list.array.TIntArrayList;
4 import gnu.trove.map.hash.TObjectIntHashMap;
5 import gnu.trove.set.hash.THashSet;
6 import gnu.trove.set.hash.TIntHashSet;
8 import java.util.ArrayList;
9 import java.util.Arrays;
10 import java.util.Collection;
11 import java.util.Iterator;
12 import java.util.List;
14 import org.simantics.db.ReadGraph;
15 import org.simantics.db.Resource;
16 import org.simantics.db.WriteGraph;
17 import org.simantics.db.exception.DatabaseException;
18 import org.simantics.db.layer0.util.RelativeReference;
19 import org.simantics.db.service.SerialisationSupport;
20 import org.simantics.diagram.connection.RouteGraph;
21 import org.simantics.diagram.connection.RouteLine;
22 import org.simantics.diagram.connection.RouteLink;
23 import org.simantics.diagram.connection.RouteNode;
24 import org.simantics.diagram.connection.RoutePoint;
25 import org.simantics.diagram.connection.RouteTerminal;
26 import org.simantics.diagram.flag.RouteGraphConnectionSplitter;
27 import org.simantics.diagram.stubs.DiagramResource;
28 import org.simantics.layer0.Layer0;
29 import org.simantics.structural.stubs.StructuralResource2;
32 * Encodes modifications to a route graph.
33 * @author Hannu Niemistö
35 public class RouteGraphModification {
36 // Identification information
41 // Modification information
42 ArrayList<Modi> modis = new ArrayList<Modi>();
44 // Two different ways to identify
46 String[] terminalIdentifiers;
49 TObjectIntHashMap<RouteNode> idMap;
52 public interface Modi {
55 public static class UpdateLine implements Modi {
60 public UpdateLine(int id, double position, boolean isHorizontal) {
62 this.position = position;
63 this.isHorizontal = isHorizontal;
66 public UpdateLine(String text) {
67 String[] parts = text.split("\\$");
68 this.id = Integer.parseInt(parts[0]);
69 this.position = Double.parseDouble(parts[1]);
70 this.isHorizontal = Boolean.parseBoolean(parts[2]);
74 public String toString() {
75 return "M" + id + "$" + position + "$" + isHorizontal;
79 public static class RemoveLink implements Modi {
83 public RemoveLink(int a, int b) {
88 public RemoveLink(String text) {
89 String[] parts = text.split("\\$");
90 this.a = Integer.parseInt(parts[0]);
91 this.b = Integer.parseInt(parts[1]);
95 public String toString() {
96 return "r" + a + "$" + b;
100 public static class RemoveLine implements Modi {
103 public RemoveLine(int a) {
107 public RemoveLine(String text) {
108 this.a = Integer.parseInt(text);
112 public String toString() {
117 public static class CreateLink implements Modi {
121 public CreateLink(int a, int b) {
126 public CreateLink(String text) {
127 String[] parts = text.split("\\$");
128 this.a = Integer.parseInt(parts[0]);
129 this.b = Integer.parseInt(parts[1]);
133 public String toString() {
134 return "c" + a + "$" + b;
138 public static class CreateLine implements Modi {
140 boolean isHorizontal;
142 public CreateLine(double position, boolean isHorizontal) {
143 this.position = position;
144 this.isHorizontal = isHorizontal;
147 public CreateLine(String text) {
148 String[] parts = text.split("\\$");
149 this.position = Double.parseDouble(parts[0]);
150 this.isHorizontal = Boolean.parseBoolean(parts[1]);
154 public String toString() {
155 return "C" + position + "$" + isHorizontal;
159 public static class Split implements Modi {
165 boolean isHorizontal;
169 public Split(int[] interface1, int[] interface2, int[] lines2,
170 int[] terminals1, int[] terminals2, boolean isHorizontal,
171 double isectX, double isectY) {
172 this.interface1 = interface1;
173 this.interface2 = interface2;
174 this.lines2 = lines2;
175 this.terminals1 = terminals1;
176 this.terminals2 = terminals2;
177 this.isHorizontal = isHorizontal;
178 this.isectX = isectX;
179 this.isectY = isectY;
182 public Split(String text) {
183 List<String> parts = Arrays.asList(text.split("\\$"));
184 Iterator<String> it = parts.iterator();
185 this.interface1 = readInts(it);
186 this.interface2 = readInts(it);
187 this.lines2 = readInts(it);
188 this.terminals1 = readInts(it);
189 this.terminals2 = readInts(it);
190 this.isHorizontal = Boolean.parseBoolean(it.next());
191 this.isectX = Double.parseDouble(it.next());
192 this.isectY = Double.parseDouble(it.next());
196 public String toString() {
197 StringBuilder b = new StringBuilder();
199 write(b, interface1);
201 write(b, interface2);
205 write(b, terminals1);
207 write(b, terminals2);
209 b.append(isHorizontal);
218 private static void write(StringBuilder b, int[] ids) {
219 b.append(ids.length);
226 private static int[] readInts(Iterator<String> it) {
227 int length = Integer.parseInt(it.next());
228 int[] result = new int[length];
229 for(int i=0;i<length;++i)
230 result[i] = Integer.parseInt(it.next());
234 public void addModi(Modi modi) {
238 public RouteGraphModification(String text) {
239 String[] parts = text.split(",");
241 lineCount = Integer.parseInt(parts[pos++]);
242 terminalCount = Integer.parseInt(parts[pos++]);
243 terminalIdentifiers = new String[terminalCount];
244 for(int i=0;i<terminalCount;++i) {
245 terminalIdentifiers[i] = parts[pos++];
247 int linkCount = Integer.parseInt(parts[pos++]);
248 links = new int[2*linkCount];
249 for(int i=0;i<links.length;++i)
250 links[i] = Integer.parseInt(parts[pos++]);
251 while(pos < parts.length) {
252 String part = parts[pos++];
253 char first = part.charAt(0);
254 part = part.substring(1);
256 case 'M': addModi(new UpdateLine(part)); break;
257 case 'R': addModi(new RemoveLine(part)); break;
258 case 'r': addModi(new RemoveLink(part)); break;
259 case 'C': addModi(new CreateLine(part)); break;
260 case 'c': addModi(new CreateLink(part)); break;
261 case 'S': addModi(new Split(part)); break;
264 resources = new Resource[lineCount + terminalCount];
267 public RouteGraphModification(SerialisationSupport ser, RouteGraph rg) throws DatabaseException {
268 Collection<RouteLine> lines = rg.getLines();
269 lineCount = lines.size();
270 Collection<RouteTerminal> terminals = rg.getTerminals();
271 terminalCount = terminals.size();
273 resources = new Resource[lineCount + terminalCount];
275 THashSet<RouteLink> linkSet = new THashSet<RouteLink>();
276 idMap = new TObjectIntHashMap<RouteNode>();
279 for(RouteLine line : lines) {
281 resources[i] = ser.getResource((Long)line.getData());
282 for(RoutePoint rp : line.getPoints()) {
283 if(rp instanceof RouteLink) {
284 RouteLink link = (RouteLink)rp;
285 if(!link.getA().isTransient() &&
286 !link.getA().isTransient())
292 for(RouteTerminal terminal : terminals) {
293 idMap.put(terminal, i);
294 resources[i] = ser.getResource((Long)terminal.getData());
297 if(rg.isSimpleConnection()) {
298 links = new int[] {0, 1};
301 links = new int[2*(terminalCount + linkSet.size())];
303 for(RouteLink link : linkSet) {
304 links[i++] = idMap.get(link.getA());
305 links[i++] = idMap.get(link.getB());
307 for(RouteTerminal terminal : terminals) {
308 links[i++] = idMap.get(terminal);
309 links[i++] = idMap.get(terminal.getLine());
314 public Resource findTerminalIdentifiers(ReadGraph g) throws DatabaseException {
315 Resource base = null;
316 terminalIdentifiers = new String[terminalCount];
317 for(int i=0;i<terminalCount;++i) {
318 Resource r = resources[lineCount + i];
319 RelativeReference ref = ElementIdentification.getConnectorIdentifier(g, r);
320 terminalIdentifiers[i] = ref.path;
328 public String toString() {
329 StringBuilder b = new StringBuilder();
334 public void toString(StringBuilder b) {
337 b.append(terminalCount);
338 for(int i=0;i<terminalCount;++i) {
340 b.append(terminalIdentifiers[i]);
343 b.append(links.length/2);
348 for(Modi modi : modis) {
354 public boolean resolveResources(ReadGraph g, Resource model) throws DatabaseException {
355 DiagramResource DIA = DiagramResource.getInstance(g);
356 StructuralResource2 STR = StructuralResource2.getInstance(g);
358 // --- Resolve connectors ---------------------------------------------
360 Resource connection = null;
362 ArrayList<List<Resource>> connectorCandidates = new ArrayList<List<Resource>>(terminalCount);
363 for(int i=0;i<terminalCount;++i)
364 connectorCandidates.add(ElementIdentification.resolveConnector(g, model, terminalIdentifiers[i]));
365 for(List<Resource> connectors : connectorCandidates) {
366 if(connectors.isEmpty())
368 if(connection == null && connectors.size() == 1) {
369 for(Resource temp : g.getObjects(connectors.get(0), STR.Connects))
370 if(g.isInstanceOf(temp, DIA.Connection)) {
376 if(connection == null)
378 loop: for(int i=0;i<terminalCount;++i) {
379 for(Resource connector : connectorCandidates.get(i))
380 if(g.hasStatement(connector, STR.Connects, connection)) {
381 resources[lineCount + i] = connector;
388 if(lineCount != g.getObjects(connection, DIA.HasInteriorRouteNode).size())
390 if(terminalCount != g.getObjects(connection, STR.IsConnectedTo).size())
396 // --- Resolve route lines --------------------------------------------
398 // Create inverse map for resources
399 TObjectIntHashMap<Resource> invResources = new TObjectIntHashMap<Resource>();
400 for(int i=0;i<terminalCount;++i) {
401 int id = lineCount + i;
402 invResources.put(resources[id], id);
405 // Create neighbor indices
406 final TIntHashSet[] neighbors = new TIntHashSet[terminalCount + lineCount];
407 for(int i=0;i<neighbors.length;++i)
408 neighbors[i] = new TIntHashSet();
409 for(int i=0;i<links.length;i+=2) {
412 if(resources[b] == null)
414 if(resources[a] == null)
419 TIntArrayList stack = new TIntArrayList();
420 TIntArrayList backlog = new TIntArrayList();
421 for(int i=0;i<terminalCount;++i)
422 stack.add(lineCount+i);
424 // Resolve route lines
425 int oldResolvedCount = 0;
426 while(invResources.size() < resources.length) {
427 oldResolvedCount = invResources.size();
428 while(!stack.isEmpty()) {
429 int id = stack.removeAt(stack.size()-1);
430 TIntHashSet ns = neighbors[id];
431 for(int n : ns.toArray())
432 if(resources[n] != null)
436 else if(ns.size() == 1) {
438 for(Resource r : g.getObjects(resources[id], DIA.AreConnected))
439 if(!invResources.containsKey(r)) {
447 final int newId = ns.iterator().next();
448 resources[newId] = det;
449 invResources.put(det, newId);
456 if(oldResolvedCount == invResources.size())
457 return false; // No progress happened
459 // Reverse backlog and swap stack and backlog
463 TIntArrayList temp = stack;
472 public Resource getConnection(ReadGraph g) throws DatabaseException {
473 if(connection == null) {
474 DiagramResource DIA = DiagramResource.getInstance(g);
476 connection = g.getSingleObject(resources[0], DIA.HasInteriorRouteNode_Inverse);
479 StructuralResource2 STR = StructuralResource2.getInstance(g);
480 for(Resource temp : g.getObjects(resources[0], STR.Connects))
481 if(g.isInstanceOf(temp, DIA.Connection)) {
490 public void runUpdates(WriteGraph g) throws DatabaseException {
491 DiagramResource DIA = DiagramResource.getInstance(g);
492 Layer0 L0 = Layer0.getInstance(g);
494 for(Modi modi_ : modis) {
495 if(modi_ instanceof UpdateLine) {
496 UpdateLine modi = (UpdateLine)modi_;
497 Resource routeLine = resources[modi.id];
498 g.claimLiteral(routeLine, DIA.HasPosition, modi.position);
499 g.claimLiteral(routeLine, DIA.IsHorizontal, modi.isHorizontal);
501 else if(modi_ instanceof RemoveLink) {
502 RemoveLink modi = (RemoveLink)modi_;
503 g.denyStatement(resources[modi.a], DIA.AreConnected, resources[modi.b]);
505 else if(modi_ instanceof RemoveLine) {
506 RemoveLine modi = (RemoveLine)modi_;
507 g.deny(resources[modi.a]);
509 else if(modi_ instanceof CreateLink) {
510 CreateLink modi = (CreateLink)modi_;
511 g.claim(resources[modi.a], DIA.AreConnected, resources[modi.b]);
513 else if(modi_ instanceof CreateLine) {
514 CreateLine modi = (CreateLine)modi_;
515 Resource routeLine = g.newResource();
516 g.claim(routeLine, L0.InstanceOf, DIA.RouteLine);
517 g.claimLiteral(routeLine, DIA.HasPosition, modi.position);
518 g.claimLiteral(routeLine, DIA.IsHorizontal, modi.isHorizontal);
519 g.claim(getConnection(g), DIA.HasInteriorRouteNode, routeLine);
520 int id = resources.length;
521 resources = Arrays.copyOf(resources, id+1);
522 resources[id] = routeLine;
524 else if(modi_ instanceof Split) {
525 Split modi = (Split)modi_;
526 RouteGraphConnectionSplitter splitter = new RouteGraphConnectionSplitter(g);
527 splitter.doSplit(g, connection,
528 toResources(modi.interface1),
529 toResources(modi.interface2),
530 toResources(modi.lines2),
531 toResources(modi.terminals1),
532 toResources(modi.terminals2),
541 public TObjectIntHashMap<RouteNode> getIdMap() {
545 public int[] toIds(ArrayList<Resource> rs) {
546 TObjectIntHashMap<Resource> rmap = new TObjectIntHashMap<Resource>();
547 for(int i=0;i<resources.length;++i)
548 rmap.put(resources[i], i);
550 int[] result = new int[rs.size()];
551 for(int i=0;i<rs.size();++i)
552 result[i] = rmap.get(rs.get(i));
557 public ArrayList<Resource> toResources(int[] ids) {
558 ArrayList<Resource> result = new ArrayList<Resource>(ids.length);
560 result.add(resources[id]);