]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/content/ConnectionUtil.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / content / ConnectionUtil.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.diagram.content;\r
13 \r
14 import java.awt.geom.AffineTransform;\r
15 import java.awt.geom.Line2D;\r
16 import java.awt.geom.Point2D;\r
17 import java.util.ArrayList;\r
18 import java.util.Collection;\r
19 import java.util.Collections;\r
20 import java.util.HashSet;\r
21 import java.util.Iterator;\r
22 import java.util.List;\r
23 import java.util.Set;\r
24 import java.util.Stack;\r
25 \r
26 import org.simantics.databoard.Bindings;\r
27 import org.simantics.db.ReadGraph;\r
28 import org.simantics.db.Resource;\r
29 import org.simantics.db.Statement;\r
30 import org.simantics.db.WriteGraph;\r
31 import org.simantics.db.WriteOnlyGraph;\r
32 import org.simantics.db.common.CommentMetadata;\r
33 import org.simantics.db.common.request.IndexRoot;\r
34 import org.simantics.db.common.utils.NameUtils;\r
35 import org.simantics.db.common.utils.OrderedSetUtils;\r
36 import org.simantics.db.exception.AssumptionException;\r
37 import org.simantics.db.exception.DatabaseException;\r
38 import org.simantics.db.exception.ValidationException;\r
39 import org.simantics.db.layer0.adapter.impl.EntityRemover;\r
40 import org.simantics.db.layer0.util.RemoverUtil;\r
41 import org.simantics.diagram.connection.ConnectionSegmentEnd;\r
42 import org.simantics.diagram.stubs.DiagramResource;\r
43 import org.simantics.diagram.synchronization.graph.BasicResources;\r
44 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
45 import org.simantics.g2d.connection.handler.ConnectionHandler;\r
46 import org.simantics.g2d.diagram.handler.PickRequest.PickFilter;\r
47 import org.simantics.g2d.diagram.handler.Topology.Terminal;\r
48 import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil.TerminalInfo;\r
49 import org.simantics.g2d.element.ElementUtils;\r
50 import org.simantics.g2d.element.IElement;\r
51 import org.simantics.g2d.element.handler.BendsHandler;\r
52 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
53 import org.simantics.g2d.element.handler.impl.BranchPointTerminal;\r
54 import org.simantics.g2d.elementclass.BranchPoint;\r
55 import org.simantics.g2d.elementclass.BranchPoint.Direction;\r
56 import org.simantics.layer0.Layer0;\r
57 import org.simantics.modeling.ModelingResources;\r
58 import org.simantics.scenegraph.utils.GeometryUtils;\r
59 import org.simantics.scl.commands.Commands;\r
60 import org.simantics.structural.stubs.StructuralResource2;\r
61 import org.simantics.structural2.modelingRules.CPConnection;\r
62 import org.simantics.structural2.modelingRules.CPConnectionJoin;\r
63 import org.simantics.structural2.modelingRules.CPTerminal;\r
64 import org.simantics.structural2.modelingRules.IConnectionPoint;\r
65 import org.simantics.utils.Development;\r
66 import org.simantics.utils.datastructures.Pair;\r
67 import org.simantics.utils.datastructures.Triple;\r
68 import org.simantics.utils.ui.AdaptionUtils;\r
69 \r
70 /**\r
71  * @author Tuukka Lehtonen\r
72  */\r
73 public final class ConnectionUtil {\r
74 \r
75     private final ReadGraph rg;\r
76     private final WriteGraph g;\r
77     private final BasicResources br;\r
78 \r
79     /**\r
80      * Construct utility with read-only support.\r
81      * @param g\r
82      */\r
83     public ConnectionUtil(ReadGraph g) {\r
84         this.rg = g;\r
85         this.g = (g instanceof WriteGraph) ? (WriteGraph) g : null;\r
86         this.br = BasicResources.getInstance(g);\r
87     }\r
88 \r
89     /**\r
90      * Construct utility with read-write support.\r
91      * @param g\r
92      */\r
93     public ConnectionUtil(WriteGraph g) {\r
94         this.rg = g;\r
95         this.g = g;\r
96         this.br = BasicResources.getInstance(g);\r
97     }\r
98 \r
99     void assertWriteSupport() {\r
100         if (g == null)\r
101             throw new UnsupportedOperationException("no write support, this ConnectionUtil is read-only");\r
102     }\r
103 \r
104     /**\r
105      * Creates a new connection element of the specified type and attaches it to\r
106      * the specified diagram composite.\r
107      * \r
108      * @param composite diagram composite\r
109      * @param type connection element type\r
110      * @return created connection\r
111      * @throws DatabaseException\r
112      */\r
113     public Resource newConnection(Resource composite, Resource type) throws DatabaseException {\r
114         assertWriteSupport();\r
115         Resource connection = newConnection(type);\r
116         // By default, add connections to the front of the diagram since most\r
117         // often it is visually the expected result.\r
118         OrderedSetUtils.addFirst(g, composite, connection);\r
119         g.claim(composite, br.L0.ConsistsOf, br.L0.PartOf, connection);\r
120         return connection;\r
121     }\r
122 \r
123     /**\r
124      * Creates a new connection element of the specified type without attaching\r
125      * it to any diagram composite.\r
126      * \r
127      * @param type connection element type\r
128      * @return created connection\r
129      * @throws DatabaseException\r
130      */\r
131     public Resource newConnection(Resource type) throws DatabaseException {\r
132         assertWriteSupport();\r
133         return newInstance(g, type);\r
134     }\r
135 \r
136     /**\r
137      * Creates a new element terminal connector (DiagramResource) for the specified connector\r
138      * @param connection\r
139      * @param hasConnector\r
140      * @return\r
141      * @throws DatabaseException\r
142      */\r
143     public Resource newConnector(Resource connection, Resource hasConnector) throws DatabaseException {\r
144         assertWriteSupport();\r
145         Resource connector = newInstance(g, br.DIA.Connector);\r
146         g.claim(connection, hasConnector, connector);\r
147         return connector;\r
148     }\r
149 \r
150     public Resource newBranchPoint(Resource connection, AffineTransform tr) throws DatabaseException {\r
151         return newBranchPoint(connection, tr, null);\r
152     }\r
153 \r
154     public Resource newBranchPoint(Resource connection, AffineTransform tr, Direction direction) throws DatabaseException {\r
155         assertWriteSupport();\r
156         Resource bp = g.newResource();\r
157         g.claim(bp, br.L0.InstanceOf, null, br.DIA.BranchPoint);\r
158         if (tr != null) {\r
159             double[] mat = new double[6];\r
160             tr.getMatrix(mat);\r
161             Resource transform = g.newResource();\r
162             g.claim(transform, br.L0.InstanceOf, null, br.G2D.Transform);\r
163             g.claimValue(transform, mat);\r
164             g.claim(bp, br.DIA.HasTransform, transform);\r
165         }\r
166         Resource tag = toDirectionTag(g, direction);\r
167         if (tag != null) {\r
168             g.claim(bp, tag, tag, bp);\r
169         }\r
170         g.claim(connection, br.DIA.HasBranchPoint, bp);\r
171         return bp;\r
172     }\r
173 \r
174     /**\r
175      * @param connection\r
176      * @param position\r
177      * @param isHorizontal\r
178      * @return\r
179      * @throws DatabaseException\r
180      */\r
181     public Resource newRouteLine(Resource connection, Double position, Boolean isHorizontal) throws DatabaseException {\r
182         assertWriteSupport();\r
183         Resource rl = newInstance(g, br.DIA.RouteLine);\r
184         if (position != null) {\r
185             g.addLiteral(rl, br.DIA.HasPosition, br.DIA.HasPosition_Inverse, br.L0.Double, position, Bindings.DOUBLE);\r
186         }\r
187         if (isHorizontal != null) {\r
188             g.addLiteral(rl, br.DIA.IsHorizontal, br.DIA.IsHorizontal_Inverse, br.L0.Boolean, isHorizontal, Bindings.BOOLEAN);\r
189         }\r
190         g.claim(connection, br.DIA.HasInteriorRouteNode, br.DIA.HasInteriorRouteNode_Inverse, rl);\r
191         return rl;\r
192     }\r
193 \r
194     ConnectionSegmentEnd getTerminalType(Terminal terminal, ConnectionSegmentEnd defaultValue) {\r
195         ConnectionSegmentEnd type = getTerminalType(terminal);\r
196         return type != null ? type : defaultValue;\r
197     }\r
198 \r
199     ConnectionSegmentEnd getTerminalType(Terminal t) {\r
200         if (t == null)\r
201             return null;\r
202 \r
203         if (t instanceof ResourceTerminal) {\r
204             return ConnectionSegmentEnd.CONNECTOR;\r
205         } else if (t instanceof BranchPointTerminal) {\r
206             return ConnectionSegmentEnd.BRANCH;\r
207         } else {\r
208             throw new IllegalArgumentException("unsupported terminal '" + t + "'");\r
209         }\r
210     }\r
211 \r
212     Resource resolveTerminalRelation(ReadGraph g, Terminal t) throws DatabaseException {\r
213         if (t == null)\r
214             return null;\r
215         if (t instanceof ResourceTerminal) {\r
216             ResourceTerminal rt = (ResourceTerminal) t;\r
217             Resource terminalRelation = DiagramGraphUtil.getConnectionPointOfTerminal(g, rt.getResource());\r
218             if (!g.isSubrelationOf(terminalRelation, br.STR.IsConnectedTo)) {\r
219                 // debug...\r
220             }\r
221             return terminalRelation;\r
222         } else {\r
223             throw new IllegalArgumentException("unsupported terminal '" + t + "' for terminal relation resolution");\r
224         }\r
225     }\r
226 \r
227     public Resource toHasConnectorRelation(EdgeEnd end) {\r
228         switch (end) {\r
229             case Begin: return br.DIA.HasPlainConnector;\r
230             case End: return br.DIA.HasArrowConnector;\r
231             default: throw new IllegalArgumentException("unsupported edge end: " + end);\r
232         }\r
233     }\r
234 \r
235     public EdgeEnd toEdgeEnd(Resource attachmentRelation, EdgeEnd defaultValue) throws DatabaseException {\r
236         if (g.isSubrelationOf(attachmentRelation, br.DIA.HasPlainConnector))\r
237             return EdgeEnd.Begin;\r
238         if (g.isSubrelationOf(attachmentRelation, br.DIA.HasArrowConnector))\r
239             return EdgeEnd.End;\r
240         return defaultValue;\r
241     }\r
242 \r
243     public Resource getAttachmentRelationForConnector(Resource connector) throws DatabaseException {\r
244         Statement connection = g.getPossibleStatement(connector, br.DIA.IsConnectorOf);\r
245         if (connection == null)\r
246             return null;\r
247         Resource attachment = g.getInverse(connection.getPredicate());\r
248         return attachment;\r
249     }\r
250 \r
251     /**\r
252      * @param connection\r
253      * @param node\r
254      * @param c\r
255      * @param end\r
256      * @param attachmentRelation the relation used for attaching the connector to the connector\r
257      * @return\r
258      * @throws DatabaseException\r
259      */\r
260     public Resource getOrCreateConnector(Resource connection, Resource node, Terminal terminal, EdgeEnd end, Resource attachmentRelation) throws DatabaseException {\r
261         assertWriteSupport();\r
262         ConnectionSegmentEnd connectorType = getTerminalType(terminal, null);\r
263         if (connectorType == null)\r
264             throw new AssumptionException("Invalid connection node", connection, node);\r
265 \r
266         switch (connectorType) {\r
267             case BRANCH:\r
268                 // NOTE: should we ensure here that (connection, br.dr.HasBranchPoint, node) exists?\r
269                 return node;\r
270 \r
271             case CONNECTOR: {\r
272                 Resource terminalRelation = resolveTerminalRelation(g, terminal);\r
273 \r
274                 if (attachmentRelation == null)\r
275                     attachmentRelation = toHasConnectorRelation(end);\r
276 \r
277                 if (!g.isSubrelationOf(attachmentRelation, br.DIA.HasConnector))\r
278                     throw new AssumptionException("attachment relation not a subrelation of Has Connector", attachmentRelation);\r
279 \r
280                 // Create new connector for the specified node terminal\r
281                 Resource terminalConnector = newConnector(connection, attachmentRelation);\r
282                 g.claim(node, terminalRelation, terminalConnector);\r
283                 return terminalConnector;\r
284             }\r
285             default:\r
286                 throw new Error("this should be unreachable code");\r
287         }\r
288     }\r
289 \r
290     public void connect(Resource connector1, Resource connector2) throws DatabaseException {\r
291         assertWriteSupport();\r
292         g.claim(connector1, br.DIA.AreConnected, br.DIA.AreConnected, connector2);\r
293     }\r
294 \r
295     public void disconnect(Resource connector1, Resource connector2) throws DatabaseException {\r
296         assertWriteSupport();\r
297         g.denyStatement(connector1, br.DIA.AreConnected, connector2);\r
298     }\r
299 \r
300     public void disconnectFromAllRouteNodes(Resource connector) throws DatabaseException {\r
301         assertWriteSupport();\r
302         g.deny(connector, br.DIA.AreConnected);\r
303     }\r
304 \r
305     public void disconnect(EdgeResource segment) throws DatabaseException {\r
306         assertWriteSupport();\r
307         disconnect(segment.first(), segment.second());\r
308     }\r
309 \r
310     public boolean isConnected(Resource connector) throws DatabaseException {\r
311         return rg.hasStatement(connector, br.DIA.AreConnected);\r
312     }\r
313 \r
314     public boolean isConnected(Resource connector, Resource toConnector) throws DatabaseException {\r
315         return rg.hasStatement(connector, br.DIA.AreConnected, toConnector);\r
316     }\r
317 \r
318     public boolean isConnectionEmpty(Resource connection) throws DatabaseException {\r
319         return !rg.hasStatement(connection, br.DIA.HasConnector)\r
320                 && !rg.hasStatement(connection, br.DIA.HasInteriorRouteNode);\r
321     }\r
322 \r
323     private void removeConnectorOrBranchPoint(Resource connectorOrBranchPoint) throws DatabaseException {\r
324 \r
325 //        // Handle correct removal of route points\r
326 //        if(g.isInstanceOf(connectorOrBranchPoint, dr.BranchPoint)) {\r
327 //            Collection<Resource> connectedConnectors = g.getObjects(connectorOrBranchPoint, dr.AreConnected);\r
328 //            if(connectedConnectors.size() == 2) {\r
329 //                Iterator<Resource> it = connectedConnectors.iterator();\r
330 //                g.claim(it.next(), dr.AreConnected, it.next());\r
331 //            }\r
332 //        }\r
333 \r
334         g.deny(connectorOrBranchPoint, br.DIA.AreConnected);\r
335 \r
336         // Removes both the terminal relation and the HasConnector relation\r
337         // to the :Connection\r
338         g.deny(connectorOrBranchPoint, br.STR.Connects);\r
339 \r
340         // If this is a branch point/route node, remove it from the connection too.\r
341         g.deny(connectorOrBranchPoint, br.DIA.IsBranchPointOf);\r
342         g.deny(connectorOrBranchPoint, br.DIA.HasInteriorRouteNode_Inverse);\r
343     }\r
344 \r
345     /**\r
346      * Removes a complete connection along with all its branch points and terminal connectors.\r
347      * \r
348      * @param connection the connection to remove\r
349      */\r
350     public void removeConnection(Resource connection) throws DatabaseException {\r
351         assertWriteSupport();\r
352 \r
353         // Add comment to change set.\r
354         CommentMetadata cm = g.getMetadata(CommentMetadata.class);\r
355         g.addMetadata(cm.add("Remove connection " + connection));\r
356 \r
357         // 1. Get all connectors/branch points\r
358         Collection<Resource> connectors = new ArrayList<Resource>();\r
359         connectors.addAll(rg.getObjects(connection, br.DIA.HasConnector));\r
360         connectors.addAll(rg.getObjects(connection, br.DIA.HasInteriorRouteNode));\r
361 \r
362         // 2. Remove all connectors/branch points\r
363         for (Resource connector : connectors) {\r
364             removeConnectorOrBranchPoint(connector);\r
365             RemoverUtil.remove(g, connector);\r
366         }\r
367 \r
368         // 3. Remove whole connection\r
369         for (Resource owner : OrderedSetUtils.getOwnerLists(g, connection, br.DIA.Diagram))\r
370             OrderedSetUtils.remove(g, owner, connection);\r
371         EntityRemover.remove(g, connection);\r
372     }\r
373 \r
374     /**\r
375      * Removes a single connector part from the graph. A connection part can be\r
376      * either a branch point or a terminal connector.\r
377      * \r
378      * @param connectorOrBranchPoint\r
379      * @throws DatabaseException\r
380      */\r
381     public void removeConnectionPart(Resource connectorOrBranchPoint) throws DatabaseException {\r
382         removeConnectorOrBranchPoint(connectorOrBranchPoint);\r
383         RemoverUtil.remove(g, connectorOrBranchPoint);\r
384     }\r
385 \r
386     /**\r
387      * Removes the specified connection segment. Checks that both ends of the\r
388      * edge segment are part of to the same connection. Steps taken:\r
389      * <ul>\r
390      * <li>Minimally this only disconnects the connection segment ends from each\r
391      * other and nothing more.</li>\r
392      * <li>After disconnecting, we check whether the segment ends are still\r
393      * connected to something. If not, the :Connector/:BranchPoint at the\r
394      * segment's end is destroyed and detached from the connection entity.</li>\r
395      * <li>Finally, if the connection entity is empty (has no connectors/branch\r
396      * points), it is also destroyed and removed from the diagram.</li>\r
397      * \r
398      * @param segment the connection segment to remove\r
399      */\r
400     public void remove(EdgeResource segment) throws DatabaseException {\r
401         remove(segment, false);\r
402     }\r
403 \r
404     /**\r
405      * Removes the specified connection segment. Steps taken:\r
406      * <ul>\r
407      * <li>Minimally this only disconnects the connection segment ends from each\r
408      * other and nothing more.</li>\r
409      * <li>After disconnecting, we check whether the segment ends are still\r
410      * connected to something. If not, the :Connector/:BranchPoint at the\r
411      * segment's end is destroyed and detached from the connection entity.</li>\r
412      * <li>Finally, if the connection entity is empty (has no connectors/branch\r
413      * points), it is also destroyed and removed from the diagram.</li>\r
414      * \r
415      * @param segment the connection segment to remove\r
416      * @param unchecked <code>false</code> to check that both ends of the\r
417      *        segment are part of the same connection before removing,\r
418      *        <code>true</code> to just remove the segment without checking\r
419      *        this. Using <code>true</code> may help in cases where the\r
420      *        connection model has become corrupted for some reason, e.g. the\r
421      *        other end of the edge has lost its link to the connection while\r
422      *        the other has not,\r
423      */\r
424     public void remove(EdgeResource segment, boolean unchecked) throws DatabaseException {\r
425         assertWriteSupport();\r
426 \r
427         if (!unchecked) {\r
428             @SuppressWarnings("unused")\r
429             Resource connection = getConnection(g, segment);\r
430         }\r
431 \r
432         // 1. disconnect segment ends\r
433         disconnect(segment);\r
434 \r
435         // 2. Remove connectors/branch points if they become fully disconnected\r
436         if (!isConnected(segment.first())) {\r
437             removeConnectorOrBranchPoint(segment.first());\r
438         }\r
439         if (!isConnected(segment.second())) {\r
440             removeConnectorOrBranchPoint(segment.second());\r
441         }\r
442 \r
443         // 3. Remove whole connection entity if it becomes empty\r
444 //        if (isConnectionEmpty(connection)) {\r
445 //            for (Resource owner : OrderedSetUtils.getOwnerLists(g, connection, dr.Diagram))\r
446 //                OrderedSetUtils.remove(g, owner, connection);\r
447 //            RemoverUtil.remove(g, connection);\r
448 //        }\r
449 \r
450     }\r
451 \r
452     /**\r
453      * Removes all DIA.Connector instances from the specified connection that\r
454      * are not used for anything.\r
455      * \r
456      * @param connection connection to examine\r
457      * @return the amount of unused connectors removed\r
458      */\r
459     public int removeUnusedConnectors(Resource connection) throws DatabaseException {\r
460         int result = 0;\r
461         for (Resource connector : getConnectors(connection, null)) {\r
462             if (!g.getObjects(connector, br.DIA.AreConnected).isEmpty())\r
463                 continue;\r
464             Collection<Resource> connects = g.getObjects(connector, br.STR.Connects);\r
465             if (connects.size() > 1)\r
466                 continue;\r
467 \r
468             removeConnectionPart(connector);\r
469             ++result;\r
470         }\r
471         return result;\r
472     }\r
473 \r
474     /**\r
475      * Removes all DIA.InteriorRouteNode instances from the specified connection that\r
476      * are not used for anything, i.e. connect to less than 2 other route nodes.\r
477      * \r
478      * @param connection connection to examine\r
479      * @return the amount of unused connectors removed\r
480      */\r
481     public int removeExtraInteriorRouteNodes(Resource connection) throws DatabaseException {\r
482         int result = 0;\r
483         for (Resource interiorRouteNode : g.getObjects(connection, br.DIA.HasInteriorRouteNode)) {\r
484             Collection<Resource> connectedTo = g.getObjects(interiorRouteNode, br.DIA.AreConnected);\r
485             if (connectedTo.size() > 1)\r
486                 continue;\r
487 \r
488             removeConnectionPart(interiorRouteNode);\r
489             ++result;\r
490         }\r
491         return result;\r
492     }\r
493 \r
494     /**\r
495      * Splits the specified connection segment by adding a new branch point in\r
496      * between the segment ends.\r
497      * \r
498      * @param segment\r
499      * @return the branch (route) point created by the split operation.\r
500      */\r
501     public Resource split(EdgeResource segment, AffineTransform splitPos) throws DatabaseException {\r
502         assertWriteSupport();\r
503 \r
504         Resource connection = getConnection(g, segment);\r
505         disconnect(segment);\r
506         Resource bp = newBranchPoint(connection, splitPos);\r
507         connect(segment.first(), bp);\r
508         connect(bp, segment.second());\r
509         return bp;\r
510     }\r
511 \r
512     /**\r
513      * Joins the connection at the selected branch point if and only if the\r
514      * branch point is a route point, i.e. it is connected to two other branch\r
515      * points or connector.\r
516      * \r
517      * @param interiorRouteNode\r
518      * @return\r
519      */\r
520     public void join(Resource interiorRouteNode) throws DatabaseException {\r
521         assertWriteSupport();\r
522 \r
523         if (!g.isInstanceOf(interiorRouteNode, br.DIA.InteriorRouteNode))\r
524             throw new ValidationException("'" + NameUtils.getSafeName(g, interiorRouteNode) + "' is not an instance of DIA.InteriorRouteNode");\r
525         @SuppressWarnings("unused")\r
526         Resource connection = getConnection(g, interiorRouteNode);\r
527         Collection<Resource> connectedTo = g.getObjects(interiorRouteNode, br.DIA.AreConnected);\r
528         if (connectedTo.size() != 2)\r
529             throw new ValidationException("Interior route node is not a discardable route line/point. It is not connected to 2 route nodes, but " + connectedTo.size() + ".");\r
530         Iterator<Resource> it = connectedTo.iterator();\r
531         Resource connector1 = it.next();\r
532         Resource connector2 = it.next();\r
533         //System.out.println("removing branch point " + GraphUtils.getReadableName(g, routeBranchPoint) + " which is connected to " + GraphUtils.getReadableName(g, connector1) + " and " + GraphUtils.getReadableName(g, connector2));\r
534         removeConnectorOrBranchPoint(interiorRouteNode);\r
535         connect(connector1, connector2);\r
536     }\r
537 \r
538     public void getConnectionSegments(Resource connection, Collection<EdgeResource> result) throws DatabaseException {\r
539 \r
540         ArrayList<EdgeResource> edges = new ArrayList<EdgeResource>();\r
541         Set<Resource> visited = new HashSet<Resource>();\r
542         Stack<Resource> todo = new Stack<Resource>();\r
543 \r
544         // Try to select input as root, this ensures correct order for simple paths\r
545         Collection<Resource> seeds = rg.getObjects(connection, br.DIA.HasArrowConnector);\r
546         if(seeds.isEmpty()) seeds = rg.getObjects(connection, br.DIA.HasPlainConnector);\r
547 \r
548         assert(!seeds.isEmpty());\r
549 \r
550         Resource seed = seeds.iterator().next();\r
551 \r
552         todo.push(seed);\r
553 \r
554         while(!todo.isEmpty()) {\r
555             Resource location = todo.pop();\r
556             if(!visited.contains(location)) {\r
557                 visited.add(location);\r
558                 for (Resource connectedTo : rg.getObjects(location, br.DIA.AreConnected)) {\r
559                     todo.add(connectedTo);\r
560                     EdgeResource edge = new EdgeResource(location, connectedTo);\r
561                     if(!edges.contains(edge)) edges.add(edge);\r
562                 }\r
563             }\r
564         }\r
565 \r
566         for(EdgeResource uer : edges) {\r
567 //            System.out.println("loaded edge " + uer.first() + " " + uer.second());\r
568             result.add(uer);\r
569         }\r
570 \r
571     }\r
572 \r
573     public Collection<Resource> getBranchPoints(Resource connection, Collection<Resource> result) throws DatabaseException {\r
574         if (result == null)\r
575             result = new ArrayList<Resource>();\r
576         result.addAll(rg.getObjects(connection, br.DIA.HasBranchPoint));\r
577         return result;\r
578     }\r
579 \r
580     public Collection<Resource> getConnectors(Resource connection, Collection<Resource> result) throws DatabaseException {\r
581         if (result == null)\r
582             result = new ArrayList<Resource>();\r
583         result.addAll(rg.getObjects(connection, br.DIA.HasConnector));\r
584         return result;\r
585     }\r
586 \r
587     public Resource getConnectedComponent(Resource connection, Resource connector) throws DatabaseException {\r
588         for (Resource connects : rg.getObjects(connector, br.STR.Connects))\r
589             if (!connects.equals(connection))\r
590                 return connects;\r
591         return null;\r
592     }\r
593 \r
594     public Statement getConnectedComponentStatement(Resource connection, Resource connector) throws DatabaseException {\r
595         for (Statement connects : rg.getStatements(connector, br.STR.Connects))\r
596             if (!connects.getObject().equals(connection))\r
597                 return connects;\r
598         return null;\r
599     }\r
600 \r
601     public void gatherEdges(Resource connection, Collection<EdgeResource> result) throws DatabaseException {\r
602         Set<Object> visited = new HashSet<Object>();\r
603         for (Resource connector : rg.getObjects(connection, br.DIA.HasConnector)) {\r
604             for (Resource connectedTo : rg.getObjects(connector, br.DIA.AreConnected)) {\r
605                 EdgeResource p = new EdgeResource(connector, connectedTo);\r
606                 if (visited.add(p)) {\r
607                     result.add(p);\r
608                 }\r
609             }\r
610         }\r
611         Collection<Resource> routeNodes = rg.getObjects(connection, br.DIA.HasInteriorRouteNode);\r
612         for (Resource routeNode : routeNodes) {\r
613             for (Resource connectedTo : rg.getObjects(routeNode, br.DIA.AreConnected)) {\r
614                 EdgeResource p = new EdgeResource(routeNode, connectedTo);\r
615                 if (visited.add(p)) {\r
616                     result.add(p);\r
617                 }\r
618             }\r
619         }\r
620     }\r
621 \r
622     public void gatherConnectionParts(Resource connection, Collection<Object> result) throws DatabaseException {\r
623         Set<Object> visited = new HashSet<Object>();\r
624         for (Resource connector : rg.getObjects(connection, br.DIA.HasConnector)) {\r
625             for (Resource connectedTo : rg.getObjects(connector, br.DIA.AreConnected)) {\r
626                 EdgeResource p = new EdgeResource(connector, connectedTo);\r
627                 if (visited.add(p)) {\r
628                     result.add(p);\r
629                 }\r
630             }\r
631         }\r
632         Collection<Resource> routeNodes = rg.getObjects(connection, br.DIA.HasInteriorRouteNode);\r
633         for (Resource routeNode : routeNodes) {\r
634             result.add(routeNode);\r
635             for (Resource connectedTo : rg.getObjects(routeNode, br.DIA.AreConnected)) {\r
636                 EdgeResource p = new EdgeResource(routeNode, connectedTo);\r
637                 if (visited.add(p)) {\r
638                     result.add(p);\r
639                 }\r
640             }\r
641         }\r
642     }\r
643 \r
644     public Collection<Resource> getConnectedComponents(Resource connection, Collection<Resource> result)\r
645     throws DatabaseException {\r
646         if (result == null)\r
647             result = new ArrayList<Resource>(2);\r
648         for (Resource connector : rg.getObjects(connection, br.DIA.HasConnector)) {\r
649             for (Resource connects : rg.getObjects(connector, br.STR.Connects)) {\r
650                 if (connects.equals(connection))\r
651                     continue;\r
652                 result.add(connects);\r
653             }\r
654         }\r
655         return result;\r
656     }\r
657 \r
658     public Collection<Resource> getConnectedConnectors(Resource connection, Collection<Resource> result)\r
659     throws DatabaseException {\r
660         if (result == null)\r
661             result = new ArrayList<Resource>(2);\r
662         for (Resource connector : rg.getObjects(connection, br.DIA.HasConnector)) {\r
663             Resource connects = getConnectedComponent(connection, connector);\r
664             if (connects != null)\r
665                 result.add( connector );\r
666         }\r
667         return result;\r
668     }\r
669 \r
670     public Collection<Resource> getTerminalConnectors(Resource node, Collection<Resource> result)\r
671     throws DatabaseException {\r
672         if (result == null)\r
673             result = new ArrayList<Resource>(2);\r
674         result.addAll( rg.getObjects(node, br.STR.IsConnectedTo) );\r
675         return result;\r
676     }\r
677 \r
678     /**\r
679      * @param g\r
680      * @param routeNode\r
681      * @return <code>null</code> if the specified resource is not part of any\r
682      *         connection\r
683      */\r
684     public static Resource tryGetConnection(ReadGraph g, Resource routeNode) throws DatabaseException {\r
685         DiagramResource dr = DiagramResource.getInstance(g);\r
686         Resource conn = g.getPossibleObject(routeNode, dr.IsConnectorOf);\r
687         if (conn == null)\r
688             conn = g.getPossibleObject(routeNode, dr.HasInteriorRouteNode_Inverse);\r
689         return conn;\r
690     }\r
691 \r
692     public static Resource tryGetConnection(ReadGraph g, EdgeResource segment) throws DatabaseException {\r
693         Resource first = tryGetConnection(g, segment.first());\r
694         Resource second = tryGetConnection(g, segment.second());\r
695         if (first == null || second == null || !first.equals(second))\r
696             return null;\r
697         return first;\r
698     }\r
699 \r
700     public static Resource tryGetMappedConnection(ReadGraph g, Resource connector) throws DatabaseException {\r
701         ModelingResources MOD = ModelingResources.getInstance(g);\r
702         return g.getPossibleObject(connector, MOD.ConnectorToConnection);\r
703     }\r
704     \r
705     public static Resource getConnection(ReadGraph g, EdgeResource segment) throws DatabaseException {\r
706         Resource first = tryGetConnection(g, segment.first());\r
707         Resource second = tryGetConnection(g, segment.second());\r
708         if (first == null && second == null)\r
709             throw new ValidationException(\r
710                     "neither connection segment end is attached to a Connection entity instance: "\r
711                     + segment.toString(g) + " - " + segment.toString());\r
712         if (first != null ^ second != null)\r
713             throw new ValidationException("both ends of connection segment "\r
714                     + segment.toString(g) + " - " + segment.toString() + " are not connected (first=" + first\r
715                     + ", second=" + second + ")");\r
716         if (!first.equals(second))\r
717             throw new ValidationException("connectors of connection segment "\r
718                     + segment.toString(g) + " - " + segment.toString() + " are part of different connections: " + first\r
719                     + " vs. " + second);\r
720         return first;\r
721     }\r
722 \r
723     public static Resource getConnection(ReadGraph g, Resource routeNode) throws DatabaseException {\r
724         Resource connection = tryGetConnection(g, routeNode);\r
725         if (connection == null)\r
726             throw new ValidationException("route node '"\r
727                     + NameUtils.getSafeName(g, routeNode) + "' is not part of any connection");\r
728         return connection;\r
729     }\r
730 \r
731     public static IConnectionPoint toConnectionPoint(ReadGraph g, Resource element, Terminal terminal) throws DatabaseException {\r
732         if (terminal instanceof ResourceTerminal) {\r
733             Resource t = ((ResourceTerminal) terminal).getResource();\r
734 \r
735             // TODO: remove this hack\r
736             if (element == null) { // Flag?\r
737                 DiagramResource DIA = DiagramResource.getInstance(g);\r
738                 Resource join = g.getPossibleObject(t, DIA.FlagIsJoinedBy);\r
739                 if (join == null)\r
740                     return null;\r
741                 return new CPConnectionJoin(join);\r
742             }\r
743             // element should be :Element\r
744             Resource bindingRelation = DiagramGraphUtil.getConnectionPointOfTerminal(g, t);\r
745             return new CPTerminal(element, bindingRelation);\r
746         } else if (terminal instanceof BranchPointTerminal) {\r
747             // element should be either : DIA.InteriorRouteNode or DIA.Connection\r
748             Resource connection = null;\r
749             DiagramResource DIA = DiagramResource.getInstance(g);\r
750             if (g.isInstanceOf(element, DIA.Connection))\r
751                 connection = element;\r
752             else\r
753                 connection = getConnection(g, element);\r
754             return new CPConnection(connection);\r
755         }\r
756         throw new IllegalArgumentException("Unrecognized Terminal class: " + terminal);\r
757     }\r
758 \r
759     public static IConnectionPoint toConnectionPoint(ReadGraph graph, IElement element, Terminal terminal) throws DatabaseException {\r
760         Resource r = (Resource) ElementUtils.getObject(element);\r
761         return ConnectionUtil.toConnectionPoint(graph, r, terminal);\r
762     }\r
763 \r
764     public static IConnectionPoint toConnectionPoint(ReadGraph graph, TerminalInfo ti) throws DatabaseException {\r
765         return ConnectionUtil.toConnectionPoint(graph, ti.e, ti.t);\r
766     }\r
767 \r
768     public static IConnectionPoint toConnectionPoint(ReadGraph graph, DesignatedTerminal t) throws DatabaseException {\r
769         return toConnectionPoint(graph, t.element, t.terminal);\r
770     }\r
771 \r
772     public static Resource toDirectionTag(ReadGraph graph, BranchPoint.Direction direction) {\r
773         if (direction == null)\r
774             return null;\r
775 \r
776         DiagramResource DIA = DiagramResource.getInstance(graph);\r
777         switch (direction) {\r
778             case Horizontal: return DIA.Horizontal;\r
779             case Vertical: return DIA.Vertical;\r
780             default: return null;\r
781         }\r
782     }\r
783 \r
784     /**\r
785      * Copied from ConnectionCommandHandler#splitConnection, duplicate code.\r
786      * \r
787      * @param toCanvasPos\r
788      * @param onEdge\r
789      * @return\r
790      */\r
791     public static Line2D resolveNearestEdgeLineSegment(Point2D toCanvasPos, IElement onEdge) {\r
792         // Try to find an initial preferred direction for the new\r
793         // branch point based on the direction of the edge's line\r
794         // segment on which the split is done.\r
795         List<Point2D> points = new ArrayList<Point2D>();\r
796         BendsHandler bh = onEdge.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
797         if (bh != null)\r
798             org.simantics.g2d.utils.GeometryUtils.getPoints(bh.getPath(onEdge), points);\r
799         Line2D nearestLine = null;\r
800         double nearestDistanceToLine = Double.MAX_VALUE;\r
801         for (int i = 0; i < points.size() - 1; ++i) {\r
802             Point2D p1 = points.get(i);\r
803             Point2D p2 = points.get(i+1);\r
804             double distanceToLine = GeometryUtils.distanceFromLine(toCanvasPos, p1, p2);\r
805             if (distanceToLine < nearestDistanceToLine) {\r
806                 nearestDistanceToLine = distanceToLine;\r
807                 if (nearestLine == null)\r
808                     nearestLine = new Line2D.Double();\r
809                 nearestLine.setLine(p1, p2);\r
810             }\r
811         }\r
812         return nearestLine;\r
813     }\r
814 \r
815     /**\r
816      * @param object\r
817      * @return\r
818      * @throws DatabaseException\r
819      */\r
820     public static IElement getSingleEdge(Object object) throws DatabaseException {\r
821         IElement e = AdaptionUtils.adaptToSingle(object, IElement.class);\r
822         if (e == null)\r
823             return null;\r
824 \r
825         if (PickFilter.FILTER_CONNECTION_EDGES.accept(e))\r
826             return e;\r
827 \r
828         if (PickFilter.FILTER_CONNECTIONS.accept(e)) {\r
829             ConnectionHandler ch = e.getElementClass().getSingleItem(ConnectionHandler.class);\r
830             Collection<IElement> bps = ch.getBranchPoints(e, null);\r
831             Collection<IElement> segs = ch.getSegments(e, null);\r
832             if (bps.isEmpty() && segs.size() == 1)\r
833                 return segs.iterator().next();\r
834         }\r
835         return null;\r
836     }\r
837 \r
838     /**\r
839      * @param object\r
840      * @return\r
841      * @throws DatabaseException\r
842      */\r
843     public static Collection<IElement> getEdges(Object object) throws DatabaseException {\r
844         IElement e = AdaptionUtils.adaptToSingle(object, IElement.class);\r
845         if (e == null)\r
846             return null;\r
847 \r
848         if (PickFilter.FILTER_CONNECTION_EDGES.accept(e))\r
849             return Collections.singleton(e);\r
850 \r
851         if (PickFilter.FILTER_CONNECTIONS.accept(e)) {\r
852             ConnectionHandler ch = e.getElementClass().getSingleItem(ConnectionHandler.class);\r
853             return ch.getSegments(e, null);\r
854         }\r
855         return null;\r
856     }\r
857 \r
858     /**\r
859      * @param graph\r
860      * @param connection\r
861      * @return\r
862      * @throws DatabaseException\r
863      */\r
864     public static Resource getConnectionTailNode(ReadGraph graph, Resource connection) throws DatabaseException {\r
865         DiagramResource DIA = DiagramResource.getInstance(graph);\r
866         StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
867         for (Resource connector : graph.getObjects(connection, DIA.HasTailConnector)) {\r
868             for (Resource node : graph.getObjects(connector, STR.Connects)) {\r
869                 if (node.equals(connection))\r
870                     continue;\r
871                 return node;\r
872             }\r
873         }\r
874         return null;\r
875     }\r
876 \r
877     /**\r
878      * @param graph\r
879      * @param connection\r
880      * @return the STR.Connects statement from the tail DIA.Connector to the\r
881      *         tail node (DIA.Element) or <code>null</code> if no tail node\r
882      *         exists\r
883      * @throws DatabaseException\r
884      */\r
885     public static Statement getConnectionTailNodeStatement(ReadGraph graph, Resource connection) throws DatabaseException {\r
886         DiagramResource DIA = DiagramResource.getInstance(graph);\r
887         StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
888         for (Resource connector : graph.getObjects(connection, DIA.HasTailConnector)) {\r
889             for (Statement connects : graph.getStatements(connector, STR.Connects)) {\r
890                 if (connects.getObject().equals(connection))\r
891                     continue;\r
892                 return connects;\r
893             }\r
894         }\r
895         return null;\r
896     }\r
897 \r
898     /**\r
899      * @param connection\r
900      * @param dx\r
901      * @param dy\r
902      * @throws DatabaseException\r
903      */\r
904     public void translateRouteNodes(Resource connection, double dx, double dy) throws DatabaseException {\r
905         Commands.get(g, "Simantics/Diagram/translateRouteNodes")\r
906             .execute(g, g.syncRequest(new IndexRoot(connection)), connection, dx, dy);\r
907     }\r
908     \r
909     public static void translateRouteNodes(WriteGraph g, Resource connection, double dx, double dy) throws DatabaseException {\r
910         DiagramResource DIA = DiagramResource.getInstance(g);\r
911         for (Resource routeNode : g.getObjects(connection, DIA.HasInteriorRouteNode)) {\r
912             if (g.isInstanceOf(routeNode, DIA.RouteLine)) {\r
913                 Double pos = g.getRelatedValue(routeNode, DIA.HasPosition, Bindings.DOUBLE);\r
914                 Boolean isHorizontal = g.getRelatedValue(routeNode, DIA.IsHorizontal, Bindings.BOOLEAN);\r
915                 pos += isHorizontal ? dy : dx;\r
916                 g.claimLiteral(routeNode, DIA.HasPosition, pos);\r
917             }\r
918         }\r
919     }\r
920 \r
921     /**\r
922      * Creates a route graph connection which has corners at the specified\r
923      * locations, starting/ending from/at the specified element terminals.\r
924      * \r
925      * if {@link Development#DEVELOPMENT} is <code>true</code> the code will\r
926      * verify that both element end-points are part of the same diagram.\r
927      * \r
928      * @param graph\r
929      *            database write access\r
930      * @param startElement\r
931      *            element to start connecting from\r
932      * @param startConnectionPoint\r
933      *            STR.ConnectedTo relation of the start element terminal\r
934      * @param endElement\r
935      *            element to end the connection at\r
936      * @param endConnectionPoint\r
937      *            STR.ConnectedTo relation of the end element terminal\r
938      * @param corners\r
939      *            the corners to create for the connection\r
940      * @return the created diagram connection resource\r
941      * @throws DatabaseException\r
942      */\r
943     public Resource createConnectionWithCorners(WriteGraph graph, Resource startElement,\r
944             Resource startConnectionPoint, Resource endElement, Resource endConnectionPoint, List<Point2D> corners)\r
945                     throws DatabaseException {\r
946         DiagramResource DIA = br.DIA;\r
947 \r
948         // Verify that both elements are part of the same diagram before connecting.\r
949         if (Development.DEVELOPMENT) {\r
950             Collection<Resource> startDiagram = OrderedSetUtils.getOwnerLists(graph, startElement, DIA.Diagram);\r
951             Collection<Resource> endDiagram = OrderedSetUtils.getOwnerLists(graph, endElement, DIA.Diagram);\r
952             if (Collections.disjoint(startDiagram, endDiagram))\r
953                 throw new IllegalArgumentException("start element " + startElement\r
954                         + " is not on same diagram as end element " + endElement + ". start diagram: " + startDiagram\r
955                         + ", end diagram: " + endDiagram);\r
956         }\r
957 \r
958         return createConnectionWithCorners(graph, DIA.RouteGraphConnection, startElement,\r
959                 startConnectionPoint, DIA.HasPlainConnector, endElement, endConnectionPoint, DIA.HasArrowConnector,\r
960                 corners);\r
961     }\r
962 \r
963     /**\r
964      * Creates a route graph connection which has corners at the specified\r
965      * locations, starting/ending from/at the specified element terminals.\r
966      * Verifies that both element end-points are part of the same diagram.\r
967      * \r
968      * @param graph database write access\r
969      * @param connectionType type of created connection (e.g. DIA.RouteGraphConnection)\r
970      * @param element1 element to start connecting from\r
971      * @param connectionPoint1 STR.ConnectedTo relation of the start element terminal \r
972      * @param hasConnector1 connector to connection attachment relation to use for element1 and connectionPoint1\r
973      * @param element2 element to end the connection at\r
974      * @param connectionPoint2 STR.ConnectedTo relation of the end element terminal\r
975      * @param hasConnector2 connector to connection attachment relation to use for element2 and connectionPoint2\r
976      * @param corners the corners to create for the connection\r
977      * @return the created diagram connection resource\r
978      * @throws DatabaseException\r
979      */\r
980     public Resource createConnectionWithCorners(WriteGraph graph, Resource connectionType,\r
981             Resource element1, Resource connectionPoint1, Resource hasConnector1, Resource element2,\r
982             Resource connectionPoint2, Resource hasConnector2, List<Point2D> corners)\r
983             throws DatabaseException {\r
984         DiagramResource DIA = br.DIA;\r
985 \r
986         if (corners.size() == 1)\r
987             throw new UnsupportedOperationException("1 corner currently not supported");\r
988 \r
989         Resource connection = newInstance(g, connectionType);\r
990         Resource connector1 = newConnector(connection, hasConnector1);\r
991         Resource connector2 = newConnector(connection, hasConnector2);\r
992         graph.claim(element1, connectionPoint1, connector1);\r
993         graph.claim(element2, connectionPoint2, connector2);\r
994 \r
995         if (corners.size() > 1) {\r
996             boolean horizontal;\r
997             Resource previousRouteNode = connector1;\r
998             for (int i=0; i<corners.size()-1; ++i) {\r
999                 Point2D p = corners.get(i);\r
1000                 Point2D p1 = corners.get(i+1);\r
1001                 horizontal = Math.abs(p1.getY() - p.getY()) < Math.abs(p1.getX() - p.getX());\r
1002                 Resource routeLine = ConnectionUtil.createRouteline(graph, connection, horizontal ? p.getY() : p.getX(), horizontal);\r
1003                 graph.claim(previousRouteNode, DIA.AreConnected, DIA.AreConnected, routeLine);\r
1004                 previousRouteNode = routeLine;\r
1005             }\r
1006             graph.claim(previousRouteNode, DIA.AreConnected, DIA.AreConnected, connector2);\r
1007         } else {\r
1008             graph.claim(connector1, DIA.AreConnected, DIA.AreConnected, connector2);\r
1009         }\r
1010 \r
1011         return connection;\r
1012     }\r
1013 \r
1014     public Resource createConnectionWithSingleLine(WriteGraph graph, Resource connectionType,\r
1015                 Collection<Triple<Resource,Resource,Resource>> endpoints,\r
1016                 double coordinate, boolean horizontal)\r
1017             throws DatabaseException {\r
1018 \r
1019         DiagramResource DIA = br.DIA;\r
1020 \r
1021         Resource connection = newInstance(g, connectionType);\r
1022 \r
1023         Resource routeLine = ConnectionUtil.createRouteline(graph, connection, coordinate, horizontal);\r
1024 \r
1025         for(Triple<Resource,Resource,Resource> endpoint : endpoints) {\r
1026                 Resource connector = newConnector(connection, endpoint.third);  \r
1027             graph.claim(endpoint.first, endpoint.second, connector);\r
1028             graph.claim(routeLine, DIA.AreConnected, DIA.AreConnected, connector);\r
1029         }\r
1030 \r
1031         return connection;\r
1032         \r
1033     }\r
1034 \r
1035     public Resource createConnection(WriteGraph graph, Resource connectionType,\r
1036                 List<Triple<Resource,Resource,Resource>> terminals,\r
1037                 List<Pair<Double, Boolean>> routeLines,\r
1038                 List<Pair<Integer,Integer>> connections) throws DatabaseException {\r
1039 \r
1040         DiagramResource DIA = br.DIA;\r
1041 \r
1042         Resource connection = newInstance(g, connectionType);\r
1043 \r
1044         Resource[] parts = new Resource[terminals.size() + routeLines.size()];\r
1045         \r
1046         int index = 0;\r
1047         \r
1048         for(Triple<Resource,Resource,Resource> terminal : terminals) {\r
1049                 Resource connector = newConnector(connection, terminal.third);  \r
1050             graph.claim(terminal.first, terminal.second, connector);\r
1051             parts[index++] = connector;\r
1052         }\r
1053 \r
1054         for(Pair<Double, Boolean> routeLine : routeLines) {\r
1055             Resource r = ConnectionUtil.createRouteline(graph, connection, routeLine.first, routeLine.second);\r
1056             parts[index++] = r;\r
1057         }\r
1058         \r
1059 //        System.err.println("Connect " + parts.length + " parts.");\r
1060         \r
1061         for(Pair<Integer,Integer> conn : connections) {\r
1062 //            System.err.println("-" + conn.first + " " + conn.second);\r
1063                 Resource part1 = parts[conn.first];\r
1064                 Resource part2 = parts[conn.second];\r
1065             graph.claim(part1, DIA.AreConnected, DIA.AreConnected, part2);\r
1066         }\r
1067 \r
1068         return connection;\r
1069         \r
1070     }\r
1071 \r
1072     /**\r
1073      * @param graph\r
1074      * @param connection\r
1075      * @param pos\r
1076      * @param isHorizontal\r
1077      * @return new route line that is attached to the specified diagram connection\r
1078      * @throws DatabaseException\r
1079      */\r
1080     public static Resource createRouteline(WriteGraph graph, Resource connection, double pos, boolean isHorizontal) throws DatabaseException {\r
1081         Layer0 L0 = Layer0.getInstance(graph);\r
1082         DiagramResource DIA = DiagramResource.getInstance(graph);\r
1083         Resource routeLine = graph.newResource();\r
1084         graph.claim(routeLine, L0.InstanceOf, null, DIA.RouteLine);\r
1085         graph.addLiteral(routeLine, DIA.HasPosition, DIA.HasPosition_Inverse, L0.Double, pos, Bindings.DOUBLE);\r
1086         graph.addLiteral(routeLine, DIA.IsHorizontal, DIA.IsHorizontal_Inverse, L0.Boolean, isHorizontal, Bindings.BOOLEAN);\r
1087         graph.claim(connection, DIA.HasInteriorRouteNode, DIA.HasInteriorRouteNode_Inverse, routeLine);\r
1088         return routeLine;\r
1089     }\r
1090 \r
1091     /**\r
1092      * @param graph database write-only access\r
1093      * @param type type of the created resource\r
1094      * @return new instance of type\r
1095      * @throws DatabaseException\r
1096      */\r
1097     private Resource newInstance(WriteOnlyGraph graph, Resource type) throws DatabaseException {\r
1098         Resource connection = graph.newResource();\r
1099         g.claim(connection, br.L0.InstanceOf, null, type);\r
1100         return connection;\r
1101     }\r
1102 \r
1103 }\r