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