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