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.Callback;
83 import org.simantics.utils.datastructures.Pair;
84 import org.simantics.utils.ui.ErrorLogger;
87 * @author Tuukka Lehtonen
89 public class ConnectionBuilder {
91 protected static class Connector extends Tuple2 {
92 public Connector(Resource attachmentRelation, Resource connector) {
93 super(attachmentRelation, connector);
95 public Resource getAttachment() {
98 public Resource getConnector() {
103 protected final IDiagram diagram;
104 protected final Resource diagramResource;
105 protected final boolean createFlags;
107 protected final ISynchronizationContext ctx;
108 protected final IElementClassProvider elementClassProvider;
109 protected final GraphLayerManager layerManager;
111 protected ConnectionUtil cu;
114 protected DiagramResource DIA;
115 protected StructuralResource2 STR;
116 protected ModelingResources MOD;
118 public ConnectionBuilder(IDiagram diagram) {
119 this.diagram = diagram;
120 this.diagramResource = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
121 this.createFlags = Boolean.TRUE.equals(diagram.getHint(DiagramHints.KEY_USE_CONNECTION_FLAGS));
123 ctx = diagram.getHint(SynchronizationHints.CONTEXT);
125 this.elementClassProvider = ctx.get(SynchronizationHints.ELEMENT_CLASS_PROVIDER);
126 this.layerManager = ctx.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER);
128 this.elementClassProvider = null;
129 this.layerManager = null;
133 protected void initializeResources(ReadGraph graph) {
134 if (this.L0 == null) {
135 this.L0 = Layer0.getInstance(graph);
136 this.DIA = DiagramResource.getInstance(graph);
137 this.STR = StructuralResource2.getInstance(graph);
138 this.MOD = ModelingResources.getInstance(graph);
145 * @param controlPoints
146 * @param startTerminal
148 * @throws DatabaseException
150 public void create(WriteGraph graph, final ConnectionJudgement judgment, Deque<ControlPoint> controlPoints,
151 TerminalInfo startTerminal, TerminalInfo endTerminal) throws DatabaseException {
152 this.cu = new ConnectionUtil(graph);
153 initializeResources(graph);
155 final IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES);
157 final Resource startDisconnectedFlag = getDisconnectedFlag(graph, startTerminal);
158 final Resource endDisconnectedFlag = getDisconnectedFlag(graph, endTerminal);
159 if (startDisconnectedFlag != null || endDisconnectedFlag != null) {
160 if (startDisconnectedFlag != null && endDisconnectedFlag != null) {
162 // Ask the user which operation to perform:
163 // a) connect the disconnected flags together with a connection join
164 // b) join the flags into a single connection
166 final VirtualGraph graphProvider = graph.getProvider();
167 final Session session = graph.getSession();
169 PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
172 IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
175 MessageDialog dialog = new MessageDialog(window.getShell(), "Connect or Join Flags?", null,
176 "Connect flags together or join them visually into a connection?",
177 MessageDialog.QUESTION_WITH_CANCEL, new String[] { "Connect Flags", "Join Flags",
180 setShellStyle(getShellStyle() | SWT.SHEET);
183 final int choice = dialog.open();
185 if (choice != 2 && choice != SWT.DEFAULT) {
186 session.asyncRequest(new WriteRequest(graphProvider) {
188 public void perform(WriteGraph graph) throws DatabaseException {
189 graph.markUndoPoint();
192 Resource join = FlagUtil.join(graph, startDisconnectedFlag, endDisconnectedFlag);
193 FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);
194 String commonLabel = scheme.generateLabel(graph, diagramResource);
195 graph.claimLiteral(startDisconnectedFlag, L0.HasLabel, DIA.FlagLabel, commonLabel);
196 graph.claimLiteral(endDisconnectedFlag, L0.HasLabel, DIA.FlagLabel, commonLabel);
198 // Set connection type according to modeling rules
199 setJoinedConnectionTypes(graph, modelingRules, judgment, join);
201 // Add comment to change set.
202 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
203 graph.addMetadata(cm.add("Connected flags"));
208 // First connect the flags together
209 Resource join = FlagUtil.join(graph, startDisconnectedFlag, endDisconnectedFlag);
211 // Set connection type according to modeling rules
212 setJoinedConnectionTypes(graph, modelingRules, judgment, join);
214 // Join the flags into a direct connection
215 new Joiner(graph).joinLocal(graph, Arrays.asList(startDisconnectedFlag, endDisconnectedFlag));
217 // Add comment to change set.
218 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
219 graph.addMetadata(cm.add("Joined flags"));
225 }, new Callback<DatabaseException>() {
227 public void run(DatabaseException e) {
229 ErrorLogger.defaultLogError(e);
239 TerminalInfo normalTerminal = null;
240 Resource flagToRemove = null;
241 Resource connection = null;
242 if (startDisconnectedFlag != null) {
243 flagToRemove = startDisconnectedFlag;
244 normalTerminal = endTerminal;
245 connection = attachedToExistingConnection(graph, startTerminal);
247 if (endDisconnectedFlag != null) {
248 flagToRemove = endDisconnectedFlag;
249 normalTerminal = startTerminal;
250 connection = attachedToExistingConnection(graph, endTerminal);
252 if (connection != null) {
253 // OK, continuing a connection from an existing disconnected flag.
257 // 2. connect normal terminal directly to the existing connection
258 Statement stm = graph.getSingleStatement(flagToRemove, STR.IsConnectedTo);
259 Collection<Resource> areConnecteds = graph.getObjects(stm.getObject(), DIA.AreConnected);
261 // Remove statement to connection connector before removing flag
262 // to prevent removal of connector and the connection.
264 new RemoveElement((Resource)diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), flagToRemove).perform(graph);
266 // Disconnect the connector from the connection and create a
267 // new connector for the element terminal.
268 cu.removeConnectionPart(stm.getObject());
269 Connector newConnector = createConnectorForNode(graph, connection,
270 (Resource) ElementUtils.getObject(normalTerminal.e), normalTerminal.t,
271 startDisconnectedFlag != null ? EdgeEnd.End : EdgeEnd.Begin, judgment);
273 for (Resource areConnected : areConnecteds)
274 graph.claim(newConnector.getConnector(), DIA.AreConnected, areConnected);
276 if (modelingRules != null && judgment.connectionType != null)
277 modelingRules.setConnectionType(graph, connection, judgment.connectionType);
279 // Add comment to change set.
280 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
281 graph.addMetadata(cm.add("Joined flags"));
282 graph.markUndoPoint();
288 // 1. Get diagram connection to construct.
289 Resource connection = getOrCreateConnection(graph, startTerminal, endTerminal);
291 // 1.1 Give running name to connection and increment the counter attached to the diagram.
292 AddElement.claimFreshElementName(graph, diagramResource, connection);
294 // 2. Add branch points
295 // 3. Create edges between branch points.
296 List<Pair<ControlPoint, Resource>> bps = Collections.emptyList();
297 Resource firstBranchPoint = null;
298 Resource lastBranchPoint = null;
299 if (!isRouteGraphConnection(graph, connection)) {
300 bps = createBranchPoints(graph, connection, controlPoints);
301 if (!bps.isEmpty()) {
302 Iterator<Pair<ControlPoint, Resource>> it = bps.iterator();
303 Pair<ControlPoint, Resource> prev = it.next();
304 firstBranchPoint = prev.second;
305 while (it.hasNext()) {
306 Pair<ControlPoint, Resource> next = it.next();
307 cu.connect(prev.second, next.second);
310 lastBranchPoint = prev.second;
314 // 4. Connect start/end terminals if those exist.
315 // If first/lastBranchPoint != null, connect to those.
316 // Otherwise connect the start/end terminals together.
317 Connector startConnector = null;
318 Connector endConnector = null;
319 IElement startFlag = null;
320 IElement endFlag = null;
322 //FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);
324 if (startTerminal != null && endTerminal != null) {
325 Resource startAttachment = chooseAttachmentRelationForNode(graph, connection, startTerminal, judgment);
326 Resource endAttachment = chooseAttachmentRelationForNode(graph, connection, endTerminal, judgment);
327 Pair<Resource, Resource> attachments = resolveEndAttachments(graph, startAttachment, endAttachment);
328 startConnector = createConnectorForNodeWithAttachment(graph, connection, startTerminal, attachments.first);
329 endConnector = createConnectorForNodeWithAttachment(graph, connection, endTerminal, attachments.second);
330 } else if (startTerminal != null) {
331 startConnector = createConnectorForNode(graph, connection, startTerminal, EdgeEnd.Begin, judgment);
333 EdgeEnd flagEnd = cu.toEdgeEnd( cu.getAttachmentRelationForConnector(startConnector.getConnector()), EdgeEnd.End ).other();
334 endFlag = createFlag(graph, connection, flagEnd, controlPoints.getLast(), FlagClass.Type.Out,
335 //scheme.generateLabel(graph, diagramResource));
337 endConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(endFlag),
338 ElementUtils.getSingleTerminal(endFlag), flagEnd, judgment);
340 } else if (endTerminal != null) {
341 endConnector = createConnectorForNode(graph, connection, endTerminal, EdgeEnd.End, judgment);
343 EdgeEnd flagEnd = cu.toEdgeEnd( cu.getAttachmentRelationForConnector(endConnector.getConnector()), EdgeEnd.Begin ).other();
344 startFlag = createFlag(graph, connection, flagEnd, controlPoints.getFirst(), FlagClass.Type.In,
345 //scheme.generateLabel(graph, diagramResource));
347 startConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(startFlag),
348 ElementUtils.getSingleTerminal(startFlag), flagEnd, judgment);
350 } else if (createFlags) {
351 startFlag = createFlag(graph, connection, EdgeEnd.Begin, controlPoints.getFirst(), FlagClass.Type.In,
352 //scheme.generateLabel(graph, diagramResource));
354 startConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(startFlag),
355 ElementUtils.getSingleTerminal(startFlag), EdgeEnd.Begin, judgment);
357 endFlag = createFlag(graph, connection, EdgeEnd.End, controlPoints.getLast(), FlagClass.Type.Out,
358 //scheme.generateLabel(graph, diagramResource));
360 endConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(endFlag),
361 ElementUtils.getSingleTerminal(endFlag), EdgeEnd.End, judgment);
364 if (firstBranchPoint == null || lastBranchPoint == null) {
365 cu.connect(startConnector.getConnector(), endConnector.getConnector());
367 cu.connect(startConnector.getConnector(), firstBranchPoint);
368 cu.connect(lastBranchPoint, endConnector.getConnector());
371 // 5. Finally, set connection type according to modeling rules
372 if (judgment.connectionType != null && modelingRules != null)
373 modelingRules.setConnectionType(graph, connection, judgment.connectionType);
375 // 5.1 Verify created flag types
376 if (startFlag != null)
377 verifyFlagType(graph, modelingRules, startFlag);
379 verifyFlagType(graph, modelingRules, endFlag);
381 // 5.2 Write ConnectionMappingSpecification to connector if necessary
382 writeConnectionMappingSpecification(graph, startTerminal, startConnector, judgment.connectionType);
383 writeConnectionMappingSpecification(graph, endTerminal, endConnector, judgment.connectionType);
385 // 6. Add comment to change set.
386 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
387 graph.addMetadata(cm.add("Added connection " + connection));
388 graph.markUndoPoint();
392 private boolean writeConnectionMappingSpecification(WriteGraph graph, TerminalInfo terminal, Connector connector, Resource connectionType)
393 throws DatabaseException {
394 Resource diagramConnRel = getConnectionRelation(graph, terminal);
395 if (diagramConnRel == null)
397 Resource connRel = graph.getPossibleObject(diagramConnRel, MOD.DiagramConnectionRelationToConnectionRelation);
398 if (connRel == null || !graph.hasStatement(connRel, MOD.NeedsConnectionMappingSpecification))
400 Resource mappingSpecification = graph.getPossibleObject(connectionType, MOD.ConnectionTypeToConnectionMappingSpecification);
401 if (mappingSpecification == null)
403 graph.claim(connector.getConnector(), MOD.HasConnectionMappingSpecification, null, mappingSpecification);
407 private static Resource getConnectionRelation(ReadGraph graph, TerminalInfo ti) throws DatabaseException {
408 if (ti != null && ti.t instanceof ResourceTerminal) {
409 Resource t = ((ResourceTerminal) ti.t).getResource();
410 Resource bindingRelation = DiagramGraphUtil.getConnectionPointOfTerminal(graph, t);
411 return bindingRelation;
418 * @param startAttachment
419 * @param endAttachment
421 * @throws DatabaseException
423 protected Pair<Resource, Resource> resolveEndAttachments(WriteGraph graph,
424 Resource startAttachment, Resource endAttachment) throws DatabaseException {
425 if (startAttachment != null && endAttachment != null)
426 return Pair.make(startAttachment, endAttachment);
428 if (startAttachment != null && endAttachment == null)
429 return Pair.make(startAttachment, getInverseAttachment(graph, startAttachment, DIA.HasArrowConnector));
430 if (startAttachment == null && endAttachment != null)
431 return Pair.make(getInverseAttachment(graph, endAttachment, DIA.HasPlainConnector), endAttachment);
433 return Pair.make(DIA.HasPlainConnector, DIA.HasArrowConnector);
440 * @throws DatabaseException
442 protected Resource getInverseAttachment(ReadGraph graph, Resource attachment, Resource defaultValue) throws DatabaseException {
443 Resource inverse = attachment != null ? graph.getPossibleObject(attachment, DIA.HasInverseAttachment) : defaultValue;
444 return inverse != null ? inverse : defaultValue;
449 * @param modelingRules
451 * @throws DatabaseException
453 protected void verifyFlagType(WriteGraph graph, IModelingRules modelingRules, IElement flagElement) throws DatabaseException {
454 if (modelingRules != null) {
455 Resource flag = flagElement.getHint(ElementHints.KEY_OBJECT);
456 FlagClass.Type flagType = flagElement.getHint(FlagClass.KEY_FLAG_TYPE);
457 FlagUtil.verifyFlagType(graph, modelingRules, flag, flagType);
465 * @param attachToLine
466 * @param controlPoints
468 * @return the DIA.Connector instance created for attaching the connection
469 * to the specified end terminal
470 * @throws DatabaseException
472 public Pair<Resource, Resource> attachToRouteGraph(
474 ConnectionJudgement judgment,
475 Resource attachToConnection,
476 Resource attachToLine,
477 Deque<ControlPoint> controlPoints,
478 TerminalInfo endTerminal,
479 FlagClass.Type flagType)
480 throws DatabaseException
482 initializeResources(graph);
483 this.cu = new ConnectionUtil(graph);
485 Resource endElement = endTerminal != null ? ElementUtils.getObject(endTerminal.e) : null;
486 if (endElement != null
487 && graph.isInstanceOf(endElement, DIA.Flag)
488 && FlagUtil.isDisconnected(graph, endElement))
490 // Connection ends in an existing but disconnected flag that
491 // should be all right to connect to because the connection
492 // judgment implies it makes a valid connection.
493 // Check that we are attaching the connection to an existing
494 // disconnected flag that is however attached to a connection.
495 Resource endTerminalConnection = ConnectionBuilder.attachedToExistingConnection(graph, endTerminal);
496 if (endTerminalConnection != null) {
497 attachConnectionToFlag(graph, judgment, attachToConnection, attachToLine, controlPoints, endTerminal);
502 Connector endConnector = null;
503 if (endTerminal != null) {
504 endConnector = createConnectorForNode(graph, attachToConnection, endTerminal, EdgeEnd.End, judgment);
505 } else if (createFlags) {
506 EdgeEnd end = flagType == FlagClass.Type.In ? EdgeEnd.Begin : EdgeEnd.End;
507 IElement endFlag = createFlag(graph, attachToConnection, end, controlPoints.getLast(), flagType, null);
508 endConnector = createConnectorForNode(graph, attachToConnection, (Resource) ElementUtils.getObject(endFlag),
509 ElementUtils.getSingleTerminal(endFlag), end, judgment);
512 cu.connect(attachToLine, endConnector.getConnector());
514 IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES);
515 if (judgment.connectionType != null && modelingRules != null) {
516 modelingRules.setConnectionType(graph, attachToConnection, judgment.connectionType);
519 writeConnectionMappingSpecification(graph, endTerminal, endConnector, judgment.connectionType);
521 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
522 graph.addMetadata(cm.add("Branched connection " + attachToConnection));
524 return Pair.make(endConnector.getAttachment(), endConnector.getConnector());
530 protected void attachConnectionToFlag(
532 ConnectionJudgement judgment,
533 Resource attachToConnection,
534 Resource attachToLine,
535 Deque<ControlPoint> controlPoints,
537 throws DatabaseException
539 // Attaching attachedConnection to an existing disconnected flag that is
540 // however attached to a connection.
542 // 1. remove flag and its connector
543 // 2. attach the two connections together by moving the route nodes
544 // of the removed flag-side connection under the remaining connection
545 // and ensuring that the route node chain will be valid after the
546 // switch. In a chain route lines, each line must have an opposite
547 // direction compared to the lines connected to it.
548 Resource flagToRemove = ElementUtils.getObject(toFlag.e);
549 Statement flagToConnector = graph.getSingleStatement(flagToRemove, STR.IsConnectedTo);
550 Resource flagConnector = flagToConnector.getObject();
551 Resource flagConnection = ConnectionUtil.getConnection(graph, flagConnector);
552 Collection<Resource> flagRouteNodes = graph.getObjects(flagConnector, DIA.AreConnected);
554 Resource connectionToKeep = attachToConnection;
555 Resource connectionToRemove = flagConnection;
556 if (!connectionToKeep.equals(connectionToRemove)) {
557 Resource hasElementToComponent1 = graph.getPossibleObject(attachToConnection, MOD.ElementToComponent);
558 Resource hasElementToComponent2 = graph.getPossibleObject(flagConnection, MOD.ElementToComponent);
559 Type flagType = FlagUtil.getFlagType(graph, flagToRemove);
560 if (hasElementToComponent1 != null && hasElementToComponent2 != null)
561 throw new UnsupportedOperationException(
562 "Both attached connection " + attachToConnection + " and flag connection " + flagConnection
563 + " have mapped components, can't decide which connection to remove in join operation");
564 if (hasElementToComponent2 != null || flagType == Type.Out) {
565 connectionToKeep = flagConnection;
566 connectionToRemove = attachToConnection;
570 // Remove flag and its connector.
571 graph.deny(flagToConnector);
572 new RemoveElement((Resource)diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), flagToRemove).perform(graph);
573 cu.removeConnectionPart(flagConnector);
575 // Attached routeline must have opposite direction than the line
576 // attached to in order for the connection to be valid.
577 Boolean attachingToHorizontalLine = graph.getPossibleRelatedValue(attachToLine, DIA.IsHorizontal, Bindings.BOOLEAN);
578 if (attachingToHorizontalLine != null) {
579 for (Resource routeNode : flagRouteNodes) {
580 Collection<Resource> routeNodesToAttachTo = removeUntilOrientedRouteline(graph, !attachingToHorizontalLine, routeNode);
581 for (Resource rn : routeNodesToAttachTo)
582 cu.connect(attachToLine, rn);
586 moveStatements(graph, connectionToRemove, connectionToKeep, DIA.HasInteriorRouteNode);
587 moveStatements(graph, connectionToRemove, connectionToKeep, DIA.HasConnector);
589 // Remove obsolete connection
590 if (!connectionToKeep.equals(connectionToRemove))
591 cu.removeConnection(connectionToRemove);
593 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
594 graph.addMetadata(cm.add("Joined connection to disconnected flag"));
597 private void moveStatements(WriteGraph graph, Resource source, Resource target, Resource movedRelation) throws DatabaseException {
598 if (!source.equals(target)) {
599 for (Statement s : graph.getStatements(source, movedRelation)) {
601 graph.claim(target, s.getPredicate(), s.getObject());
606 private Collection<Resource> removeUntilOrientedRouteline(WriteGraph graph, boolean expectedOrientation, Resource routeNode) throws DatabaseException {
607 List<Resource> result = new ArrayList<>(2);
608 Deque<Resource> work = new ArrayDeque<>(2);
609 work.addLast(routeNode);
610 while (!work.isEmpty()) {
611 Resource rn = work.removeFirst();
612 if (graph.isInstanceOf(rn, DIA.RouteLine)) {
613 Boolean isHorizontal = graph.getPossibleRelatedValue(rn, DIA.IsHorizontal, Bindings.BOOLEAN);
614 if (isHorizontal != null && expectedOrientation != isHorizontal) {
615 for (Resource rnn : graph.getObjects(rn, DIA.AreConnected))
617 cu.removeConnectionPart(rn);
626 protected boolean isRouteGraphConnection(ReadGraph graph, Resource connection) throws DatabaseException {
627 initializeResources(graph);
628 return graph.isInstanceOf(connection, DIA.RouteGraphConnection);
635 * @throws DatabaseException
637 public static Resource attachedToExistingConnection(ReadGraph graph, TerminalInfo ti) throws DatabaseException {
638 Object obj = ElementUtils.getObject(ti.e);
639 Resource cp = DiagramGraphUtil.getConnectionPointOfTerminal(graph, ti.t);
640 if (obj instanceof Resource && cp != null) {
641 Resource e = (Resource) obj;
642 for (Resource connector : graph.getObjects(e, cp)) {
643 Resource connection = ConnectionUtil.tryGetConnection(graph, connector);
644 if (connection != null)
655 * @throws DatabaseException
657 public Resource getOrCreateConnection(ReadGraph graph, TerminalInfo... tis) throws DatabaseException {
658 // Resolve if adding to existing connection.
659 Resource connection = null;
660 for (TerminalInfo ti : tis) {
661 connection = getExistingConnection(graph, ti);
662 if (connection != null)
666 if (connection == null) {
667 // No existing connection, create new.
668 ElementClass connectionClass = elementClassProvider.get(ElementClasses.CONNECTION);
669 Resource connectionClassResource = ElementUtils.checkedAdapt(connectionClass, Resource.class);
670 connection = cu.newConnection(diagramResource, connectionClassResource);
679 * @param controlPoints
681 * @throws DatabaseException
683 public List<Pair<ControlPoint, Resource>> createBranchPoints(WriteGraph graph, Resource connection,
684 Collection<ControlPoint> controlPoints) throws DatabaseException {
685 List<Pair<ControlPoint, Resource>> bps = new ArrayList<Pair<ControlPoint, Resource>>(controlPoints.size());
686 for(ControlPoint cp : controlPoints) {
687 if (cp.isAttachedToTerminal())
688 // Terminal attachments do not need branch points.
691 Resource bp = cu.newBranchPoint(connection,
692 AffineTransform.getTranslateInstance(cp.getPosition().getX(), cp.getPosition().getY()),
694 bps.add(Pair.make(cp, bp));
706 * @throws DatabaseException
708 protected Resource chooseAttachmentRelationForNode(ReadGraph graph,
709 Resource connection, TerminalInfo ti, ConnectionJudgement judgment)
710 throws DatabaseException {
711 Resource node = (Resource) ElementUtils.getObject(ti.e);
712 return chooseAttachmentRelationForNode(graph, connection, node, ti.t, judgment);
722 * @return the calculated attachment relation or <code>null</code> if the
723 * result is ambiguous
724 * @throws DatabaseException
726 protected Resource chooseAttachmentRelationForNode(ReadGraph graph,
727 Resource connection, Resource element, Terminal terminal,
728 ConnectionJudgement judgment) throws DatabaseException {
729 IConnectionPoint cp = ConnectionUtil.toConnectionPoint(graph, element, terminal);
730 CPTerminal cpt = (cp instanceof CPTerminal) ? (CPTerminal) cp : null;
731 Resource attachment = judgment.attachmentRelations.get(graph, cpt);
739 * @param connectTo resource to connect the new connector to if not
742 * @return <used attachment relation, the new DIA.Connector instance>. The
743 * attachment relation is <code>null</code> if it was chosen based
744 * on EdgeEnd instead of being defined
745 * @throws DatabaseException
747 protected Connector createConnectorForNode(WriteGraph graph, Resource connection, TerminalInfo ti, EdgeEnd end,
748 ConnectionJudgement judgment) throws DatabaseException {
749 Resource node = (Resource) ElementUtils.getObject(ti.e);
750 return createConnectorForNode(graph, connection, node, ti.t, end, judgment);
761 * @return <used attachment relation, the new DIA.Connector instance>. The
762 * attachment relation is <code>null</code> if it was chosen based
763 * on EdgeEnd instead of being defined
764 * @throws DatabaseException
766 protected Connector createConnectorForNode(WriteGraph graph, Resource connection, Resource element, Terminal terminal,
767 EdgeEnd end, ConnectionJudgement judgment) throws DatabaseException {
768 IConnectionPoint cp = ConnectionUtil.toConnectionPoint(graph, element, terminal);
769 CPTerminal cpt = (cp instanceof CPTerminal) ? (CPTerminal) cp : null;
770 Resource attachment = judgment.attachmentRelations.get(graph, cpt);
771 if (attachment == null)
772 attachment = cu.toHasConnectorRelation(end);
773 Resource connector = cu.getOrCreateConnector(connection, element, terminal, end, attachment);
774 return new Connector(attachment, connector);
782 * @return <used attachment relation, the new DIA.Connector instance>
783 * @throws DatabaseException
785 protected Connector createConnectorForNodeWithAttachment(WriteGraph graph,
786 Resource connection, TerminalInfo ti, Resource attachment)
787 throws DatabaseException {
788 Resource node = (Resource) ElementUtils.getObject(ti.e);
789 return createConnectorForNodeWithAttachment(graph, connection, node, ti.t, attachment);
798 * @return <used attachment relation, the new DIA.Connector instance>
799 * @throws DatabaseException
801 protected Connector createConnectorForNodeWithAttachment(WriteGraph graph,
802 Resource connection, Resource element, Terminal terminal,
803 Resource attachment) throws DatabaseException {
804 Resource connector = cu.getOrCreateConnector(connection, element, terminal, null, attachment);
805 return new Connector(attachment, connector);
814 * @param label <code>null</code> to leave flag without label
815 * @return an element describing the new created flag resource
816 * @throws DatabaseException
818 public IElement createFlag(WriteGraph graph, Resource connection, EdgeEnd end, ControlPoint cp,
819 FlagClass.Type type, String label) throws DatabaseException {
820 ElementClass flagClass = elementClassProvider.get(ElementClasses.FLAG);
821 IElement flagElement = Element.spawnNew(flagClass);
822 Resource flagClassResource = ElementUtils.checkedAdapt(flagClass, Resource.class);
824 Layer0 L0 = Layer0.getInstance(graph);
825 G2DResource G2D = G2DResource.getInstance(graph);
826 DiagramResource DIA = DiagramResource.getInstance(graph);
828 Resource flag = graph.newResource();
829 graph.claim(flag, L0.InstanceOf, null, flagClassResource);
830 flagElement.setHint(ElementHints.KEY_OBJECT, flag);
832 OrderedSetUtils.add(graph, diagramResource, flag);
834 AffineTransform at = AffineTransform.getTranslateInstance(cp.getPosition().getX(), cp.getPosition().getY());
835 flagElement.setHint(ElementHints.KEY_TRANSFORM, at);
836 double[] matrix = new double[6];
837 at.getMatrix(matrix);
838 graph.claimLiteral(flag, DIA.HasTransform, G2D.Transform, matrix);
840 flagElement.setHint(FlagClass.KEY_FLAG_TYPE, type);
841 graph.claim(flag, DIA.HasFlagType, null, DiagramGraphUtil.toFlagTypeResource(DIA, type));
843 graph.claimLiteral(flag, L0.HasLabel, DIA.FlagLabel, label, Bindings.STRING);
845 // Give running name to flag and increment the counter attached to the diagram.
846 AddElement.claimFreshElementName(graph, diagramResource, flag);
848 // Make the diagram consist of the new element
849 graph.claim(diagramResource, L0.ConsistsOf, flag);
851 // Put the element on all the currently active layers if possible.
852 if (layerManager != null) {
853 layerManager.removeFromAllLayers(graph, flag);
854 layerManager.putElementOnVisibleLayers(diagram, graph, flag);
857 // Add flag to possible IO table
858 IOTablesInfo ioTablesInfo = IOTableUtil.getIOTablesInfo(graph,
859 (Resource)diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE));
860 ioTablesInfo.updateBinding(graph, DIA, flag, at.getTranslateX(), at.getTranslateY());
869 * @throws DatabaseException
871 protected static Resource getExistingConnection(ReadGraph graph, TerminalInfo ti) throws DatabaseException {
873 if (isConnection(ti.e)) {
874 Object obj = ElementUtils.getObject(ti.e);
875 if (obj instanceof Resource) {
876 Resource c = (Resource) obj;
877 return graph.isInstanceOf(c, DiagramResource.getInstance(graph).Connection) ? c : null;
879 } else if (isBranchPoint(ti.e)) {
880 Object obj = ElementUtils.getObject(ti.e);
881 if (obj instanceof Resource) {
882 return ConnectionUtil.tryGetConnection(graph, (Resource) obj);
889 protected static boolean isConnection(IElement e) {
890 return e.getElementClass().containsClass(ConnectionHandler.class);
897 protected static boolean isBranchPoint(IElement e) {
898 return e.getElementClass().containsClass(BranchPoint.class);
905 * @throws DatabaseException
907 protected static Resource getDisconnectedFlag(ReadGraph graph, TerminalInfo terminal) throws DatabaseException {
908 if (terminal != null) {
909 Object obj = ElementUtils.getObject(terminal.e);
910 if (obj instanceof Resource) {
911 Resource flag = (Resource) obj;
912 if (graph.isInstanceOf(flag, DiagramResource.getInstance(graph).Flag)
913 && FlagUtil.isDisconnected(graph, flag))
922 * @param modelingRules
925 * @throws DatabaseException
927 protected static void setJoinedConnectionTypes(WriteGraph graph, IModelingRules modelingRules,
928 ConnectionJudgement judgment, Resource join) throws DatabaseException {
929 if (modelingRules != null && judgment != null && judgment.connectionType != null) {
930 DiagramResource DIA = DiagramResource.getInstance(graph);
931 StructuralResource2 STR = StructuralResource2.getInstance(graph);
932 List<Resource> connections = new ArrayList<Resource>(2);
933 for (Resource flag : graph.getObjects(join, DIA.FlagIsJoinedBy)) {
934 for (Resource connector : graph.getObjects(flag, STR.IsConnectedTo)) {
935 Resource connection = ConnectionUtil.tryGetConnection(graph, connector);
936 if (connection != null)
937 connections.add(connection);
940 for (Resource connection : connections)
941 modelingRules.setConnectionType(graph, connection, judgment.connectionType);