1 /*******************************************************************************
2 * Copyright (c) 2007, 2016 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 * Semantum Oy - Fixed bug #6364
12 *******************************************************************************/
13 package org.simantics.diagram.participant;
15 import java.awt.geom.AffineTransform;
16 import java.util.ArrayDeque;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.Deque;
22 import java.util.Iterator;
23 import java.util.List;
25 import org.eclipse.jface.dialogs.MessageDialog;
26 import org.eclipse.swt.SWT;
27 import org.eclipse.ui.IWorkbenchWindow;
28 import org.eclipse.ui.PlatformUI;
29 import org.simantics.databoard.Bindings;
30 import org.simantics.db.ReadGraph;
31 import org.simantics.db.Resource;
32 import org.simantics.db.Session;
33 import org.simantics.db.Statement;
34 import org.simantics.db.VirtualGraph;
35 import org.simantics.db.WriteGraph;
36 import org.simantics.db.common.CommentMetadata;
37 import org.simantics.db.common.request.WriteRequest;
38 import org.simantics.db.common.utils.OrderedSetUtils;
39 import org.simantics.db.exception.DatabaseException;
40 import org.simantics.diagram.content.ConnectionUtil;
41 import org.simantics.diagram.content.ResourceTerminal;
42 import org.simantics.diagram.flag.DiagramFlagPreferences;
43 import org.simantics.diagram.flag.FlagLabelingScheme;
44 import org.simantics.diagram.flag.FlagUtil;
45 import org.simantics.diagram.flag.IOTableUtil;
46 import org.simantics.diagram.flag.IOTablesInfo;
47 import org.simantics.diagram.flag.Joiner;
48 import org.simantics.diagram.stubs.DiagramResource;
49 import org.simantics.diagram.stubs.G2DResource;
50 import org.simantics.diagram.synchronization.ISynchronizationContext;
51 import org.simantics.diagram.synchronization.SynchronizationHints;
52 import org.simantics.diagram.synchronization.graph.AddElement;
53 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
54 import org.simantics.diagram.synchronization.graph.GraphSynchronizationHints;
55 import org.simantics.diagram.synchronization.graph.RemoveElement;
56 import org.simantics.diagram.synchronization.graph.layer.GraphLayerManager;
57 import org.simantics.diagram.ui.DiagramModelHints;
58 import org.simantics.g2d.connection.handler.ConnectionHandler;
59 import org.simantics.g2d.diagram.DiagramHints;
60 import org.simantics.g2d.diagram.IDiagram;
61 import org.simantics.g2d.diagram.handler.Topology.Terminal;
62 import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil.TerminalInfo;
63 import org.simantics.g2d.element.ElementClass;
64 import org.simantics.g2d.element.ElementClasses;
65 import org.simantics.g2d.element.ElementHints;
66 import org.simantics.g2d.element.ElementUtils;
67 import org.simantics.g2d.element.IElement;
68 import org.simantics.g2d.element.IElementClassProvider;
69 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
70 import org.simantics.g2d.element.impl.Element;
71 import org.simantics.g2d.elementclass.BranchPoint;
72 import org.simantics.g2d.elementclass.FlagClass;
73 import org.simantics.g2d.elementclass.FlagClass.Type;
74 import org.simantics.layer0.Layer0;
75 import org.simantics.modeling.ModelingResources;
76 import org.simantics.scl.runtime.tuple.Tuple2;
77 import org.simantics.structural.stubs.StructuralResource2;
78 import org.simantics.structural2.modelingRules.CPTerminal;
79 import org.simantics.structural2.modelingRules.ConnectionJudgement;
80 import org.simantics.structural2.modelingRules.IConnectionPoint;
81 import org.simantics.structural2.modelingRules.IModelingRules;
82 import org.simantics.utils.datastructures.Pair;
83 import org.simantics.utils.ui.ErrorLogger;
86 * @author Tuukka Lehtonen
88 public class ConnectionBuilder {
90 protected static class Connector extends Tuple2 {
91 public Connector(Resource attachmentRelation, Resource connector) {
92 super(attachmentRelation, connector);
94 public Resource getAttachment() {
97 public Resource getConnector() {
102 protected final IDiagram diagram;
103 protected final Resource diagramResource;
104 protected final boolean createFlags;
106 protected final ISynchronizationContext ctx;
107 protected final IElementClassProvider elementClassProvider;
108 protected final GraphLayerManager layerManager;
110 protected ConnectionUtil cu;
113 protected DiagramResource DIA;
114 protected StructuralResource2 STR;
115 protected ModelingResources MOD;
117 public ConnectionBuilder(IDiagram diagram) {
118 this.diagram = diagram;
119 this.diagramResource = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
120 this.createFlags = Boolean.TRUE.equals(diagram.getHint(DiagramHints.KEY_USE_CONNECTION_FLAGS));
122 ctx = diagram.getHint(SynchronizationHints.CONTEXT);
124 this.elementClassProvider = ctx.get(SynchronizationHints.ELEMENT_CLASS_PROVIDER);
125 this.layerManager = ctx.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER);
127 this.elementClassProvider = null;
128 this.layerManager = null;
132 protected void initializeResources(ReadGraph graph) {
133 if (this.L0 == null) {
134 this.L0 = Layer0.getInstance(graph);
135 this.DIA = DiagramResource.getInstance(graph);
136 this.STR = StructuralResource2.getInstance(graph);
137 this.MOD = ModelingResources.getInstance(graph);
144 * @param controlPoints
145 * @param startTerminal
147 * @throws DatabaseException
149 public void create(WriteGraph graph, final ConnectionJudgement judgment, Deque<ControlPoint> controlPoints,
150 TerminalInfo startTerminal, TerminalInfo endTerminal) throws DatabaseException {
151 this.cu = new ConnectionUtil(graph);
152 initializeResources(graph);
154 final IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES);
156 final Resource startDisconnectedFlag = getDisconnectedFlag(graph, startTerminal);
157 final Resource endDisconnectedFlag = getDisconnectedFlag(graph, endTerminal);
158 if (startDisconnectedFlag != null || endDisconnectedFlag != null) {
159 if (startDisconnectedFlag != null && endDisconnectedFlag != null) {
161 // Ask the user which operation to perform:
162 // a) connect the disconnected flags together with a connection join
163 // b) join the flags into a single connection
165 final VirtualGraph graphProvider = graph.getProvider();
166 final Session session = graph.getSession();
168 PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
171 IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
174 MessageDialog dialog = new MessageDialog(window.getShell(), "Connect or Join Flags?", null,
175 "Connect flags together or join them visually into a connection?",
176 MessageDialog.QUESTION_WITH_CANCEL, new String[] { "Connect Flags", "Join Flags",
179 setShellStyle(getShellStyle() | SWT.SHEET);
182 final int choice = dialog.open();
184 if (choice != 2 && choice != SWT.DEFAULT) {
185 session.asyncRequest(new WriteRequest(graphProvider) {
187 public void perform(WriteGraph graph) throws DatabaseException {
188 graph.markUndoPoint();
191 Resource join = FlagUtil.join(graph, startDisconnectedFlag, endDisconnectedFlag);
192 FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);
193 String commonLabel = scheme.generateLabel(graph, diagramResource);
194 graph.claimLiteral(startDisconnectedFlag, L0.HasLabel, DIA.FlagLabel, commonLabel);
195 graph.claimLiteral(endDisconnectedFlag, L0.HasLabel, DIA.FlagLabel, commonLabel);
197 // Set connection type according to modeling rules
198 setJoinedConnectionTypes(graph, modelingRules, judgment, join);
200 // Add comment to change set.
201 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
202 graph.addMetadata(cm.add("Connected flags"));
207 // First connect the flags together
208 Resource join = FlagUtil.join(graph, startDisconnectedFlag, endDisconnectedFlag);
210 // Set connection type according to modeling rules
211 setJoinedConnectionTypes(graph, modelingRules, judgment, join);
213 // Join the flags into a direct connection
214 new Joiner(graph).joinLocal(graph, Arrays.asList(startDisconnectedFlag, endDisconnectedFlag));
216 // Add comment to change set.
217 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
218 graph.addMetadata(cm.add("Joined flags"));
226 ErrorLogger.defaultLogError(e);
235 TerminalInfo normalTerminal = null;
236 Resource flagToRemove = null;
237 Resource connection = null;
238 if (startDisconnectedFlag != null) {
239 flagToRemove = startDisconnectedFlag;
240 normalTerminal = endTerminal;
241 connection = attachedToExistingConnection(graph, startTerminal);
243 if (endDisconnectedFlag != null) {
244 flagToRemove = endDisconnectedFlag;
245 normalTerminal = startTerminal;
246 connection = attachedToExistingConnection(graph, endTerminal);
248 if (connection != null) {
249 // OK, continuing a connection from an existing disconnected flag.
253 // 2. connect normal terminal directly to the existing connection
254 Statement stm = graph.getSingleStatement(flagToRemove, STR.IsConnectedTo);
255 Collection<Resource> areConnecteds = graph.getObjects(stm.getObject(), DIA.AreConnected);
257 // Remove statement to connection connector before removing flag
258 // to prevent removal of connector and the connection.
260 new RemoveElement((Resource)diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), flagToRemove).perform(graph);
262 // Disconnect the connector from the connection and create a
263 // new connector for the element terminal.
264 cu.removeConnectionPart(stm.getObject());
265 Connector newConnector = createConnectorForNode(graph, connection,
266 (Resource) ElementUtils.getObject(normalTerminal.e), normalTerminal.t,
267 startDisconnectedFlag != null ? EdgeEnd.End : EdgeEnd.Begin, judgment);
269 for (Resource areConnected : areConnecteds)
270 graph.claim(newConnector.getConnector(), DIA.AreConnected, areConnected);
272 if (modelingRules != null && judgment.connectionType != null)
273 modelingRules.setConnectionType(graph, connection, judgment.connectionType);
275 // Add comment to change set.
276 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
277 graph.addMetadata(cm.add("Joined flags"));
278 graph.markUndoPoint();
284 // 1. Get diagram connection to construct.
285 Resource connection = getOrCreateConnection(graph, startTerminal, endTerminal);
287 // 1.1 Give running name to connection and increment the counter attached to the diagram.
288 AddElement.claimFreshElementName(graph, diagramResource, connection);
290 // 2. Add branch points
291 // 3. Create edges between branch points.
292 List<Pair<ControlPoint, Resource>> bps = Collections.emptyList();
293 Resource firstBranchPoint = null;
294 Resource lastBranchPoint = null;
295 if (!isRouteGraphConnection(graph, connection)) {
296 bps = createBranchPoints(graph, connection, controlPoints);
297 if (!bps.isEmpty()) {
298 Iterator<Pair<ControlPoint, Resource>> it = bps.iterator();
299 Pair<ControlPoint, Resource> prev = it.next();
300 firstBranchPoint = prev.second;
301 while (it.hasNext()) {
302 Pair<ControlPoint, Resource> next = it.next();
303 cu.connect(prev.second, next.second);
306 lastBranchPoint = prev.second;
310 // 4. Connect start/end terminals if those exist.
311 // If first/lastBranchPoint != null, connect to those.
312 // Otherwise connect the start/end terminals together.
313 Connector startConnector = null;
314 Connector endConnector = null;
315 IElement startFlag = null;
316 IElement endFlag = null;
318 //FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);
320 if (startTerminal != null && endTerminal != null) {
321 Resource startAttachment = chooseAttachmentRelationForNode(graph, connection, startTerminal, judgment);
322 Resource endAttachment = chooseAttachmentRelationForNode(graph, connection, endTerminal, judgment);
323 Pair<Resource, Resource> attachments = resolveEndAttachments(graph, startAttachment, endAttachment);
324 startConnector = createConnectorForNodeWithAttachment(graph, connection, startTerminal, attachments.first);
325 endConnector = createConnectorForNodeWithAttachment(graph, connection, endTerminal, attachments.second);
326 } else if (startTerminal != null) {
327 startConnector = createConnectorForNode(graph, connection, startTerminal, EdgeEnd.Begin, judgment);
329 EdgeEnd flagEnd = cu.toEdgeEnd( cu.getAttachmentRelationForConnector(startConnector.getConnector()), EdgeEnd.End ).other();
330 endFlag = createFlag(graph, connection, flagEnd, controlPoints.getLast(), FlagClass.Type.Out,
331 //scheme.generateLabel(graph, diagramResource));
333 endConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(endFlag),
334 ElementUtils.getSingleTerminal(endFlag), flagEnd, judgment);
336 } else if (endTerminal != null) {
337 endConnector = createConnectorForNode(graph, connection, endTerminal, EdgeEnd.End, judgment);
339 EdgeEnd flagEnd = cu.toEdgeEnd( cu.getAttachmentRelationForConnector(endConnector.getConnector()), EdgeEnd.Begin ).other();
340 startFlag = createFlag(graph, connection, flagEnd, controlPoints.getFirst(), FlagClass.Type.In,
341 //scheme.generateLabel(graph, diagramResource));
343 startConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(startFlag),
344 ElementUtils.getSingleTerminal(startFlag), flagEnd, judgment);
346 } else if (createFlags) {
347 startFlag = createFlag(graph, connection, EdgeEnd.Begin, controlPoints.getFirst(), FlagClass.Type.In,
348 //scheme.generateLabel(graph, diagramResource));
350 startConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(startFlag),
351 ElementUtils.getSingleTerminal(startFlag), EdgeEnd.Begin, judgment);
353 endFlag = createFlag(graph, connection, EdgeEnd.End, controlPoints.getLast(), FlagClass.Type.Out,
354 //scheme.generateLabel(graph, diagramResource));
356 endConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(endFlag),
357 ElementUtils.getSingleTerminal(endFlag), EdgeEnd.End, judgment);
360 if (firstBranchPoint == null || lastBranchPoint == null) {
361 cu.connect(startConnector.getConnector(), endConnector.getConnector());
363 cu.connect(startConnector.getConnector(), firstBranchPoint);
364 cu.connect(lastBranchPoint, endConnector.getConnector());
367 // 5. Finally, set connection type according to modeling rules
368 if (judgment.connectionType != null && modelingRules != null)
369 modelingRules.setConnectionType(graph, connection, judgment.connectionType);
371 // 5.1 Verify created flag types
372 if (startFlag != null)
373 verifyFlagType(graph, modelingRules, startFlag);
375 verifyFlagType(graph, modelingRules, endFlag);
377 // 5.2 Write ConnectionMappingSpecification to connector if necessary
378 writeConnectionMappingSpecification(graph, startTerminal, startConnector, judgment.connectionType);
379 writeConnectionMappingSpecification(graph, endTerminal, endConnector, judgment.connectionType);
381 // 6. Add comment to change set.
382 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
383 graph.addMetadata(cm.add("Added connection " + connection));
384 graph.markUndoPoint();
388 private boolean writeConnectionMappingSpecification(WriteGraph graph, TerminalInfo terminal, Connector connector, Resource connectionType)
389 throws DatabaseException {
390 Resource diagramConnRel = getConnectionRelation(graph, terminal);
391 if (diagramConnRel == null)
393 Resource connRel = graph.getPossibleObject(diagramConnRel, MOD.DiagramConnectionRelationToConnectionRelation);
394 if (connRel == null || !graph.hasStatement(connRel, MOD.NeedsConnectionMappingSpecification))
396 Resource mappingSpecification = graph.getPossibleObject(connectionType, MOD.ConnectionTypeToConnectionMappingSpecification);
397 if (mappingSpecification == null)
399 graph.claim(connector.getConnector(), MOD.HasConnectionMappingSpecification, null, mappingSpecification);
403 private static Resource getConnectionRelation(ReadGraph graph, TerminalInfo ti) throws DatabaseException {
404 if (ti != null && ti.t instanceof ResourceTerminal) {
405 Resource t = ((ResourceTerminal) ti.t).getResource();
406 Resource bindingRelation = DiagramGraphUtil.getConnectionPointOfTerminal(graph, t);
407 return bindingRelation;
414 * @param startAttachment
415 * @param endAttachment
417 * @throws DatabaseException
419 protected Pair<Resource, Resource> resolveEndAttachments(WriteGraph graph,
420 Resource startAttachment, Resource endAttachment) throws DatabaseException {
421 if (startAttachment != null && endAttachment != null)
422 return Pair.make(startAttachment, endAttachment);
424 if (startAttachment != null && endAttachment == null)
425 return Pair.make(startAttachment, getInverseAttachment(graph, startAttachment, DIA.HasArrowConnector));
426 if (startAttachment == null && endAttachment != null)
427 return Pair.make(getInverseAttachment(graph, endAttachment, DIA.HasPlainConnector), endAttachment);
429 return Pair.make(DIA.HasPlainConnector, DIA.HasArrowConnector);
436 * @throws DatabaseException
438 protected Resource getInverseAttachment(ReadGraph graph, Resource attachment, Resource defaultValue) throws DatabaseException {
439 Resource inverse = attachment != null ? graph.getPossibleObject(attachment, DIA.HasInverseAttachment) : defaultValue;
440 return inverse != null ? inverse : defaultValue;
445 * @param modelingRules
447 * @throws DatabaseException
449 protected void verifyFlagType(WriteGraph graph, IModelingRules modelingRules, IElement flagElement) throws DatabaseException {
450 if (modelingRules != null) {
451 Resource flag = flagElement.getHint(ElementHints.KEY_OBJECT);
452 FlagClass.Type flagType = flagElement.getHint(FlagClass.KEY_FLAG_TYPE);
453 FlagUtil.verifyFlagType(graph, modelingRules, flag, flagType);
461 * @param attachToLine
462 * @param controlPoints
464 * @return the DIA.Connector instance created for attaching the connection
465 * to the specified end terminal
466 * @throws DatabaseException
468 public Pair<Resource, Resource> attachToRouteGraph(
470 ConnectionJudgement judgment,
471 Resource attachToConnection,
472 Resource attachToLine,
473 Deque<ControlPoint> controlPoints,
474 TerminalInfo endTerminal,
475 FlagClass.Type flagType)
476 throws DatabaseException
478 initializeResources(graph);
479 this.cu = new ConnectionUtil(graph);
481 Resource endElement = endTerminal != null ? ElementUtils.getObject(endTerminal.e) : null;
482 if (endElement != null
483 && graph.isInstanceOf(endElement, DIA.Flag)
484 && FlagUtil.isDisconnected(graph, endElement))
486 // Connection ends in an existing but disconnected flag that
487 // should be all right to connect to because the connection
488 // judgment implies it makes a valid connection.
489 // Check that we are attaching the connection to an existing
490 // disconnected flag that is however attached to a connection.
491 Resource endTerminalConnection = ConnectionBuilder.attachedToExistingConnection(graph, endTerminal);
492 if (endTerminalConnection != null) {
493 attachConnectionToFlag(graph, judgment, attachToConnection, attachToLine, controlPoints, endTerminal);
498 Connector endConnector = null;
499 if (endTerminal != null) {
500 endConnector = createConnectorForNode(graph, attachToConnection, endTerminal, EdgeEnd.End, judgment);
501 } else if (createFlags) {
502 EdgeEnd end = flagType == FlagClass.Type.In ? EdgeEnd.Begin : EdgeEnd.End;
503 IElement endFlag = createFlag(graph, attachToConnection, end, controlPoints.getLast(), flagType, null);
504 endConnector = createConnectorForNode(graph, attachToConnection, (Resource) ElementUtils.getObject(endFlag),
505 ElementUtils.getSingleTerminal(endFlag), end, judgment);
508 cu.connect(attachToLine, endConnector.getConnector());
510 IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES);
511 if (judgment.connectionType != null && modelingRules != null) {
512 modelingRules.setConnectionType(graph, attachToConnection, judgment.connectionType);
515 writeConnectionMappingSpecification(graph, endTerminal, endConnector, judgment.connectionType);
517 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
518 graph.addMetadata(cm.add("Branched connection " + attachToConnection));
520 return Pair.make(endConnector.getAttachment(), endConnector.getConnector());
526 protected void attachConnectionToFlag(
528 ConnectionJudgement judgment,
529 Resource attachToConnection,
530 Resource attachToLine,
531 Deque<ControlPoint> controlPoints,
533 throws DatabaseException
535 // Attaching attachedConnection to an existing disconnected flag that is
536 // however attached to a connection.
538 // 1. remove flag and its connector
539 // 2. attach the two connections together by moving the route nodes
540 // of the removed flag-side connection under the remaining connection
541 // and ensuring that the route node chain will be valid after the
542 // switch. In a chain route lines, each line must have an opposite
543 // direction compared to the lines connected to it.
544 Resource flagToRemove = ElementUtils.getObject(toFlag.e);
545 Statement flagToConnector = graph.getSingleStatement(flagToRemove, STR.IsConnectedTo);
546 Resource flagConnector = flagToConnector.getObject();
547 Resource flagConnection = ConnectionUtil.getConnection(graph, flagConnector);
548 Collection<Resource> flagRouteNodes = graph.getObjects(flagConnector, DIA.AreConnected);
550 Resource connectionToKeep = attachToConnection;
551 Resource connectionToRemove = flagConnection;
552 if (!connectionToKeep.equals(connectionToRemove)) {
553 Resource hasElementToComponent1 = graph.getPossibleObject(attachToConnection, MOD.ElementToComponent);
554 Resource hasElementToComponent2 = graph.getPossibleObject(flagConnection, MOD.ElementToComponent);
555 Type flagType = FlagUtil.getFlagType(graph, flagToRemove);
556 if (hasElementToComponent1 != null && hasElementToComponent2 != null)
557 throw new UnsupportedOperationException(
558 "Both attached connection " + attachToConnection + " and flag connection " + flagConnection
559 + " have mapped components, can't decide which connection to remove in join operation");
560 if (hasElementToComponent2 != null || flagType == Type.Out) {
561 connectionToKeep = flagConnection;
562 connectionToRemove = attachToConnection;
566 // Remove flag and its connector.
567 graph.deny(flagToConnector);
568 new RemoveElement((Resource)diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), flagToRemove).perform(graph);
569 cu.removeConnectionPart(flagConnector);
571 // Attached routeline must have opposite direction than the line
572 // attached to in order for the connection to be valid.
573 Boolean attachingToHorizontalLine = graph.getPossibleRelatedValue(attachToLine, DIA.IsHorizontal, Bindings.BOOLEAN);
574 if (attachingToHorizontalLine != null) {
575 for (Resource routeNode : flagRouteNodes) {
576 Collection<Resource> routeNodesToAttachTo = removeUntilOrientedRouteline(graph, !attachingToHorizontalLine, routeNode);
577 for (Resource rn : routeNodesToAttachTo)
578 cu.connect(attachToLine, rn);
582 moveStatements(graph, connectionToRemove, connectionToKeep, DIA.HasInteriorRouteNode);
583 moveStatements(graph, connectionToRemove, connectionToKeep, DIA.HasConnector);
585 // Remove obsolete connection
586 if (!connectionToKeep.equals(connectionToRemove))
587 cu.removeConnection(connectionToRemove);
589 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
590 graph.addMetadata(cm.add("Joined connection to disconnected flag"));
593 private void moveStatements(WriteGraph graph, Resource source, Resource target, Resource movedRelation) throws DatabaseException {
594 if (!source.equals(target)) {
595 for (Statement s : graph.getStatements(source, movedRelation)) {
597 graph.claim(target, s.getPredicate(), s.getObject());
602 private Collection<Resource> removeUntilOrientedRouteline(WriteGraph graph, boolean expectedOrientation, Resource routeNode) throws DatabaseException {
603 List<Resource> result = new ArrayList<>(2);
604 Deque<Resource> work = new ArrayDeque<>(2);
605 work.addLast(routeNode);
606 while (!work.isEmpty()) {
607 Resource rn = work.removeFirst();
608 if (graph.isInstanceOf(rn, DIA.RouteLine)) {
609 Boolean isHorizontal = graph.getPossibleRelatedValue(rn, DIA.IsHorizontal, Bindings.BOOLEAN);
610 if (isHorizontal != null && expectedOrientation != isHorizontal) {
611 for (Resource rnn : graph.getObjects(rn, DIA.AreConnected))
613 cu.removeConnectionPart(rn);
622 protected boolean isRouteGraphConnection(ReadGraph graph, Resource connection) throws DatabaseException {
623 initializeResources(graph);
624 return graph.isInstanceOf(connection, DIA.RouteGraphConnection);
631 * @throws DatabaseException
633 public static Resource attachedToExistingConnection(ReadGraph graph, TerminalInfo ti) throws DatabaseException {
634 Object obj = ElementUtils.getObject(ti.e);
635 Resource cp = DiagramGraphUtil.getConnectionPointOfTerminal(graph, ti.t);
636 if (obj instanceof Resource && cp != null) {
637 Resource e = (Resource) obj;
638 for (Resource connector : graph.getObjects(e, cp)) {
639 Resource connection = ConnectionUtil.tryGetConnection(graph, connector);
640 if (connection != null)
651 * @throws DatabaseException
653 public Resource getOrCreateConnection(ReadGraph graph, TerminalInfo... tis) throws DatabaseException {
654 // Resolve if adding to existing connection.
655 Resource connection = null;
656 for (TerminalInfo ti : tis) {
657 connection = getExistingConnection(graph, ti);
658 if (connection != null)
662 if (connection == null) {
663 // No existing connection, create new.
664 ElementClass connectionClass = elementClassProvider.get(ElementClasses.CONNECTION);
665 Resource connectionClassResource = ElementUtils.checkedAdapt(connectionClass, Resource.class);
666 connection = cu.newConnection(diagramResource, connectionClassResource);
675 * @param controlPoints
677 * @throws DatabaseException
679 public List<Pair<ControlPoint, Resource>> createBranchPoints(WriteGraph graph, Resource connection,
680 Collection<ControlPoint> controlPoints) throws DatabaseException {
681 List<Pair<ControlPoint, Resource>> bps = new ArrayList<Pair<ControlPoint, Resource>>(controlPoints.size());
682 for(ControlPoint cp : controlPoints) {
683 if (cp.isAttachedToTerminal())
684 // Terminal attachments do not need branch points.
687 Resource bp = cu.newBranchPoint(connection,
688 AffineTransform.getTranslateInstance(cp.getPosition().getX(), cp.getPosition().getY()),
690 bps.add(Pair.make(cp, bp));
702 * @throws DatabaseException
704 protected Resource chooseAttachmentRelationForNode(ReadGraph graph,
705 Resource connection, TerminalInfo ti, ConnectionJudgement judgment)
706 throws DatabaseException {
707 Resource node = (Resource) ElementUtils.getObject(ti.e);
708 return chooseAttachmentRelationForNode(graph, connection, node, ti.t, judgment);
718 * @return the calculated attachment relation or <code>null</code> if the
719 * result is ambiguous
720 * @throws DatabaseException
722 protected Resource chooseAttachmentRelationForNode(ReadGraph graph,
723 Resource connection, Resource element, Terminal terminal,
724 ConnectionJudgement judgment) throws DatabaseException {
725 IConnectionPoint cp = ConnectionUtil.toConnectionPoint(graph, element, terminal);
726 CPTerminal cpt = (cp instanceof CPTerminal) ? (CPTerminal) cp : null;
727 Resource attachment = judgment.attachmentRelations.get(graph, cpt);
735 * @param connectTo resource to connect the new connector to if not
738 * @return <used attachment relation, the new DIA.Connector instance>. The
739 * attachment relation is <code>null</code> if it was chosen based
740 * on EdgeEnd instead of being defined
741 * @throws DatabaseException
743 protected Connector createConnectorForNode(WriteGraph graph, Resource connection, TerminalInfo ti, EdgeEnd end,
744 ConnectionJudgement judgment) throws DatabaseException {
745 Resource node = (Resource) ElementUtils.getObject(ti.e);
746 return createConnectorForNode(graph, connection, node, ti.t, end, judgment);
757 * @return <used attachment relation, the new DIA.Connector instance>. The
758 * attachment relation is <code>null</code> if it was chosen based
759 * on EdgeEnd instead of being defined
760 * @throws DatabaseException
762 protected Connector createConnectorForNode(WriteGraph graph, Resource connection, Resource element, Terminal terminal,
763 EdgeEnd end, ConnectionJudgement judgment) throws DatabaseException {
764 IConnectionPoint cp = ConnectionUtil.toConnectionPoint(graph, element, terminal);
765 CPTerminal cpt = (cp instanceof CPTerminal) ? (CPTerminal) cp : null;
766 Resource attachment = judgment.attachmentRelations.get(graph, cpt);
767 if (attachment == null)
768 attachment = cu.toHasConnectorRelation(end);
769 Resource connector = cu.getOrCreateConnector(connection, element, terminal, end, attachment);
770 return new Connector(attachment, connector);
778 * @return <used attachment relation, the new DIA.Connector instance>
779 * @throws DatabaseException
781 protected Connector createConnectorForNodeWithAttachment(WriteGraph graph,
782 Resource connection, TerminalInfo ti, Resource attachment)
783 throws DatabaseException {
784 Resource node = (Resource) ElementUtils.getObject(ti.e);
785 return createConnectorForNodeWithAttachment(graph, connection, node, ti.t, attachment);
794 * @return <used attachment relation, the new DIA.Connector instance>
795 * @throws DatabaseException
797 protected Connector createConnectorForNodeWithAttachment(WriteGraph graph,
798 Resource connection, Resource element, Terminal terminal,
799 Resource attachment) throws DatabaseException {
800 Resource connector = cu.getOrCreateConnector(connection, element, terminal, null, attachment);
801 return new Connector(attachment, connector);
810 * @param label <code>null</code> to leave flag without label
811 * @return an element describing the new created flag resource
812 * @throws DatabaseException
814 public IElement createFlag(WriteGraph graph, Resource connection, EdgeEnd end, ControlPoint cp,
815 FlagClass.Type type, String label) throws DatabaseException {
816 ElementClass flagClass = elementClassProvider.get(ElementClasses.FLAG);
817 IElement flagElement = Element.spawnNew(flagClass);
818 Resource flagClassResource = ElementUtils.checkedAdapt(flagClass, Resource.class);
820 Layer0 L0 = Layer0.getInstance(graph);
821 G2DResource G2D = G2DResource.getInstance(graph);
822 DiagramResource DIA = DiagramResource.getInstance(graph);
824 Resource flag = graph.newResource();
825 graph.claim(flag, L0.InstanceOf, null, flagClassResource);
826 flagElement.setHint(ElementHints.KEY_OBJECT, flag);
828 OrderedSetUtils.add(graph, diagramResource, flag);
830 AffineTransform at = AffineTransform.getTranslateInstance(cp.getPosition().getX(), cp.getPosition().getY());
831 flagElement.setHint(ElementHints.KEY_TRANSFORM, at);
832 double[] matrix = new double[6];
833 at.getMatrix(matrix);
834 graph.claimLiteral(flag, DIA.HasTransform, G2D.Transform, matrix);
836 flagElement.setHint(FlagClass.KEY_FLAG_TYPE, type);
837 graph.claim(flag, DIA.HasFlagType, null, DiagramGraphUtil.toFlagTypeResource(DIA, type));
839 graph.claimLiteral(flag, L0.HasLabel, DIA.FlagLabel, label, Bindings.STRING);
841 // Give running name to flag and increment the counter attached to the diagram.
842 AddElement.claimFreshElementName(graph, diagramResource, flag);
844 // Make the diagram consist of the new element
845 graph.claim(diagramResource, L0.ConsistsOf, flag);
847 // Put the element on all the currently active layers if possible.
848 if (layerManager != null) {
849 layerManager.removeFromAllLayers(graph, flag);
850 layerManager.putElementOnVisibleLayers(diagram, graph, flag);
853 // Add flag to possible IO table
854 IOTablesInfo ioTablesInfo = IOTableUtil.getIOTablesInfo(graph,
855 (Resource)diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE));
856 ioTablesInfo.updateBinding(graph, DIA, flag, at.getTranslateX(), at.getTranslateY());
865 * @throws DatabaseException
867 protected static Resource getExistingConnection(ReadGraph graph, TerminalInfo ti) throws DatabaseException {
869 if (isConnection(ti.e)) {
870 Object obj = ElementUtils.getObject(ti.e);
871 if (obj instanceof Resource) {
872 Resource c = (Resource) obj;
873 return graph.isInstanceOf(c, DiagramResource.getInstance(graph).Connection) ? c : null;
875 } else if (isBranchPoint(ti.e)) {
876 Object obj = ElementUtils.getObject(ti.e);
877 if (obj instanceof Resource) {
878 return ConnectionUtil.tryGetConnection(graph, (Resource) obj);
885 protected static boolean isConnection(IElement e) {
886 return e.getElementClass().containsClass(ConnectionHandler.class);
893 protected static boolean isBranchPoint(IElement e) {
894 return e.getElementClass().containsClass(BranchPoint.class);
901 * @throws DatabaseException
903 protected static Resource getDisconnectedFlag(ReadGraph graph, TerminalInfo terminal) throws DatabaseException {
904 if (terminal != null) {
905 Object obj = ElementUtils.getObject(terminal.e);
906 if (obj instanceof Resource) {
907 Resource flag = (Resource) obj;
908 if (graph.isInstanceOf(flag, DiagramResource.getInstance(graph).Flag)
909 && FlagUtil.isDisconnected(graph, flag))
918 * @param modelingRules
921 * @throws DatabaseException
923 protected static void setJoinedConnectionTypes(WriteGraph graph, IModelingRules modelingRules,
924 ConnectionJudgement judgment, Resource join) throws DatabaseException {
925 if (modelingRules != null && judgment != null && judgment.connectionType != null) {
926 DiagramResource DIA = DiagramResource.getInstance(graph);
927 StructuralResource2 STR = StructuralResource2.getInstance(graph);
928 List<Resource> connections = new ArrayList<Resource>(2);
929 for (Resource flag : graph.getObjects(join, DIA.FlagIsJoinedBy)) {
930 for (Resource connector : graph.getObjects(flag, STR.IsConnectedTo)) {
931 Resource connection = ConnectionUtil.tryGetConnection(graph, connector);
932 if (connection != null)
933 connections.add(connection);
936 for (Resource connection : connections)
937 modelingRules.setConnectionType(graph, connection, judgment.connectionType);