--- /dev/null
+/*******************************************************************************\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