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