1 package org.simantics.diagram.handler;
\r
3 import static org.simantics.diagram.handler.Paster.ComposedCutProcedure.compose;
\r
4 import gnu.trove.map.hash.THashMap;
\r
5 import gnu.trove.set.hash.THashSet;
\r
7 import java.awt.geom.AffineTransform;
\r
8 import java.awt.geom.Point2D;
\r
9 import java.util.ArrayDeque;
\r
10 import java.util.Collection;
\r
11 import java.util.Deque;
\r
12 import java.util.HashMap;
\r
13 import java.util.HashSet;
\r
14 import java.util.Map;
\r
15 import java.util.Queue;
\r
16 import java.util.Set;
\r
18 import org.simantics.databoard.Bindings;
\r
19 import org.simantics.db.ReadGraph;
\r
20 import org.simantics.db.Resource;
\r
21 import org.simantics.db.Session;
\r
22 import org.simantics.db.Statement;
\r
23 import org.simantics.db.WriteGraph;
\r
24 import org.simantics.db.common.CommentMetadata;
\r
25 import org.simantics.db.common.request.IndexRoot;
\r
26 import org.simantics.db.common.request.PossibleTypedParent;
\r
27 import org.simantics.db.common.request.WriteRequest;
\r
28 import org.simantics.db.common.utils.CommonDBUtils;
\r
29 import org.simantics.db.common.utils.NameUtils;
\r
30 import org.simantics.db.common.utils.OrderedSetUtils;
\r
31 import org.simantics.db.exception.DatabaseException;
\r
32 import org.simantics.db.layer0.util.RemoverUtil;
\r
33 import org.simantics.db.request.Write;
\r
34 import org.simantics.diagram.content.ConnectionUtil;
\r
35 import org.simantics.diagram.flag.DiagramFlagPreferences;
\r
36 import org.simantics.diagram.flag.FlagLabelingScheme;
\r
37 import org.simantics.diagram.flag.FlagUtil;
\r
38 import org.simantics.diagram.flag.IOTableUtil;
\r
39 import org.simantics.diagram.flag.IOTablesInfo;
\r
40 import org.simantics.diagram.handler.PasteOperation.ForceCopyReferences;
\r
41 import org.simantics.diagram.internal.DebugPolicy;
\r
42 import org.simantics.diagram.stubs.DiagramResource;
\r
43 import org.simantics.diagram.synchronization.CopyAdvisor;
\r
44 import org.simantics.diagram.synchronization.ErrorHandler;
\r
45 import org.simantics.diagram.synchronization.IModifiableSynchronizationContext;
\r
46 import org.simantics.diagram.synchronization.StatementEvaluation;
\r
47 import org.simantics.diagram.synchronization.SynchronizationException;
\r
48 import org.simantics.diagram.synchronization.SynchronizationHints;
\r
49 import org.simantics.diagram.synchronization.graph.AddConnection;
\r
50 import org.simantics.diagram.synchronization.graph.AddElement;
\r
51 import org.simantics.diagram.synchronization.graph.CopyAdvisorUtil;
\r
52 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
\r
53 import org.simantics.diagram.synchronization.graph.GraphSynchronizationHints;
\r
54 import org.simantics.diagram.synchronization.graph.layer.GraphLayerManager;
\r
55 import org.simantics.g2d.element.IElement;
\r
56 import org.simantics.layer0.Layer0;
\r
57 import org.simantics.modeling.ModelingResources;
\r
58 import org.simantics.operation.Layer0X;
\r
59 import org.simantics.scl.runtime.tuple.Tuple2;
\r
60 import org.simantics.scl.runtime.tuple.Tuple3;
\r
61 import org.simantics.structural.stubs.StructuralResource2;
\r
62 import org.simantics.structural2.modelingRules.CPTerminal;
\r
63 import org.simantics.structural2.modelingRules.ConnectionJudgement;
\r
64 import org.simantics.structural2.modelingRules.IConnectionPoint;
\r
65 import org.simantics.utils.datastructures.BinaryFunction;
\r
66 import org.simantics.utils.datastructures.map.Tuple;
\r
69 * @author Tuukka Lehtonen
\r
71 public class Paster {
\r
73 private final boolean DEBUG = DebugPolicy.DEBUG_COPY_PASTE | true;
\r
75 private final Session session;
\r
77 private final PasteOperation op;
\r
79 private final Resource sourceDiagram;
\r
81 private final Resource targetDiagram;
\r
83 private final IModifiableSynchronizationContext targetContext;
\r
86 * Set for the duration of the write.
\r
88 private WriteGraph graph;
\r
89 private ConnectionUtil cu;
\r
92 private Layer0X L0X;
\r
93 private DiagramResource DIA;
\r
94 private ModelingResources MOD;
\r
95 private StructuralResource2 STR;
\r
98 * A translating affine transform that can be pre-concatenated to existing
\r
99 * affine transforms to move them around according to the paste operation
\r
100 * offset specification.
\r
102 private AffineTransform offsetTransform;
\r
105 * A node map for post-processing copied resources
\r
107 private NodeMap nodeMap;
\r
109 private Resource sourceRoot;
\r
110 private Resource targetRoot;
\r
111 private String sourceRootUri;
\r
112 private String targetRootUri;
\r
113 private boolean operateWithinSameRoot;
\r
118 * @throws DatabaseException
\r
120 public Paster(Session session, PasteOperation op) throws DatabaseException {
\r
121 this.session = session;
\r
124 this.sourceDiagram = op.sourceDiagram;
\r
125 this.targetDiagram = op.targetDiagram;
\r
126 if (this.sourceDiagram == null)
\r
127 throw new IllegalArgumentException("source diagram has no resource");
\r
128 if (this.targetDiagram == null)
\r
129 throw new IllegalArgumentException("target diagram has no resource");
\r
131 this.targetContext = (IModifiableSynchronizationContext) op.target.getHint(SynchronizationHints.CONTEXT);
\r
132 if (this.targetContext == null)
\r
133 throw new IllegalArgumentException("target diagram has no synchronization context");
\r
135 this.offsetTransform = AffineTransform.getTranslateInstance(op.offset.getX(), op.offset.getY());
\r
138 private String toString(PasteOperation op) {
\r
139 StringBuilder sb = new StringBuilder();
\r
140 sb.append("Diagram paste ");
\r
141 sb.append(op.ea.all.size());
\r
142 sb.append(" element(s) ");
\r
146 sb.append("copied");
\r
147 sb.append(" from ");
\r
148 sb.append(op.sourceDiagram);
\r
150 sb.append(op.targetDiagram);
\r
151 return sb.toString();
\r
154 public void perform() throws DatabaseException {
\r
155 final String comment = toString(op);
\r
156 Write request = new WriteRequest() {
\r
158 public void perform(WriteGraph graph) throws DatabaseException {
\r
159 graph.markUndoPoint();
\r
160 Paster.this.perform(graph);
\r
161 // Add comment to change set.
\r
162 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
\r
163 graph.addMetadata(cm.add(comment));
\r
166 session.syncRequest(request);
\r
169 public void perform(WriteGraph graph) throws DatabaseException {
\r
170 L0 = Layer0.getInstance(graph);
\r
171 L0X = Layer0X.getInstance(graph);
\r
172 STR = StructuralResource2.getInstance(graph);
\r
173 DIA = DiagramResource.getInstance(graph);
\r
174 MOD = ModelingResources.getInstance(graph);
\r
175 this.graph = graph;
\r
176 this.cu = new ConnectionUtil(graph);
\r
177 this.sourceRoot = graph.sync(new IndexRoot(sourceDiagram));
\r
178 this.targetRoot = graph.sync(new IndexRoot(targetDiagram));
\r
179 this.sourceRootUri = graph.getURI(sourceRoot);
\r
180 this.targetRootUri = graph.getURI(targetRoot);
\r
181 this.operateWithinSameRoot = sourceRoot.equals(targetRoot);
\r
187 } catch (DatabaseException e) {
\r
189 } catch (Exception e) {
\r
190 throw new DatabaseException(e);
\r
197 private void onFinish() {
\r
198 final CopyAdvisor advisor = op.target.getHint(SynchronizationHints.COPY_ADVISOR);
\r
199 if (advisor != null) {
\r
201 targetContext.set(GraphSynchronizationHints.READ_TRANSACTION, graph);
\r
202 targetContext.set(GraphSynchronizationHints.WRITE_TRANSACTION, graph);
\r
203 advisor.onFinish(targetContext);
\r
204 } catch (SynchronizationException e) {
\r
205 ErrorHandler eh = targetContext.get(SynchronizationHints.ERROR_HANDLER);
\r
206 eh.error(e.getMessage(), e);
\r
208 targetContext.set(GraphSynchronizationHints.READ_TRANSACTION, null);
\r
209 targetContext.set(GraphSynchronizationHints.WRITE_TRANSACTION, null);
\r
214 // ------------------------------------------------------------------------
\r
216 // ------------------------------------------------------------------------
\r
218 interface Procedure {
\r
219 void execute(Resource resource) throws Exception;
\r
222 public void forEachResourceElement(String description, Collection<?> elements, Procedure procedure)
\r
224 for (Object object : elements) {
\r
225 if (object instanceof Resource) {
\r
226 procedure.execute((Resource) object);
\r
229 System.out.println("[" + description + "] Skipping non-resource element: " + object);
\r
235 private void applyPasteOffset(Resource forResource) throws DatabaseException {
\r
236 applyOffset(forResource, op.offset);
\r
239 private void applyOffset(Resource forResource, Point2D offset) throws DatabaseException {
\r
240 AffineTransform at = DiagramGraphUtil.getTransform(graph, forResource);
\r
241 at.preConcatenate(AffineTransform.getTranslateInstance(offset.getX(), offset.getY()));
\r
242 DiagramGraphUtil.setTransform(graph, forResource, at);
\r
245 private void applyPasteOffsetToRouteLine(Resource routeLine) throws DatabaseException {
\r
246 Boolean isHorizontal = graph.getPossibleRelatedValue(routeLine, DIA.IsHorizontal, Bindings.BOOLEAN);
\r
247 Double pos = graph.getPossibleRelatedValue(routeLine, DIA.HasPosition, Bindings.DOUBLE);
\r
250 if (Boolean.TRUE.equals(isHorizontal))
\r
251 pos += op.offset.getY();
\r
253 pos += op.offset.getX();
\r
254 graph.claimLiteral(routeLine, DIA.HasPosition, pos, Bindings.DOUBLE);
\r
257 // ------------------------------------------------------------------------
\r
259 // ------------------------------------------------------------------------
\r
261 Resource parentElement(Resource resource, Resource referenceRelation) throws DatabaseException {
\r
262 // Only allow cutting if reference element has a parent and it is selected for cutting also.
\r
263 Resource referencedParentComponent = graph.getPossibleObject(resource, referenceRelation);
\r
264 if (referencedParentComponent == null)
\r
266 return graph.getPossibleObject(referencedParentComponent, MOD.ComponentToElement);
\r
269 boolean parentIsIncludedInCut(Resource resource, Resource referenceRelation, boolean noParentElementReturnValue) throws DatabaseException {
\r
270 Resource referencedElement = parentElement(resource, referenceRelation);
\r
271 if (referencedElement != null)
\r
272 return op.ea.all.contains(referencedElement);
\r
273 return noParentElementReturnValue;
\r
276 private void cut() throws Exception {
\r
277 final GraphLayerManager glm = targetContext.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER);
\r
279 final THashSet<Resource> cutElements = new THashSet<Resource>();
\r
280 final CutProcedure registerNames = new CutProcedure() {
\r
281 void postCut(Resource resource, Object cutResult) throws Exception {
\r
282 String name = graph.getPossibleRelatedValue(resource, L0.HasName, Bindings.STRING);
\r
283 if (name != null) {
\r
284 cutElements.add(resource);
\r
289 final CutProcedure nodeCutProcedure = new CutProcedure() {
\r
291 void postCut(Resource resource, Object cutResult) throws Exception {
\r
292 if (cutResult != null) {
\r
293 applyPasteOffset(resource);
\r
296 glm.removeFromAllLayers(graph, resource);
\r
297 glm.putElementOnVisibleLayers(op.target, graph, resource);
\r
303 CutProcedure flagCutProcedure = new CutProcedure() {
\r
305 boolean preCut(Resource resource) throws Exception {
\r
306 return nodeCutProcedure.preCut(resource);
\r
309 void postCut(Resource resource, Object cutResult) throws Exception {
\r
310 nodeCutProcedure.postCut(resource, cutResult);
\r
312 if (FlagUtil.isJoinedInSingleDiagram(graph, resource)) {
\r
313 FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);
\r
314 String commonLabel = scheme.generateLabel(graph, targetDiagram);
\r
315 graph.claimLiteral(resource, L0.HasLabel, DIA.FlagLabel, commonLabel);
\r
316 for (Resource otherFlag : FlagUtil.getCounterparts(graph, resource))
\r
317 graph.claimLiteral(otherFlag, L0.HasLabel, DIA.FlagLabel, commonLabel, Bindings.STRING);
\r
320 IOTablesInfo ioTablesInfo = IOTableUtil.getIOTablesInfo(graph, op.targetDiagram);
\r
321 double[] transform = graph.getRelatedValue(resource, DIA.HasTransform, Bindings.DOUBLE_ARRAY);
\r
322 ioTablesInfo.updateBinding(graph, DIA, resource, transform[4], transform[5]);
\r
324 // #11077: fix STR.JoinsComposite relations from joins related to moved flags.
\r
325 // Otherwise the JoinsComposite relations will be wrong after the cut-operation.
\r
326 for (Resource join : graph.getObjects(resource, DIA.FlagIsJoinedBy))
\r
327 fixConnectionJoin(join);
\r
330 Set<Resource> flagComposites = new HashSet<>();
\r
331 Set<Resource> joinedComposites = new HashSet<>();
\r
332 Set<Resource> invalidJoinedComposites = new HashSet<>();
\r
334 void fixConnectionJoin(Resource join) throws DatabaseException {
\r
335 Collection<Resource> flags = graph.getObjects(join, DIA.JoinsFlag);
\r
336 if (flags.size() < 2) {
\r
337 // Broken join, remove it. Joins that have
\r
338 // < 2 flags attached to it shouldn't exist.
\r
341 flagComposites.clear();
\r
342 possibleCompositesOfElements(flags, flagComposites);
\r
343 joinedComposites.clear();
\r
344 joinedComposites.addAll( graph.getObjects(join, STR.JoinsComposite) );
\r
346 // Calculate which JoinsComposites need to be added and which removed.
\r
347 invalidJoinedComposites.clear();
\r
348 invalidJoinedComposites.addAll(joinedComposites);
\r
349 invalidJoinedComposites.removeAll(flagComposites);
\r
350 flagComposites.removeAll(joinedComposites);
\r
352 if (!invalidJoinedComposites.isEmpty()) {
\r
353 for (Resource invalidComposite : invalidJoinedComposites)
\r
354 graph.deny(join, STR.JoinsComposite, invalidComposite);
\r
356 if (!flagComposites.isEmpty()) {
\r
357 for (Resource joinedComposite : flagComposites)
\r
358 graph.claim(join, STR.JoinsComposite, joinedComposite);
\r
363 Set<Resource> possibleCompositesOfElements(Collection<Resource> elements, Set<Resource> result) throws DatabaseException {
\r
364 for (Resource e : elements) {
\r
365 Resource composite = possibleCompositeOfElement(e);
\r
366 if (composite != null)
\r
367 result.add(composite);
\r
372 Resource possibleCompositeOfElement(Resource element) throws DatabaseException {
\r
373 Resource diagram = graph.getPossibleObject(element, L0.PartOf);
\r
374 return diagram != null ? graph.getPossibleObject(diagram, MOD.DiagramToComposite) : null;
\r
378 CutProcedure monitorCutProcedure = new CutProcedure() {
\r
380 void postCut(Resource resource, Object cutResult) throws DatabaseException {
\r
381 if (cutResult != null) {
\r
382 Resource parentElement = parentElement(resource, DIA.HasMonitorComponent);
\r
383 if (parentElement == null) {
\r
384 applyPasteOffset(resource);
\r
385 } else if (!op.ea.all.contains(parentElement)) {
\r
386 Point2D offset = op.offset;
\r
387 if (!op.sameDiagram()) {
\r
388 Resource parentDiagram = graph.sync(new PossibleTypedParent(parentElement, DIA.Diagram));
\r
389 AffineTransform monitoredComponentTr = DiagramGraphUtil.getWorldTransform(graph, parentElement);
\r
390 if (op.targetDiagram.equals(parentDiagram)) {
\r
391 // Monitor is moved back to the parent element diagram.
\r
392 // Must make monitor position relative to the parent position.
\r
393 offset = new Point2D.Double(
\r
394 op.offset.getX() - monitoredComponentTr.getTranslateX(),
\r
395 op.offset.getY() - monitoredComponentTr.getTranslateY());
\r
397 // Monitor is moved to another diagram than the parent element diagram.
\r
398 // Must make monitor position absolute.
\r
399 offset = new Point2D.Double(
\r
400 op.offset.getX() + monitoredComponentTr.getTranslateX(),
\r
401 op.offset.getY() + monitoredComponentTr.getTranslateY());
\r
404 applyOffset(resource, offset);
\r
408 glm.removeFromAllLayers(graph, resource);
\r
409 glm.putElementOnVisibleLayers(op.target, graph, resource);
\r
415 CutProcedure referenceElementCutProcedure = new CutProcedure() {
\r
417 boolean preCut(Resource resource) throws DatabaseException {
\r
418 // Only allow cutting if reference element has a parent and it is selected for cutting also.
\r
419 return parentIsIncludedInCut(resource, MOD.HasParentComponent, true);
\r
422 void postCut(Resource resource, Object cutResult) throws Exception {
\r
423 if (cutResult != null) {
\r
424 if (!parentIsIncludedInCut(resource, MOD.HasParentComponent, false)) {
\r
425 applyPasteOffset(resource);
\r
429 glm.removeFromAllLayers(graph, resource);
\r
430 glm.putElementOnVisibleLayers(op.target, graph, resource);
\r
436 CutProcedure connectionCutProcedure = new CutProcedure() {
\r
438 void postCut(Resource resource, Object cutResult) throws Exception {
\r
439 if (cutResult != null) {
\r
440 for (Resource rn : graph.getObjects(resource, DIA.HasInteriorRouteNode)) {
\r
441 if (graph.isInstanceOf(rn, DIA.BranchPoint))
\r
442 applyPasteOffset(rn);
\r
443 else if (graph.isInstanceOf(rn, DIA.RouteLine))
\r
444 applyPasteOffsetToRouteLine(rn);
\r
448 glm.removeFromAllLayers(graph, resource);
\r
449 glm.putElementOnVisibleLayers(op.target, graph, resource);
\r
455 // Before cutting, disconnect all nodes from connections that are not in
\r
456 // the cut connection set.
\r
458 final Set<Resource> selectedConnections = new HashSet<Resource>();
\r
459 forEachResourceElement("Gather connections", op.ea.connections, new Procedure() {
\r
461 public void execute(Resource resource) throws Exception {
\r
462 selectedConnections.add(resource);
\r
466 Set<Resource> affectedConnections = new HashSet<Resource>();
\r
467 disconnectExcludedConnections("Disconnect Nodes", op.ea.nodeList, selectedConnections, affectedConnections);
\r
468 disconnectExcludedConnections("Disconnect Flags", op.ea.flags, selectedConnections, affectedConnections);
\r
470 for (Resource connection : affectedConnections) {
\r
471 // Leave the connection only if it has:
\r
472 // - at least one truly connected :DIA.Connector
\r
473 // - at least 1 route/branch points
\r
474 int connectedConnectors = cu.getConnectedConnectors(connection, null).size();
\r
475 int branchPoints = cu.getBranchPoints(connection, null).size();
\r
476 if (connectedConnectors > 0 && branchPoints > 0)
\r
479 // Remove the whole connection.
\r
480 cu.removeConnection(connection);
\r
483 cut("Cut Nodes", op.ea.nodeList, compose(nodeCutProcedure, registerNames));
\r
484 cut("Cut Others", op.ea.others, compose(nodeCutProcedure, registerNames));
\r
485 // Cut reference elements after nodes so that parent relationships can be restored
\r
486 // but before connections so that connections to the reference elements can be copied.
\r
487 cut("Cut References", op.ea.references, compose(referenceElementCutProcedure, registerNames));
\r
488 cut("Cut Flags", op.ea.flags, compose(flagCutProcedure, registerNames));
\r
489 cut("Cut Connections", op.ea.connections, compose(connectionCutProcedure, registerNames));
\r
490 cut("Cut Monitors", op.ea.monitors, compose(monitorCutProcedure, registerNames));
\r
492 // Make sure that all the pasted nodes have unique names in their new namespace.
\r
493 // Element names are only diagram-locally unique so this must be done after cut-paste.
\r
494 for (Resource element : cutElements)
\r
495 AddElement.claimFreshElementName(graph, targetDiagram, element);
\r
501 * @param description
\r
503 * @param affectedConnections
\r
505 * @throws Exception
\r
507 private Set<Resource> disconnectExcludedConnections(String description, Collection<Resource> nodes,
\r
508 final Set<Resource> selectedConnections, final Set<Resource> affectedConnections) throws Exception {
\r
509 final StructuralResource2 str = StructuralResource2.getInstance(graph);
\r
511 // Disconnect each connection that is not a part of selectedConnections
\r
512 // but is attached to the listed nodes.
\r
513 forEachResourceElement(description, nodes, new Procedure() {
\r
515 public void execute(Resource resource) throws Exception {
\r
516 for (Resource connector : graph.getObjects(resource, str.IsConnectedTo)) {
\r
517 Resource connection = ConnectionUtil.tryGetConnection(graph, connector);
\r
518 if (connection == null) {
\r
519 // This is a stray connector that has no purpose and should be removed.
\r
520 cu.removeConnectionPart(connector);
\r
523 if (selectedConnections.contains(connection))
\r
526 cu.removeConnectionPart(connector);
\r
527 affectedConnections.add(connection);
\r
532 return affectedConnections;
\r
536 * @param description
\r
538 * @param cutProcedure custom pre- and post-cut processing, may be <code>null</code>
\r
539 * @throws Exception
\r
541 private void cut(final String description, Collection<Resource> elements, final CutProcedure cutProcedure)
\r
543 final CopyAdvisor advisor = op.target.getHint(SynchronizationHints.COPY_ADVISOR);
\r
545 forEachResourceElement(description, elements, new Procedure() {
\r
547 public void execute(Resource resource) throws Exception {
\r
549 System.out.println("[" + description + "] " + NameUtils.getSafeName(graph, resource, true));
\r
551 if (cutProcedure != null && !cutProcedure.preCut(resource)) {
\r
553 System.out.println("[" + description + "] ignoring element cut for " + NameUtils.getSafeName(graph, resource, true));
\r
557 Object result = CopyAdvisorUtil.cut(targetContext, graph, advisor, resource, sourceDiagram, targetDiagram);
\r
560 System.out.println("[" + description + "] RESULT: " + result);
\r
562 if (cutProcedure != null)
\r
563 cutProcedure.postCut(resource, result);
\r
568 static class CutProcedure {
\r
569 boolean preCut(Resource resource) throws Exception { return true; }
\r
570 void postCut(Resource resource, Object cutResult) throws Exception {}
\r
573 static class ComposedCutProcedure extends CutProcedure {
\r
574 private final CutProcedure[] procedures;
\r
576 public static ComposedCutProcedure compose(CutProcedure... procedures) {
\r
577 return new ComposedCutProcedure(procedures);
\r
580 public ComposedCutProcedure(CutProcedure... procedures) {
\r
581 this.procedures = procedures;
\r
584 boolean preCut(Resource resource) throws Exception {
\r
585 for (CutProcedure proc : procedures)
\r
586 if (!proc.preCut(resource))
\r
590 void postCut(Resource resource, Object cutResult) throws Exception {
\r
591 for (CutProcedure proc : procedures)
\r
592 proc.postCut(resource, cutResult);
\r
596 // ------------------------------------------------------------------------
\r
597 // COPY LOGIC SUPPORT CLASSES
\r
598 // ------------------------------------------------------------------------
\r
600 static class IdentifiedElement extends Tuple {
\r
601 public IdentifiedElement(Resource object, IElement element) {
\r
602 super(object, element);
\r
604 public Resource getObject() {
\r
605 return (Resource) getField(0);
\r
607 public IElement getElement() {
\r
608 return (IElement) getField(1);
\r
612 static public class NodeMap {
\r
614 Map<Resource, IdentifiedElement> resourceMap = new HashMap<Resource, IdentifiedElement>();
\r
615 Map<IElement, IdentifiedElement> elementMap = new HashMap<IElement, IdentifiedElement>();
\r
617 public void put(Resource sourceResource, IElement sourceElement, IdentifiedElement dst) {
\r
618 if (sourceResource == null)
\r
619 throw new NullPointerException("null source resource");
\r
620 resourceMap.put(sourceResource, dst);
\r
621 if (sourceElement != null)
\r
622 elementMap.put(sourceElement, dst);
\r
625 public IdentifiedElement get(Resource source) {
\r
626 return resourceMap.get(source);
\r
629 public IdentifiedElement get(IElement source) {
\r
630 return elementMap.get(source);
\r
633 public Set<Resource> allResources() {
\r
634 return resourceMap.keySet();
\r
637 public Resource getResource(Resource source) {
\r
638 IdentifiedElement ie = resourceMap.get(source);
\r
640 return ie.getObject();
\r
645 public Resource getResource(IElement source) {
\r
646 IdentifiedElement ie = elementMap.get(source);
\r
648 return ie.getObject();
\r
655 static class ResourceMap extends HashMap<Resource, Resource> {
\r
656 private static final long serialVersionUID = 687528035082504835L;
\r
659 static class StatementMap extends HashMap<Resource, Statement> {
\r
660 private static final long serialVersionUID = 8520092255776208395L;
\r
663 static class MapQueue<K,V> {
\r
664 Map<K, Deque<V>> map = new HashMap<K, Deque<V>>();
\r
665 public void offer(K key, V value) {
\r
666 Deque<V> deque = map.get(key);
\r
668 map.put(key, deque = new ArrayDeque<V>());
\r
669 deque.offer(value);
\r
671 public V poll(K key) {
\r
672 Deque<V> deque = map.get(key);
\r
675 V value = deque.poll();
\r
676 if (deque.isEmpty())
\r
682 // ------------------------------------------------------------------------
\r
684 // ------------------------------------------------------------------------
\r
687 * This is necessary to have DIA.Flag type copied over to the copied flag.
\r
688 * Diagram mapping will have problems and potentially break the
\r
689 * configuration if the type is not the same as in the source.
\r
691 BinaryFunction<StatementEvaluation, ReadGraph, Statement> statementAdvisor =
\r
692 new BinaryFunction<StatementEvaluation, ReadGraph, Statement>() {
\r
694 public StatementEvaluation call(ReadGraph graph, Statement stm) {
\r
695 if (DIA.HasFlagType.equals(stm.getPredicate()))
\r
696 return StatementEvaluation.INCLUDE;
\r
697 return StatementEvaluation.USE_DEFAULT;
\r
701 CopyProcedure nodeCopyProcedure = new CopyProcedure() {
\r
702 Resource copy(Resource source) throws Exception {
\r
703 Layer0 L0 = Layer0.getInstance(graph);
\r
705 Resource copy = null;
\r
706 final CopyAdvisor advisor = op.target.getHint(SynchronizationHints.COPY_ADVISOR);
\r
707 if (advisor != null) {
\r
708 Resource sourceComposite = graph.getPossibleObject(source, L0.PartOf);
\r
709 if (sourceComposite == null || !graph.isInstanceOf(source, DIA.Composite)) {
\r
710 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
711 sourceComposite = OrderedSetUtils.getSingleOwnerList(graph, source, DIA.Composite);
\r
713 copy = CopyAdvisorUtil.copy(targetContext, graph, advisor, source, sourceComposite, op.targetDiagram);
\r
716 if (copy == null) {
\r
717 copy = CopyAdvisorUtil.copy2(graph, source, statementAdvisor);
\r
720 graph.deny(copy, MOD.IsTemplatized, copy);
\r
722 // Add comment to change set.
\r
723 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
\r
724 graph.addMetadata(cm.add("Copied element " + source + " to " + copy));
\r
726 // Add the new element to the diagram composite
\r
727 OrderedSetUtils.add(graph, op.targetDiagram, copy);
\r
729 // Give running name to element and increment the counter attached to the diagram.
\r
730 AddElement.claimFreshElementName(graph, op.targetDiagram, copy);
\r
732 // Make the diagram consist of the new element
\r
733 graph.claim(op.targetDiagram, L0.ConsistsOf, copy);
\r
735 // Put the element on all the currently active layers if possible.
\r
736 GraphLayerManager glm = targetContext.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER);
\r
738 glm.removeFromAllLayers(graph, copy);
\r
739 glm.putElementOnVisibleLayers(op.target, graph, copy);
\r
745 void postCopy(Resource source, Resource copy) throws Exception {
\r
746 CopyPasteUtil.copyElementPosition(graph, op.ctx, source, copy, op.offset);
\r
750 private void copy() throws Exception {
\r
751 nodeMap = new NodeMap();
\r
753 CommonDBUtils.selectClusterSet(graph, targetDiagram);
\r
755 // Fill nodeMap with initial Resource->Resource mappings
\r
756 if (op.initialNodeMap != null) {
\r
757 for (Map.Entry<Resource, Resource> entry : op.initialNodeMap.entrySet()) {
\r
758 nodeMap.put(entry.getKey(), null, new IdentifiedElement(entry.getValue(), null));
\r
762 // Perform copies in a suitable order
\r
763 copyNodes( nodeMap );
\r
764 // Copy reference elements after nodes so that parent relationships can be restored
\r
765 // but before connections so that connections to the reference elements can be copied.
\r
766 copyReferences( nodeMap );
\r
767 copyFlags( nodeMap );
\r
768 copyConnections( nodeMap );
\r
769 // Copy monitors last since their parents must have been copied already.
\r
770 copyMonitors( nodeMap );
\r
776 private NodeMap copyNodes(final NodeMap nodeMap) throws Exception {
\r
777 copy("Copy Others", op.ea.others, nodeMap, nodeCopyProcedure);
\r
778 copy("Copy Nodes", op.ea.nodeList, nodeMap, nodeCopyProcedure);
\r
783 private NodeMap copyReferences(final NodeMap nodeMap) throws Exception {
\r
784 final boolean forceCopyReferences = op.hasOption(ForceCopyReferences.class);
\r
786 copy("Copy References", op.ea.references, nodeMap, new CopyProcedure() {
\r
788 Resource copy(Resource source) throws Exception {
\r
789 // Don't copy unless the parent component is copied too.
\r
790 Resource sourceParentComponent = graph.getPossibleObject(source, MOD.HasParentComponent);
\r
791 if (sourceParentComponent == null)
\r
793 Resource sourceParentElement = graph.getPossibleObject(sourceParentComponent, MOD.ComponentToElement);
\r
794 if (sourceParentElement != null) {
\r
795 if (!forceCopyReferences && !op.ea.all.contains(sourceParentElement))
\r
797 // Find copied component
\r
798 IdentifiedElement copiedParentElement = nodeMap.get(sourceParentElement);
\r
799 if (copiedParentElement == null)
\r
801 Resource copiedParentComponent = graph.getPossibleObject(copiedParentElement.getObject(), MOD.ElementToComponent);
\r
802 if (copiedParentComponent == null)
\r
804 return copyReference(source, copiedParentComponent);
\r
806 // Check that the component is part of a diagramless composite before proceeding
\r
807 Resource partOf = graph.getPossibleObject(sourceParentComponent, L0.PartOf);
\r
808 if (partOf == null || graph.hasStatement(partOf, MOD.CompositeToDiagram))
\r
810 // Resolve the matching parent component from the target context.
\r
811 Resource targetParentComponent = resolveTargetComponent(sourceParentComponent);
\r
812 if (targetParentComponent == null)
\r
814 return copyReference(source, targetParentComponent);
\r
818 private Resource resolveTargetComponent(Resource sourceParentComponent) throws DatabaseException {
\r
819 if (operateWithinSameRoot)
\r
820 return sourceParentComponent;
\r
821 // Directly map relative source component URI into target root namespace.
\r
822 String sourceUri = graph.getURI(sourceParentComponent);
\r
823 String targetUri = sourceUri.replace(sourceRootUri, targetRootUri);
\r
824 Resource targetParentComponent = graph.getPossibleResource(targetUri);
\r
825 return targetParentComponent;
\r
828 private Resource copyReference(Resource source, Resource parentComponent) throws Exception {
\r
829 Resource referenceRelation = graph.getPossibleObject(source, MOD.HasReferenceRelation);
\r
830 if (referenceRelation == null)
\r
833 Resource relationCopy = CopyAdvisorUtil.copy4(graph, referenceRelation);
\r
834 if (relationCopy == null)
\r
837 Resource copy = nodeCopyProcedure.copy(source);
\r
839 // WORKAROUND: The result consists of a badly copied reference relation.
\r
840 // Remove it. How the relation is copied depends on whether the copy target
\r
841 // is the same model or not. If it is, the relation is copied, but invalidly
\r
842 // and if the target is not the same model, the relation is simply referenced
\r
843 // with a uni-directional L0.ConsistsOf relation.
\r
844 for (Resource o : graph.getObjects(copy, L0.ConsistsOf)) {
\r
845 boolean ownedByCopy = graph.hasStatement(o, L0.PartOf, copy);
\r
847 graph.deny(copy, L0.ConsistsOf, o);
\r
848 RemoverUtil.remove(graph, o);
\r
850 graph.deny(copy, L0.ConsistsOf, o);
\r
854 // The element the copied reference is attached to was also copied.
\r
855 // This means that we must attach the copied reference to its
\r
856 // original component's copy.
\r
857 graph.deny(copy, MOD.HasParentComponent);
\r
858 if(parentComponent != null)
\r
859 graph.claim(copy, MOD.HasParentComponent, MOD.HasParentComponent_Inverse, parentComponent);
\r
861 // Attach reference relation
\r
862 graph.claim(copy, L0.ConsistsOf, L0.PartOf, relationCopy);
\r
863 graph.claim(copy, MOD.HasReferenceRelation, MOD.HasReferenceRelation_Inverse, relationCopy);
\r
869 void postCopy(Resource source, Resource copy) throws Exception {
\r
870 // Must fix element position if the copied reference element
\r
871 // doesn't have a visible parent element.
\r
872 Resource parentComponent = graph.getPossibleObject(source, MOD.HasParentComponent);
\r
873 if (parentComponent == null)
\r
875 Resource parentElement = graph.getPossibleObject(parentComponent, MOD.ComponentToElement);
\r
876 if (parentElement == null)
\r
877 CopyPasteUtil.copyElementPosition(graph, op.ctx, source, copy, op.offset);
\r
884 private NodeMap copyFlags(NodeMap nodeMap) throws Exception {
\r
885 final Layer0 l0 = Layer0.getInstance(graph);
\r
886 final DiagramResource dia = DiagramResource.getInstance(graph);
\r
889 private final Map<Resource, Resource> selectedFlags = new HashMap<Resource, Resource>();
\r
890 private final Map<Resource, Resource> flagSelectedCounterpart = new HashMap<Resource, Resource>();
\r
893 * Analyze which flag pairs are selected
\r
895 * @throws DatabaseException
\r
897 private void analyzeFlagSelection() throws DatabaseException {
\r
898 for (Resource flag : op.ea.flags) {
\r
899 selectedFlags.put(flag, flag);
\r
901 for (Resource flag : selectedFlags.keySet()) {
\r
902 boolean external = FlagUtil.isExternal(graph, flag);
\r
903 boolean inSingleDiagram = FlagUtil.isJoinedInSingleDiagram(graph, flag);
\r
904 if (!external && inSingleDiagram) {
\r
905 // FIXME: this doesn't take into account local merged flags, which is a corner case but still possible
\r
906 Resource counterpart = FlagUtil.getPossibleCounterpart(graph, flag);
\r
907 if (selectedFlags.containsKey(counterpart)) {
\r
908 flagSelectedCounterpart.put(flag, counterpart);
\r
909 flagSelectedCounterpart.put(counterpart, flag);
\r
916 * Reconnect copied flag pairs.
\r
917 * @throws DatabaseException
\r
919 private void reconnectLocalFlagPairs(NodeMap nodeMap) throws DatabaseException {
\r
920 FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);
\r
921 Resource diagram = op.targetDiagram;
\r
923 Set<Resource> visited = new HashSet<Resource>();
\r
924 ArrayDeque<Resource> queue = new ArrayDeque<Resource>(flagSelectedCounterpart.values());
\r
925 while (!queue.isEmpty()) {
\r
926 Resource flag = queue.poll();
\r
927 Resource counterpart = flagSelectedCounterpart.get(flag);
\r
928 if (!visited.add(flag) || !visited.add(counterpart) || counterpart == null)
\r
932 Resource flagSourceElement = selectedFlags.get(flag);
\r
933 Resource counterpartSourceElement = selectedFlags.get(counterpart);
\r
935 IdentifiedElement flagCopy = nodeMap.get(flagSourceElement);
\r
936 IdentifiedElement counterpartCopy = nodeMap.get(counterpartSourceElement);
\r
938 FlagUtil.join(graph, flagCopy.getObject(), counterpartCopy.getObject());
\r
940 // Provide fresh labeling for connected flags if possible
\r
941 if (scheme != null) {
\r
942 String label = scheme.generateLabel(graph, diagram);
\r
943 if (label != null) {
\r
944 graph.claimLiteral(flagCopy.getObject(), l0.HasLabel, dia.FlagLabel, label, Bindings.STRING);
\r
945 graph.claimLiteral(counterpartCopy.getObject(), l0.HasLabel, dia.FlagLabel, label, Bindings.STRING);
\r
951 public void perform(NodeMap nodeMap) throws Exception {
\r
952 analyzeFlagSelection();
\r
954 copy("Copy Flags", op.ea.flags, nodeMap, new CopyProcedure() {
\r
956 Resource copy(Resource source) throws Exception {
\r
957 return nodeCopyProcedure.copy(source);
\r
960 public void postCopy(Resource source, Resource copy) throws Exception {
\r
961 AffineTransform at = CopyPasteUtil.copyElementPosition(graph, op.ctx, source, copy, op.offset);
\r
963 // Update flag table binding
\r
964 IOTablesInfo ioTablesInfo = IOTableUtil.getIOTablesInfo(graph, op.targetDiagram);
\r
965 ioTablesInfo.updateBinding(graph, DIA, copy, at.getTranslateX(), at.getTranslateY());
\r
967 // All label properties must be removed from
\r
968 // the copied flags. Disconnected flags are
\r
969 // not supposed to have labels, and the right
\r
970 // place to reset the labels is when the flags
\r
971 // are reconnected to their respective
\r
973 graph.denyValue(copy, l0.HasLabel);
\r
977 reconnectLocalFlagPairs(nodeMap);
\r
982 new FlagCopy().perform( nodeMap );
\r
986 private NodeMap copyMonitors(final NodeMap nodeMap) throws Exception {
\r
987 copy("Copy Monitors", op.ea.monitors, nodeMap, new CopyProcedure() {
\r
989 Resource copy(Resource source) throws Exception {
\r
990 // Don't copy monitors if they are copied without
\r
991 // their parent element into another root (model).
\r
992 if (!operateWithinSameRoot) {
\r
993 Resource monitorComponent = graph.getPossibleObject(source, DIA.HasMonitorComponent);
\r
994 if (monitorComponent != null) {
\r
995 Resource monitorElement = graph.getPossibleObject(monitorComponent, MOD.ComponentToElement);
\r
996 if (monitorElement == null || !op.ea.all.contains(monitorElement))
\r
1000 Resource copy = nodeCopyProcedure.copy(source);
\r
1004 void postCopy(Resource source, Resource copy) throws Exception {
\r
1005 // Find the component and diagram element the source monitor is
\r
1007 Resource monitorElement = null;
\r
1008 Resource monitorComponent = graph.getPossibleObject(source, DIA.HasMonitorComponent);
\r
1009 if (monitorComponent != null) {
\r
1010 monitorElement = graph.getPossibleObject(monitorComponent, MOD.ComponentToElement);
\r
1013 if (monitorElement != null && op.ea.all.contains(monitorElement)) {
\r
1014 // The element the copied monitor is attached was also copied.
\r
1015 // This means that we must attach the copied monitor to its
\r
1016 // original components copy.
\r
1018 // Remove old association
\r
1019 graph.deny(copy, DIA.HasMonitorComponent);
\r
1021 // Associate to copied component
\r
1022 IdentifiedElement parent = nodeMap.get(monitorElement);
\r
1023 if (parent != null) {
\r
1024 monitorComponent = graph.getPossibleObject(parent.getObject(), MOD.ElementToComponent);
\r
1025 if (monitorComponent != null)
\r
1026 graph.claim(copy, DIA.HasMonitorComponent, monitorComponent);
\r
1028 //throw new PasteException("no parent could be found for monitored element " + monitoredElement);
\r
1031 // The element the copied monitor is attached was not copied
\r
1032 // or there is no element for the monitored component.
\r
1033 // This means that the copied monitor must be kept attached
\r
1034 // to the same component no matter where it is in the model,
\r
1035 // unless the copy is done into another model.
\r
1036 if (operateWithinSameRoot && monitorComponent != null)
\r
1037 graph.claim(copy, DIA.HasMonitorComponent, monitorComponent);
\r
1039 Point2D offset = op.offset;
\r
1040 if (!op.sameDiagram()) {
\r
1041 if (monitorElement != null) {
\r
1042 // Monitor doesn't have a diagram parent element any
\r
1043 // more, must recalculate its offset.
\r
1044 AffineTransform monitoredComponentTr = DiagramGraphUtil.getWorldTransform(graph, monitorElement);
\r
1045 offset = new Point2D.Double(
\r
1046 op.offset.getX() + monitoredComponentTr.getTranslateX(),
\r
1047 op.offset.getY() + monitoredComponentTr.getTranslateY());
\r
1050 CopyPasteUtil.copyElementPosition(graph, op.ctx, source, copy, offset);
\r
1053 // Copy monitor suffix from original to copy.
\r
1054 String monitorSuffix = graph.getPossibleRelatedValue(source, DIA.HasMonitorSuffix, Bindings.STRING);
\r
1055 if (monitorSuffix != null)
\r
1056 graph.claimLiteral(copy, DIA.HasMonitorSuffix, monitorSuffix, Bindings.STRING);
\r
1058 // Copy used property obtains for monitor template data.
\r
1059 graph.deny(copy, L0X.ObtainsProperty);
\r
1060 for (Statement stm : graph.getStatements(source, L0X.ObtainsProperty)) {
\r
1061 graph.claim(copy, stm.getPredicate(), null, stm.getObject());
\r
1070 * @param description
\r
1073 * @param copyProcedure
\r
1074 * @throws Exception
\r
1076 private void copy(final String description, Collection<Resource> elements, final NodeMap nodeMap,
\r
1077 final CopyProcedure copyProcedure) throws Exception {
\r
1078 if (copyProcedure == null)
\r
1079 throw new IllegalArgumentException("null copy procedure");
\r
1081 forEachResourceElement(description, elements, new Procedure() {
\r
1083 public void execute(Resource resource) throws Exception {
\r
1085 System.out.println("[" + description + "] " + NameUtils.getSafeName(graph, resource, true));
\r
1086 Resource copy = copyProcedure.copy(resource);
\r
1087 if (copy != null) {
\r
1089 System.out.println("[" + description + "] " + NameUtils.getSafeName(graph, resource, true) + " copied as " + NameUtils.getSafeName(graph, copy, true));
\r
1090 nodeMap.put(resource, null, new IdentifiedElement(copy, null));
\r
1091 if (op.copyMap != null)
\r
1092 op.copyMap.put(resource, copy);
\r
1093 copyProcedure.postCopy(resource, copy);
\r
1099 public static class RouteLine extends Tuple2 {
\r
1100 public RouteLine(Double position, Boolean horizontal) {
\r
1101 super(position, horizontal);
\r
1103 public double getPosition() {
\r
1104 Double pos = (Double) get(0);
\r
1105 return pos != null ? pos : 0.0;
\r
1107 public boolean isHorizontal() {
\r
1108 return Boolean.TRUE.equals(get(1));
\r
1112 public static class BranchPoint extends Tuple3 {
\r
1113 public BranchPoint(AffineTransform at, Boolean horizontal, Boolean vertical) {
\r
1114 super(at, horizontal, vertical);
\r
1116 public AffineTransform getTransform() {
\r
1117 return (AffineTransform) get(0);
\r
1121 public static RouteLine readRouteLine(ReadGraph graph, Resource src) throws DatabaseException {
\r
1122 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
1123 Double pos = graph.getPossibleRelatedValue(src, DIA.HasPosition, Bindings.DOUBLE);
\r
1124 Boolean hor = graph.getPossibleRelatedValue(src, DIA.IsHorizontal, Bindings.BOOLEAN);
\r
1125 return new RouteLine(pos, hor);
\r
1128 public static BranchPoint readBranchPoint(ReadGraph graph, Resource src) throws DatabaseException {
\r
1129 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
1130 AffineTransform at = DiagramGraphUtil.getTransform(graph, src);
\r
1131 boolean hor = graph.hasStatement(src, DIA.Horizontal);
\r
1132 boolean ver = graph.hasStatement(src, DIA.Vertical);
\r
1133 return new BranchPoint(at, hor, ver);
\r
1139 * @throws Exception
\r
1141 private NodeMap copyConnections(final NodeMap nodeMap) throws Exception {
\r
1142 final StructuralResource2 STR = StructuralResource2.getInstance(graph);
\r
1143 final DiagramResource DIA = DiagramResource.getInstance(graph);
\r
1145 // final IModelingRules rules = graph.syncRequest(DiagramRequests.getModelingRules(op.sourceDiagram, null));
\r
1146 // if (rules == null)
\r
1147 // throw new IllegalArgumentException("source diagram offers no modeling rules");
\r
1149 final CopyAdvisor ca = op.target.getHint(SynchronizationHints.COPY_ADVISOR);
\r
1151 throw new UnsupportedOperationException("Cannot copy connections, no copy advisor available for diagram "
\r
1154 forEachResourceElement("Copy Connections", op.ea.connections, new Procedure() {
\r
1156 public void execute(Resource sourceObject) throws DatabaseException {
\r
1157 copyConnection(sourceObject);
\r
1160 private void copyConnection(Resource sourceObject) throws DatabaseException {
\r
1161 // For associating source<->destination connection parts
\r
1162 final Map<Object, Object> resourceMap = new THashMap<Object, Object>();
\r
1163 // For associating source connectors to source nodes
\r
1164 final StatementMap connectorToNode = new StatementMap();
\r
1166 // 1. copy connection
\r
1167 // - This will also copy interior route nodes
\r
1168 // - But will leave out the DIA.AreConnected relations between route nodes
\r
1169 Resource sourceDiagram = graph.getPossibleObject(sourceObject, Layer0.getInstance(graph).PartOf);
\r
1170 if (sourceDiagram == null)
\r
1171 sourceDiagram = OrderedSetUtils.getSingleOwnerList(graph, sourceObject, DIA.Diagram);
\r
1172 Resource copy = CopyAdvisorUtil.copy(targetContext, graph, ca, sourceObject, sourceDiagram, op.targetDiagram, resourceMap);
\r
1174 throw new UnsupportedOperationException("Could not copy connection " + sourceObject);
\r
1175 OrderedSetUtils.addFirst(graph, op.targetDiagram, copy);
\r
1177 graph.deny(copy, MOD.IsTemplatized, copy);
\r
1179 AddElement.claimFreshElementName(graph, op.targetDiagram, copy);
\r
1181 AddConnection.copyConnectionType(graph, sourceObject, copy);
\r
1183 GraphLayerManager glm = targetContext.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER);
\r
1184 if (glm != null) {
\r
1185 glm.removeFromAllLayers(graph, copy);
\r
1186 glm.putElementOnVisibleLayers(op.target, graph, copy);
\r
1189 nodeMap.put(sourceObject, null, new IdentifiedElement(copy, null));
\r
1190 if (op.copyMap != null)
\r
1191 op.copyMap.put(sourceObject, copy);
\r
1193 // WORKAROUND: CopyAdvisorUtil.copy(..., resourceMap)
\r
1194 // implementations do not all support filling the resource map.
\r
1195 // Thus we resort to the old logic if resourceMap is empty at this point.
\r
1196 final boolean mapResources = resourceMap.isEmpty();
\r
1198 // 2. associate source connection parts to destination connection parts
\r
1201 Collection<Statement> sourceHasConnectors = graph.getStatements(sourceObject, DIA.HasConnector);
\r
1202 MapQueue<Resource, Resource> connectorsByType = new MapQueue<Resource, Resource>();
\r
1203 for (Statement hasConnector : sourceHasConnectors) {
\r
1204 connectorsByType.offer(hasConnector.getPredicate(), hasConnector.getObject());
\r
1205 for (Statement connects : graph.getStatements(hasConnector.getObject(), STR.Connects)) {
\r
1206 if (!sourceObject.equals(connects.getObject())) {
\r
1207 connectorToNode.put(hasConnector.getObject(), connects);
\r
1212 if (mapResources) {
\r
1213 for (Statement hasConnector : graph.getStatements(copy, DIA.HasConnector)) {
\r
1214 Resource srcConnector = connectorsByType.poll(hasConnector.getPredicate());
\r
1215 resourceMap.put(srcConnector, hasConnector.getObject());
\r
1218 // 2.2. Offset interior route nodes
\r
1219 Collection<Resource> sourceInteriorRouteNodes = graph.getObjects(sourceObject, DIA.HasInteriorRouteNode);
\r
1220 if (mapResources) {
\r
1221 // WORKAROUND: for cases where resourceMap was not filled by
\r
1222 // the copy operation. Still needed because TG copying does
\r
1223 // not output this information.
\r
1224 Queue<Resource> branchPoints = new ArrayDeque<Resource>(sourceInteriorRouteNodes.size());
\r
1225 Queue<Resource> routeLines = new ArrayDeque<Resource>(sourceInteriorRouteNodes.size());
\r
1226 for (Resource dst : graph.getObjects(copy, DIA.HasInteriorRouteNode)) {
\r
1227 if (graph.isInstanceOf(dst, DIA.BranchPoint))
\r
1228 branchPoints.offer(dst);
\r
1229 else if (graph.isInstanceOf(dst, DIA.RouteLine))
\r
1230 routeLines.offer(dst);
\r
1232 for (Resource src : sourceInteriorRouteNodes) {
\r
1233 if (graph.isInstanceOf(src, DIA.BranchPoint)) {
\r
1234 Resource dst = branchPoints.poll();
\r
1235 resourceMap.put(src, dst);
\r
1236 BranchPoint bp = readBranchPoint(graph, src);
\r
1237 AffineTransform at = bp.getTransform();
\r
1238 at.preConcatenate(offsetTransform);
\r
1239 DiagramGraphUtil.setTransform(graph, dst, at);
\r
1241 else if (graph.isInstanceOf(src, DIA.RouteLine)) {
\r
1242 Resource dst = routeLines.poll();
\r
1243 resourceMap.put(src, dst);
\r
1244 RouteLine rl = readRouteLine(graph, src);
\r
1245 double newPos = rl.getPosition() + (rl.isHorizontal() ? op.offset.getY() : op.offset.getX());
\r
1246 graph.claimLiteral(dst, DIA.HasPosition, newPos, Bindings.DOUBLE);
\r
1250 for (Resource src : sourceInteriorRouteNodes) {
\r
1251 Resource dst = (Resource) resourceMap.get(src);
\r
1252 if (dst != null) {
\r
1253 if (graph.isInstanceOf(src, DIA.BranchPoint)) {
\r
1254 BranchPoint bp = readBranchPoint(graph, src);
\r
1255 AffineTransform at = bp.getTransform();
\r
1256 at.preConcatenate(offsetTransform);
\r
1257 DiagramGraphUtil.setTransform(graph, dst, at);
\r
1258 } else if (graph.isInstanceOf(src, DIA.RouteLine)) {
\r
1259 RouteLine rl = readRouteLine(graph, src);
\r
1260 double newPos = rl.getPosition() + (rl.isHorizontal() ? op.offset.getY() : op.offset.getX());
\r
1261 graph.claimLiteral(dst, DIA.HasPosition, newPos, Bindings.DOUBLE);
\r
1267 // 3. Connect connection parts according to how the source is connected
\r
1268 for (Resource src : sourceInteriorRouteNodes) {
\r
1269 Resource dst = (Resource) resourceMap.get(src);
\r
1270 for (Resource connectedToSrc : graph.getObjects(src, DIA.AreConnected)) {
\r
1271 Resource connectedToDst = (Resource) resourceMap.get(connectedToSrc);
\r
1272 if (connectedToDst != null) {
\r
1273 graph.claim(dst, DIA.AreConnected, DIA.AreConnected, connectedToDst);
\r
1275 throw new DatabaseException("Connection copying failed due to an invalid DIA.AreConnected link between source resources " + src + " <-> " + connectedToSrc);
\r
1279 for (Statement hasConnector : sourceHasConnectors) {
\r
1280 Resource srcConnector = hasConnector.getObject();
\r
1281 Resource dstConnector = (Resource) resourceMap.get(srcConnector);
\r
1282 Statement srcConnects = connectorToNode.get(srcConnector);
\r
1284 // Connect to copied nodes
\r
1285 IdentifiedElement dstNode = nodeMap.get(srcConnects.getObject());
\r
1286 if (dstNode == null)
\r
1287 throw new DatabaseException("Source element "
\r
1288 + NameUtils.getURIOrSafeNameInternal(graph, srcConnects.getObject())
\r
1289 + " not copied causing copying of connection "
\r
1290 + NameUtils.getURIOrSafeNameInternal(graph, sourceObject)
\r
1292 graph.claim(dstConnector, srcConnects.getPredicate(), dstNode.getObject());
\r
1294 // Connect to other copied route nodes
\r
1295 for (Resource connectedToSrc : graph.getObjects(srcConnector, DIA.AreConnected)) {
\r
1296 Resource connectedToDst = (Resource) resourceMap.get(connectedToSrc);
\r
1297 graph.claim(dstConnector, DIA.AreConnected, DIA.AreConnected, connectedToDst);
\r
1301 // 4. Make sure MOD.ConnectorToComponent relations are copied as well.
\r
1302 // Otherwise diagram mapping will do bad things on the model.
\r
1303 Resource sourceComponent = graph.getPossibleObject(sourceObject, MOD.ElementToComponent);
\r
1304 if (sourceComponent != null) {
\r
1305 for (Statement hasConnector : sourceHasConnectors) {
\r
1306 Resource sourceConnector = hasConnector.getObject();
\r
1307 Resource targetConnector = (Resource) resourceMap.get(sourceConnector);
\r
1308 // Should have been defined back in steps 1-2.
\r
1309 assert targetConnector != null;
\r
1310 Statement sourceConnectorToComponent = graph.getPossibleStatement(sourceConnector, MOD.ConnectorToComponent);
\r
1311 if (sourceConnectorToComponent == null)
\r
1313 if (!sourceConnectorToComponent.getObject().equals(sourceComponent))
\r
1315 Resource targetComponent = graph.getPossibleObject(copy, MOD.ElementToComponent);
\r
1316 if (targetComponent == null)
\r
1319 graph.claim(targetConnector, sourceConnectorToComponent.getPredicate(), targetComponent);
\r
1321 // #6190 & apros:#11435: Ensure that MOD.HasConnectionMappingSpecification is added to target
\r
1322 for (Resource connectionMappingSpec : graph.getObjects(sourceConnector, MOD.HasConnectionMappingSpecification))
\r
1323 graph.claim(targetConnector, MOD.HasConnectionMappingSpecification, connectionMappingSpec);
\r
1332 class CopyProcedure {
\r
1333 Resource copy(Resource source) throws Exception { throw new UnsupportedOperationException(); }
\r
1334 void postCopy(Resource source, Resource copy) throws Exception {}
\r
1338 * @param judgment <code>null</code> if no judgement is available in which
\r
1339 * case defaultValue is always returned
\r
1340 * @param connectionPoint
\r
1341 * @param defaultValue
\r
1343 * @throws DatabaseException
\r
1345 @SuppressWarnings("unused")
\r
1346 private static Resource getAttachmentRelation(ReadGraph graph, ConnectionJudgement judgment,
\r
1347 IConnectionPoint connectionPoint, Resource defaultValue) throws DatabaseException {
\r
1348 if (judgment == null || !(connectionPoint instanceof CPTerminal) || judgment.attachmentRelations == null)
\r
1349 return defaultValue;
\r
1350 Resource attachment = judgment.attachmentRelations.get(graph, (CPTerminal) connectionPoint);
\r
1351 return attachment != null ? attachment : defaultValue;
\r
1355 * Get node map of copied variables. Map contains original and new resources.
\r
1357 * @return NodeMap of copied resources or null if copy has not been performed
\r
1359 public NodeMap getNodeMap() {
\r