1 package org.simantics.diagram.handler;
3 import static org.simantics.diagram.handler.Paster.ComposedCutProcedure.compose;
5 import java.awt.geom.AffineTransform;
6 import java.awt.geom.Point2D;
7 import java.util.ArrayDeque;
8 import java.util.Collection;
9 import java.util.Deque;
10 import java.util.HashMap;
11 import java.util.HashSet;
13 import java.util.Queue;
15 import java.util.function.BiFunction;
17 import org.simantics.databoard.Bindings;
18 import org.simantics.db.ReadGraph;
19 import org.simantics.db.Resource;
20 import org.simantics.db.Session;
21 import org.simantics.db.Statement;
22 import org.simantics.db.WriteGraph;
23 import org.simantics.db.common.CommentMetadata;
24 import org.simantics.db.common.request.IndexRoot;
25 import org.simantics.db.common.request.PossibleTypedParent;
26 import org.simantics.db.common.request.WriteRequest;
27 import org.simantics.db.common.utils.CommonDBUtils;
28 import org.simantics.db.common.utils.NameUtils;
29 import org.simantics.db.common.utils.OrderedSetUtils;
30 import org.simantics.db.exception.DatabaseException;
31 import org.simantics.db.layer0.util.Layer0Utils;
32 import org.simantics.db.layer0.util.RemoverUtil;
33 import org.simantics.db.request.Write;
34 import org.simantics.diagram.content.ConnectionUtil;
35 import org.simantics.diagram.flag.DiagramFlagPreferences;
36 import org.simantics.diagram.flag.FlagLabelingScheme;
37 import org.simantics.diagram.flag.FlagUtil;
38 import org.simantics.diagram.flag.IOTableUtil;
39 import org.simantics.diagram.flag.IOTablesInfo;
40 import org.simantics.diagram.handler.PasteOperation.ForceCopyReferences;
41 import org.simantics.diagram.internal.DebugPolicy;
42 import org.simantics.diagram.stubs.DiagramResource;
43 import org.simantics.diagram.synchronization.CopyAdvisor;
44 import org.simantics.diagram.synchronization.ErrorHandler;
45 import org.simantics.diagram.synchronization.IModifiableSynchronizationContext;
46 import org.simantics.diagram.synchronization.StatementEvaluation;
47 import org.simantics.diagram.synchronization.SynchronizationException;
48 import org.simantics.diagram.synchronization.SynchronizationHints;
49 import org.simantics.diagram.synchronization.graph.AddConnection;
50 import org.simantics.diagram.synchronization.graph.AddElement;
51 import org.simantics.diagram.synchronization.graph.CopyAdvisorUtil;
52 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
53 import org.simantics.diagram.synchronization.graph.GraphSynchronizationHints;
54 import org.simantics.diagram.synchronization.graph.layer.GraphLayerManager;
55 import org.simantics.g2d.element.IElement;
56 import org.simantics.layer0.Layer0;
57 import org.simantics.modeling.ModelingResources;
58 import org.simantics.operation.Layer0X;
59 import org.simantics.scl.runtime.tuple.Tuple2;
60 import org.simantics.scl.runtime.tuple.Tuple3;
61 import org.simantics.structural.stubs.StructuralResource2;
62 import org.simantics.structural2.modelingRules.CPTerminal;
63 import org.simantics.structural2.modelingRules.ConnectionJudgement;
64 import org.simantics.structural2.modelingRules.IConnectionPoint;
65 import org.simantics.utils.datastructures.map.Tuple;
67 import gnu.trove.map.hash.THashMap;
68 import gnu.trove.set.hash.THashSet;
71 * @author Tuukka Lehtonen
75 private final boolean DEBUG = DebugPolicy.DEBUG_COPY_PASTE | true;
77 private final Session session;
79 private final PasteOperation op;
81 private final Resource sourceDiagram;
83 private final Resource targetDiagram;
85 private final IModifiableSynchronizationContext targetContext;
88 * Set for the duration of the write.
90 private WriteGraph graph;
91 private ConnectionUtil cu;
95 private DiagramResource DIA;
96 private ModelingResources MOD;
97 private StructuralResource2 STR;
100 * A translating affine transform that can be pre-concatenated to existing
101 * affine transforms to move them around according to the paste operation
102 * offset specification.
104 private AffineTransform offsetTransform;
107 * A node map for post-processing copied resources
109 private NodeMap nodeMap;
111 private Resource sourceRoot;
112 private Resource targetRoot;
113 private String sourceRootUri;
114 private String targetRootUri;
115 private boolean operateWithinSameRoot;
120 * @throws DatabaseException
122 public Paster(Session session, PasteOperation op) throws DatabaseException {
123 this.session = session;
126 this.sourceDiagram = op.sourceDiagram;
127 this.targetDiagram = op.targetDiagram;
128 if (this.sourceDiagram == null)
129 throw new IllegalArgumentException("source diagram has no resource");
130 if (this.targetDiagram == null)
131 throw new IllegalArgumentException("target diagram has no resource");
133 this.targetContext = (IModifiableSynchronizationContext) op.target.getHint(SynchronizationHints.CONTEXT);
134 if (this.targetContext == null)
135 throw new IllegalArgumentException("target diagram has no synchronization context");
137 this.offsetTransform = AffineTransform.getTranslateInstance(op.offset.getX(), op.offset.getY());
140 private String toString(PasteOperation op) {
141 StringBuilder sb = new StringBuilder();
142 sb.append("Diagram paste ");
143 sb.append(op.ea.all.size());
144 sb.append(" element(s) ");
150 sb.append(op.sourceDiagram);
152 sb.append(op.targetDiagram);
153 return sb.toString();
156 public void perform() throws DatabaseException {
157 final String comment = toString(op);
158 Write request = new WriteRequest() {
160 public void perform(WriteGraph graph) throws DatabaseException {
161 graph.markUndoPoint();
162 Paster.this.perform(graph);
163 // Add comment to change set.
164 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
165 graph.addMetadata(cm.add(comment));
168 session.syncRequest(request);
171 public void perform(WriteGraph graph) throws DatabaseException {
172 L0 = Layer0.getInstance(graph);
173 L0X = Layer0X.getInstance(graph);
174 STR = StructuralResource2.getInstance(graph);
175 DIA = DiagramResource.getInstance(graph);
176 MOD = ModelingResources.getInstance(graph);
178 this.cu = new ConnectionUtil(graph);
179 this.sourceRoot = graph.sync(new IndexRoot(sourceDiagram));
180 this.targetRoot = graph.sync(new IndexRoot(targetDiagram));
181 this.sourceRootUri = graph.getURI(sourceRoot);
182 this.targetRootUri = graph.getURI(targetRoot);
183 this.operateWithinSameRoot = sourceRoot.equals(targetRoot);
189 } catch (DatabaseException e) {
191 } catch (Exception e) {
192 throw new DatabaseException(e);
199 private void onFinish() {
200 final CopyAdvisor advisor = op.target.getHint(SynchronizationHints.COPY_ADVISOR);
201 if (advisor != null) {
203 targetContext.set(GraphSynchronizationHints.READ_TRANSACTION, graph);
204 targetContext.set(GraphSynchronizationHints.WRITE_TRANSACTION, graph);
205 advisor.onFinish(targetContext);
206 } catch (SynchronizationException e) {
207 ErrorHandler eh = targetContext.get(SynchronizationHints.ERROR_HANDLER);
208 eh.error(e.getMessage(), e);
210 targetContext.set(GraphSynchronizationHints.READ_TRANSACTION, null);
211 targetContext.set(GraphSynchronizationHints.WRITE_TRANSACTION, null);
216 // ------------------------------------------------------------------------
218 // ------------------------------------------------------------------------
220 interface Procedure {
221 void execute(Resource resource) throws Exception;
224 public void forEachResourceElement(String description, Collection<?> elements, Procedure procedure)
226 for (Object object : elements) {
227 if (object instanceof Resource) {
228 procedure.execute((Resource) object);
231 System.out.println("[" + description + "] Skipping non-resource element: " + object);
237 private void applyPasteOffset(Resource forResource) throws DatabaseException {
238 applyOffset(forResource, op.offset);
241 private void applyOffset(Resource forResource, Point2D offset) throws DatabaseException {
242 AffineTransform at = DiagramGraphUtil.getTransform(graph, forResource);
243 at.preConcatenate(AffineTransform.getTranslateInstance(offset.getX(), offset.getY()));
244 DiagramGraphUtil.setTransform(graph, forResource, at);
247 private void applyPasteOffsetToRouteLine(Resource routeLine) throws DatabaseException {
248 Boolean isHorizontal = graph.getPossibleRelatedValue(routeLine, DIA.IsHorizontal, Bindings.BOOLEAN);
249 Double pos = graph.getPossibleRelatedValue(routeLine, DIA.HasPosition, Bindings.DOUBLE);
252 if (Boolean.TRUE.equals(isHorizontal))
253 pos += op.offset.getY();
255 pos += op.offset.getX();
256 graph.claimLiteral(routeLine, DIA.HasPosition, pos, Bindings.DOUBLE);
259 // ------------------------------------------------------------------------
261 // ------------------------------------------------------------------------
263 Resource parentElement(Resource resource, Resource referenceRelation) throws DatabaseException {
264 // Only allow cutting if reference element has a parent and it is selected for cutting also.
265 Resource referencedParentComponent = graph.getPossibleObject(resource, referenceRelation);
266 if (referencedParentComponent == null)
268 return graph.getPossibleObject(referencedParentComponent, MOD.ComponentToElement);
271 boolean parentIsIncludedInCut(Resource resource, Resource referenceRelation, boolean noParentElementReturnValue) throws DatabaseException {
272 Resource referencedElement = parentElement(resource, referenceRelation);
273 if (referencedElement != null)
274 return op.ea.all.contains(referencedElement);
275 return noParentElementReturnValue;
278 protected void cut() throws Exception {
279 final GraphLayerManager glm = targetContext.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER);
281 final THashSet<Resource> cutElements = new THashSet<Resource>();
282 final CutProcedure registerNames = new CutProcedure() {
283 void postCut(Resource resource, Object cutResult) throws Exception {
284 String name = graph.getPossibleRelatedValue(resource, L0.HasName, Bindings.STRING);
286 cutElements.add(resource);
291 final CutProcedure nodeCutProcedure = new CutProcedure() {
293 void postCut(Resource resource, Object cutResult) throws Exception {
294 if (cutResult != null) {
295 applyPasteOffset(resource);
298 glm.removeFromAllLayers(graph, resource);
299 glm.putElementOnVisibleLayers(op.target, graph, resource);
305 CutProcedure flagCutProcedure = new CutProcedure() {
307 boolean preCut(Resource resource) throws Exception {
308 return nodeCutProcedure.preCut(resource);
311 void postCut(Resource resource, Object cutResult) throws Exception {
312 nodeCutProcedure.postCut(resource, cutResult);
314 if (FlagUtil.isJoinedInSingleDiagram(graph, resource)) {
315 FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);
316 String commonLabel = scheme.generateLabel(graph, targetDiagram);
317 graph.claimLiteral(resource, L0.HasLabel, DIA.FlagLabel, commonLabel);
318 for (Resource otherFlag : FlagUtil.getCounterparts(graph, resource))
319 graph.claimLiteral(otherFlag, L0.HasLabel, DIA.FlagLabel, commonLabel, Bindings.STRING);
322 IOTablesInfo ioTablesInfo = IOTableUtil.getIOTablesInfo(graph, op.targetDiagram);
323 double[] transform = graph.getRelatedValue(resource, DIA.HasTransform, Bindings.DOUBLE_ARRAY);
324 ioTablesInfo.updateBinding(graph, DIA, resource, transform[4], transform[5]);
326 // #11077: fix STR.JoinsComposite relations from joins related to moved flags.
327 // Otherwise the JoinsComposite relations will be wrong after the cut-operation.
328 for (Resource join : graph.getObjects(resource, DIA.FlagIsJoinedBy))
329 fixConnectionJoin(join);
332 Set<Resource> flagComposites = new HashSet<>();
333 Set<Resource> joinedComposites = new HashSet<>();
334 Set<Resource> invalidJoinedComposites = new HashSet<>();
336 void fixConnectionJoin(Resource join) throws DatabaseException {
337 Collection<Resource> flags = graph.getObjects(join, DIA.JoinsFlag);
338 if (flags.size() < 2) {
339 // Broken join, remove it. Joins that have
340 // < 2 flags attached to it shouldn't exist.
343 flagComposites.clear();
344 possibleCompositesOfElements(flags, flagComposites);
345 joinedComposites.clear();
346 joinedComposites.addAll( graph.getObjects(join, STR.JoinsComposite) );
348 // Calculate which JoinsComposites need to be added and which removed.
349 invalidJoinedComposites.clear();
350 invalidJoinedComposites.addAll(joinedComposites);
351 invalidJoinedComposites.removeAll(flagComposites);
352 flagComposites.removeAll(joinedComposites);
354 if (!invalidJoinedComposites.isEmpty()) {
355 for (Resource invalidComposite : invalidJoinedComposites)
356 graph.deny(join, STR.JoinsComposite, invalidComposite);
358 if (!flagComposites.isEmpty()) {
359 for (Resource joinedComposite : flagComposites)
360 graph.claim(join, STR.JoinsComposite, joinedComposite);
365 Set<Resource> possibleCompositesOfElements(Collection<Resource> elements, Set<Resource> result) throws DatabaseException {
366 for (Resource e : elements) {
367 Resource composite = possibleCompositeOfElement(e);
368 if (composite != null)
369 result.add(composite);
374 Resource possibleCompositeOfElement(Resource element) throws DatabaseException {
375 Resource diagram = graph.getPossibleObject(element, L0.PartOf);
376 return diagram != null ? graph.getPossibleObject(diagram, MOD.DiagramToComposite) : null;
380 CutProcedure monitorCutProcedure = new CutProcedure() {
382 void postCut(Resource resource, Object cutResult) throws DatabaseException {
383 if (cutResult != null) {
384 Resource parentElement = parentElement(resource, DIA.HasMonitorComponent);
385 if (parentElement == null) {
386 applyPasteOffset(resource);
387 } else if (!op.ea.all.contains(parentElement)) {
388 Point2D offset = op.offset;
389 if (!op.sameDiagram()) {
390 Resource parentDiagram = graph.sync(new PossibleTypedParent(parentElement, DIA.Diagram));
391 AffineTransform monitoredComponentTr = DiagramGraphUtil.getWorldTransform(graph, parentElement);
392 if (op.targetDiagram.equals(parentDiagram)) {
393 // Monitor is moved back to the parent element diagram.
394 // Must make monitor position relative to the parent position.
395 offset = new Point2D.Double(
396 op.offset.getX() - monitoredComponentTr.getTranslateX(),
397 op.offset.getY() - monitoredComponentTr.getTranslateY());
399 // Monitor is moved to another diagram than the parent element diagram.
400 // Must make monitor position absolute.
401 offset = new Point2D.Double(
402 op.offset.getX() + monitoredComponentTr.getTranslateX(),
403 op.offset.getY() + monitoredComponentTr.getTranslateY());
406 applyOffset(resource, offset);
410 glm.removeFromAllLayers(graph, resource);
411 glm.putElementOnVisibleLayers(op.target, graph, resource);
417 CutProcedure referenceElementCutProcedure = new CutProcedure() {
419 boolean preCut(Resource resource) throws DatabaseException {
420 // Only allow cutting if reference element has a parent and it is selected for cutting also.
421 return parentIsIncludedInCut(resource, MOD.HasParentComponent, true);
424 void postCut(Resource resource, Object cutResult) throws Exception {
425 if (cutResult != null) {
426 if (!parentIsIncludedInCut(resource, MOD.HasParentComponent, false)) {
427 applyPasteOffset(resource);
431 glm.removeFromAllLayers(graph, resource);
432 glm.putElementOnVisibleLayers(op.target, graph, resource);
438 CutProcedure connectionCutProcedure = new CutProcedure() {
440 void postCut(Resource resource, Object cutResult) throws Exception {
441 if (cutResult != null) {
442 for (Resource rn : graph.getObjects(resource, DIA.HasInteriorRouteNode)) {
443 if (graph.isInstanceOf(rn, DIA.BranchPoint))
444 applyPasteOffset(rn);
445 else if (graph.isInstanceOf(rn, DIA.RouteLine))
446 applyPasteOffsetToRouteLine(rn);
450 glm.removeFromAllLayers(graph, resource);
451 glm.putElementOnVisibleLayers(op.target, graph, resource);
457 // Before cutting, disconnect all nodes from connections that are not in
458 // the cut connection set.
460 final Set<Resource> selectedConnections = new HashSet<Resource>();
461 forEachResourceElement("Gather connections", op.ea.connections, new Procedure() {
463 public void execute(Resource resource) throws Exception {
464 selectedConnections.add(resource);
468 Set<Resource> affectedConnections = new HashSet<Resource>();
469 disconnectExcludedConnections("Disconnect Nodes", op.ea.nodeList, selectedConnections, affectedConnections);
470 disconnectExcludedConnections("Disconnect Flags", op.ea.flags, selectedConnections, affectedConnections);
472 for (Resource connection : affectedConnections) {
473 // Leave the connection only if it has:
474 // - at least one truly connected :DIA.Connector
475 // - at least 1 route/branch points
476 int connectedConnectors = cu.getConnectedConnectors(connection, null).size();
477 int branchPoints = cu.getBranchPoints(connection, null).size();
478 if (connectedConnectors > 0 && branchPoints > 0)
481 // Remove the whole connection.
482 cu.removeConnection(connection);
485 cut("Cut Nodes", op.ea.nodeList, compose(nodeCutProcedure, registerNames));
486 cut("Cut Others", op.ea.others, compose(nodeCutProcedure, registerNames));
487 // Cut reference elements after nodes so that parent relationships can be restored
488 // but before connections so that connections to the reference elements can be copied.
489 cut("Cut References", op.ea.references, compose(referenceElementCutProcedure, registerNames));
490 cut("Cut Flags", op.ea.flags, compose(flagCutProcedure, registerNames));
491 cut("Cut Connections", op.ea.connections, compose(connectionCutProcedure, registerNames));
492 cut("Cut Monitors", op.ea.monitors, compose(monitorCutProcedure, registerNames));
494 // Make sure that all the pasted nodes have unique names in their new namespace.
495 // Element names are only diagram-locally unique so this must be done after cut-paste.
496 for (Resource element : cutElements)
497 AddElement.claimFreshElementName(graph, targetDiagram, element);
505 * @param affectedConnections
509 private Set<Resource> disconnectExcludedConnections(String description, Collection<Resource> nodes,
510 final Set<Resource> selectedConnections, final Set<Resource> affectedConnections) throws Exception {
511 final StructuralResource2 str = StructuralResource2.getInstance(graph);
513 // Disconnect each connection that is not a part of selectedConnections
514 // but is attached to the listed nodes.
515 forEachResourceElement(description, nodes, new Procedure() {
517 public void execute(Resource resource) throws Exception {
518 for (Resource connector : graph.getObjects(resource, str.IsConnectedTo)) {
519 Resource connection = ConnectionUtil.tryGetConnection(graph, connector);
520 if (connection == null) {
521 // This is a stray connector that has no purpose and should be removed.
522 cu.removeConnectionPart(connector);
525 if (selectedConnections.contains(connection))
528 cu.removeConnectionPart(connector);
529 affectedConnections.add(connection);
534 return affectedConnections;
540 * @param cutProcedure custom pre- and post-cut processing, may be <code>null</code>
543 private void cut(final String description, Collection<Resource> elements, final CutProcedure cutProcedure)
545 final CopyAdvisor advisor = op.target.getHint(SynchronizationHints.COPY_ADVISOR);
547 forEachResourceElement(description, elements, new Procedure() {
549 public void execute(Resource resource) throws Exception {
551 System.out.println("[" + description + "] " + NameUtils.getSafeName(graph, resource, true));
553 if (cutProcedure != null && !cutProcedure.preCut(resource)) {
555 System.out.println("[" + description + "] ignoring element cut for " + NameUtils.getSafeName(graph, resource, true));
559 Object result = CopyAdvisorUtil.cut(targetContext, graph, advisor, resource, sourceDiagram, targetDiagram);
562 System.out.println("[" + description + "] RESULT: " + result);
564 if (cutProcedure != null)
565 cutProcedure.postCut(resource, result);
570 static class CutProcedure {
571 boolean preCut(Resource resource) throws Exception { return true; }
572 void postCut(Resource resource, Object cutResult) throws Exception {}
575 static class ComposedCutProcedure extends CutProcedure {
576 private final CutProcedure[] procedures;
578 public static ComposedCutProcedure compose(CutProcedure... procedures) {
579 return new ComposedCutProcedure(procedures);
582 public ComposedCutProcedure(CutProcedure... procedures) {
583 this.procedures = procedures;
586 boolean preCut(Resource resource) throws Exception {
587 for (CutProcedure proc : procedures)
588 if (!proc.preCut(resource))
592 void postCut(Resource resource, Object cutResult) throws Exception {
593 for (CutProcedure proc : procedures)
594 proc.postCut(resource, cutResult);
598 // ------------------------------------------------------------------------
599 // COPY LOGIC SUPPORT CLASSES
600 // ------------------------------------------------------------------------
602 static class IdentifiedElement extends Tuple {
603 public IdentifiedElement(Resource object, IElement element) {
604 super(object, element);
606 public Resource getObject() {
607 return (Resource) getField(0);
609 public IElement getElement() {
610 return (IElement) getField(1);
614 static public class NodeMap {
616 Map<Resource, IdentifiedElement> resourceMap = new HashMap<Resource, IdentifiedElement>();
617 Map<IElement, IdentifiedElement> elementMap = new HashMap<IElement, IdentifiedElement>();
619 public void put(Resource sourceResource, IElement sourceElement, IdentifiedElement dst) {
620 if (sourceResource == null)
621 throw new NullPointerException("null source resource");
622 resourceMap.put(sourceResource, dst);
623 if (sourceElement != null)
624 elementMap.put(sourceElement, dst);
627 public IdentifiedElement get(Resource source) {
628 return resourceMap.get(source);
631 public IdentifiedElement get(IElement source) {
632 return elementMap.get(source);
635 public Set<Resource> allResources() {
636 return resourceMap.keySet();
639 public Resource getResource(Resource source) {
640 IdentifiedElement ie = resourceMap.get(source);
642 return ie.getObject();
647 public Resource getResource(IElement source) {
648 IdentifiedElement ie = elementMap.get(source);
650 return ie.getObject();
657 static class ResourceMap extends HashMap<Resource, Resource> {
658 private static final long serialVersionUID = 687528035082504835L;
661 static class StatementMap extends HashMap<Resource, Statement> {
662 private static final long serialVersionUID = 8520092255776208395L;
665 static class MapQueue<K,V> {
666 Map<K, Deque<V>> map = new HashMap<K, Deque<V>>();
667 public void offer(K key, V value) {
668 Deque<V> deque = map.get(key);
670 map.put(key, deque = new ArrayDeque<V>());
673 public V poll(K key) {
674 Deque<V> deque = map.get(key);
677 V value = deque.poll();
684 // ------------------------------------------------------------------------
686 // ------------------------------------------------------------------------
689 * This is necessary to have DIA.Flag type copied over to the copied flag.
690 * Diagram mapping will have problems and potentially break the
691 * configuration if the type is not the same as in the source.
693 BiFunction<ReadGraph, Statement, StatementEvaluation> statementAdvisor =
694 new BiFunction<ReadGraph, Statement, StatementEvaluation>() {
696 public StatementEvaluation apply(ReadGraph graph, Statement stm) {
697 if (DIA.HasFlagType.equals(stm.getPredicate()))
698 return StatementEvaluation.INCLUDE;
699 return StatementEvaluation.USE_DEFAULT;
703 CopyProcedure nodeCopyProcedure = new CopyProcedure() {
704 Resource copy(Resource source) throws Exception {
705 Layer0 L0 = Layer0.getInstance(graph);
707 Resource copy = null;
708 final CopyAdvisor advisor = op.target.getHint(SynchronizationHints.COPY_ADVISOR);
709 if (advisor != null) {
710 Resource sourceComposite = graph.getPossibleObject(source, L0.PartOf);
711 if (sourceComposite == null || !graph.isInstanceOf(source, DIA.Composite)) {
712 DiagramResource DIA = DiagramResource.getInstance(graph);
713 sourceComposite = OrderedSetUtils.getSingleOwnerList(graph, source, DIA.Composite);
715 copy = CopyAdvisorUtil.copy(targetContext, graph, advisor, source, sourceComposite, op.targetDiagram);
719 copy = CopyAdvisorUtil.copy2(graph, source, statementAdvisor);
722 graph.deny(copy, MOD.IsTemplatized, copy);
724 // Add comment to change set.
725 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
726 graph.addMetadata(cm.add("Copied element " + source + " to " + copy));
728 // Add the new element to the diagram composite
729 OrderedSetUtils.add(graph, op.targetDiagram, copy);
731 // Give running name to element and increment the counter attached to the diagram.
732 AddElement.claimFreshElementName(graph, op.targetDiagram, copy);
734 // Make the diagram consist of the new element
735 graph.claim(op.targetDiagram, L0.ConsistsOf, copy);
737 // Put the element on all the currently active layers if possible.
738 GraphLayerManager glm = targetContext.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER);
740 glm.removeFromAllLayers(graph, copy);
741 glm.putElementOnVisibleLayers(op.target, graph, copy);
747 void postCopy(Resource source, Resource copy) throws Exception {
748 CopyPasteUtil.copyElementPosition(graph, op.ctx, source, copy, op.offset);
752 protected void copy() throws Exception {
753 nodeMap = new NodeMap();
755 CommonDBUtils.selectClusterSet(graph, targetDiagram);
757 // Fill nodeMap with initial Resource->Resource mappings
758 if (op.initialNodeMap != null) {
759 for (Map.Entry<Resource, Resource> entry : op.initialNodeMap.entrySet()) {
760 nodeMap.put(entry.getKey(), null, new IdentifiedElement(entry.getValue(), null));
764 // Perform copies in a suitable order
765 copyNodes( nodeMap );
766 // Copy reference elements after nodes so that parent relationships can be restored
767 // but before connections so that connections to the reference elements can be copied.
768 copyReferences( nodeMap );
769 copyFlags( nodeMap );
770 copyConnections( nodeMap );
771 // Copy monitors last since their parents must have been copied already.
772 copyMonitors( nodeMap );
778 private NodeMap copyNodes(final NodeMap nodeMap) throws Exception {
779 copy("Copy Others", op.ea.others, nodeMap, nodeCopyProcedure);
780 copy("Copy Nodes", op.ea.nodeList, nodeMap, nodeCopyProcedure);
785 private NodeMap copyReferences(final NodeMap nodeMap) throws Exception {
786 final boolean forceCopyReferences = op.hasOption(ForceCopyReferences.class);
788 copy("Copy References", op.ea.references, nodeMap, new CopyProcedure() {
790 Resource copy(Resource source) throws Exception {
791 // Don't copy unless the parent component is copied too.
792 Resource sourceParentComponent = graph.getPossibleObject(source, MOD.HasParentComponent);
793 if (sourceParentComponent == null)
795 Resource sourceParentElement = graph.getPossibleObject(sourceParentComponent, MOD.ComponentToElement);
796 if (sourceParentElement != null) {
797 if (!forceCopyReferences && !op.ea.all.contains(sourceParentElement))
799 // Find copied component
800 IdentifiedElement copiedParentElement = nodeMap.get(sourceParentElement);
801 if (copiedParentElement == null)
803 Resource copiedParentComponent = graph.getPossibleObject(copiedParentElement.getObject(), MOD.ElementToComponent);
804 if (copiedParentComponent == null)
806 return copyReference(source, copiedParentComponent);
808 // Check that the component is part of a diagramless composite before proceeding
809 Resource partOf = graph.getPossibleObject(sourceParentComponent, L0.PartOf);
810 if (partOf == null || graph.hasStatement(partOf, MOD.CompositeToDiagram))
812 // Resolve the matching parent component from the target context.
813 Resource targetParentComponent = resolveTargetComponent(sourceParentComponent);
814 if (targetParentComponent == null)
816 return copyReference(source, targetParentComponent);
820 private Resource resolveTargetComponent(Resource sourceParentComponent) throws DatabaseException {
821 if (operateWithinSameRoot)
822 return sourceParentComponent;
823 // Directly map relative source component URI into target root namespace.
824 String sourceUri = graph.getURI(sourceParentComponent);
825 String targetUri = sourceUri.replace(sourceRootUri, targetRootUri);
826 Resource targetParentComponent = graph.getPossibleResource(targetUri);
827 return targetParentComponent;
830 private Resource copyReference(Resource source, Resource parentComponent) throws Exception {
831 Resource referenceRelation = graph.getPossibleObject(source, MOD.HasReferenceRelation);
832 if (referenceRelation == null)
835 Resource relationCopy = CopyAdvisorUtil.copy4(graph, referenceRelation);
836 if (relationCopy == null)
839 Resource copy = nodeCopyProcedure.copy(source);
841 // WORKAROUND: The result consists of a badly copied reference relation.
842 // Remove it. How the relation is copied depends on whether the copy target
843 // is the same model or not. If it is, the relation is copied, but invalidly
844 // and if the target is not the same model, the relation is simply referenced
845 // with a uni-directional L0.ConsistsOf relation.
846 for (Resource o : graph.getObjects(copy, L0.ConsistsOf)) {
847 boolean ownedByCopy = graph.hasStatement(o, L0.PartOf, copy);
849 graph.deny(copy, L0.ConsistsOf, o);
850 RemoverUtil.remove(graph, o);
852 graph.deny(copy, L0.ConsistsOf, o);
856 // The element the copied reference is attached to was also copied.
857 // This means that we must attach the copied reference to its
858 // original component's copy.
859 graph.deny(copy, MOD.HasParentComponent);
860 if(parentComponent != null)
861 graph.claim(copy, MOD.HasParentComponent, MOD.HasParentComponent_Inverse, parentComponent);
863 // Attach reference relation
864 graph.claim(copy, L0.ConsistsOf, L0.PartOf, relationCopy);
865 graph.claim(copy, MOD.HasReferenceRelation, MOD.HasReferenceRelation_Inverse, relationCopy);
867 // #7348: renew reference relation GUID identifiers properly
868 Layer0Utils.renewIdentifier(graph, relationCopy);
869 for (Resource invRel : graph.getObjects(relationCopy, L0.ConsistsOf))
870 Layer0Utils.renewIdentifier(graph, invRel);
876 void postCopy(Resource source, Resource copy) throws Exception {
877 // Must fix element position if the copied reference element
878 // doesn't have a visible parent element.
879 Resource parentComponent = graph.getPossibleObject(source, MOD.HasParentComponent);
880 if (parentComponent == null)
882 Resource parentElement = graph.getPossibleObject(parentComponent, MOD.ComponentToElement);
883 if (parentElement == null)
884 CopyPasteUtil.copyElementPosition(graph, op.ctx, source, copy, op.offset);
891 private NodeMap copyFlags(NodeMap nodeMap) throws Exception {
892 final Layer0 l0 = Layer0.getInstance(graph);
893 final DiagramResource dia = DiagramResource.getInstance(graph);
896 private final Map<Resource, Resource> selectedFlags = new HashMap<Resource, Resource>();
897 private final Map<Resource, Resource> flagSelectedCounterpart = new HashMap<Resource, Resource>();
900 * Analyze which flag pairs are selected
902 * @throws DatabaseException
904 private void analyzeFlagSelection() throws DatabaseException {
905 for (Resource flag : op.ea.flags) {
906 selectedFlags.put(flag, flag);
908 for (Resource flag : selectedFlags.keySet()) {
909 boolean external = FlagUtil.isExternal(graph, flag);
910 boolean inSingleDiagram = FlagUtil.isJoinedInSingleDiagram(graph, flag);
911 if (!external && inSingleDiagram) {
912 // FIXME: this doesn't take into account local merged flags, which is a corner case but still possible
913 Resource counterpart = FlagUtil.getPossibleCounterpart(graph, flag);
914 if (selectedFlags.containsKey(counterpart)) {
915 flagSelectedCounterpart.put(flag, counterpart);
916 flagSelectedCounterpart.put(counterpart, flag);
923 * Reconnect copied flag pairs.
924 * @throws DatabaseException
926 private void reconnectLocalFlagPairs(NodeMap nodeMap) throws DatabaseException {
927 FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);
928 Resource diagram = op.targetDiagram;
930 Set<Resource> visited = new HashSet<Resource>();
931 ArrayDeque<Resource> queue = new ArrayDeque<Resource>(flagSelectedCounterpart.values());
932 while (!queue.isEmpty()) {
933 Resource flag = queue.poll();
934 Resource counterpart = flagSelectedCounterpart.get(flag);
935 if (!visited.add(flag) || !visited.add(counterpart) || counterpart == null)
939 Resource flagSourceElement = selectedFlags.get(flag);
940 Resource counterpartSourceElement = selectedFlags.get(counterpart);
942 IdentifiedElement flagCopy = nodeMap.get(flagSourceElement);
943 IdentifiedElement counterpartCopy = nodeMap.get(counterpartSourceElement);
945 FlagUtil.join(graph, flagCopy.getObject(), counterpartCopy.getObject());
947 // Provide fresh labeling for connected flags if possible
948 if (scheme != null) {
949 String label = scheme.generateLabel(graph, diagram);
951 graph.claimLiteral(flagCopy.getObject(), l0.HasLabel, dia.FlagLabel, label, Bindings.STRING);
952 graph.claimLiteral(counterpartCopy.getObject(), l0.HasLabel, dia.FlagLabel, label, Bindings.STRING);
958 public void perform(NodeMap nodeMap) throws Exception {
959 analyzeFlagSelection();
961 copy("Copy Flags", op.ea.flags, nodeMap, new CopyProcedure() {
963 Resource copy(Resource source) throws Exception {
964 return nodeCopyProcedure.copy(source);
967 public void postCopy(Resource source, Resource copy) throws Exception {
968 AffineTransform at = CopyPasteUtil.copyElementPosition(graph, op.ctx, source, copy, op.offset);
970 // Update flag table binding
971 IOTablesInfo ioTablesInfo = IOTableUtil.getIOTablesInfo(graph, op.targetDiagram);
972 ioTablesInfo.updateBinding(graph, DIA, copy, at.getTranslateX(), at.getTranslateY());
974 // All label properties must be removed from
975 // the copied flags. Disconnected flags are
976 // not supposed to have labels, and the right
977 // place to reset the labels is when the flags
978 // are reconnected to their respective
980 graph.denyValue(copy, l0.HasLabel);
984 reconnectLocalFlagPairs(nodeMap);
989 new FlagCopy().perform( nodeMap );
993 private NodeMap copyMonitors(final NodeMap nodeMap) throws Exception {
994 copy("Copy Monitors", op.ea.monitors, nodeMap, new CopyProcedure() {
996 Resource copy(Resource source) throws Exception {
997 // Don't copy monitors if they are copied without
998 // their parent element into another root (model).
999 if (!operateWithinSameRoot) {
1000 Resource monitorComponent = graph.getPossibleObject(source, DIA.HasMonitorComponent);
1001 if (monitorComponent != null) {
1002 Resource monitorElement = graph.getPossibleObject(monitorComponent, MOD.ComponentToElement);
1003 if (monitorElement == null || !op.ea.all.contains(monitorElement))
1007 Resource copy = nodeCopyProcedure.copy(source);
1011 void postCopy(Resource source, Resource copy) throws Exception {
1012 // Find the component and diagram element the source monitor is
1014 Resource monitorElement = null;
1015 Resource monitorComponent = graph.getPossibleObject(source, DIA.HasMonitorComponent);
1016 if (monitorComponent != null) {
1017 monitorElement = graph.getPossibleObject(monitorComponent, MOD.ComponentToElement);
1020 if (monitorElement != null && op.ea.all.contains(monitorElement)) {
1021 // The element the copied monitor is attached was also copied.
1022 // This means that we must attach the copied monitor to its
1023 // original components copy.
1025 // Remove old association
1026 graph.deny(copy, DIA.HasMonitorComponent);
1028 // Associate to copied component
1029 IdentifiedElement parent = nodeMap.get(monitorElement);
1030 if (parent != null) {
1031 monitorComponent = graph.getPossibleObject(parent.getObject(), MOD.ElementToComponent);
1032 if (monitorComponent != null)
1033 graph.claim(copy, DIA.HasMonitorComponent, monitorComponent);
1035 //throw new PasteException("no parent could be found for monitored element " + monitoredElement);
1038 // The element the copied monitor is attached was not copied
1039 // or there is no element for the monitored component.
1040 // This means that the copied monitor must be kept attached
1041 // to the same component no matter where it is in the model,
1042 // unless the copy is done into another model.
1043 if (operateWithinSameRoot && monitorComponent != null)
1044 graph.claim(copy, DIA.HasMonitorComponent, monitorComponent);
1046 Point2D offset = op.offset;
1047 if (!op.sameDiagram()) {
1048 if (monitorElement != null) {
1049 // Monitor doesn't have a diagram parent element any
1050 // more, must recalculate its offset.
1051 AffineTransform monitoredComponentTr = DiagramGraphUtil.getWorldTransform(graph, monitorElement);
1052 offset = new Point2D.Double(
1053 op.offset.getX() + monitoredComponentTr.getTranslateX(),
1054 op.offset.getY() + monitoredComponentTr.getTranslateY());
1057 CopyPasteUtil.copyElementPosition(graph, op.ctx, source, copy, offset);
1060 // Copy monitor suffix from original to copy.
1061 String monitorSuffix = graph.getPossibleRelatedValue(source, DIA.HasMonitorSuffix, Bindings.STRING);
1062 if (monitorSuffix != null)
1063 graph.claimLiteral(copy, DIA.HasMonitorSuffix, monitorSuffix, Bindings.STRING);
1065 // Copy used property obtains for monitor template data.
1066 graph.deny(copy, L0X.ObtainsProperty);
1067 for (Statement stm : graph.getStatements(source, L0X.ObtainsProperty)) {
1068 graph.claim(copy, stm.getPredicate(), null, stm.getObject());
1077 * @param description
1080 * @param copyProcedure
1083 private void copy(final String description, Collection<Resource> elements, final NodeMap nodeMap,
1084 final CopyProcedure copyProcedure) throws Exception {
1085 if (copyProcedure == null)
1086 throw new IllegalArgumentException("null copy procedure");
1088 forEachResourceElement(description, elements, new Procedure() {
1090 public void execute(Resource resource) throws Exception {
1092 System.out.println("[" + description + "] " + NameUtils.getSafeName(graph, resource, true));
1093 Resource copy = copyProcedure.copy(resource);
1096 System.out.println("[" + description + "] " + NameUtils.getSafeName(graph, resource, true) + " copied as " + NameUtils.getSafeName(graph, copy, true));
1097 nodeMap.put(resource, null, new IdentifiedElement(copy, null));
1098 if (op.copyMap != null)
1099 op.copyMap.put(resource, copy);
1100 copyProcedure.postCopy(resource, copy);
1106 public static class RouteLine extends Tuple2 {
1107 public RouteLine(Double position, Boolean horizontal) {
1108 super(position, horizontal);
1110 public double getPosition() {
1111 Double pos = (Double) get(0);
1112 return pos != null ? pos : 0.0;
1114 public boolean isHorizontal() {
1115 return Boolean.TRUE.equals(get(1));
1119 public static class BranchPoint extends Tuple3 {
1120 public BranchPoint(AffineTransform at, Boolean horizontal, Boolean vertical) {
1121 super(at, horizontal, vertical);
1123 public AffineTransform getTransform() {
1124 return (AffineTransform) get(0);
1128 public static RouteLine readRouteLine(ReadGraph graph, Resource src) throws DatabaseException {
1129 DiagramResource DIA = DiagramResource.getInstance(graph);
1130 Double pos = graph.getPossibleRelatedValue(src, DIA.HasPosition, Bindings.DOUBLE);
1131 Boolean hor = graph.getPossibleRelatedValue(src, DIA.IsHorizontal, Bindings.BOOLEAN);
1132 return new RouteLine(pos, hor);
1135 public static BranchPoint readBranchPoint(ReadGraph graph, Resource src) throws DatabaseException {
1136 DiagramResource DIA = DiagramResource.getInstance(graph);
1137 AffineTransform at = DiagramGraphUtil.getTransform(graph, src);
1138 boolean hor = graph.hasStatement(src, DIA.Horizontal);
1139 boolean ver = graph.hasStatement(src, DIA.Vertical);
1140 return new BranchPoint(at, hor, ver);
1148 private NodeMap copyConnections(final NodeMap nodeMap) throws Exception {
1149 final StructuralResource2 STR = StructuralResource2.getInstance(graph);
1150 final DiagramResource DIA = DiagramResource.getInstance(graph);
1152 // final IModelingRules rules = graph.syncRequest(DiagramRequests.getModelingRules(op.sourceDiagram, null));
1153 // if (rules == null)
1154 // throw new IllegalArgumentException("source diagram offers no modeling rules");
1156 final CopyAdvisor ca = op.target.getHint(SynchronizationHints.COPY_ADVISOR);
1158 throw new UnsupportedOperationException("Cannot copy connections, no copy advisor available for diagram "
1161 forEachResourceElement("Copy Connections", op.ea.connections, new Procedure() {
1163 public void execute(Resource sourceObject) throws DatabaseException {
1164 copyConnection(sourceObject);
1167 private void copyConnection(Resource sourceObject) throws DatabaseException {
1168 // For associating source<->destination connection parts
1169 final Map<Object, Object> resourceMap = new THashMap<Object, Object>();
1170 // For associating source connectors to source nodes
1171 final StatementMap connectorToNode = new StatementMap();
1173 // 1. copy connection
1174 // - This will also copy interior route nodes
1175 // - But will leave out the DIA.AreConnected relations between route nodes
1176 Resource sourceDiagram = graph.getPossibleObject(sourceObject, Layer0.getInstance(graph).PartOf);
1177 if (sourceDiagram == null)
1178 sourceDiagram = OrderedSetUtils.getSingleOwnerList(graph, sourceObject, DIA.Diagram);
1179 Resource copy = CopyAdvisorUtil.copy(targetContext, graph, ca, sourceObject, sourceDiagram, op.targetDiagram, resourceMap);
1181 throw new UnsupportedOperationException("Could not copy connection " + sourceObject);
1182 OrderedSetUtils.addFirst(graph, op.targetDiagram, copy);
1184 graph.deny(copy, MOD.IsTemplatized, copy);
1186 AddElement.claimFreshElementName(graph, op.targetDiagram, copy);
1188 AddConnection.copyConnectionType(graph, sourceObject, copy);
1190 GraphLayerManager glm = targetContext.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER);
1192 glm.removeFromAllLayers(graph, copy);
1193 glm.putElementOnVisibleLayers(op.target, graph, copy);
1196 nodeMap.put(sourceObject, null, new IdentifiedElement(copy, null));
1197 if (op.copyMap != null)
1198 op.copyMap.put(sourceObject, copy);
1200 // WORKAROUND: CopyAdvisorUtil.copy(..., resourceMap)
1201 // implementations do not all support filling the resource map.
1202 // Thus we resort to the old logic if resourceMap is empty at this point.
1203 final boolean mapResources = resourceMap.isEmpty();
1205 // 2. associate source connection parts to destination connection parts
1208 Collection<Statement> sourceHasConnectors = graph.getStatements(sourceObject, DIA.HasConnector);
1209 MapQueue<Resource, Resource> connectorsByType = new MapQueue<Resource, Resource>();
1210 for (Statement hasConnector : sourceHasConnectors) {
1211 connectorsByType.offer(hasConnector.getPredicate(), hasConnector.getObject());
1212 for (Statement connects : graph.getStatements(hasConnector.getObject(), STR.Connects)) {
1213 if (!sourceObject.equals(connects.getObject())) {
1214 connectorToNode.put(hasConnector.getObject(), connects);
1220 for (Statement hasConnector : graph.getStatements(copy, DIA.HasConnector)) {
1221 Resource srcConnector = connectorsByType.poll(hasConnector.getPredicate());
1222 resourceMap.put(srcConnector, hasConnector.getObject());
1225 // 2.2. Offset interior route nodes
1226 Collection<Resource> sourceInteriorRouteNodes = graph.getObjects(sourceObject, DIA.HasInteriorRouteNode);
1228 // WORKAROUND: for cases where resourceMap was not filled by
1229 // the copy operation. Still needed because TG copying does
1230 // not output this information.
1231 Queue<Resource> branchPoints = new ArrayDeque<Resource>(sourceInteriorRouteNodes.size());
1232 Queue<Resource> routeLines = new ArrayDeque<Resource>(sourceInteriorRouteNodes.size());
1233 for (Resource dst : graph.getObjects(copy, DIA.HasInteriorRouteNode)) {
1234 if (graph.isInstanceOf(dst, DIA.BranchPoint))
1235 branchPoints.offer(dst);
1236 else if (graph.isInstanceOf(dst, DIA.RouteLine))
1237 routeLines.offer(dst);
1239 for (Resource src : sourceInteriorRouteNodes) {
1240 if (graph.isInstanceOf(src, DIA.BranchPoint)) {
1241 Resource dst = branchPoints.poll();
1242 resourceMap.put(src, dst);
1243 BranchPoint bp = readBranchPoint(graph, src);
1244 AffineTransform at = bp.getTransform();
1245 at.preConcatenate(offsetTransform);
1246 DiagramGraphUtil.setTransform(graph, dst, at);
1248 else if (graph.isInstanceOf(src, DIA.RouteLine)) {
1249 Resource dst = routeLines.poll();
1250 resourceMap.put(src, dst);
1251 RouteLine rl = readRouteLine(graph, src);
1252 double newPos = rl.getPosition() + (rl.isHorizontal() ? op.offset.getY() : op.offset.getX());
1253 graph.claimLiteral(dst, DIA.HasPosition, newPos, Bindings.DOUBLE);
1257 for (Resource src : sourceInteriorRouteNodes) {
1258 Resource dst = (Resource) resourceMap.get(src);
1260 if (graph.isInstanceOf(src, DIA.BranchPoint)) {
1261 BranchPoint bp = readBranchPoint(graph, src);
1262 AffineTransform at = bp.getTransform();
1263 at.preConcatenate(offsetTransform);
1264 DiagramGraphUtil.setTransform(graph, dst, at);
1265 } else if (graph.isInstanceOf(src, DIA.RouteLine)) {
1266 RouteLine rl = readRouteLine(graph, src);
1267 double newPos = rl.getPosition() + (rl.isHorizontal() ? op.offset.getY() : op.offset.getX());
1268 graph.claimLiteral(dst, DIA.HasPosition, newPos, Bindings.DOUBLE);
1274 // 3. Connect connection parts according to how the source is connected
1275 for (Resource src : sourceInteriorRouteNodes) {
1276 Resource dst = (Resource) resourceMap.get(src);
1277 for (Resource connectedToSrc : graph.getObjects(src, DIA.AreConnected)) {
1278 Resource connectedToDst = (Resource) resourceMap.get(connectedToSrc);
1279 if (connectedToDst != null) {
1280 graph.claim(dst, DIA.AreConnected, DIA.AreConnected, connectedToDst);
1282 throw new DatabaseException("Connection copying failed due to an invalid DIA.AreConnected link between source resources " + src + " <-> " + connectedToSrc);
1286 for (Statement hasConnector : sourceHasConnectors) {
1287 Resource srcConnector = hasConnector.getObject();
1288 Resource dstConnector = (Resource) resourceMap.get(srcConnector);
1289 Statement srcConnects = connectorToNode.get(srcConnector);
1291 // Connect to copied nodes
1292 IdentifiedElement dstNode = nodeMap.get(srcConnects.getObject());
1293 if (dstNode == null)
1294 throw new DatabaseException("Source element "
1295 + NameUtils.getURIOrSafeNameInternal(graph, srcConnects.getObject())
1296 + " not copied causing copying of connection "
1297 + NameUtils.getURIOrSafeNameInternal(graph, sourceObject)
1299 graph.claim(dstConnector, srcConnects.getPredicate(), dstNode.getObject());
1301 // Connect to other copied route nodes
1302 for (Resource connectedToSrc : graph.getObjects(srcConnector, DIA.AreConnected)) {
1303 Resource connectedToDst = (Resource) resourceMap.get(connectedToSrc);
1304 graph.claim(dstConnector, DIA.AreConnected, DIA.AreConnected, connectedToDst);
1308 // 4. Make sure MOD.ConnectorToComponent relations are copied as well.
1309 // Otherwise diagram mapping will do bad things on the model.
1310 Resource sourceComponent = graph.getPossibleObject(sourceObject, MOD.ElementToComponent);
1311 if (sourceComponent != null) {
1312 for (Statement hasConnector : sourceHasConnectors) {
1313 Resource sourceConnector = hasConnector.getObject();
1314 Resource targetConnector = (Resource) resourceMap.get(sourceConnector);
1315 // Should have been defined back in steps 1-2.
1316 assert targetConnector != null;
1317 Statement sourceConnectorToComponent = graph.getPossibleStatement(sourceConnector, MOD.ConnectorToComponent);
1318 if (sourceConnectorToComponent == null)
1320 if (!sourceConnectorToComponent.getObject().equals(sourceComponent))
1322 Resource targetComponent = graph.getPossibleObject(copy, MOD.ElementToComponent);
1323 if (targetComponent == null)
1326 graph.claim(targetConnector, sourceConnectorToComponent.getPredicate(), targetComponent);
1328 // #6190 & apros:#11435: Ensure that MOD.HasConnectionMappingSpecification is added to target
1329 for (Resource connectionMappingSpec : graph.getObjects(sourceConnector, MOD.HasConnectionMappingSpecification))
1330 graph.claim(targetConnector, MOD.HasConnectionMappingSpecification, connectionMappingSpec);
1339 class CopyProcedure {
1340 Resource copy(Resource source) throws Exception { throw new UnsupportedOperationException(); }
1341 void postCopy(Resource source, Resource copy) throws Exception {}
1345 * @param judgment <code>null</code> if no judgement is available in which
1346 * case defaultValue is always returned
1347 * @param connectionPoint
1348 * @param defaultValue
1350 * @throws DatabaseException
1352 @SuppressWarnings("unused")
1353 private static Resource getAttachmentRelation(ReadGraph graph, ConnectionJudgement judgment,
1354 IConnectionPoint connectionPoint, Resource defaultValue) throws DatabaseException {
1355 if (judgment == null || !(connectionPoint instanceof CPTerminal) || judgment.attachmentRelations == null)
1356 return defaultValue;
1357 Resource attachment = judgment.attachmentRelations.get(graph, (CPTerminal) connectionPoint);
1358 return attachment != null ? attachment : defaultValue;
1362 * Get node map of copied variables. Map contains original and new resources.
1364 * @return NodeMap of copied resources or null if copy has not been performed
1366 public NodeMap getNodeMap() {
1370 protected PasteOperation getOperation() {
1374 public WriteGraph getGraph() {