]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/participant/ConnectionBuilder.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / participant / ConnectionBuilder.java
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/participant/ConnectionBuilder.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/participant/ConnectionBuilder.java
new file mode 100644 (file)
index 0000000..6b78af1
--- /dev/null
@@ -0,0 +1,919 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2016 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
+ *     Semantum Oy - Fixed bug #6364\r
+ *******************************************************************************/\r
+package org.simantics.diagram.participant;\r
+\r
+import java.awt.geom.AffineTransform;\r
+import java.util.ArrayDeque;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Deque;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+import org.eclipse.jface.dialogs.MessageDialog;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.ui.IWorkbenchWindow;\r
+import org.eclipse.ui.PlatformUI;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.Statement;\r
+import org.simantics.db.VirtualGraph;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.CommentMetadata;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.common.utils.OrderedSetUtils;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.diagram.content.ConnectionUtil;\r
+import org.simantics.diagram.content.ResourceTerminal;\r
+import org.simantics.diagram.flag.DiagramFlagPreferences;\r
+import org.simantics.diagram.flag.FlagLabelingScheme;\r
+import org.simantics.diagram.flag.FlagUtil;\r
+import org.simantics.diagram.flag.IOTableUtil;\r
+import org.simantics.diagram.flag.IOTablesInfo;\r
+import org.simantics.diagram.flag.Joiner;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.diagram.stubs.G2DResource;\r
+import org.simantics.diagram.synchronization.ISynchronizationContext;\r
+import org.simantics.diagram.synchronization.SynchronizationHints;\r
+import org.simantics.diagram.synchronization.graph.AddElement;\r
+import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
+import org.simantics.diagram.synchronization.graph.GraphSynchronizationHints;\r
+import org.simantics.diagram.synchronization.graph.RemoveElement;\r
+import org.simantics.diagram.synchronization.graph.layer.GraphLayerManager;\r
+import org.simantics.diagram.ui.DiagramModelHints;\r
+import org.simantics.g2d.connection.handler.ConnectionHandler;\r
+import org.simantics.g2d.diagram.DiagramHints;\r
+import org.simantics.g2d.diagram.IDiagram;\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.ElementClass;\r
+import org.simantics.g2d.element.ElementClasses;\r
+import org.simantics.g2d.element.ElementHints;\r
+import org.simantics.g2d.element.ElementUtils;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.element.IElementClassProvider;\r
+import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
+import org.simantics.g2d.element.impl.Element;\r
+import org.simantics.g2d.elementclass.BranchPoint;\r
+import org.simantics.g2d.elementclass.FlagClass;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.scl.runtime.tuple.Tuple2;\r
+import org.simantics.structural.stubs.StructuralResource2;\r
+import org.simantics.structural2.modelingRules.CPTerminal;\r
+import org.simantics.structural2.modelingRules.ConnectionJudgement;\r
+import org.simantics.structural2.modelingRules.IConnectionPoint;\r
+import org.simantics.structural2.modelingRules.IModelingRules;\r
+import org.simantics.utils.datastructures.Callback;\r
+import org.simantics.utils.datastructures.Pair;\r
+import org.simantics.utils.ui.ErrorLogger;\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class ConnectionBuilder {\r
+\r
+    protected static class Connector extends Tuple2 {\r
+        public Connector(Resource attachmentRelation, Resource connector) {\r
+            super(attachmentRelation, connector);\r
+        }\r
+        public Resource getAttachment() {\r
+            return (Resource) c0;\r
+        }\r
+        public Resource getConnector() {\r
+            return (Resource) c1;\r
+        }\r
+    }\r
+\r
+    protected final IDiagram                diagram;\r
+    protected final Resource                diagramResource;\r
+    protected final boolean                 createFlags;\r
+\r
+    protected final ISynchronizationContext ctx;\r
+    protected final IElementClassProvider   elementClassProvider;\r
+    protected final GraphLayerManager       layerManager;\r
+\r
+    protected ConnectionUtil                cu;\r
+\r
+    protected Layer0                        L0;\r
+    protected DiagramResource               DIA;\r
+    protected StructuralResource2           STR;\r
+    protected ModelingResources             MOD;\r
+\r
+    public ConnectionBuilder(IDiagram diagram) {\r
+        this.diagram = diagram;\r
+        this.diagramResource = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);\r
+        this.createFlags = Boolean.TRUE.equals(diagram.getHint(DiagramHints.KEY_USE_CONNECTION_FLAGS));\r
+\r
+        ctx = diagram.getHint(SynchronizationHints.CONTEXT);\r
+        if (ctx != null) {\r
+            this.elementClassProvider = ctx.get(SynchronizationHints.ELEMENT_CLASS_PROVIDER);\r
+            this.layerManager = ctx.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER);\r
+        } else {\r
+            this.elementClassProvider = null;\r
+            this.layerManager = null;\r
+        }\r
+    }\r
+\r
+    protected void initializeResources(ReadGraph graph) {\r
+        if (this.L0 == null) {\r
+            this.L0 = Layer0.getInstance(graph);\r
+            this.DIA = DiagramResource.getInstance(graph);\r
+            this.STR = StructuralResource2.getInstance(graph);\r
+            this.MOD = ModelingResources.getInstance(graph);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param judgment\r
+     * @param controlPoints\r
+     * @param startTerminal\r
+     * @param endTerminal\r
+     * @throws DatabaseException\r
+     */\r
+    public void create(WriteGraph graph, final ConnectionJudgement judgment, Deque<ControlPoint> controlPoints,\r
+            TerminalInfo startTerminal, TerminalInfo endTerminal) throws DatabaseException {\r
+        this.cu = new ConnectionUtil(graph);\r
+        initializeResources(graph);\r
+\r
+        final IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES);\r
+\r
+        final Resource startDisconnectedFlag = getDisconnectedFlag(graph, startTerminal);\r
+        final Resource endDisconnectedFlag = getDisconnectedFlag(graph, endTerminal);\r
+        if (startDisconnectedFlag != null || endDisconnectedFlag != null) {\r
+            if (startDisconnectedFlag != null && endDisconnectedFlag != null) {\r
+\r
+                // Ask the user which operation to perform:\r
+                // a) connect the disconnected flags together with a connection join\r
+                // b) join the flags into a single connection\r
+\r
+                final VirtualGraph graphProvider = graph.getProvider();\r
+                final Session session = graph.getSession();\r
+\r
+                PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {\r
+                    @Override\r
+                    public void run() {\r
+                        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();\r
+                        if (window == null)\r
+                            return;\r
+                        MessageDialog dialog = new MessageDialog(window.getShell(), "Connect or Join Flags?", null,\r
+                                "Connect flags together or join them visually into a connection?",\r
+                                MessageDialog.QUESTION_WITH_CANCEL, new String[] { "Connect Flags", "Join Flags",\r
+                                        "Cancel" }, 0) {\r
+                            {\r
+                                setShellStyle(getShellStyle() | SWT.SHEET);\r
+                            }\r
+                        };\r
+                        final int choice = dialog.open();\r
+\r
+                        if (choice != 2 && choice != SWT.DEFAULT) {\r
+                            session.asyncRequest(new WriteRequest(graphProvider) {\r
+                                @Override\r
+                                public void perform(WriteGraph graph) throws DatabaseException {\r
+                                    graph.markUndoPoint();\r
+                                    switch (choice) {\r
+                                        case 0: {\r
+                                            Resource join = FlagUtil.join(graph, startDisconnectedFlag, endDisconnectedFlag);\r
+                                            FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);\r
+                                            String commonLabel = scheme.generateLabel(graph, diagramResource);\r
+                                            graph.claimLiteral(startDisconnectedFlag, L0.HasLabel, DIA.FlagLabel, commonLabel);\r
+                                            graph.claimLiteral(endDisconnectedFlag, L0.HasLabel, DIA.FlagLabel, commonLabel);\r
+\r
+                                            // Set connection type according to modeling rules\r
+                                            setJoinedConnectionTypes(graph, modelingRules, judgment, join);\r
+\r
+                                            // Add comment to change set.\r
+                                            CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
+                                            graph.addMetadata(cm.add("Connected flags"));\r
+\r
+                                            return;\r
+                                        }\r
+                                        case 1: {\r
+                                            // First connect the flags together\r
+                                            Resource join = FlagUtil.join(graph, startDisconnectedFlag, endDisconnectedFlag);\r
+\r
+                                            // Set connection type according to modeling rules\r
+                                            setJoinedConnectionTypes(graph, modelingRules, judgment, join);\r
+\r
+                                            // Join the flags into a direct connection\r
+                                            new Joiner(graph).joinLocal(graph, Arrays.asList(startDisconnectedFlag, endDisconnectedFlag));\r
+\r
+                                            // Add comment to change set.\r
+                                            CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
+                                            graph.addMetadata(cm.add("Joined flags"));\r
+\r
+                                            return;\r
+                                        }\r
+                                    }\r
+                                }\r
+                            }, new Callback<DatabaseException>() {\r
+                                @Override\r
+                                public void run(DatabaseException e) {\r
+                                    if (e != null)\r
+                                        ErrorLogger.defaultLogError(e);\r
+                                }\r
+                            });\r
+                        }\r
+                    }\r
+                });\r
+\r
+                return;\r
+            }\r
+\r
+            TerminalInfo normalTerminal = null;\r
+            Resource flagToRemove = null;\r
+            Resource connection = null;\r
+            if (startDisconnectedFlag != null) {\r
+                flagToRemove = startDisconnectedFlag;\r
+                normalTerminal = endTerminal;\r
+                connection = attachedToExistingConnection(graph, startTerminal);\r
+            }\r
+            if (endDisconnectedFlag != null) {\r
+                flagToRemove = endDisconnectedFlag;\r
+                normalTerminal = startTerminal;\r
+                connection = attachedToExistingConnection(graph, endTerminal);\r
+            }\r
+            if (connection != null) {\r
+                // OK, continuing a connection from an existing disconnected flag.\r
+\r
+                // STEPS TO PERFORM:\r
+                // 1. remove flag\r
+                // 2. connect normal terminal directly to the existing connection\r
+                Statement stm = graph.getSingleStatement(flagToRemove, STR.IsConnectedTo);\r
+                Collection<Resource> areConnecteds = graph.getObjects(stm.getObject(), DIA.AreConnected);\r
+\r
+                // Remove statement to connection connector before removing flag\r
+                // to prevent removal of connector and the connection.\r
+                graph.deny(stm);\r
+                new RemoveElement((Resource)diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), flagToRemove).perform(graph);\r
+\r
+                // Disconnect the connector from the connection and create a\r
+                // new connector for the element terminal.\r
+                cu.removeConnectionPart(stm.getObject());\r
+                Connector newConnector = createConnectorForNode(graph, connection,\r
+                        (Resource) ElementUtils.getObject(normalTerminal.e), normalTerminal.t,\r
+                        startDisconnectedFlag != null ? EdgeEnd.End : EdgeEnd.Begin, judgment);\r
+\r
+                for (Resource areConnected : areConnecteds)\r
+                    graph.claim(newConnector.getConnector(), DIA.AreConnected, areConnected);\r
+\r
+                if (modelingRules != null && judgment.connectionType != null)\r
+                    modelingRules.setConnectionType(graph, connection, judgment.connectionType);\r
+\r
+                // Add comment to change set.\r
+                CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
+                graph.addMetadata(cm.add("Joined flags"));\r
+                graph.markUndoPoint();\r
+                this.cu = null;\r
+                return;\r
+            }\r
+        }\r
+\r
+        // 1. Get diagram connection to construct.\r
+        Resource connection = getOrCreateConnection(graph, startTerminal, endTerminal);\r
+\r
+        // 1.1 Give running name to connection and increment the counter attached to the diagram.\r
+        AddElement.claimFreshElementName(graph, diagramResource, connection);\r
+\r
+        // 2. Add branch points\r
+        // 3. Create edges between branch points.\r
+        List<Pair<ControlPoint, Resource>> bps = Collections.emptyList();\r
+        Resource firstBranchPoint = null;\r
+        Resource lastBranchPoint = null;\r
+        if (!isRouteGraphConnection(graph, connection)) {\r
+            bps = createBranchPoints(graph, connection, controlPoints);\r
+            if (!bps.isEmpty()) {\r
+                Iterator<Pair<ControlPoint, Resource>> it = bps.iterator();\r
+                Pair<ControlPoint, Resource> prev = it.next();\r
+                firstBranchPoint = prev.second;\r
+                while (it.hasNext()) {\r
+                    Pair<ControlPoint, Resource> next = it.next();\r
+                    cu.connect(prev.second, next.second);\r
+                    prev = next;\r
+                }\r
+                lastBranchPoint = prev.second;\r
+            }\r
+        }\r
+\r
+        // 4. Connect start/end terminals if those exist.\r
+        // If first/lastBranchPoint != null, connect to those.\r
+        // Otherwise connect the start/end terminals together.\r
+        Connector startConnector = null;\r
+        Connector endConnector = null;\r
+        IElement startFlag = null;\r
+        IElement endFlag = null;\r
+\r
+        //FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);\r
+\r
+        if (startTerminal != null && endTerminal != null) {\r
+            Resource startAttachment = chooseAttachmentRelationForNode(graph, connection, startTerminal, judgment);\r
+            Resource endAttachment = chooseAttachmentRelationForNode(graph, connection, endTerminal, judgment);\r
+            Pair<Resource, Resource> attachments = resolveEndAttachments(graph, startAttachment, endAttachment);\r
+            startConnector = createConnectorForNodeWithAttachment(graph, connection, startTerminal, attachments.first);\r
+            endConnector = createConnectorForNodeWithAttachment(graph, connection, endTerminal, attachments.second);\r
+        } else if (startTerminal != null) {\r
+            startConnector = createConnectorForNode(graph, connection, startTerminal, EdgeEnd.Begin, judgment);\r
+            if (createFlags) {\r
+                EdgeEnd flagEnd = cu.toEdgeEnd( cu.getAttachmentRelationForConnector(startConnector.getConnector()), EdgeEnd.End ).other();\r
+                endFlag = createFlag(graph, connection, flagEnd, controlPoints.getLast(), FlagClass.Type.Out,\r
+                        //scheme.generateLabel(graph, diagramResource));\r
+                        null);\r
+                endConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(endFlag),\r
+                        ElementUtils.getSingleTerminal(endFlag), flagEnd, judgment);\r
+            }\r
+        } else if (endTerminal != null) {\r
+            endConnector = createConnectorForNode(graph, connection, endTerminal, EdgeEnd.End, judgment);\r
+            if (createFlags) {\r
+                EdgeEnd flagEnd = cu.toEdgeEnd( cu.getAttachmentRelationForConnector(endConnector.getConnector()), EdgeEnd.Begin ).other();\r
+                startFlag = createFlag(graph, connection, flagEnd, controlPoints.getFirst(), FlagClass.Type.In,\r
+                        //scheme.generateLabel(graph, diagramResource));\r
+                        null);\r
+                startConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(startFlag),\r
+                        ElementUtils.getSingleTerminal(startFlag), flagEnd, judgment);\r
+            }\r
+        } else if (createFlags) {\r
+            startFlag = createFlag(graph, connection, EdgeEnd.Begin, controlPoints.getFirst(), FlagClass.Type.In,\r
+                    //scheme.generateLabel(graph, diagramResource));\r
+                    null);\r
+            startConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(startFlag),\r
+                    ElementUtils.getSingleTerminal(startFlag), EdgeEnd.Begin, judgment);\r
+\r
+            endFlag = createFlag(graph, connection, EdgeEnd.End, controlPoints.getLast(), FlagClass.Type.Out,\r
+                    //scheme.generateLabel(graph, diagramResource));\r
+                    null);\r
+            endConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(endFlag),\r
+                    ElementUtils.getSingleTerminal(endFlag), EdgeEnd.End, judgment);\r
+        }\r
+\r
+        if (firstBranchPoint == null || lastBranchPoint == null) {\r
+            cu.connect(startConnector.getConnector(), endConnector.getConnector());\r
+        } else {\r
+            cu.connect(startConnector.getConnector(), firstBranchPoint);\r
+            cu.connect(lastBranchPoint, endConnector.getConnector());\r
+        }\r
+\r
+        // 5. Finally, set connection type according to modeling rules\r
+        if (judgment.connectionType != null && modelingRules != null)\r
+            modelingRules.setConnectionType(graph, connection, judgment.connectionType);\r
+\r
+        // 5.1 Verify created flag types\r
+        if (startFlag != null)\r
+            verifyFlagType(graph, modelingRules, startFlag);\r
+        if (endFlag != null)\r
+            verifyFlagType(graph, modelingRules, endFlag);\r
+\r
+        // 5.2 Write ConnectionMappingSpecification to connector if necessary\r
+        writeConnectionMappingSpecification(graph, startTerminal, startConnector, judgment.connectionType);\r
+        writeConnectionMappingSpecification(graph, endTerminal, endConnector, judgment.connectionType);\r
+\r
+        // 6. Add comment to change set.\r
+        CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
+        graph.addMetadata(cm.add("Added connection " + connection));\r
+        graph.markUndoPoint();\r
+        this.cu = null;\r
+    }\r
+\r
+    private boolean writeConnectionMappingSpecification(WriteGraph graph, TerminalInfo terminal, Connector connector, Resource connectionType)\r
+            throws DatabaseException {\r
+        Resource diagramConnRel = getConnectionRelation(graph, terminal);\r
+        if (diagramConnRel == null)\r
+            return false;\r
+        Resource connRel = graph.getPossibleObject(diagramConnRel, MOD.DiagramConnectionRelationToConnectionRelation);\r
+        if (connRel == null || !graph.hasStatement(connRel, MOD.NeedsConnectionMappingSpecification))\r
+            return false;\r
+        Resource mappingSpecification = graph.getPossibleObject(connectionType, MOD.ConnectionTypeToConnectionMappingSpecification);\r
+        if (mappingSpecification == null)\r
+            return false;\r
+        graph.claim(connector.getConnector(), MOD.HasConnectionMappingSpecification, null, mappingSpecification);\r
+        return true;\r
+    }\r
+\r
+    private static Resource getConnectionRelation(ReadGraph graph, TerminalInfo ti) throws DatabaseException {\r
+        if (ti != null && ti.t instanceof ResourceTerminal) {\r
+            Resource t = ((ResourceTerminal) ti.t).getResource();\r
+            Resource bindingRelation = DiagramGraphUtil.getConnectionPointOfTerminal(graph, t);\r
+            return bindingRelation;\r
+        }\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param startAttachment\r
+     * @param endAttachment\r
+     * @return\r
+     * @throws DatabaseException \r
+     */\r
+    protected Pair<Resource, Resource> resolveEndAttachments(WriteGraph graph,\r
+            Resource startAttachment, Resource endAttachment) throws DatabaseException {\r
+        if (startAttachment != null && endAttachment != null)\r
+            return Pair.make(startAttachment, endAttachment);\r
+\r
+        if (startAttachment != null && endAttachment == null)\r
+            return Pair.make(startAttachment, getInverseAttachment(graph, startAttachment, DIA.HasArrowConnector));\r
+        if (startAttachment == null && endAttachment != null)\r
+            return Pair.make(getInverseAttachment(graph, endAttachment, DIA.HasPlainConnector), endAttachment);\r
+\r
+        return Pair.make(DIA.HasPlainConnector, DIA.HasArrowConnector);\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param attachment\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    protected Resource getInverseAttachment(ReadGraph graph, Resource attachment, Resource defaultValue) throws DatabaseException {\r
+        Resource inverse = attachment != null ? graph.getPossibleObject(attachment, DIA.HasInverseAttachment) : defaultValue;\r
+        return inverse != null ? inverse : defaultValue;\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param modelingRules\r
+     * @param flagElement\r
+     * @throws DatabaseException\r
+     */\r
+    protected void verifyFlagType(WriteGraph graph, IModelingRules modelingRules, IElement flagElement) throws DatabaseException {\r
+        if (modelingRules != null) {\r
+            Resource flag = flagElement.getHint(ElementHints.KEY_OBJECT);\r
+            FlagClass.Type flagType = flagElement.getHint(FlagClass.KEY_FLAG_TYPE);\r
+            FlagUtil.verifyFlagType(graph, modelingRules, flag, flagType);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param judgment\r
+     * @param connection\r
+     * @param attachToLine\r
+     * @param controlPoints\r
+     * @param endTerminal\r
+     * @return the DIA.Connector instance created for attaching the connection\r
+     *         to the specified end terminal\r
+     * @throws DatabaseException\r
+     */\r
+    public Pair<Resource, Resource> attachToRouteGraph(\r
+            WriteGraph graph,\r
+            ConnectionJudgement judgment,\r
+            Resource attachToConnection,\r
+            Resource attachToLine,\r
+            Deque<ControlPoint> controlPoints,\r
+            TerminalInfo endTerminal)\r
+                    throws DatabaseException\r
+    {\r
+        initializeResources(graph);\r
+        this.cu = new ConnectionUtil(graph);\r
+        try {\r
+            Resource endElement = endTerminal != null ? ElementUtils.getObject(endTerminal.e) : null;\r
+            if (endElement != null\r
+                    && graph.isInstanceOf(endElement, DIA.Flag)\r
+                    && FlagUtil.isDisconnected(graph, endElement))\r
+            {\r
+                // Connection ends in an existing but disconnected flag that\r
+                // should be all right to connect to because the connection\r
+                // judgment implies it makes a valid connection.\r
+                // Check that we are attaching the connection to an existing\r
+                // disconnected flag that is however attached to a connection.\r
+                Resource endTerminalConnection = ConnectionBuilder.attachedToExistingConnection(graph, endTerminal);\r
+                if (endTerminalConnection != null) {\r
+                    attachConnectionToFlag(graph, judgment, attachToConnection, attachToLine, controlPoints, endTerminal);\r
+                    return null;\r
+                }\r
+            }\r
+\r
+            Connector endConnector = null;\r
+            if (endTerminal != null) {\r
+                endConnector = createConnectorForNode(graph, attachToConnection, endTerminal, EdgeEnd.End, judgment);\r
+            } else if (createFlags) {\r
+                IElement endFlag = createFlag(graph, attachToConnection, EdgeEnd.End, controlPoints.getLast(), FlagClass.Type.Out, null);\r
+                endConnector = createConnectorForNode(graph, attachToConnection, (Resource) ElementUtils.getObject(endFlag),\r
+                        ElementUtils.getSingleTerminal(endFlag), EdgeEnd.End, judgment);\r
+            }\r
+\r
+            cu.connect(attachToLine, endConnector.getConnector());\r
+\r
+            IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES);\r
+            if (judgment.connectionType != null && modelingRules != null) {\r
+                modelingRules.setConnectionType(graph, attachToConnection, judgment.connectionType);\r
+            }\r
+\r
+            writeConnectionMappingSpecification(graph, endTerminal, endConnector, judgment.connectionType);\r
+\r
+            CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
+            graph.addMetadata(cm.add("Branched connection " + attachToConnection));\r
+\r
+            return Pair.make(endConnector.getAttachment(), endConnector.getConnector());\r
+        } finally {\r
+            this.cu = null;\r
+        }\r
+    }\r
+\r
+    protected void attachConnectionToFlag(\r
+            WriteGraph graph,\r
+            ConnectionJudgement judgment,\r
+            Resource attachToConnection,\r
+            Resource attachToLine,\r
+            Deque<ControlPoint> controlPoints,\r
+            TerminalInfo toFlag)\r
+                    throws DatabaseException\r
+    {\r
+        // Attaching attachedConnection to an existing disconnected flag that is\r
+        // however attached to a connection.\r
+        // STEPS:\r
+        // 1. remove flag and its connector\r
+        // 2. attach the two connections together by moving the route nodes\r
+        //    of the removed flag-side connection under the remaining connection\r
+        //    and ensuring that the route node chain will be valid after the\r
+        //    switch. In a chain route lines, each line must have an opposite\r
+        //    direction compared to the lines connected to it.\r
+        Resource flagToRemove = ElementUtils.getObject(toFlag.e);\r
+        Statement flagToConnector = graph.getSingleStatement(flagToRemove, STR.IsConnectedTo);\r
+        Resource flagConnector = flagToConnector.getObject();\r
+        Resource flagConnection = ConnectionUtil.getConnection(graph, flagConnector);\r
+        Collection<Resource> flagRouteNodes = graph.getObjects(flagConnector, DIA.AreConnected);\r
+\r
+        // Remove flag and its connector.\r
+        graph.deny(flagToConnector);\r
+        new RemoveElement((Resource)diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), flagToRemove).perform(graph);\r
+        cu.removeConnectionPart(flagConnector);\r
+\r
+        // Attached routeline must have opposite direction than the line\r
+        // attached to in order for the connection to be valid.\r
+        Boolean attachingToHorizontalLine = graph.getPossibleRelatedValue(attachToLine, DIA.IsHorizontal, Bindings.BOOLEAN);\r
+        if (attachingToHorizontalLine != null) {\r
+            for (Resource routeNode : flagRouteNodes) {\r
+                Collection<Resource> routeNodesToAttachTo = removeUntilOrientedRouteline(graph, !attachingToHorizontalLine, routeNode);\r
+                for (Resource rn : routeNodesToAttachTo)\r
+                    cu.connect(attachToLine, rn);\r
+            }\r
+        }\r
+\r
+        for (Statement routeNode : graph.getStatements(flagConnection, DIA.HasInteriorRouteNode)) {\r
+            graph.deny(routeNode);\r
+            graph.claim(attachToConnection, routeNode.getPredicate(), routeNode.getObject());\r
+        }\r
+        for (Statement connector : graph.getStatements(flagConnection, DIA.HasConnector)) {\r
+            graph.deny(connector);\r
+            graph.claim(attachToConnection, connector.getPredicate(), connector.getObject());\r
+        }\r
+\r
+        CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
+        graph.addMetadata(cm.add("Joined connection to disconnected flag"));\r
+    }\r
+\r
+    private Collection<Resource> removeUntilOrientedRouteline(WriteGraph graph, boolean expectedOrientation, Resource routeNode) throws DatabaseException {\r
+        List<Resource> result = new ArrayList<>(2);\r
+        Deque<Resource> work = new ArrayDeque<>(2);\r
+        work.addLast(routeNode);\r
+        while (!work.isEmpty()) {\r
+            Resource rn = work.removeFirst();\r
+            if (graph.isInstanceOf(rn, DIA.RouteLine)) {\r
+                Boolean isHorizontal = graph.getPossibleRelatedValue(rn, DIA.IsHorizontal, Bindings.BOOLEAN);\r
+                if (isHorizontal != null && expectedOrientation != isHorizontal) {\r
+                    for (Resource rnn : graph.getObjects(rn, DIA.AreConnected))\r
+                        work.addLast(rnn);\r
+                    cu.removeConnectionPart(rn);\r
+                    continue;\r
+                }\r
+            }\r
+            result.add(rn);\r
+        }\r
+        return result;\r
+    }\r
+\r
+    protected boolean isRouteGraphConnection(ReadGraph graph, Resource connection) throws DatabaseException {\r
+        initializeResources(graph);\r
+        return graph.isInstanceOf(connection, DIA.RouteGraphConnection);\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param ti\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    public static Resource attachedToExistingConnection(ReadGraph graph, TerminalInfo ti) throws DatabaseException {\r
+        Object obj = ElementUtils.getObject(ti.e);\r
+        Resource cp = DiagramGraphUtil.getConnectionPointOfTerminal(graph, ti.t);\r
+        if (obj instanceof Resource && cp != null) {\r
+            Resource e = (Resource) obj;\r
+            for (Resource connector : graph.getObjects(e, cp)) {\r
+                Resource connection = ConnectionUtil.tryGetConnection(graph, connector);\r
+                if (connection != null)\r
+                    return connection;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param tis\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    public Resource getOrCreateConnection(ReadGraph graph, TerminalInfo... tis) throws DatabaseException {\r
+        // Resolve if adding to existing connection.\r
+        Resource connection = null;\r
+        for (TerminalInfo ti : tis) {\r
+            connection = getExistingConnection(graph, ti);\r
+            if (connection != null)\r
+                break;\r
+        }\r
+\r
+        if (connection == null) {\r
+            // No existing connection, create new.\r
+            ElementClass connectionClass = elementClassProvider.get(ElementClasses.CONNECTION);\r
+            Resource connectionClassResource = ElementUtils.checkedAdapt(connectionClass, Resource.class);\r
+            connection = cu.newConnection(diagramResource, connectionClassResource);\r
+        }\r
+\r
+        return connection;\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param connection\r
+     * @param controlPoints\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    public List<Pair<ControlPoint, Resource>> createBranchPoints(WriteGraph graph, Resource connection,\r
+            Collection<ControlPoint> controlPoints)    throws DatabaseException {\r
+        List<Pair<ControlPoint, Resource>> bps = new ArrayList<Pair<ControlPoint, Resource>>(controlPoints.size());\r
+        for(ControlPoint cp : controlPoints) {\r
+            if (cp.isAttachedToTerminal())\r
+                // Terminal attachments do not need branch points.\r
+                continue;\r
+\r
+            Resource bp = cu.newBranchPoint(connection,\r
+                    AffineTransform.getTranslateInstance(cp.getPosition().getX(), cp.getPosition().getY()),\r
+                    cp.getDirection());\r
+            bps.add(Pair.make(cp, bp));\r
+        }\r
+        return bps;\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param connection\r
+     * @param ti\r
+     * @param end\r
+     * @param judgment\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    protected Resource chooseAttachmentRelationForNode(ReadGraph graph,\r
+            Resource connection, TerminalInfo ti, ConnectionJudgement judgment)\r
+            throws DatabaseException {\r
+        Resource node = (Resource) ElementUtils.getObject(ti.e);\r
+        return chooseAttachmentRelationForNode(graph, connection, node, ti.t, judgment);\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param connection\r
+     * @param element\r
+     * @param terminal\r
+     * @param end\r
+     * @param judgment\r
+     * @return the calculated attachment relation or <code>null</code> if the\r
+     *         result is ambiguous\r
+     * @throws DatabaseException\r
+     */\r
+    protected Resource chooseAttachmentRelationForNode(ReadGraph graph,\r
+            Resource connection, Resource element, Terminal terminal,\r
+            ConnectionJudgement judgment) throws DatabaseException {\r
+        IConnectionPoint cp = ConnectionUtil.toConnectionPoint(graph, element, terminal);\r
+        CPTerminal cpt = (cp instanceof CPTerminal) ? (CPTerminal) cp : null;\r
+        Resource attachment = judgment.attachmentRelations.get(graph, cpt);\r
+        return attachment;\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param connection\r
+     * @param ti\r
+     * @param connectTo resource to connect the new connector to if not\r
+     *        <code>null</code>\r
+     * @param judgment\r
+     * @return <used attachment relation, the new DIA.Connector instance>. The\r
+     *         attachment relation is <code>null</code> if it was chosen based\r
+     *         on EdgeEnd instead of being defined\r
+     * @throws DatabaseException\r
+     */\r
+    protected Connector createConnectorForNode(WriteGraph graph, Resource connection, TerminalInfo ti, EdgeEnd end,\r
+            ConnectionJudgement judgment) throws DatabaseException {\r
+        Resource node = (Resource) ElementUtils.getObject(ti.e);\r
+        return createConnectorForNode(graph, connection, node, ti.t, end, judgment);\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param connection\r
+     * @param element\r
+     * @param terminal\r
+     * @param end\r
+     * @param connectTo\r
+     * @param judgment\r
+     * @return <used attachment relation, the new DIA.Connector instance>. The\r
+     *         attachment relation is <code>null</code> if it was chosen based\r
+     *         on EdgeEnd instead of being defined\r
+     * @throws DatabaseException\r
+     */\r
+    protected Connector createConnectorForNode(WriteGraph graph, Resource connection, Resource element, Terminal terminal,\r
+            EdgeEnd end, ConnectionJudgement judgment) throws DatabaseException {\r
+        IConnectionPoint cp = ConnectionUtil.toConnectionPoint(graph, element, terminal);\r
+        CPTerminal cpt = (cp instanceof CPTerminal) ? (CPTerminal) cp : null;\r
+        Resource attachment = judgment.attachmentRelations.get(graph, cpt);\r
+        if (attachment == null)\r
+            attachment = cu.toHasConnectorRelation(end);\r
+        Resource connector = cu.getOrCreateConnector(connection, element, terminal, end, attachment);\r
+        return new Connector(attachment, connector);\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param connection\r
+     * @param ti\r
+     * @param attachment\r
+     * @return <used attachment relation, the new DIA.Connector instance>\r
+     * @throws DatabaseException\r
+     */\r
+    protected Connector createConnectorForNodeWithAttachment(WriteGraph graph,\r
+            Resource connection, TerminalInfo ti, Resource attachment)\r
+            throws DatabaseException {\r
+        Resource node = (Resource) ElementUtils.getObject(ti.e);\r
+        return createConnectorForNodeWithAttachment(graph, connection, node, ti.t, attachment);\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param connection\r
+     * @param element\r
+     * @param terminal\r
+     * @param attachment\r
+     * @return <used attachment relation, the new DIA.Connector instance>\r
+     * @throws DatabaseException\r
+     */\r
+    protected Connector createConnectorForNodeWithAttachment(WriteGraph graph,\r
+            Resource connection, Resource element, Terminal terminal,\r
+            Resource attachment) throws DatabaseException {\r
+        Resource connector = cu.getOrCreateConnector(connection, element, terminal, null, attachment);\r
+        return new Connector(attachment, connector);\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param connection\r
+     * @param end\r
+     * @param cp\r
+     * @param type\r
+     * @param label <code>null</code> to leave flag without label\r
+     * @return an element describing the new created flag resource\r
+     * @throws DatabaseException\r
+     */\r
+    public IElement createFlag(WriteGraph graph, Resource connection, EdgeEnd end, ControlPoint cp,\r
+            FlagClass.Type type, String label) throws DatabaseException {\r
+        ElementClass flagClass = elementClassProvider.get(ElementClasses.FLAG);\r
+        IElement flagElement = Element.spawnNew(flagClass);\r
+        Resource flagClassResource = ElementUtils.checkedAdapt(flagClass, Resource.class);\r
+\r
+        Layer0 L0 = Layer0.getInstance(graph);\r
+        G2DResource G2D = G2DResource.getInstance(graph);\r
+        DiagramResource DIA = DiagramResource.getInstance(graph);\r
+\r
+        Resource flag = graph.newResource();\r
+        graph.claim(flag, L0.InstanceOf, null, flagClassResource);\r
+        flagElement.setHint(ElementHints.KEY_OBJECT, flag);\r
+\r
+        OrderedSetUtils.add(graph, diagramResource, flag);\r
+\r
+        AffineTransform at = AffineTransform.getTranslateInstance(cp.getPosition().getX(), cp.getPosition().getY());\r
+        flagElement.setHint(ElementHints.KEY_TRANSFORM, at);\r
+        double[] matrix = new double[6];\r
+        at.getMatrix(matrix);\r
+        graph.claimLiteral(flag, DIA.HasTransform, G2D.Transform, matrix);\r
+\r
+        flagElement.setHint(FlagClass.KEY_FLAG_TYPE, type);\r
+        graph.claim(flag, DIA.HasFlagType, null, DiagramGraphUtil.toFlagTypeResource(DIA, type));\r
+        if (label != null)\r
+            graph.claimLiteral(flag, L0.HasLabel, DIA.FlagLabel, label, Bindings.STRING);\r
+\r
+        // Give running name to flag and increment the counter attached to the diagram.\r
+        AddElement.claimFreshElementName(graph, diagramResource, flag);\r
+\r
+        // Make the diagram consist of the new element\r
+        graph.claim(diagramResource, L0.ConsistsOf, flag);\r
+\r
+        // Put the element on all the currently active layers if possible.\r
+        if (layerManager != null) {\r
+            layerManager.removeFromAllLayers(graph, flag);\r
+            layerManager.putElementOnVisibleLayers(diagram, graph, flag);\r
+        }\r
+        \r
+        // Add flag to possible IO table\r
+        IOTablesInfo ioTablesInfo = IOTableUtil.getIOTablesInfo(graph, \r
+                (Resource)diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE));\r
+        ioTablesInfo.updateBinding(graph, DIA, flag, at.getTranslateX(), at.getTranslateY());\r
+        \r
+        return flagElement;\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param ti\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    protected static Resource getExistingConnection(ReadGraph graph, TerminalInfo ti) throws DatabaseException {\r
+        if (ti != null) {\r
+            if (isConnection(ti.e)) {\r
+                Object obj = ElementUtils.getObject(ti.e);\r
+                if (obj instanceof Resource) {\r
+                    Resource c = (Resource) obj;\r
+                    return graph.isInstanceOf(c, DiagramResource.getInstance(graph).Connection) ? c : null;\r
+                }\r
+            } else if (isBranchPoint(ti.e)) {\r
+                Object obj = ElementUtils.getObject(ti.e);\r
+                if (obj instanceof Resource) {\r
+                    return ConnectionUtil.tryGetConnection(graph, (Resource) obj);\r
+                }\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    protected static boolean isConnection(IElement e) {\r
+        return e.getElementClass().containsClass(ConnectionHandler.class);\r
+    }\r
+\r
+    /**\r
+     * @param e\r
+     * @return\r
+     */\r
+    protected static boolean isBranchPoint(IElement e) {\r
+        return e.getElementClass().containsClass(BranchPoint.class);\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param terminal\r
+     * @return\r
+     * @throws DatabaseException \r
+     */\r
+    protected static Resource getDisconnectedFlag(ReadGraph graph, TerminalInfo terminal) throws DatabaseException {\r
+        if (terminal != null) {\r
+            Object obj = ElementUtils.getObject(terminal.e);\r
+            if (obj instanceof Resource) {\r
+                Resource flag = (Resource) obj;\r
+                if (graph.isInstanceOf(flag, DiagramResource.getInstance(graph).Flag)\r
+                        && FlagUtil.isDisconnected(graph, flag))\r
+                    return flag;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param modelingRules\r
+     * @param judgment\r
+     * @param join\r
+     * @throws DatabaseException\r
+     */\r
+    protected static void setJoinedConnectionTypes(WriteGraph graph, IModelingRules modelingRules,\r
+            ConnectionJudgement judgment, Resource join) throws DatabaseException {\r
+        if (modelingRules != null && judgment != null && judgment.connectionType != null) {\r
+            DiagramResource DIA = DiagramResource.getInstance(graph);\r
+            StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
+            List<Resource> connections = new ArrayList<Resource>(2);\r
+            for (Resource flag : graph.getObjects(join, DIA.FlagIsJoinedBy)) {\r
+                for (Resource connector : graph.getObjects(flag, STR.IsConnectedTo)) {\r
+                    Resource connection = ConnectionUtil.tryGetConnection(graph, connector);\r
+                    if (connection != null)\r
+                        connections.add(connection);\r
+                }\r
+            }\r
+            for (Resource connection : connections)\r
+                modelingRules.setConnectionType(graph, connection, judgment.connectionType);\r
+        }\r
+    }\r
+\r
+}
\ No newline at end of file