1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2016 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 * Semantum Oy - Fixed bug #6364
\r
12 *******************************************************************************/
\r
13 package org.simantics.diagram.participant;
\r
15 import java.awt.geom.AffineTransform;
\r
16 import java.util.ArrayDeque;
\r
17 import java.util.ArrayList;
\r
18 import java.util.Arrays;
\r
19 import java.util.Collection;
\r
20 import java.util.Collections;
\r
21 import java.util.Deque;
\r
22 import java.util.Iterator;
\r
23 import java.util.List;
\r
25 import org.eclipse.jface.dialogs.MessageDialog;
\r
26 import org.eclipse.swt.SWT;
\r
27 import org.eclipse.ui.IWorkbenchWindow;
\r
28 import org.eclipse.ui.PlatformUI;
\r
29 import org.simantics.databoard.Bindings;
\r
30 import org.simantics.db.ReadGraph;
\r
31 import org.simantics.db.Resource;
\r
32 import org.simantics.db.Session;
\r
33 import org.simantics.db.Statement;
\r
34 import org.simantics.db.VirtualGraph;
\r
35 import org.simantics.db.WriteGraph;
\r
36 import org.simantics.db.common.CommentMetadata;
\r
37 import org.simantics.db.common.request.WriteRequest;
\r
38 import org.simantics.db.common.utils.OrderedSetUtils;
\r
39 import org.simantics.db.exception.DatabaseException;
\r
40 import org.simantics.diagram.content.ConnectionUtil;
\r
41 import org.simantics.diagram.content.ResourceTerminal;
\r
42 import org.simantics.diagram.flag.DiagramFlagPreferences;
\r
43 import org.simantics.diagram.flag.FlagLabelingScheme;
\r
44 import org.simantics.diagram.flag.FlagUtil;
\r
45 import org.simantics.diagram.flag.IOTableUtil;
\r
46 import org.simantics.diagram.flag.IOTablesInfo;
\r
47 import org.simantics.diagram.flag.Joiner;
\r
48 import org.simantics.diagram.stubs.DiagramResource;
\r
49 import org.simantics.diagram.stubs.G2DResource;
\r
50 import org.simantics.diagram.synchronization.ISynchronizationContext;
\r
51 import org.simantics.diagram.synchronization.SynchronizationHints;
\r
52 import org.simantics.diagram.synchronization.graph.AddElement;
\r
53 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
\r
54 import org.simantics.diagram.synchronization.graph.GraphSynchronizationHints;
\r
55 import org.simantics.diagram.synchronization.graph.RemoveElement;
\r
56 import org.simantics.diagram.synchronization.graph.layer.GraphLayerManager;
\r
57 import org.simantics.diagram.ui.DiagramModelHints;
\r
58 import org.simantics.g2d.connection.handler.ConnectionHandler;
\r
59 import org.simantics.g2d.diagram.DiagramHints;
\r
60 import org.simantics.g2d.diagram.IDiagram;
\r
61 import org.simantics.g2d.diagram.handler.Topology.Terminal;
\r
62 import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil.TerminalInfo;
\r
63 import org.simantics.g2d.element.ElementClass;
\r
64 import org.simantics.g2d.element.ElementClasses;
\r
65 import org.simantics.g2d.element.ElementHints;
\r
66 import org.simantics.g2d.element.ElementUtils;
\r
67 import org.simantics.g2d.element.IElement;
\r
68 import org.simantics.g2d.element.IElementClassProvider;
\r
69 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
\r
70 import org.simantics.g2d.element.impl.Element;
\r
71 import org.simantics.g2d.elementclass.BranchPoint;
\r
72 import org.simantics.g2d.elementclass.FlagClass;
\r
73 import org.simantics.layer0.Layer0;
\r
74 import org.simantics.modeling.ModelingResources;
\r
75 import org.simantics.scl.runtime.tuple.Tuple2;
\r
76 import org.simantics.structural.stubs.StructuralResource2;
\r
77 import org.simantics.structural2.modelingRules.CPTerminal;
\r
78 import org.simantics.structural2.modelingRules.ConnectionJudgement;
\r
79 import org.simantics.structural2.modelingRules.IConnectionPoint;
\r
80 import org.simantics.structural2.modelingRules.IModelingRules;
\r
81 import org.simantics.utils.datastructures.Callback;
\r
82 import org.simantics.utils.datastructures.Pair;
\r
83 import org.simantics.utils.ui.ErrorLogger;
\r
86 * @author Tuukka Lehtonen
\r
88 public class ConnectionBuilder {
\r
90 protected static class Connector extends Tuple2 {
\r
91 public Connector(Resource attachmentRelation, Resource connector) {
\r
92 super(attachmentRelation, connector);
\r
94 public Resource getAttachment() {
\r
95 return (Resource) c0;
\r
97 public Resource getConnector() {
\r
98 return (Resource) c1;
\r
102 protected final IDiagram diagram;
\r
103 protected final Resource diagramResource;
\r
104 protected final boolean createFlags;
\r
106 protected final ISynchronizationContext ctx;
\r
107 protected final IElementClassProvider elementClassProvider;
\r
108 protected final GraphLayerManager layerManager;
\r
110 protected ConnectionUtil cu;
\r
112 protected Layer0 L0;
\r
113 protected DiagramResource DIA;
\r
114 protected StructuralResource2 STR;
\r
115 protected ModelingResources MOD;
\r
117 public ConnectionBuilder(IDiagram diagram) {
\r
118 this.diagram = diagram;
\r
119 this.diagramResource = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
\r
120 this.createFlags = Boolean.TRUE.equals(diagram.getHint(DiagramHints.KEY_USE_CONNECTION_FLAGS));
\r
122 ctx = diagram.getHint(SynchronizationHints.CONTEXT);
\r
124 this.elementClassProvider = ctx.get(SynchronizationHints.ELEMENT_CLASS_PROVIDER);
\r
125 this.layerManager = ctx.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER);
\r
127 this.elementClassProvider = null;
\r
128 this.layerManager = null;
\r
132 protected void initializeResources(ReadGraph graph) {
\r
133 if (this.L0 == null) {
\r
134 this.L0 = Layer0.getInstance(graph);
\r
135 this.DIA = DiagramResource.getInstance(graph);
\r
136 this.STR = StructuralResource2.getInstance(graph);
\r
137 this.MOD = ModelingResources.getInstance(graph);
\r
144 * @param controlPoints
\r
145 * @param startTerminal
\r
146 * @param endTerminal
\r
147 * @throws DatabaseException
\r
149 public void create(WriteGraph graph, final ConnectionJudgement judgment, Deque<ControlPoint> controlPoints,
\r
150 TerminalInfo startTerminal, TerminalInfo endTerminal) throws DatabaseException {
\r
151 this.cu = new ConnectionUtil(graph);
\r
152 initializeResources(graph);
\r
154 final IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES);
\r
156 final Resource startDisconnectedFlag = getDisconnectedFlag(graph, startTerminal);
\r
157 final Resource endDisconnectedFlag = getDisconnectedFlag(graph, endTerminal);
\r
158 if (startDisconnectedFlag != null || endDisconnectedFlag != null) {
\r
159 if (startDisconnectedFlag != null && endDisconnectedFlag != null) {
\r
161 // Ask the user which operation to perform:
\r
162 // a) connect the disconnected flags together with a connection join
\r
163 // b) join the flags into a single connection
\r
165 final VirtualGraph graphProvider = graph.getProvider();
\r
166 final Session session = graph.getSession();
\r
168 PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
\r
170 public void run() {
\r
171 IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
\r
172 if (window == null)
\r
174 MessageDialog dialog = new MessageDialog(window.getShell(), "Connect or Join Flags?", null,
\r
175 "Connect flags together or join them visually into a connection?",
\r
176 MessageDialog.QUESTION_WITH_CANCEL, new String[] { "Connect Flags", "Join Flags",
\r
179 setShellStyle(getShellStyle() | SWT.SHEET);
\r
182 final int choice = dialog.open();
\r
184 if (choice != 2 && choice != SWT.DEFAULT) {
\r
185 session.asyncRequest(new WriteRequest(graphProvider) {
\r
187 public void perform(WriteGraph graph) throws DatabaseException {
\r
188 graph.markUndoPoint();
\r
191 Resource join = FlagUtil.join(graph, startDisconnectedFlag, endDisconnectedFlag);
\r
192 FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);
\r
193 String commonLabel = scheme.generateLabel(graph, diagramResource);
\r
194 graph.claimLiteral(startDisconnectedFlag, L0.HasLabel, DIA.FlagLabel, commonLabel);
\r
195 graph.claimLiteral(endDisconnectedFlag, L0.HasLabel, DIA.FlagLabel, commonLabel);
\r
197 // Set connection type according to modeling rules
\r
198 setJoinedConnectionTypes(graph, modelingRules, judgment, join);
\r
200 // Add comment to change set.
\r
201 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
\r
202 graph.addMetadata(cm.add("Connected flags"));
\r
207 // First connect the flags together
\r
208 Resource join = FlagUtil.join(graph, startDisconnectedFlag, endDisconnectedFlag);
\r
210 // Set connection type according to modeling rules
\r
211 setJoinedConnectionTypes(graph, modelingRules, judgment, join);
\r
213 // Join the flags into a direct connection
\r
214 new Joiner(graph).joinLocal(graph, Arrays.asList(startDisconnectedFlag, endDisconnectedFlag));
\r
216 // Add comment to change set.
\r
217 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
\r
218 graph.addMetadata(cm.add("Joined flags"));
\r
224 }, new Callback<DatabaseException>() {
\r
226 public void run(DatabaseException e) {
\r
228 ErrorLogger.defaultLogError(e);
\r
238 TerminalInfo normalTerminal = null;
\r
239 Resource flagToRemove = null;
\r
240 Resource connection = null;
\r
241 if (startDisconnectedFlag != null) {
\r
242 flagToRemove = startDisconnectedFlag;
\r
243 normalTerminal = endTerminal;
\r
244 connection = attachedToExistingConnection(graph, startTerminal);
\r
246 if (endDisconnectedFlag != null) {
\r
247 flagToRemove = endDisconnectedFlag;
\r
248 normalTerminal = startTerminal;
\r
249 connection = attachedToExistingConnection(graph, endTerminal);
\r
251 if (connection != null) {
\r
252 // OK, continuing a connection from an existing disconnected flag.
\r
254 // STEPS TO PERFORM:
\r
256 // 2. connect normal terminal directly to the existing connection
\r
257 Statement stm = graph.getSingleStatement(flagToRemove, STR.IsConnectedTo);
\r
258 Collection<Resource> areConnecteds = graph.getObjects(stm.getObject(), DIA.AreConnected);
\r
260 // Remove statement to connection connector before removing flag
\r
261 // to prevent removal of connector and the connection.
\r
263 new RemoveElement((Resource)diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), flagToRemove).perform(graph);
\r
265 // Disconnect the connector from the connection and create a
\r
266 // new connector for the element terminal.
\r
267 cu.removeConnectionPart(stm.getObject());
\r
268 Connector newConnector = createConnectorForNode(graph, connection,
\r
269 (Resource) ElementUtils.getObject(normalTerminal.e), normalTerminal.t,
\r
270 startDisconnectedFlag != null ? EdgeEnd.End : EdgeEnd.Begin, judgment);
\r
272 for (Resource areConnected : areConnecteds)
\r
273 graph.claim(newConnector.getConnector(), DIA.AreConnected, areConnected);
\r
275 if (modelingRules != null && judgment.connectionType != null)
\r
276 modelingRules.setConnectionType(graph, connection, judgment.connectionType);
\r
278 // Add comment to change set.
\r
279 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
\r
280 graph.addMetadata(cm.add("Joined flags"));
\r
281 graph.markUndoPoint();
\r
287 // 1. Get diagram connection to construct.
\r
288 Resource connection = getOrCreateConnection(graph, startTerminal, endTerminal);
\r
290 // 1.1 Give running name to connection and increment the counter attached to the diagram.
\r
291 AddElement.claimFreshElementName(graph, diagramResource, connection);
\r
293 // 2. Add branch points
\r
294 // 3. Create edges between branch points.
\r
295 List<Pair<ControlPoint, Resource>> bps = Collections.emptyList();
\r
296 Resource firstBranchPoint = null;
\r
297 Resource lastBranchPoint = null;
\r
298 if (!isRouteGraphConnection(graph, connection)) {
\r
299 bps = createBranchPoints(graph, connection, controlPoints);
\r
300 if (!bps.isEmpty()) {
\r
301 Iterator<Pair<ControlPoint, Resource>> it = bps.iterator();
\r
302 Pair<ControlPoint, Resource> prev = it.next();
\r
303 firstBranchPoint = prev.second;
\r
304 while (it.hasNext()) {
\r
305 Pair<ControlPoint, Resource> next = it.next();
\r
306 cu.connect(prev.second, next.second);
\r
309 lastBranchPoint = prev.second;
\r
313 // 4. Connect start/end terminals if those exist.
\r
314 // If first/lastBranchPoint != null, connect to those.
\r
315 // Otherwise connect the start/end terminals together.
\r
316 Connector startConnector = null;
\r
317 Connector endConnector = null;
\r
318 IElement startFlag = null;
\r
319 IElement endFlag = null;
\r
321 //FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);
\r
323 if (startTerminal != null && endTerminal != null) {
\r
324 Resource startAttachment = chooseAttachmentRelationForNode(graph, connection, startTerminal, judgment);
\r
325 Resource endAttachment = chooseAttachmentRelationForNode(graph, connection, endTerminal, judgment);
\r
326 Pair<Resource, Resource> attachments = resolveEndAttachments(graph, startAttachment, endAttachment);
\r
327 startConnector = createConnectorForNodeWithAttachment(graph, connection, startTerminal, attachments.first);
\r
328 endConnector = createConnectorForNodeWithAttachment(graph, connection, endTerminal, attachments.second);
\r
329 } else if (startTerminal != null) {
\r
330 startConnector = createConnectorForNode(graph, connection, startTerminal, EdgeEnd.Begin, judgment);
\r
332 EdgeEnd flagEnd = cu.toEdgeEnd( cu.getAttachmentRelationForConnector(startConnector.getConnector()), EdgeEnd.End ).other();
\r
333 endFlag = createFlag(graph, connection, flagEnd, controlPoints.getLast(), FlagClass.Type.Out,
\r
334 //scheme.generateLabel(graph, diagramResource));
\r
336 endConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(endFlag),
\r
337 ElementUtils.getSingleTerminal(endFlag), flagEnd, judgment);
\r
339 } else if (endTerminal != null) {
\r
340 endConnector = createConnectorForNode(graph, connection, endTerminal, EdgeEnd.End, judgment);
\r
342 EdgeEnd flagEnd = cu.toEdgeEnd( cu.getAttachmentRelationForConnector(endConnector.getConnector()), EdgeEnd.Begin ).other();
\r
343 startFlag = createFlag(graph, connection, flagEnd, controlPoints.getFirst(), FlagClass.Type.In,
\r
344 //scheme.generateLabel(graph, diagramResource));
\r
346 startConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(startFlag),
\r
347 ElementUtils.getSingleTerminal(startFlag), flagEnd, judgment);
\r
349 } else if (createFlags) {
\r
350 startFlag = createFlag(graph, connection, EdgeEnd.Begin, controlPoints.getFirst(), FlagClass.Type.In,
\r
351 //scheme.generateLabel(graph, diagramResource));
\r
353 startConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(startFlag),
\r
354 ElementUtils.getSingleTerminal(startFlag), EdgeEnd.Begin, judgment);
\r
356 endFlag = createFlag(graph, connection, EdgeEnd.End, controlPoints.getLast(), FlagClass.Type.Out,
\r
357 //scheme.generateLabel(graph, diagramResource));
\r
359 endConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(endFlag),
\r
360 ElementUtils.getSingleTerminal(endFlag), EdgeEnd.End, judgment);
\r
363 if (firstBranchPoint == null || lastBranchPoint == null) {
\r
364 cu.connect(startConnector.getConnector(), endConnector.getConnector());
\r
366 cu.connect(startConnector.getConnector(), firstBranchPoint);
\r
367 cu.connect(lastBranchPoint, endConnector.getConnector());
\r
370 // 5. Finally, set connection type according to modeling rules
\r
371 if (judgment.connectionType != null && modelingRules != null)
\r
372 modelingRules.setConnectionType(graph, connection, judgment.connectionType);
\r
374 // 5.1 Verify created flag types
\r
375 if (startFlag != null)
\r
376 verifyFlagType(graph, modelingRules, startFlag);
\r
377 if (endFlag != null)
\r
378 verifyFlagType(graph, modelingRules, endFlag);
\r
380 // 5.2 Write ConnectionMappingSpecification to connector if necessary
\r
381 writeConnectionMappingSpecification(graph, startTerminal, startConnector, judgment.connectionType);
\r
382 writeConnectionMappingSpecification(graph, endTerminal, endConnector, judgment.connectionType);
\r
384 // 6. Add comment to change set.
\r
385 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
\r
386 graph.addMetadata(cm.add("Added connection " + connection));
\r
387 graph.markUndoPoint();
\r
391 private boolean writeConnectionMappingSpecification(WriteGraph graph, TerminalInfo terminal, Connector connector, Resource connectionType)
\r
392 throws DatabaseException {
\r
393 Resource diagramConnRel = getConnectionRelation(graph, terminal);
\r
394 if (diagramConnRel == null)
\r
396 Resource connRel = graph.getPossibleObject(diagramConnRel, MOD.DiagramConnectionRelationToConnectionRelation);
\r
397 if (connRel == null || !graph.hasStatement(connRel, MOD.NeedsConnectionMappingSpecification))
\r
399 Resource mappingSpecification = graph.getPossibleObject(connectionType, MOD.ConnectionTypeToConnectionMappingSpecification);
\r
400 if (mappingSpecification == null)
\r
402 graph.claim(connector.getConnector(), MOD.HasConnectionMappingSpecification, null, mappingSpecification);
\r
406 private static Resource getConnectionRelation(ReadGraph graph, TerminalInfo ti) throws DatabaseException {
\r
407 if (ti != null && ti.t instanceof ResourceTerminal) {
\r
408 Resource t = ((ResourceTerminal) ti.t).getResource();
\r
409 Resource bindingRelation = DiagramGraphUtil.getConnectionPointOfTerminal(graph, t);
\r
410 return bindingRelation;
\r
417 * @param startAttachment
\r
418 * @param endAttachment
\r
420 * @throws DatabaseException
\r
422 protected Pair<Resource, Resource> resolveEndAttachments(WriteGraph graph,
\r
423 Resource startAttachment, Resource endAttachment) throws DatabaseException {
\r
424 if (startAttachment != null && endAttachment != null)
\r
425 return Pair.make(startAttachment, endAttachment);
\r
427 if (startAttachment != null && endAttachment == null)
\r
428 return Pair.make(startAttachment, getInverseAttachment(graph, startAttachment, DIA.HasArrowConnector));
\r
429 if (startAttachment == null && endAttachment != null)
\r
430 return Pair.make(getInverseAttachment(graph, endAttachment, DIA.HasPlainConnector), endAttachment);
\r
432 return Pair.make(DIA.HasPlainConnector, DIA.HasArrowConnector);
\r
437 * @param attachment
\r
439 * @throws DatabaseException
\r
441 protected Resource getInverseAttachment(ReadGraph graph, Resource attachment, Resource defaultValue) throws DatabaseException {
\r
442 Resource inverse = attachment != null ? graph.getPossibleObject(attachment, DIA.HasInverseAttachment) : defaultValue;
\r
443 return inverse != null ? inverse : defaultValue;
\r
448 * @param modelingRules
\r
449 * @param flagElement
\r
450 * @throws DatabaseException
\r
452 protected void verifyFlagType(WriteGraph graph, IModelingRules modelingRules, IElement flagElement) throws DatabaseException {
\r
453 if (modelingRules != null) {
\r
454 Resource flag = flagElement.getHint(ElementHints.KEY_OBJECT);
\r
455 FlagClass.Type flagType = flagElement.getHint(FlagClass.KEY_FLAG_TYPE);
\r
456 FlagUtil.verifyFlagType(graph, modelingRules, flag, flagType);
\r
463 * @param connection
\r
464 * @param attachToLine
\r
465 * @param controlPoints
\r
466 * @param endTerminal
\r
467 * @return the DIA.Connector instance created for attaching the connection
\r
468 * to the specified end terminal
\r
469 * @throws DatabaseException
\r
471 public Pair<Resource, Resource> attachToRouteGraph(
\r
473 ConnectionJudgement judgment,
\r
474 Resource attachToConnection,
\r
475 Resource attachToLine,
\r
476 Deque<ControlPoint> controlPoints,
\r
477 TerminalInfo endTerminal)
\r
478 throws DatabaseException
\r
480 initializeResources(graph);
\r
481 this.cu = new ConnectionUtil(graph);
\r
483 Resource endElement = endTerminal != null ? ElementUtils.getObject(endTerminal.e) : null;
\r
484 if (endElement != null
\r
485 && graph.isInstanceOf(endElement, DIA.Flag)
\r
486 && FlagUtil.isDisconnected(graph, endElement))
\r
488 // Connection ends in an existing but disconnected flag that
\r
489 // should be all right to connect to because the connection
\r
490 // judgment implies it makes a valid connection.
\r
491 // Check that we are attaching the connection to an existing
\r
492 // disconnected flag that is however attached to a connection.
\r
493 Resource endTerminalConnection = ConnectionBuilder.attachedToExistingConnection(graph, endTerminal);
\r
494 if (endTerminalConnection != null) {
\r
495 attachConnectionToFlag(graph, judgment, attachToConnection, attachToLine, controlPoints, endTerminal);
\r
500 Connector endConnector = null;
\r
501 if (endTerminal != null) {
\r
502 endConnector = createConnectorForNode(graph, attachToConnection, endTerminal, EdgeEnd.End, judgment);
\r
503 } else if (createFlags) {
\r
504 IElement endFlag = createFlag(graph, attachToConnection, EdgeEnd.End, controlPoints.getLast(), FlagClass.Type.Out, null);
\r
505 endConnector = createConnectorForNode(graph, attachToConnection, (Resource) ElementUtils.getObject(endFlag),
\r
506 ElementUtils.getSingleTerminal(endFlag), EdgeEnd.End, judgment);
\r
509 cu.connect(attachToLine, endConnector.getConnector());
\r
511 IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES);
\r
512 if (judgment.connectionType != null && modelingRules != null) {
\r
513 modelingRules.setConnectionType(graph, attachToConnection, judgment.connectionType);
\r
516 writeConnectionMappingSpecification(graph, endTerminal, endConnector, judgment.connectionType);
\r
518 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
\r
519 graph.addMetadata(cm.add("Branched connection " + attachToConnection));
\r
521 return Pair.make(endConnector.getAttachment(), endConnector.getConnector());
\r
527 protected void attachConnectionToFlag(
\r
529 ConnectionJudgement judgment,
\r
530 Resource attachToConnection,
\r
531 Resource attachToLine,
\r
532 Deque<ControlPoint> controlPoints,
\r
533 TerminalInfo toFlag)
\r
534 throws DatabaseException
\r
536 // Attaching attachedConnection to an existing disconnected flag that is
\r
537 // however attached to a connection.
\r
539 // 1. remove flag and its connector
\r
540 // 2. attach the two connections together by moving the route nodes
\r
541 // of the removed flag-side connection under the remaining connection
\r
542 // and ensuring that the route node chain will be valid after the
\r
543 // switch. In a chain route lines, each line must have an opposite
\r
544 // direction compared to the lines connected to it.
\r
545 Resource flagToRemove = ElementUtils.getObject(toFlag.e);
\r
546 Statement flagToConnector = graph.getSingleStatement(flagToRemove, STR.IsConnectedTo);
\r
547 Resource flagConnector = flagToConnector.getObject();
\r
548 Resource flagConnection = ConnectionUtil.getConnection(graph, flagConnector);
\r
549 Collection<Resource> flagRouteNodes = graph.getObjects(flagConnector, DIA.AreConnected);
\r
551 // Remove flag and its connector.
\r
552 graph.deny(flagToConnector);
\r
553 new RemoveElement((Resource)diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE), flagToRemove).perform(graph);
\r
554 cu.removeConnectionPart(flagConnector);
\r
556 // Attached routeline must have opposite direction than the line
\r
557 // attached to in order for the connection to be valid.
\r
558 Boolean attachingToHorizontalLine = graph.getPossibleRelatedValue(attachToLine, DIA.IsHorizontal, Bindings.BOOLEAN);
\r
559 if (attachingToHorizontalLine != null) {
\r
560 for (Resource routeNode : flagRouteNodes) {
\r
561 Collection<Resource> routeNodesToAttachTo = removeUntilOrientedRouteline(graph, !attachingToHorizontalLine, routeNode);
\r
562 for (Resource rn : routeNodesToAttachTo)
\r
563 cu.connect(attachToLine, rn);
\r
567 for (Statement routeNode : graph.getStatements(flagConnection, DIA.HasInteriorRouteNode)) {
\r
568 graph.deny(routeNode);
\r
569 graph.claim(attachToConnection, routeNode.getPredicate(), routeNode.getObject());
\r
571 for (Statement connector : graph.getStatements(flagConnection, DIA.HasConnector)) {
\r
572 graph.deny(connector);
\r
573 graph.claim(attachToConnection, connector.getPredicate(), connector.getObject());
\r
576 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
\r
577 graph.addMetadata(cm.add("Joined connection to disconnected flag"));
\r
580 private Collection<Resource> removeUntilOrientedRouteline(WriteGraph graph, boolean expectedOrientation, Resource routeNode) throws DatabaseException {
\r
581 List<Resource> result = new ArrayList<>(2);
\r
582 Deque<Resource> work = new ArrayDeque<>(2);
\r
583 work.addLast(routeNode);
\r
584 while (!work.isEmpty()) {
\r
585 Resource rn = work.removeFirst();
\r
586 if (graph.isInstanceOf(rn, DIA.RouteLine)) {
\r
587 Boolean isHorizontal = graph.getPossibleRelatedValue(rn, DIA.IsHorizontal, Bindings.BOOLEAN);
\r
588 if (isHorizontal != null && expectedOrientation != isHorizontal) {
\r
589 for (Resource rnn : graph.getObjects(rn, DIA.AreConnected))
\r
591 cu.removeConnectionPart(rn);
\r
600 protected boolean isRouteGraphConnection(ReadGraph graph, Resource connection) throws DatabaseException {
\r
601 initializeResources(graph);
\r
602 return graph.isInstanceOf(connection, DIA.RouteGraphConnection);
\r
609 * @throws DatabaseException
\r
611 public static Resource attachedToExistingConnection(ReadGraph graph, TerminalInfo ti) throws DatabaseException {
\r
612 Object obj = ElementUtils.getObject(ti.e);
\r
613 Resource cp = DiagramGraphUtil.getConnectionPointOfTerminal(graph, ti.t);
\r
614 if (obj instanceof Resource && cp != null) {
\r
615 Resource e = (Resource) obj;
\r
616 for (Resource connector : graph.getObjects(e, cp)) {
\r
617 Resource connection = ConnectionUtil.tryGetConnection(graph, connector);
\r
618 if (connection != null)
\r
629 * @throws DatabaseException
\r
631 public Resource getOrCreateConnection(ReadGraph graph, TerminalInfo... tis) throws DatabaseException {
\r
632 // Resolve if adding to existing connection.
\r
633 Resource connection = null;
\r
634 for (TerminalInfo ti : tis) {
\r
635 connection = getExistingConnection(graph, ti);
\r
636 if (connection != null)
\r
640 if (connection == null) {
\r
641 // No existing connection, create new.
\r
642 ElementClass connectionClass = elementClassProvider.get(ElementClasses.CONNECTION);
\r
643 Resource connectionClassResource = ElementUtils.checkedAdapt(connectionClass, Resource.class);
\r
644 connection = cu.newConnection(diagramResource, connectionClassResource);
\r
652 * @param connection
\r
653 * @param controlPoints
\r
655 * @throws DatabaseException
\r
657 public List<Pair<ControlPoint, Resource>> createBranchPoints(WriteGraph graph, Resource connection,
\r
658 Collection<ControlPoint> controlPoints) throws DatabaseException {
\r
659 List<Pair<ControlPoint, Resource>> bps = new ArrayList<Pair<ControlPoint, Resource>>(controlPoints.size());
\r
660 for(ControlPoint cp : controlPoints) {
\r
661 if (cp.isAttachedToTerminal())
\r
662 // Terminal attachments do not need branch points.
\r
665 Resource bp = cu.newBranchPoint(connection,
\r
666 AffineTransform.getTranslateInstance(cp.getPosition().getX(), cp.getPosition().getY()),
\r
667 cp.getDirection());
\r
668 bps.add(Pair.make(cp, bp));
\r
675 * @param connection
\r
680 * @throws DatabaseException
\r
682 protected Resource chooseAttachmentRelationForNode(ReadGraph graph,
\r
683 Resource connection, TerminalInfo ti, ConnectionJudgement judgment)
\r
684 throws DatabaseException {
\r
685 Resource node = (Resource) ElementUtils.getObject(ti.e);
\r
686 return chooseAttachmentRelationForNode(graph, connection, node, ti.t, judgment);
\r
691 * @param connection
\r
696 * @return the calculated attachment relation or <code>null</code> if the
\r
697 * result is ambiguous
\r
698 * @throws DatabaseException
\r
700 protected Resource chooseAttachmentRelationForNode(ReadGraph graph,
\r
701 Resource connection, Resource element, Terminal terminal,
\r
702 ConnectionJudgement judgment) throws DatabaseException {
\r
703 IConnectionPoint cp = ConnectionUtil.toConnectionPoint(graph, element, terminal);
\r
704 CPTerminal cpt = (cp instanceof CPTerminal) ? (CPTerminal) cp : null;
\r
705 Resource attachment = judgment.attachmentRelations.get(graph, cpt);
\r
711 * @param connection
\r
713 * @param connectTo resource to connect the new connector to if not
\r
714 * <code>null</code>
\r
716 * @return <used attachment relation, the new DIA.Connector instance>. The
\r
717 * attachment relation is <code>null</code> if it was chosen based
\r
718 * on EdgeEnd instead of being defined
\r
719 * @throws DatabaseException
\r
721 protected Connector createConnectorForNode(WriteGraph graph, Resource connection, TerminalInfo ti, EdgeEnd end,
\r
722 ConnectionJudgement judgment) throws DatabaseException {
\r
723 Resource node = (Resource) ElementUtils.getObject(ti.e);
\r
724 return createConnectorForNode(graph, connection, node, ti.t, end, judgment);
\r
729 * @param connection
\r
735 * @return <used attachment relation, the new DIA.Connector instance>. The
\r
736 * attachment relation is <code>null</code> if it was chosen based
\r
737 * on EdgeEnd instead of being defined
\r
738 * @throws DatabaseException
\r
740 protected Connector createConnectorForNode(WriteGraph graph, Resource connection, Resource element, Terminal terminal,
\r
741 EdgeEnd end, ConnectionJudgement judgment) throws DatabaseException {
\r
742 IConnectionPoint cp = ConnectionUtil.toConnectionPoint(graph, element, terminal);
\r
743 CPTerminal cpt = (cp instanceof CPTerminal) ? (CPTerminal) cp : null;
\r
744 Resource attachment = judgment.attachmentRelations.get(graph, cpt);
\r
745 if (attachment == null)
\r
746 attachment = cu.toHasConnectorRelation(end);
\r
747 Resource connector = cu.getOrCreateConnector(connection, element, terminal, end, attachment);
\r
748 return new Connector(attachment, connector);
\r
753 * @param connection
\r
755 * @param attachment
\r
756 * @return <used attachment relation, the new DIA.Connector instance>
\r
757 * @throws DatabaseException
\r
759 protected Connector createConnectorForNodeWithAttachment(WriteGraph graph,
\r
760 Resource connection, TerminalInfo ti, Resource attachment)
\r
761 throws DatabaseException {
\r
762 Resource node = (Resource) ElementUtils.getObject(ti.e);
\r
763 return createConnectorForNodeWithAttachment(graph, connection, node, ti.t, attachment);
\r
768 * @param connection
\r
771 * @param attachment
\r
772 * @return <used attachment relation, the new DIA.Connector instance>
\r
773 * @throws DatabaseException
\r
775 protected Connector createConnectorForNodeWithAttachment(WriteGraph graph,
\r
776 Resource connection, Resource element, Terminal terminal,
\r
777 Resource attachment) throws DatabaseException {
\r
778 Resource connector = cu.getOrCreateConnector(connection, element, terminal, null, attachment);
\r
779 return new Connector(attachment, connector);
\r
784 * @param connection
\r
788 * @param label <code>null</code> to leave flag without label
\r
789 * @return an element describing the new created flag resource
\r
790 * @throws DatabaseException
\r
792 public IElement createFlag(WriteGraph graph, Resource connection, EdgeEnd end, ControlPoint cp,
\r
793 FlagClass.Type type, String label) throws DatabaseException {
\r
794 ElementClass flagClass = elementClassProvider.get(ElementClasses.FLAG);
\r
795 IElement flagElement = Element.spawnNew(flagClass);
\r
796 Resource flagClassResource = ElementUtils.checkedAdapt(flagClass, Resource.class);
\r
798 Layer0 L0 = Layer0.getInstance(graph);
\r
799 G2DResource G2D = G2DResource.getInstance(graph);
\r
800 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
802 Resource flag = graph.newResource();
\r
803 graph.claim(flag, L0.InstanceOf, null, flagClassResource);
\r
804 flagElement.setHint(ElementHints.KEY_OBJECT, flag);
\r
806 OrderedSetUtils.add(graph, diagramResource, flag);
\r
808 AffineTransform at = AffineTransform.getTranslateInstance(cp.getPosition().getX(), cp.getPosition().getY());
\r
809 flagElement.setHint(ElementHints.KEY_TRANSFORM, at);
\r
810 double[] matrix = new double[6];
\r
811 at.getMatrix(matrix);
\r
812 graph.claimLiteral(flag, DIA.HasTransform, G2D.Transform, matrix);
\r
814 flagElement.setHint(FlagClass.KEY_FLAG_TYPE, type);
\r
815 graph.claim(flag, DIA.HasFlagType, null, DiagramGraphUtil.toFlagTypeResource(DIA, type));
\r
817 graph.claimLiteral(flag, L0.HasLabel, DIA.FlagLabel, label, Bindings.STRING);
\r
819 // Give running name to flag and increment the counter attached to the diagram.
\r
820 AddElement.claimFreshElementName(graph, diagramResource, flag);
\r
822 // Make the diagram consist of the new element
\r
823 graph.claim(diagramResource, L0.ConsistsOf, flag);
\r
825 // Put the element on all the currently active layers if possible.
\r
826 if (layerManager != null) {
\r
827 layerManager.removeFromAllLayers(graph, flag);
\r
828 layerManager.putElementOnVisibleLayers(diagram, graph, flag);
\r
831 // Add flag to possible IO table
\r
832 IOTablesInfo ioTablesInfo = IOTableUtil.getIOTablesInfo(graph,
\r
833 (Resource)diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE));
\r
834 ioTablesInfo.updateBinding(graph, DIA, flag, at.getTranslateX(), at.getTranslateY());
\r
836 return flagElement;
\r
843 * @throws DatabaseException
\r
845 protected static Resource getExistingConnection(ReadGraph graph, TerminalInfo ti) throws DatabaseException {
\r
847 if (isConnection(ti.e)) {
\r
848 Object obj = ElementUtils.getObject(ti.e);
\r
849 if (obj instanceof Resource) {
\r
850 Resource c = (Resource) obj;
\r
851 return graph.isInstanceOf(c, DiagramResource.getInstance(graph).Connection) ? c : null;
\r
853 } else if (isBranchPoint(ti.e)) {
\r
854 Object obj = ElementUtils.getObject(ti.e);
\r
855 if (obj instanceof Resource) {
\r
856 return ConnectionUtil.tryGetConnection(graph, (Resource) obj);
\r
863 protected static boolean isConnection(IElement e) {
\r
864 return e.getElementClass().containsClass(ConnectionHandler.class);
\r
871 protected static boolean isBranchPoint(IElement e) {
\r
872 return e.getElementClass().containsClass(BranchPoint.class);
\r
879 * @throws DatabaseException
\r
881 protected static Resource getDisconnectedFlag(ReadGraph graph, TerminalInfo terminal) throws DatabaseException {
\r
882 if (terminal != null) {
\r
883 Object obj = ElementUtils.getObject(terminal.e);
\r
884 if (obj instanceof Resource) {
\r
885 Resource flag = (Resource) obj;
\r
886 if (graph.isInstanceOf(flag, DiagramResource.getInstance(graph).Flag)
\r
887 && FlagUtil.isDisconnected(graph, flag))
\r
896 * @param modelingRules
\r
899 * @throws DatabaseException
\r
901 protected static void setJoinedConnectionTypes(WriteGraph graph, IModelingRules modelingRules,
\r
902 ConnectionJudgement judgment, Resource join) throws DatabaseException {
\r
903 if (modelingRules != null && judgment != null && judgment.connectionType != null) {
\r
904 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
905 StructuralResource2 STR = StructuralResource2.getInstance(graph);
\r
906 List<Resource> connections = new ArrayList<Resource>(2);
\r
907 for (Resource flag : graph.getObjects(join, DIA.FlagIsJoinedBy)) {
\r
908 for (Resource connector : graph.getObjects(flag, STR.IsConnectedTo)) {
\r
909 Resource connection = ConnectionUtil.tryGetConnection(graph, connector);
\r
910 if (connection != null)
\r
911 connections.add(connection);
\r
914 for (Resource connection : connections)
\r
915 modelingRules.setConnectionType(graph, connection, judgment.connectionType);
\r