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