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;
166 boolean invertFlagRotation;
170 public Split(int[] interface1, int[] interface2, int[] lines2,
171 int[] terminals1, int[] terminals2, boolean isHorizontal,
172 boolean invertFlagRotation,
173 double isectX, double isectY) {
174 this.interface1 = interface1;
175 this.interface2 = interface2;
176 this.lines2 = lines2;
177 this.terminals1 = terminals1;
178 this.terminals2 = terminals2;
179 this.isHorizontal = isHorizontal;
180 this.invertFlagRotation = invertFlagRotation;
181 this.isectX = isectX;
182 this.isectY = isectY;
185 public Split(String text) {
186 List<String> parts = Arrays.asList(text.split("\\$"));
187 Iterator<String> it = parts.iterator();
188 this.interface1 = readInts(it);
189 this.interface2 = readInts(it);
190 this.lines2 = readInts(it);
191 this.terminals1 = readInts(it);
192 this.terminals2 = readInts(it);
193 this.isHorizontal = Boolean.parseBoolean(it.next());
194 this.invertFlagRotation = Boolean.parseBoolean(it.next());
195 this.isectX = Double.parseDouble(it.next());
196 this.isectY = Double.parseDouble(it.next());
200 public String toString() {
201 StringBuilder b = new StringBuilder();
203 write(b, interface1);
205 write(b, interface2);
209 write(b, terminals1);
211 write(b, terminals2);
213 b.append(isHorizontal);
215 b.append(invertFlagRotation);
224 private static void write(StringBuilder b, int[] ids) {
225 b.append(ids.length);
232 private static int[] readInts(Iterator<String> it) {
233 int length = Integer.parseInt(it.next());
234 int[] result = new int[length];
235 for(int i=0;i<length;++i)
236 result[i] = Integer.parseInt(it.next());
240 public void addModi(Modi modi) {
244 public RouteGraphModification(String text) {
245 String[] parts = text.split(",");
247 lineCount = Integer.parseInt(parts[pos++]);
248 terminalCount = Integer.parseInt(parts[pos++]);
249 terminalIdentifiers = new String[terminalCount];
250 for(int i=0;i<terminalCount;++i) {
251 terminalIdentifiers[i] = parts[pos++];
253 int linkCount = Integer.parseInt(parts[pos++]);
254 links = new int[2*linkCount];
255 for(int i=0;i<links.length;++i)
256 links[i] = Integer.parseInt(parts[pos++]);
257 while(pos < parts.length) {
258 String part = parts[pos++];
259 char first = part.charAt(0);
260 part = part.substring(1);
262 case 'M': addModi(new UpdateLine(part)); break;
263 case 'R': addModi(new RemoveLine(part)); break;
264 case 'r': addModi(new RemoveLink(part)); break;
265 case 'C': addModi(new CreateLine(part)); break;
266 case 'c': addModi(new CreateLink(part)); break;
267 case 'S': addModi(new Split(part)); break;
270 resources = new Resource[lineCount + terminalCount];
273 public RouteGraphModification(SerialisationSupport ser, RouteGraph rg) throws DatabaseException {
274 Collection<RouteLine> lines = rg.getLines();
275 lineCount = lines.size();
276 Collection<RouteTerminal> terminals = rg.getTerminals();
277 terminalCount = terminals.size();
279 resources = new Resource[lineCount + terminalCount];
281 THashSet<RouteLink> linkSet = new THashSet<RouteLink>();
282 idMap = new TObjectIntHashMap<RouteNode>();
285 for(RouteLine line : lines) {
287 resources[i] = ser.getResource((Long)line.getData());
288 for(RoutePoint rp : line.getPoints()) {
289 if(rp instanceof RouteLink) {
290 RouteLink link = (RouteLink)rp;
291 if(!link.getA().isTransient() &&
292 !link.getA().isTransient())
298 for(RouteTerminal terminal : terminals) {
299 idMap.put(terminal, i);
300 resources[i] = ser.getResource((Long)terminal.getData());
303 if(rg.isSimpleConnection()) {
304 links = new int[] {0, 1};
307 links = new int[2*(terminalCount + linkSet.size())];
309 for(RouteLink link : linkSet) {
310 links[i++] = idMap.get(link.getA());
311 links[i++] = idMap.get(link.getB());
313 for(RouteTerminal terminal : terminals) {
314 links[i++] = idMap.get(terminal);
315 links[i++] = idMap.get(terminal.getLine());
320 public Resource findTerminalIdentifiers(ReadGraph g) throws DatabaseException {
321 Resource base = null;
322 terminalIdentifiers = new String[terminalCount];
323 for(int i=0;i<terminalCount;++i) {
324 Resource r = resources[lineCount + i];
325 RelativeReference ref = ElementIdentification.getConnectorIdentifier(g, r);
326 terminalIdentifiers[i] = ref.path;
334 public String toString() {
335 StringBuilder b = new StringBuilder();
340 public void toString(StringBuilder b) {
343 b.append(terminalCount);
344 for(int i=0;i<terminalCount;++i) {
346 b.append(terminalIdentifiers[i]);
349 b.append(links.length/2);
354 for(Modi modi : modis) {
360 public boolean resolveResources(ReadGraph g, Resource model) throws DatabaseException {
361 DiagramResource DIA = DiagramResource.getInstance(g);
362 StructuralResource2 STR = StructuralResource2.getInstance(g);
364 // --- Resolve connectors ---------------------------------------------
366 Resource connection = null;
368 ArrayList<List<Resource>> connectorCandidates = new ArrayList<List<Resource>>(terminalCount);
369 for(int i=0;i<terminalCount;++i)
370 connectorCandidates.add(ElementIdentification.resolveConnector(g, model, terminalIdentifiers[i]));
371 for(List<Resource> connectors : connectorCandidates) {
372 if(connectors.isEmpty())
374 if(connection == null && connectors.size() == 1) {
375 for(Resource temp : g.getObjects(connectors.get(0), STR.Connects))
376 if(g.isInstanceOf(temp, DIA.Connection)) {
382 if(connection == null)
384 loop: for(int i=0;i<terminalCount;++i) {
385 for(Resource connector : connectorCandidates.get(i))
386 if(g.hasStatement(connector, STR.Connects, connection)) {
387 resources[lineCount + i] = connector;
394 if(lineCount != g.getObjects(connection, DIA.HasInteriorRouteNode).size())
396 if(terminalCount != g.getObjects(connection, STR.IsConnectedTo).size())
402 // --- Resolve route lines --------------------------------------------
404 // Create inverse map for resources
405 TObjectIntHashMap<Resource> invResources = new TObjectIntHashMap<Resource>();
406 for(int i=0;i<terminalCount;++i) {
407 int id = lineCount + i;
408 invResources.put(resources[id], id);
411 // Create neighbor indices
412 final TIntHashSet[] neighbors = new TIntHashSet[terminalCount + lineCount];
413 for(int i=0;i<neighbors.length;++i)
414 neighbors[i] = new TIntHashSet();
415 for(int i=0;i<links.length;i+=2) {
418 if(resources[b] == null)
420 if(resources[a] == null)
425 TIntArrayList stack = new TIntArrayList();
426 TIntArrayList backlog = new TIntArrayList();
427 for(int i=0;i<terminalCount;++i)
428 stack.add(lineCount+i);
430 // Resolve route lines
431 int oldResolvedCount = 0;
432 while(invResources.size() < resources.length) {
433 oldResolvedCount = invResources.size();
434 while(!stack.isEmpty()) {
435 int id = stack.removeAt(stack.size()-1);
436 TIntHashSet ns = neighbors[id];
437 for(int n : ns.toArray())
438 if(resources[n] != null)
442 else if(ns.size() == 1) {
444 for(Resource r : g.getObjects(resources[id], DIA.AreConnected))
445 if(!invResources.containsKey(r)) {
453 final int newId = ns.iterator().next();
454 resources[newId] = det;
455 invResources.put(det, newId);
462 if(oldResolvedCount == invResources.size())
463 return false; // No progress happened
465 // Reverse backlog and swap stack and backlog
469 TIntArrayList temp = stack;
478 public Resource getConnection(ReadGraph g) throws DatabaseException {
479 if(connection == null) {
480 DiagramResource DIA = DiagramResource.getInstance(g);
482 connection = g.getSingleObject(resources[0], DIA.HasInteriorRouteNode_Inverse);
485 StructuralResource2 STR = StructuralResource2.getInstance(g);
486 for(Resource temp : g.getObjects(resources[0], STR.Connects))
487 if(g.isInstanceOf(temp, DIA.Connection)) {
496 public void runUpdates(WriteGraph g) throws DatabaseException {
497 DiagramResource DIA = DiagramResource.getInstance(g);
498 Layer0 L0 = Layer0.getInstance(g);
500 for(Modi modi_ : modis) {
501 if(modi_ instanceof UpdateLine) {
502 UpdateLine modi = (UpdateLine)modi_;
503 Resource routeLine = resources[modi.id];
504 g.claimLiteral(routeLine, DIA.HasPosition, modi.position);
505 g.claimLiteral(routeLine, DIA.IsHorizontal, modi.isHorizontal);
507 else if(modi_ instanceof RemoveLink) {
508 RemoveLink modi = (RemoveLink)modi_;
509 g.denyStatement(resources[modi.a], DIA.AreConnected, resources[modi.b]);
511 else if(modi_ instanceof RemoveLine) {
512 RemoveLine modi = (RemoveLine)modi_;
513 g.deny(resources[modi.a]);
515 else if(modi_ instanceof CreateLink) {
516 CreateLink modi = (CreateLink)modi_;
517 g.claim(resources[modi.a], DIA.AreConnected, resources[modi.b]);
519 else if(modi_ instanceof CreateLine) {
520 CreateLine modi = (CreateLine)modi_;
521 Resource routeLine = g.newResource();
522 g.claim(routeLine, L0.InstanceOf, DIA.RouteLine);
523 g.claimLiteral(routeLine, DIA.HasPosition, modi.position);
524 g.claimLiteral(routeLine, DIA.IsHorizontal, modi.isHorizontal);
525 g.claim(getConnection(g), DIA.HasInteriorRouteNode, routeLine);
526 int id = resources.length;
527 resources = Arrays.copyOf(resources, id+1);
528 resources[id] = routeLine;
530 else if(modi_ instanceof Split) {
531 Split modi = (Split)modi_;
532 RouteGraphConnectionSplitter splitter = new RouteGraphConnectionSplitter(g);
533 splitter.doSplit(g, connection,
534 toResources(modi.interface1),
535 toResources(modi.interface2),
536 toResources(modi.lines2),
537 toResources(modi.terminals1),
538 toResources(modi.terminals2),
540 modi.invertFlagRotation,
548 public TObjectIntHashMap<RouteNode> getIdMap() {
552 public int[] toIds(ArrayList<Resource> rs) {
553 TObjectIntHashMap<Resource> rmap = new TObjectIntHashMap<Resource>();
554 for(int i=0;i<resources.length;++i)
555 rmap.put(resources[i], i);
557 int[] result = new int[rs.size()];
558 for(int i=0;i<rs.size();++i)
559 result[i] = rmap.get(rs.get(i));
564 public ArrayList<Resource> toResources(int[] ids) {
565 ArrayList<Resource> result = new ArrayList<Resource>(ids.length);
567 result.add(resources[id]);