]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - 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
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/content/ConnectionUtil.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/content/ConnectionUtil.java
new file mode 100644 (file)
index 0000000..be9a594
--- /dev/null
@@ -0,0 +1,1103 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.diagram.content;\r
+\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Line2D;\r
+import java.awt.geom.Point2D;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.HashSet;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.Stack;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Statement;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.WriteOnlyGraph;\r
+import org.simantics.db.common.CommentMetadata;\r
+import org.simantics.db.common.request.IndexRoot;\r
+import org.simantics.db.common.utils.NameUtils;\r
+import org.simantics.db.common.utils.OrderedSetUtils;\r
+import org.simantics.db.exception.AssumptionException;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.exception.ValidationException;\r
+import org.simantics.db.layer0.adapter.impl.EntityRemover;\r
+import org.simantics.db.layer0.util.RemoverUtil;\r
+import org.simantics.diagram.connection.ConnectionSegmentEnd;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.diagram.synchronization.graph.BasicResources;\r
+import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
+import org.simantics.g2d.connection.handler.ConnectionHandler;\r
+import org.simantics.g2d.diagram.handler.PickRequest.PickFilter;\r
+import org.simantics.g2d.diagram.handler.Topology.Terminal;\r
+import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil.TerminalInfo;\r
+import org.simantics.g2d.element.ElementUtils;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.element.handler.BendsHandler;\r
+import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
+import org.simantics.g2d.element.handler.impl.BranchPointTerminal;\r
+import org.simantics.g2d.elementclass.BranchPoint;\r
+import org.simantics.g2d.elementclass.BranchPoint.Direction;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.scenegraph.utils.GeometryUtils;\r
+import org.simantics.scl.commands.Commands;\r
+import org.simantics.structural.stubs.StructuralResource2;\r
+import org.simantics.structural2.modelingRules.CPConnection;\r
+import org.simantics.structural2.modelingRules.CPConnectionJoin;\r
+import org.simantics.structural2.modelingRules.CPTerminal;\r
+import org.simantics.structural2.modelingRules.IConnectionPoint;\r
+import org.simantics.utils.Development;\r
+import org.simantics.utils.datastructures.Pair;\r
+import org.simantics.utils.datastructures.Triple;\r
+import org.simantics.utils.ui.AdaptionUtils;\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public final class ConnectionUtil {\r
+\r
+    private final ReadGraph rg;\r
+    private final WriteGraph g;\r
+    private final BasicResources br;\r
+\r
+    /**\r
+     * Construct utility with read-only support.\r
+     * @param g\r
+     */\r
+    public ConnectionUtil(ReadGraph g) {\r
+        this.rg = g;\r
+        this.g = (g instanceof WriteGraph) ? (WriteGraph) g : null;\r
+        this.br = BasicResources.getInstance(g);\r
+    }\r
+\r
+    /**\r
+     * Construct utility with read-write support.\r
+     * @param g\r
+     */\r
+    public ConnectionUtil(WriteGraph g) {\r
+        this.rg = g;\r
+        this.g = g;\r
+        this.br = BasicResources.getInstance(g);\r
+    }\r
+\r
+    void assertWriteSupport() {\r
+        if (g == null)\r
+            throw new UnsupportedOperationException("no write support, this ConnectionUtil is read-only");\r
+    }\r
+\r
+    /**\r
+     * Creates a new connection element of the specified type and attaches it to\r
+     * the specified diagram composite.\r
+     * \r
+     * @param composite diagram composite\r
+     * @param type connection element type\r
+     * @return created connection\r
+     * @throws DatabaseException\r
+     */\r
+    public Resource newConnection(Resource composite, Resource type) throws DatabaseException {\r
+        assertWriteSupport();\r
+        Resource connection = newConnection(type);\r
+        // By default, add connections to the front of the diagram since most\r
+        // often it is visually the expected result.\r
+        OrderedSetUtils.addFirst(g, composite, connection);\r
+        g.claim(composite, br.L0.ConsistsOf, br.L0.PartOf, connection);\r
+        return connection;\r
+    }\r
+\r
+    /**\r
+     * Creates a new connection element of the specified type without attaching\r
+     * it to any diagram composite.\r
+     * \r
+     * @param type connection element type\r
+     * @return created connection\r
+     * @throws DatabaseException\r
+     */\r
+    public Resource newConnection(Resource type) throws DatabaseException {\r
+        assertWriteSupport();\r
+        return newInstance(g, type);\r
+    }\r
+\r
+    /**\r
+     * Creates a new element terminal connector (DiagramResource) for the specified connector\r
+     * @param connection\r
+     * @param hasConnector\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    public Resource newConnector(Resource connection, Resource hasConnector) throws DatabaseException {\r
+        assertWriteSupport();\r
+        Resource connector = newInstance(g, br.DIA.Connector);\r
+        g.claim(connection, hasConnector, connector);\r
+        return connector;\r
+    }\r
+\r
+    public Resource newBranchPoint(Resource connection, AffineTransform tr) throws DatabaseException {\r
+        return newBranchPoint(connection, tr, null);\r
+    }\r
+\r
+    public Resource newBranchPoint(Resource connection, AffineTransform tr, Direction direction) throws DatabaseException {\r
+        assertWriteSupport();\r
+        Resource bp = g.newResource();\r
+        g.claim(bp, br.L0.InstanceOf, null, br.DIA.BranchPoint);\r
+        if (tr != null) {\r
+            double[] mat = new double[6];\r
+            tr.getMatrix(mat);\r
+            Resource transform = g.newResource();\r
+            g.claim(transform, br.L0.InstanceOf, null, br.G2D.Transform);\r
+            g.claimValue(transform, mat);\r
+            g.claim(bp, br.DIA.HasTransform, transform);\r
+        }\r
+        Resource tag = toDirectionTag(g, direction);\r
+        if (tag != null) {\r
+            g.claim(bp, tag, tag, bp);\r
+        }\r
+        g.claim(connection, br.DIA.HasBranchPoint, bp);\r
+        return bp;\r
+    }\r
+\r
+    /**\r
+     * @param connection\r
+     * @param position\r
+     * @param isHorizontal\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    public Resource newRouteLine(Resource connection, Double position, Boolean isHorizontal) throws DatabaseException {\r
+        assertWriteSupport();\r
+        Resource rl = newInstance(g, br.DIA.RouteLine);\r
+        if (position != null) {\r
+            g.addLiteral(rl, br.DIA.HasPosition, br.DIA.HasPosition_Inverse, br.L0.Double, position, Bindings.DOUBLE);\r
+        }\r
+        if (isHorizontal != null) {\r
+            g.addLiteral(rl, br.DIA.IsHorizontal, br.DIA.IsHorizontal_Inverse, br.L0.Boolean, isHorizontal, Bindings.BOOLEAN);\r
+        }\r
+        g.claim(connection, br.DIA.HasInteriorRouteNode, br.DIA.HasInteriorRouteNode_Inverse, rl);\r
+        return rl;\r
+    }\r
+\r
+    ConnectionSegmentEnd getTerminalType(Terminal terminal, ConnectionSegmentEnd defaultValue) {\r
+        ConnectionSegmentEnd type = getTerminalType(terminal);\r
+        return type != null ? type : defaultValue;\r
+    }\r
+\r
+    ConnectionSegmentEnd getTerminalType(Terminal t) {\r
+        if (t == null)\r
+            return null;\r
+\r
+        if (t instanceof ResourceTerminal) {\r
+            return ConnectionSegmentEnd.CONNECTOR;\r
+        } else if (t instanceof BranchPointTerminal) {\r
+            return ConnectionSegmentEnd.BRANCH;\r
+        } else {\r
+            throw new IllegalArgumentException("unsupported terminal '" + t + "'");\r
+        }\r
+    }\r
+\r
+    Resource resolveTerminalRelation(ReadGraph g, Terminal t) throws DatabaseException {\r
+        if (t == null)\r
+            return null;\r
+        if (t instanceof ResourceTerminal) {\r
+            ResourceTerminal rt = (ResourceTerminal) t;\r
+            Resource terminalRelation = DiagramGraphUtil.getConnectionPointOfTerminal(g, rt.getResource());\r
+            if (!g.isSubrelationOf(terminalRelation, br.STR.IsConnectedTo)) {\r
+                // debug...\r
+            }\r
+            return terminalRelation;\r
+        } else {\r
+            throw new IllegalArgumentException("unsupported terminal '" + t + "' for terminal relation resolution");\r
+        }\r
+    }\r
+\r
+    public Resource toHasConnectorRelation(EdgeEnd end) {\r
+        switch (end) {\r
+            case Begin: return br.DIA.HasPlainConnector;\r
+            case End: return br.DIA.HasArrowConnector;\r
+            default: throw new IllegalArgumentException("unsupported edge end: " + end);\r
+        }\r
+    }\r
+\r
+    public EdgeEnd toEdgeEnd(Resource attachmentRelation, EdgeEnd defaultValue) throws DatabaseException {\r
+        if (g.isSubrelationOf(attachmentRelation, br.DIA.HasPlainConnector))\r
+            return EdgeEnd.Begin;\r
+        if (g.isSubrelationOf(attachmentRelation, br.DIA.HasArrowConnector))\r
+            return EdgeEnd.End;\r
+        return defaultValue;\r
+    }\r
+\r
+    public Resource getAttachmentRelationForConnector(Resource connector) throws DatabaseException {\r
+        Statement connection = g.getPossibleStatement(connector, br.DIA.IsConnectorOf);\r
+        if (connection == null)\r
+            return null;\r
+        Resource attachment = g.getInverse(connection.getPredicate());\r
+        return attachment;\r
+    }\r
+\r
+    /**\r
+     * @param connection\r
+     * @param node\r
+     * @param c\r
+     * @param end\r
+     * @param attachmentRelation the relation used for attaching the connector to the connector\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    public Resource getOrCreateConnector(Resource connection, Resource node, Terminal terminal, EdgeEnd end, Resource attachmentRelation) throws DatabaseException {\r
+        assertWriteSupport();\r
+        ConnectionSegmentEnd connectorType = getTerminalType(terminal, null);\r
+        if (connectorType == null)\r
+            throw new AssumptionException("Invalid connection node", connection, node);\r
+\r
+        switch (connectorType) {\r
+            case BRANCH:\r
+                // NOTE: should we ensure here that (connection, br.dr.HasBranchPoint, node) exists?\r
+                return node;\r
+\r
+            case CONNECTOR: {\r
+                Resource terminalRelation = resolveTerminalRelation(g, terminal);\r
+\r
+                if (attachmentRelation == null)\r
+                    attachmentRelation = toHasConnectorRelation(end);\r
+\r
+                if (!g.isSubrelationOf(attachmentRelation, br.DIA.HasConnector))\r
+                    throw new AssumptionException("attachment relation not a subrelation of Has Connector", attachmentRelation);\r
+\r
+                // Create new connector for the specified node terminal\r
+                Resource terminalConnector = newConnector(connection, attachmentRelation);\r
+                g.claim(node, terminalRelation, terminalConnector);\r
+                return terminalConnector;\r
+            }\r
+            default:\r
+                throw new Error("this should be unreachable code");\r
+        }\r
+    }\r
+\r
+    public void connect(Resource connector1, Resource connector2) throws DatabaseException {\r
+        assertWriteSupport();\r
+        g.claim(connector1, br.DIA.AreConnected, br.DIA.AreConnected, connector2);\r
+    }\r
+\r
+    public void disconnect(Resource connector1, Resource connector2) throws DatabaseException {\r
+        assertWriteSupport();\r
+        g.denyStatement(connector1, br.DIA.AreConnected, connector2);\r
+    }\r
+\r
+    public void disconnectFromAllRouteNodes(Resource connector) throws DatabaseException {\r
+        assertWriteSupport();\r
+        g.deny(connector, br.DIA.AreConnected);\r
+    }\r
+\r
+    public void disconnect(EdgeResource segment) throws DatabaseException {\r
+        assertWriteSupport();\r
+        disconnect(segment.first(), segment.second());\r
+    }\r
+\r
+    public boolean isConnected(Resource connector) throws DatabaseException {\r
+        return rg.hasStatement(connector, br.DIA.AreConnected);\r
+    }\r
+\r
+    public boolean isConnected(Resource connector, Resource toConnector) throws DatabaseException {\r
+        return rg.hasStatement(connector, br.DIA.AreConnected, toConnector);\r
+    }\r
+\r
+    public boolean isConnectionEmpty(Resource connection) throws DatabaseException {\r
+        return !rg.hasStatement(connection, br.DIA.HasConnector)\r
+                && !rg.hasStatement(connection, br.DIA.HasInteriorRouteNode);\r
+    }\r
+\r
+    private void removeConnectorOrBranchPoint(Resource connectorOrBranchPoint) throws DatabaseException {\r
+\r
+//        // Handle correct removal of route points\r
+//        if(g.isInstanceOf(connectorOrBranchPoint, dr.BranchPoint)) {\r
+//            Collection<Resource> connectedConnectors = g.getObjects(connectorOrBranchPoint, dr.AreConnected);\r
+//            if(connectedConnectors.size() == 2) {\r
+//                Iterator<Resource> it = connectedConnectors.iterator();\r
+//                g.claim(it.next(), dr.AreConnected, it.next());\r
+//            }\r
+//        }\r
+\r
+        g.deny(connectorOrBranchPoint, br.DIA.AreConnected);\r
+\r
+        // Removes both the terminal relation and the HasConnector relation\r
+        // to the :Connection\r
+        g.deny(connectorOrBranchPoint, br.STR.Connects);\r
+\r
+        // If this is a branch point/route node, remove it from the connection too.\r
+        g.deny(connectorOrBranchPoint, br.DIA.IsBranchPointOf);\r
+        g.deny(connectorOrBranchPoint, br.DIA.HasInteriorRouteNode_Inverse);\r
+    }\r
+\r
+    /**\r
+     * Removes a complete connection along with all its branch points and terminal connectors.\r
+     * \r
+     * @param connection the connection to remove\r
+     */\r
+    public void removeConnection(Resource connection) throws DatabaseException {\r
+        assertWriteSupport();\r
+\r
+        // Add comment to change set.\r
+        CommentMetadata cm = g.getMetadata(CommentMetadata.class);\r
+        g.addMetadata(cm.add("Remove connection " + connection));\r
+\r
+        // 1. Get all connectors/branch points\r
+        Collection<Resource> connectors = new ArrayList<Resource>();\r
+        connectors.addAll(rg.getObjects(connection, br.DIA.HasConnector));\r
+        connectors.addAll(rg.getObjects(connection, br.DIA.HasInteriorRouteNode));\r
+\r
+        // 2. Remove all connectors/branch points\r
+        for (Resource connector : connectors) {\r
+            removeConnectorOrBranchPoint(connector);\r
+            RemoverUtil.remove(g, connector);\r
+        }\r
+\r
+        // 3. Remove whole connection\r
+        for (Resource owner : OrderedSetUtils.getOwnerLists(g, connection, br.DIA.Diagram))\r
+            OrderedSetUtils.remove(g, owner, connection);\r
+        EntityRemover.remove(g, connection);\r
+    }\r
+\r
+    /**\r
+     * Removes a single connector part from the graph. A connection part can be\r
+     * either a branch point or a terminal connector.\r
+     * \r
+     * @param connectorOrBranchPoint\r
+     * @throws DatabaseException\r
+     */\r
+    public void removeConnectionPart(Resource connectorOrBranchPoint) throws DatabaseException {\r
+        removeConnectorOrBranchPoint(connectorOrBranchPoint);\r
+        RemoverUtil.remove(g, connectorOrBranchPoint);\r
+    }\r
+\r
+    /**\r
+     * Removes the specified connection segment. Checks that both ends of the\r
+     * edge segment are part of to the same connection. Steps taken:\r
+     * <ul>\r
+     * <li>Minimally this only disconnects the connection segment ends from each\r
+     * other and nothing more.</li>\r
+     * <li>After disconnecting, we check whether the segment ends are still\r
+     * connected to something. If not, the :Connector/:BranchPoint at the\r
+     * segment's end is destroyed and detached from the connection entity.</li>\r
+     * <li>Finally, if the connection entity is empty (has no connectors/branch\r
+     * points), it is also destroyed and removed from the diagram.</li>\r
+     * \r
+     * @param segment the connection segment to remove\r
+     */\r
+    public void remove(EdgeResource segment) throws DatabaseException {\r
+        remove(segment, false);\r
+    }\r
+\r
+    /**\r
+     * Removes the specified connection segment. Steps taken:\r
+     * <ul>\r
+     * <li>Minimally this only disconnects the connection segment ends from each\r
+     * other and nothing more.</li>\r
+     * <li>After disconnecting, we check whether the segment ends are still\r
+     * connected to something. If not, the :Connector/:BranchPoint at the\r
+     * segment's end is destroyed and detached from the connection entity.</li>\r
+     * <li>Finally, if the connection entity is empty (has no connectors/branch\r
+     * points), it is also destroyed and removed from the diagram.</li>\r
+     * \r
+     * @param segment the connection segment to remove\r
+     * @param unchecked <code>false</code> to check that both ends of the\r
+     *        segment are part of the same connection before removing,\r
+     *        <code>true</code> to just remove the segment without checking\r
+     *        this. Using <code>true</code> may help in cases where the\r
+     *        connection model has become corrupted for some reason, e.g. the\r
+     *        other end of the edge has lost its link to the connection while\r
+     *        the other has not,\r
+     */\r
+    public void remove(EdgeResource segment, boolean unchecked) throws DatabaseException {\r
+        assertWriteSupport();\r
+\r
+        if (!unchecked) {\r
+            @SuppressWarnings("unused")\r
+            Resource connection = getConnection(g, segment);\r
+        }\r
+\r
+        // 1. disconnect segment ends\r
+        disconnect(segment);\r
+\r
+        // 2. Remove connectors/branch points if they become fully disconnected\r
+        if (!isConnected(segment.first())) {\r
+            removeConnectorOrBranchPoint(segment.first());\r
+        }\r
+        if (!isConnected(segment.second())) {\r
+            removeConnectorOrBranchPoint(segment.second());\r
+        }\r
+\r
+        // 3. Remove whole connection entity if it becomes empty\r
+//        if (isConnectionEmpty(connection)) {\r
+//            for (Resource owner : OrderedSetUtils.getOwnerLists(g, connection, dr.Diagram))\r
+//                OrderedSetUtils.remove(g, owner, connection);\r
+//            RemoverUtil.remove(g, connection);\r
+//        }\r
+\r
+    }\r
+\r
+    /**\r
+     * Removes all DIA.Connector instances from the specified connection that\r
+     * are not used for anything.\r
+     * \r
+     * @param connection connection to examine\r
+     * @return the amount of unused connectors removed\r
+     */\r
+    public int removeUnusedConnectors(Resource connection) throws DatabaseException {\r
+        int result = 0;\r
+        for (Resource connector : getConnectors(connection, null)) {\r
+            if (!g.getObjects(connector, br.DIA.AreConnected).isEmpty())\r
+                continue;\r
+            Collection<Resource> connects = g.getObjects(connector, br.STR.Connects);\r
+            if (connects.size() > 1)\r
+                continue;\r
+\r
+            removeConnectionPart(connector);\r
+            ++result;\r
+        }\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Removes all DIA.InteriorRouteNode instances from the specified connection that\r
+     * are not used for anything, i.e. connect to less than 2 other route nodes.\r
+     * \r
+     * @param connection connection to examine\r
+     * @return the amount of unused connectors removed\r
+     */\r
+    public int removeExtraInteriorRouteNodes(Resource connection) throws DatabaseException {\r
+        int result = 0;\r
+        for (Resource interiorRouteNode : g.getObjects(connection, br.DIA.HasInteriorRouteNode)) {\r
+            Collection<Resource> connectedTo = g.getObjects(interiorRouteNode, br.DIA.AreConnected);\r
+            if (connectedTo.size() > 1)\r
+                continue;\r
+\r
+            removeConnectionPart(interiorRouteNode);\r
+            ++result;\r
+        }\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Splits the specified connection segment by adding a new branch point in\r
+     * between the segment ends.\r
+     * \r
+     * @param segment\r
+     * @return the branch (route) point created by the split operation.\r
+     */\r
+    public Resource split(EdgeResource segment, AffineTransform splitPos) throws DatabaseException {\r
+        assertWriteSupport();\r
+\r
+        Resource connection = getConnection(g, segment);\r
+        disconnect(segment);\r
+        Resource bp = newBranchPoint(connection, splitPos);\r
+        connect(segment.first(), bp);\r
+        connect(bp, segment.second());\r
+        return bp;\r
+    }\r
+\r
+    /**\r
+     * Joins the connection at the selected branch point if and only if the\r
+     * branch point is a route point, i.e. it is connected to two other branch\r
+     * points or connector.\r
+     * \r
+     * @param interiorRouteNode\r
+     * @return\r
+     */\r
+    public void join(Resource interiorRouteNode) throws DatabaseException {\r
+        assertWriteSupport();\r
+\r
+        if (!g.isInstanceOf(interiorRouteNode, br.DIA.InteriorRouteNode))\r
+            throw new ValidationException("'" + NameUtils.getSafeName(g, interiorRouteNode) + "' is not an instance of DIA.InteriorRouteNode");\r
+        @SuppressWarnings("unused")\r
+        Resource connection = getConnection(g, interiorRouteNode);\r
+        Collection<Resource> connectedTo = g.getObjects(interiorRouteNode, br.DIA.AreConnected);\r
+        if (connectedTo.size() != 2)\r
+            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
+        Iterator<Resource> it = connectedTo.iterator();\r
+        Resource connector1 = it.next();\r
+        Resource connector2 = it.next();\r
+        //System.out.println("removing branch point " + GraphUtils.getReadableName(g, routeBranchPoint) + " which is connected to " + GraphUtils.getReadableName(g, connector1) + " and " + GraphUtils.getReadableName(g, connector2));\r
+        removeConnectorOrBranchPoint(interiorRouteNode);\r
+        connect(connector1, connector2);\r
+    }\r
+\r
+    public void getConnectionSegments(Resource connection, Collection<EdgeResource> result) throws DatabaseException {\r
+\r
+        ArrayList<EdgeResource> edges = new ArrayList<EdgeResource>();\r
+        Set<Resource> visited = new HashSet<Resource>();\r
+        Stack<Resource> todo = new Stack<Resource>();\r
+\r
+        // Try to select input as root, this ensures correct order for simple paths\r
+        Collection<Resource> seeds = rg.getObjects(connection, br.DIA.HasArrowConnector);\r
+        if(seeds.isEmpty()) seeds = rg.getObjects(connection, br.DIA.HasPlainConnector);\r
+\r
+        assert(!seeds.isEmpty());\r
+\r
+        Resource seed = seeds.iterator().next();\r
+\r
+        todo.push(seed);\r
+\r
+        while(!todo.isEmpty()) {\r
+            Resource location = todo.pop();\r
+            if(!visited.contains(location)) {\r
+                visited.add(location);\r
+                for (Resource connectedTo : rg.getObjects(location, br.DIA.AreConnected)) {\r
+                    todo.add(connectedTo);\r
+                    EdgeResource edge = new EdgeResource(location, connectedTo);\r
+                    if(!edges.contains(edge)) edges.add(edge);\r
+                }\r
+            }\r
+        }\r
+\r
+        for(EdgeResource uer : edges) {\r
+//            System.out.println("loaded edge " + uer.first() + " " + uer.second());\r
+            result.add(uer);\r
+        }\r
+\r
+    }\r
+\r
+    public Collection<Resource> getBranchPoints(Resource connection, Collection<Resource> result) throws DatabaseException {\r
+        if (result == null)\r
+            result = new ArrayList<Resource>();\r
+        result.addAll(rg.getObjects(connection, br.DIA.HasBranchPoint));\r
+        return result;\r
+    }\r
+\r
+    public Collection<Resource> getConnectors(Resource connection, Collection<Resource> result) throws DatabaseException {\r
+        if (result == null)\r
+            result = new ArrayList<Resource>();\r
+        result.addAll(rg.getObjects(connection, br.DIA.HasConnector));\r
+        return result;\r
+    }\r
+\r
+    public Resource getConnectedComponent(Resource connection, Resource connector) throws DatabaseException {\r
+        for (Resource connects : rg.getObjects(connector, br.STR.Connects))\r
+            if (!connects.equals(connection))\r
+                return connects;\r
+        return null;\r
+    }\r
+\r
+    public Statement getConnectedComponentStatement(Resource connection, Resource connector) throws DatabaseException {\r
+        for (Statement connects : rg.getStatements(connector, br.STR.Connects))\r
+            if (!connects.getObject().equals(connection))\r
+                return connects;\r
+        return null;\r
+    }\r
+\r
+    public void gatherEdges(Resource connection, Collection<EdgeResource> result) throws DatabaseException {\r
+        Set<Object> visited = new HashSet<Object>();\r
+        for (Resource connector : rg.getObjects(connection, br.DIA.HasConnector)) {\r
+            for (Resource connectedTo : rg.getObjects(connector, br.DIA.AreConnected)) {\r
+                EdgeResource p = new EdgeResource(connector, connectedTo);\r
+                if (visited.add(p)) {\r
+                    result.add(p);\r
+                }\r
+            }\r
+        }\r
+        Collection<Resource> routeNodes = rg.getObjects(connection, br.DIA.HasInteriorRouteNode);\r
+        for (Resource routeNode : routeNodes) {\r
+            for (Resource connectedTo : rg.getObjects(routeNode, br.DIA.AreConnected)) {\r
+                EdgeResource p = new EdgeResource(routeNode, connectedTo);\r
+                if (visited.add(p)) {\r
+                    result.add(p);\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    public void gatherConnectionParts(Resource connection, Collection<Object> result) throws DatabaseException {\r
+        Set<Object> visited = new HashSet<Object>();\r
+        for (Resource connector : rg.getObjects(connection, br.DIA.HasConnector)) {\r
+            for (Resource connectedTo : rg.getObjects(connector, br.DIA.AreConnected)) {\r
+                EdgeResource p = new EdgeResource(connector, connectedTo);\r
+                if (visited.add(p)) {\r
+                    result.add(p);\r
+                }\r
+            }\r
+        }\r
+        Collection<Resource> routeNodes = rg.getObjects(connection, br.DIA.HasInteriorRouteNode);\r
+        for (Resource routeNode : routeNodes) {\r
+            result.add(routeNode);\r
+            for (Resource connectedTo : rg.getObjects(routeNode, br.DIA.AreConnected)) {\r
+                EdgeResource p = new EdgeResource(routeNode, connectedTo);\r
+                if (visited.add(p)) {\r
+                    result.add(p);\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    public Collection<Resource> getConnectedComponents(Resource connection, Collection<Resource> result)\r
+    throws DatabaseException {\r
+        if (result == null)\r
+            result = new ArrayList<Resource>(2);\r
+        for (Resource connector : rg.getObjects(connection, br.DIA.HasConnector)) {\r
+            for (Resource connects : rg.getObjects(connector, br.STR.Connects)) {\r
+                if (connects.equals(connection))\r
+                    continue;\r
+                result.add(connects);\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+\r
+    public Collection<Resource> getConnectedConnectors(Resource connection, Collection<Resource> result)\r
+    throws DatabaseException {\r
+        if (result == null)\r
+            result = new ArrayList<Resource>(2);\r
+        for (Resource connector : rg.getObjects(connection, br.DIA.HasConnector)) {\r
+            Resource connects = getConnectedComponent(connection, connector);\r
+            if (connects != null)\r
+                result.add( connector );\r
+        }\r
+        return result;\r
+    }\r
+\r
+    public Collection<Resource> getTerminalConnectors(Resource node, Collection<Resource> result)\r
+    throws DatabaseException {\r
+        if (result == null)\r
+            result = new ArrayList<Resource>(2);\r
+        result.addAll( rg.getObjects(node, br.STR.IsConnectedTo) );\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * @param g\r
+     * @param routeNode\r
+     * @return <code>null</code> if the specified resource is not part of any\r
+     *         connection\r
+     */\r
+    public static Resource tryGetConnection(ReadGraph g, Resource routeNode) throws DatabaseException {\r
+        DiagramResource dr = DiagramResource.getInstance(g);\r
+        Resource conn = g.getPossibleObject(routeNode, dr.IsConnectorOf);\r
+        if (conn == null)\r
+            conn = g.getPossibleObject(routeNode, dr.HasInteriorRouteNode_Inverse);\r
+        return conn;\r
+    }\r
+\r
+    public static Resource tryGetConnection(ReadGraph g, EdgeResource segment) throws DatabaseException {\r
+        Resource first = tryGetConnection(g, segment.first());\r
+        Resource second = tryGetConnection(g, segment.second());\r
+        if (first == null || second == null || !first.equals(second))\r
+            return null;\r
+        return first;\r
+    }\r
+\r
+    public static Resource tryGetMappedConnection(ReadGraph g, Resource connector) throws DatabaseException {\r
+        ModelingResources MOD = ModelingResources.getInstance(g);\r
+        return g.getPossibleObject(connector, MOD.ConnectorToConnection);\r
+    }\r
+    \r
+    public static Resource getConnection(ReadGraph g, EdgeResource segment) throws DatabaseException {\r
+        Resource first = tryGetConnection(g, segment.first());\r
+        Resource second = tryGetConnection(g, segment.second());\r
+        if (first == null && second == null)\r
+            throw new ValidationException(\r
+                    "neither connection segment end is attached to a Connection entity instance: "\r
+                    + segment.toString(g) + " - " + segment.toString());\r
+        if (first != null ^ second != null)\r
+            throw new ValidationException("both ends of connection segment "\r
+                    + segment.toString(g) + " - " + segment.toString() + " are not connected (first=" + first\r
+                    + ", second=" + second + ")");\r
+        if (!first.equals(second))\r
+            throw new ValidationException("connectors of connection segment "\r
+                    + segment.toString(g) + " - " + segment.toString() + " are part of different connections: " + first\r
+                    + " vs. " + second);\r
+        return first;\r
+    }\r
+\r
+    public static Resource getConnection(ReadGraph g, Resource routeNode) throws DatabaseException {\r
+        Resource connection = tryGetConnection(g, routeNode);\r
+        if (connection == null)\r
+            throw new ValidationException("route node '"\r
+                    + NameUtils.getSafeName(g, routeNode) + "' is not part of any connection");\r
+        return connection;\r
+    }\r
+\r
+    public static IConnectionPoint toConnectionPoint(ReadGraph g, Resource element, Terminal terminal) throws DatabaseException {\r
+        if (terminal instanceof ResourceTerminal) {\r
+            Resource t = ((ResourceTerminal) terminal).getResource();\r
+\r
+            // TODO: remove this hack\r
+            if (element == null) { // Flag?\r
+                DiagramResource DIA = DiagramResource.getInstance(g);\r
+                Resource join = g.getPossibleObject(t, DIA.FlagIsJoinedBy);\r
+                if (join == null)\r
+                    return null;\r
+                return new CPConnectionJoin(join);\r
+            }\r
+            // element should be :Element\r
+            Resource bindingRelation = DiagramGraphUtil.getConnectionPointOfTerminal(g, t);\r
+            return new CPTerminal(element, bindingRelation);\r
+        } else if (terminal instanceof BranchPointTerminal) {\r
+            // element should be either : DIA.InteriorRouteNode or DIA.Connection\r
+            Resource connection = null;\r
+            DiagramResource DIA = DiagramResource.getInstance(g);\r
+            if (g.isInstanceOf(element, DIA.Connection))\r
+                connection = element;\r
+            else\r
+                connection = getConnection(g, element);\r
+            return new CPConnection(connection);\r
+        }\r
+        throw new IllegalArgumentException("Unrecognized Terminal class: " + terminal);\r
+    }\r
+\r
+    public static IConnectionPoint toConnectionPoint(ReadGraph graph, IElement element, Terminal terminal) throws DatabaseException {\r
+        Resource r = (Resource) ElementUtils.getObject(element);\r
+        return ConnectionUtil.toConnectionPoint(graph, r, terminal);\r
+    }\r
+\r
+    public static IConnectionPoint toConnectionPoint(ReadGraph graph, TerminalInfo ti) throws DatabaseException {\r
+        return ConnectionUtil.toConnectionPoint(graph, ti.e, ti.t);\r
+    }\r
+\r
+    public static IConnectionPoint toConnectionPoint(ReadGraph graph, DesignatedTerminal t) throws DatabaseException {\r
+        return toConnectionPoint(graph, t.element, t.terminal);\r
+    }\r
+\r
+    public static Resource toDirectionTag(ReadGraph graph, BranchPoint.Direction direction) {\r
+        if (direction == null)\r
+            return null;\r
+\r
+        DiagramResource DIA = DiagramResource.getInstance(graph);\r
+        switch (direction) {\r
+            case Horizontal: return DIA.Horizontal;\r
+            case Vertical: return DIA.Vertical;\r
+            default: return null;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Copied from ConnectionCommandHandler#splitConnection, duplicate code.\r
+     * \r
+     * @param toCanvasPos\r
+     * @param onEdge\r
+     * @return\r
+     */\r
+    public static Line2D resolveNearestEdgeLineSegment(Point2D toCanvasPos, IElement onEdge) {\r
+        // Try to find an initial preferred direction for the new\r
+        // branch point based on the direction of the edge's line\r
+        // segment on which the split is done.\r
+        List<Point2D> points = new ArrayList<Point2D>();\r
+        BendsHandler bh = onEdge.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
+        if (bh != null)\r
+            org.simantics.g2d.utils.GeometryUtils.getPoints(bh.getPath(onEdge), points);\r
+        Line2D nearestLine = null;\r
+        double nearestDistanceToLine = Double.MAX_VALUE;\r
+        for (int i = 0; i < points.size() - 1; ++i) {\r
+            Point2D p1 = points.get(i);\r
+            Point2D p2 = points.get(i+1);\r
+            double distanceToLine = GeometryUtils.distanceFromLine(toCanvasPos, p1, p2);\r
+            if (distanceToLine < nearestDistanceToLine) {\r
+                nearestDistanceToLine = distanceToLine;\r
+                if (nearestLine == null)\r
+                    nearestLine = new Line2D.Double();\r
+                nearestLine.setLine(p1, p2);\r
+            }\r
+        }\r
+        return nearestLine;\r
+    }\r
+\r
+    /**\r
+     * @param object\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    public static IElement getSingleEdge(Object object) throws DatabaseException {\r
+        IElement e = AdaptionUtils.adaptToSingle(object, IElement.class);\r
+        if (e == null)\r
+            return null;\r
+\r
+        if (PickFilter.FILTER_CONNECTION_EDGES.accept(e))\r
+            return e;\r
+\r
+        if (PickFilter.FILTER_CONNECTIONS.accept(e)) {\r
+            ConnectionHandler ch = e.getElementClass().getSingleItem(ConnectionHandler.class);\r
+            Collection<IElement> bps = ch.getBranchPoints(e, null);\r
+            Collection<IElement> segs = ch.getSegments(e, null);\r
+            if (bps.isEmpty() && segs.size() == 1)\r
+                return segs.iterator().next();\r
+        }\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * @param object\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    public static Collection<IElement> getEdges(Object object) throws DatabaseException {\r
+        IElement e = AdaptionUtils.adaptToSingle(object, IElement.class);\r
+        if (e == null)\r
+            return null;\r
+\r
+        if (PickFilter.FILTER_CONNECTION_EDGES.accept(e))\r
+            return Collections.singleton(e);\r
+\r
+        if (PickFilter.FILTER_CONNECTIONS.accept(e)) {\r
+            ConnectionHandler ch = e.getElementClass().getSingleItem(ConnectionHandler.class);\r
+            return ch.getSegments(e, null);\r
+        }\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param connection\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    public static Resource getConnectionTailNode(ReadGraph graph, Resource connection) throws DatabaseException {\r
+        DiagramResource DIA = DiagramResource.getInstance(graph);\r
+        StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
+        for (Resource connector : graph.getObjects(connection, DIA.HasTailConnector)) {\r
+            for (Resource node : graph.getObjects(connector, STR.Connects)) {\r
+                if (node.equals(connection))\r
+                    continue;\r
+                return node;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param connection\r
+     * @return the STR.Connects statement from the tail DIA.Connector to the\r
+     *         tail node (DIA.Element) or <code>null</code> if no tail node\r
+     *         exists\r
+     * @throws DatabaseException\r
+     */\r
+    public static Statement getConnectionTailNodeStatement(ReadGraph graph, Resource connection) throws DatabaseException {\r
+        DiagramResource DIA = DiagramResource.getInstance(graph);\r
+        StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
+        for (Resource connector : graph.getObjects(connection, DIA.HasTailConnector)) {\r
+            for (Statement connects : graph.getStatements(connector, STR.Connects)) {\r
+                if (connects.getObject().equals(connection))\r
+                    continue;\r
+                return connects;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * @param connection\r
+     * @param dx\r
+     * @param dy\r
+     * @throws DatabaseException\r
+     */\r
+    public void translateRouteNodes(Resource connection, double dx, double dy) throws DatabaseException {\r
+        Commands.get(g, "Simantics/Diagram/translateRouteNodes")\r
+            .execute(g, g.syncRequest(new IndexRoot(connection)), connection, dx, dy);\r
+    }\r
+    \r
+    public static void translateRouteNodes(WriteGraph g, Resource connection, double dx, double dy) throws DatabaseException {\r
+        DiagramResource DIA = DiagramResource.getInstance(g);\r
+        for (Resource routeNode : g.getObjects(connection, DIA.HasInteriorRouteNode)) {\r
+            if (g.isInstanceOf(routeNode, DIA.RouteLine)) {\r
+                Double pos = g.getRelatedValue(routeNode, DIA.HasPosition, Bindings.DOUBLE);\r
+                Boolean isHorizontal = g.getRelatedValue(routeNode, DIA.IsHorizontal, Bindings.BOOLEAN);\r
+                pos += isHorizontal ? dy : dx;\r
+                g.claimLiteral(routeNode, DIA.HasPosition, pos);\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Creates a route graph connection which has corners at the specified\r
+     * locations, starting/ending from/at the specified element terminals.\r
+     * \r
+     * if {@link Development#DEVELOPMENT} is <code>true</code> the code will\r
+     * verify that both element end-points are part of the same diagram.\r
+     * \r
+     * @param graph\r
+     *            database write access\r
+     * @param startElement\r
+     *            element to start connecting from\r
+     * @param startConnectionPoint\r
+     *            STR.ConnectedTo relation of the start element terminal\r
+     * @param endElement\r
+     *            element to end the connection at\r
+     * @param endConnectionPoint\r
+     *            STR.ConnectedTo relation of the end element terminal\r
+     * @param corners\r
+     *            the corners to create for the connection\r
+     * @return the created diagram connection resource\r
+     * @throws DatabaseException\r
+     */\r
+    public Resource createConnectionWithCorners(WriteGraph graph, Resource startElement,\r
+            Resource startConnectionPoint, Resource endElement, Resource endConnectionPoint, List<Point2D> corners)\r
+                    throws DatabaseException {\r
+        DiagramResource DIA = br.DIA;\r
+\r
+        // Verify that both elements are part of the same diagram before connecting.\r
+        if (Development.DEVELOPMENT) {\r
+            Collection<Resource> startDiagram = OrderedSetUtils.getOwnerLists(graph, startElement, DIA.Diagram);\r
+            Collection<Resource> endDiagram = OrderedSetUtils.getOwnerLists(graph, endElement, DIA.Diagram);\r
+            if (Collections.disjoint(startDiagram, endDiagram))\r
+                throw new IllegalArgumentException("start element " + startElement\r
+                        + " is not on same diagram as end element " + endElement + ". start diagram: " + startDiagram\r
+                        + ", end diagram: " + endDiagram);\r
+        }\r
+\r
+        return createConnectionWithCorners(graph, DIA.RouteGraphConnection, startElement,\r
+                startConnectionPoint, DIA.HasPlainConnector, endElement, endConnectionPoint, DIA.HasArrowConnector,\r
+                corners);\r
+    }\r
+\r
+    /**\r
+     * Creates a route graph connection which has corners at the specified\r
+     * locations, starting/ending from/at the specified element terminals.\r
+     * Verifies that both element end-points are part of the same diagram.\r
+     * \r
+     * @param graph database write access\r
+     * @param connectionType type of created connection (e.g. DIA.RouteGraphConnection)\r
+     * @param element1 element to start connecting from\r
+     * @param connectionPoint1 STR.ConnectedTo relation of the start element terminal \r
+     * @param hasConnector1 connector to connection attachment relation to use for element1 and connectionPoint1\r
+     * @param element2 element to end the connection at\r
+     * @param connectionPoint2 STR.ConnectedTo relation of the end element terminal\r
+     * @param hasConnector2 connector to connection attachment relation to use for element2 and connectionPoint2\r
+     * @param corners the corners to create for the connection\r
+     * @return the created diagram connection resource\r
+     * @throws DatabaseException\r
+     */\r
+    public Resource createConnectionWithCorners(WriteGraph graph, Resource connectionType,\r
+            Resource element1, Resource connectionPoint1, Resource hasConnector1, Resource element2,\r
+            Resource connectionPoint2, Resource hasConnector2, List<Point2D> corners)\r
+            throws DatabaseException {\r
+        DiagramResource DIA = br.DIA;\r
+\r
+        if (corners.size() == 1)\r
+            throw new UnsupportedOperationException("1 corner currently not supported");\r
+\r
+        Resource connection = newInstance(g, connectionType);\r
+        Resource connector1 = newConnector(connection, hasConnector1);\r
+        Resource connector2 = newConnector(connection, hasConnector2);\r
+        graph.claim(element1, connectionPoint1, connector1);\r
+        graph.claim(element2, connectionPoint2, connector2);\r
+\r
+        if (corners.size() > 1) {\r
+            boolean horizontal;\r
+            Resource previousRouteNode = connector1;\r
+            for (int i=0; i<corners.size()-1; ++i) {\r
+                Point2D p = corners.get(i);\r
+                Point2D p1 = corners.get(i+1);\r
+                horizontal = Math.abs(p1.getY() - p.getY()) < Math.abs(p1.getX() - p.getX());\r
+                Resource routeLine = ConnectionUtil.createRouteline(graph, connection, horizontal ? p.getY() : p.getX(), horizontal);\r
+                graph.claim(previousRouteNode, DIA.AreConnected, DIA.AreConnected, routeLine);\r
+                previousRouteNode = routeLine;\r
+            }\r
+            graph.claim(previousRouteNode, DIA.AreConnected, DIA.AreConnected, connector2);\r
+        } else {\r
+            graph.claim(connector1, DIA.AreConnected, DIA.AreConnected, connector2);\r
+        }\r
+\r
+        return connection;\r
+    }\r
+\r
+    public Resource createConnectionWithSingleLine(WriteGraph graph, Resource connectionType,\r
+               Collection<Triple<Resource,Resource,Resource>> endpoints,\r
+               double coordinate, boolean horizontal)\r
+            throws DatabaseException {\r
+\r
+       DiagramResource DIA = br.DIA;\r
+\r
+        Resource connection = newInstance(g, connectionType);\r
+\r
+        Resource routeLine = ConnectionUtil.createRouteline(graph, connection, coordinate, horizontal);\r
+\r
+        for(Triple<Resource,Resource,Resource> endpoint : endpoints) {\r
+               Resource connector = newConnector(connection, endpoint.third);  \r
+            graph.claim(endpoint.first, endpoint.second, connector);\r
+            graph.claim(routeLine, DIA.AreConnected, DIA.AreConnected, connector);\r
+        }\r
+\r
+        return connection;\r
+        \r
+    }\r
+\r
+    public Resource createConnection(WriteGraph graph, Resource connectionType,\r
+               List<Triple<Resource,Resource,Resource>> terminals,\r
+               List<Pair<Double, Boolean>> routeLines,\r
+               List<Pair<Integer,Integer>> connections) throws DatabaseException {\r
+\r
+       DiagramResource DIA = br.DIA;\r
+\r
+        Resource connection = newInstance(g, connectionType);\r
+\r
+        Resource[] parts = new Resource[terminals.size() + routeLines.size()];\r
+        \r
+        int index = 0;\r
+        \r
+        for(Triple<Resource,Resource,Resource> terminal : terminals) {\r
+               Resource connector = newConnector(connection, terminal.third);  \r
+            graph.claim(terminal.first, terminal.second, connector);\r
+            parts[index++] = connector;\r
+        }\r
+\r
+        for(Pair<Double, Boolean> routeLine : routeLines) {\r
+            Resource r = ConnectionUtil.createRouteline(graph, connection, routeLine.first, routeLine.second);\r
+            parts[index++] = r;\r
+        }\r
+        \r
+//        System.err.println("Connect " + parts.length + " parts.");\r
+        \r
+        for(Pair<Integer,Integer> conn : connections) {\r
+//            System.err.println("-" + conn.first + " " + conn.second);\r
+               Resource part1 = parts[conn.first];\r
+               Resource part2 = parts[conn.second];\r
+            graph.claim(part1, DIA.AreConnected, DIA.AreConnected, part2);\r
+        }\r
+\r
+        return connection;\r
+        \r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param connection\r
+     * @param pos\r
+     * @param isHorizontal\r
+     * @return new route line that is attached to the specified diagram connection\r
+     * @throws DatabaseException\r
+     */\r
+    public static Resource createRouteline(WriteGraph graph, Resource connection, double pos, boolean isHorizontal) throws DatabaseException {\r
+        Layer0 L0 = Layer0.getInstance(graph);\r
+        DiagramResource DIA = DiagramResource.getInstance(graph);\r
+        Resource routeLine = graph.newResource();\r
+        graph.claim(routeLine, L0.InstanceOf, null, DIA.RouteLine);\r
+        graph.addLiteral(routeLine, DIA.HasPosition, DIA.HasPosition_Inverse, L0.Double, pos, Bindings.DOUBLE);\r
+        graph.addLiteral(routeLine, DIA.IsHorizontal, DIA.IsHorizontal_Inverse, L0.Boolean, isHorizontal, Bindings.BOOLEAN);\r
+        graph.claim(connection, DIA.HasInteriorRouteNode, DIA.HasInteriorRouteNode_Inverse, routeLine);\r
+        return routeLine;\r
+    }\r
+\r
+    /**\r
+     * @param graph database write-only access\r
+     * @param type type of the created resource\r
+     * @return new instance of type\r
+     * @throws DatabaseException\r
+     */\r
+    private Resource newInstance(WriteOnlyGraph graph, Resource type) throws DatabaseException {\r
+        Resource connection = graph.newResource();\r
+        g.claim(connection, br.L0.InstanceOf, null, type);\r
+        return connection;\r
+    }\r
+\r
+}\r