1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.diagram.synchronization.graph;
16 import java.util.function.BiFunction;
18 import org.eclipse.core.runtime.NullProgressMonitor;
19 import org.simantics.databoard.Bindings;
20 import org.simantics.databoard.binding.Binding;
21 import org.simantics.databoard.type.Datatype;
22 import org.simantics.db.ReadGraph;
23 import org.simantics.db.Resource;
24 import org.simantics.db.Statement;
25 import org.simantics.db.WriteGraph;
26 import org.simantics.db.common.utils.NameUtils;
27 import org.simantics.db.exception.DatabaseException;
28 import org.simantics.db.layer0.adapter.CopyHandler;
29 import org.simantics.db.layer0.adapter.impl.FixedRootImportAdvisor;
30 import org.simantics.db.layer0.util.ClipboardUtils;
31 import org.simantics.db.layer0.util.SimanticsClipboard;
32 import org.simantics.db.layer0.util.SimanticsClipboard.Representation;
33 import org.simantics.db.layer0.util.SimanticsClipboardImpl;
34 import org.simantics.db.layer0.util.SimanticsKeys;
35 import org.simantics.diagram.adapter.GraphToDiagramSynchronizer;
36 import org.simantics.diagram.internal.DebugPolicy;
37 import org.simantics.diagram.synchronization.CopyAdvisor;
38 import org.simantics.diagram.synchronization.CopyAdvisor.Evaluation;
39 import org.simantics.diagram.synchronization.ErrorHandler;
40 import org.simantics.diagram.synchronization.IModifiableSynchronizationContext;
41 import org.simantics.diagram.synchronization.StatementEvaluation;
42 import org.simantics.diagram.synchronization.SynchronizationException;
43 import org.simantics.diagram.synchronization.SynchronizationHints;
44 import org.simantics.graph.db.TransferableGraphs;
45 import org.simantics.graph.representation.TransferableGraph1;
46 import org.simantics.layer0.Layer0;
47 import org.simantics.utils.datastructures.BinaryFunction;
49 import gnu.trove.map.hash.THashMap;
50 import gnu.trove.set.hash.THashSet;
53 * This class contains utility methods for the basic cut/copy operations
54 * performed related to cut-copy-pasting diagram/configuration component and
59 * {@link #cut(IModifiableSynchronizationContext, WriteGraph, CopyAdvisor, Resource, Resource, Resource)}
61 * {@link #copy(IModifiableSynchronizationContext, WriteGraph, CopyAdvisor, Resource, Resource, Resource)}
62 * are available for making it easier to properly invoke
63 * {@link CopyAdvisor#cut(org.simantics.diagram.synchronization.ISynchronizationContext, Object, Object, Object)}
65 * {@link CopyAdvisor#copy(org.simantics.diagram.synchronization.ISynchronizationContext, Object, Object, Object)}
66 * operations in a diagram/graph transaction context.
69 * Methods {@link #copy(WriteGraph, Resource, BinaryFunction)},
70 * {@link #copy2(WriteGraph, Resource, BinaryFunction)},
71 * {@link #copy3(WriteGraph, Resource, Resource, BinaryFunction)},
72 * {@link #copy4(WriteGraph, Resource)} and
73 * {@link #copy4(WriteGraph, Resource, CopyHandler)} offer differently
74 * functioning versions of copying a single resource in the graph that are
75 * mainly tuned for copying diagram elements and configuration components.
78 * <b>IMPORTANT:</b> Note that copy, copy2 and copy3 cannot handle copying of ordered sets
81 * @author Tuukka Lehtonen
83 public class CopyAdvisorUtil {
85 public static final boolean DEBUG_COPY = DebugPolicy.DEBUG_COPY_PASTE;
87 private static class Statement4 {
88 public final Statement stm;
89 public final Resource inverse;
90 public Statement4(Statement stm, Resource inverse) {
92 this.inverse = inverse;
97 * @param context a synchronization context instance, such as
98 * {@link GraphToDiagramSynchronizer}
99 * @param g handle for graph writing
100 * @param ca the advisor for the copy operation
101 * @param cut the resource that is about to be cut
102 * @param sourceContainer the container from which the cut argument is about
104 * @param targetContainer the container into which the cut argument is about
106 * @return the result of
107 * {@link CopyAdvisor#cut(org.simantics.diagram.synchronization.ISynchronizationContext, Object, Object, Object)}
108 * @throws DatabaseException
110 public static Object cut(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca, Resource cut, Resource sourceContainer, Resource targetContainer) throws DatabaseException {
112 System.out.println("Attempting to cut component " + NameUtils.getSafeName(g, cut, true));
114 context.set(GraphSynchronizationHints.READ_TRANSACTION, g);
115 context.set(GraphSynchronizationHints.WRITE_TRANSACTION, g);
116 return ca.cut(context, cut, sourceContainer, targetContainer);
117 } catch (SynchronizationException e) {
118 ErrorHandler eh = context.get(SynchronizationHints.ERROR_HANDLER);
119 eh.error(e.getMessage(), e);
121 context.set(GraphSynchronizationHints.READ_TRANSACTION, null);
122 context.set(GraphSynchronizationHints.WRITE_TRANSACTION, null);
128 * @param context a synchronization context instance, such as
129 * {@link GraphToDiagramSynchronizer}
130 * @param g handle for graph writing
131 * @param ca the advisor for the copy operation
132 * @param copyOf the resource that is about to be copied
133 * @param sourceContainer the container of the resource that will be copied
134 * @param targetContainer the to-be container of the copied resource instance
135 * @return the copied resource
136 * @throws DatabaseException
138 public static Resource copy(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca,
139 Resource copyOf, Resource sourceContainer, Resource targetContainer) throws DatabaseException {
140 Resource resource = null;
142 System.out.println("Attempting to copy component " + NameUtils.getSafeName(g, copyOf, true));
144 context.set(GraphSynchronizationHints.READ_TRANSACTION, g);
145 context.set(GraphSynchronizationHints.WRITE_TRANSACTION, g);
146 Evaluation eval = ca.canCopy(context, copyOf, sourceContainer, targetContainer);
148 System.out.println(" CopyAdvisor(" + ca + ").canCopy evaluation result: " + eval);
149 if (CopyAdvisor.SUPPORTED.contains(eval)) {
150 Object copy = ca.copy(context, copyOf, sourceContainer, targetContainer);
152 System.out.println(" CopyAdvisor(" + ca + ").copy result: " + copy);
153 if (copy instanceof Resource) {
154 resource = (Resource) copy;
156 throw new UnsupportedOperationException("Cannot copy element " + copyOf);
159 } catch (SynchronizationException e) {
160 ErrorHandler eh = context.get(SynchronizationHints.ERROR_HANDLER);
161 eh.error(e.getMessage(), e);
162 // throwing exception allows canceling failed copy!
163 throw new DatabaseException(e);
165 context.set(GraphSynchronizationHints.READ_TRANSACTION, null);
166 context.set(GraphSynchronizationHints.WRITE_TRANSACTION, null);
172 * @param context a synchronization context instance, such as
173 * {@link GraphToDiagramSynchronizer}
174 * @param g handle for graph writing
175 * @param ca the advisor for the copy operation
176 * @param copyOf the resource that is about to be copied
177 * @param sourceContainer the container of the resource that will be copied
178 * @param targetContainer the to-be container of the copied resource instance
179 * @param map a map for storing the correspondences between original and
180 * copied objects. This is used to output data from the copy process.
181 * @return the copied resource
182 * @throws DatabaseException
184 public static Resource copy(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca,
185 Resource copyOf, Resource sourceContainer, Resource targetContainer, Map<Object, Object> map)
186 throws DatabaseException {
187 Resource resource = null;
189 System.out.println("Attempting to copy component " + NameUtils.getSafeName(g, copyOf, true));
191 context.set(GraphSynchronizationHints.READ_TRANSACTION, g);
192 context.set(GraphSynchronizationHints.WRITE_TRANSACTION, g);
193 Evaluation eval = ca.canCopy(context, copyOf, sourceContainer, targetContainer);
195 System.out.println(" CopyAdvisor(" + ca + ").canCopy evaluation result: " + eval);
196 if (CopyAdvisor.SUPPORTED.contains(eval)) {
197 Object copy = ca.copy(context, copyOf, sourceContainer, targetContainer, map);
199 System.out.println(" CopyAdvisor(" + ca + ").copy result: " + copy);
200 System.out.println(" CopyAdvisor(" + ca + ").copy result map: " + map);
202 if (copy instanceof Resource) {
203 resource = (Resource) copy;
205 throw new UnsupportedOperationException("Cannot copy element " + copyOf);
208 } catch (SynchronizationException e) {
209 ErrorHandler eh = context.get(SynchronizationHints.ERROR_HANDLER);
210 eh.error(e.getMessage(), e);
211 // throwing exception allows canceling failed copy!
212 throw new DatabaseException(e);
214 context.set(GraphSynchronizationHints.READ_TRANSACTION, null);
215 context.set(GraphSynchronizationHints.WRITE_TRANSACTION, null);
221 * Creates and returns a copy of the specified source resource based on the
222 * standard Layer0 relation hierarchy by recursively including all resources
223 * in the copy that the source and is composed of (see
224 * {@link Layer0#IsComposedOf}). The routine will always copy at least all
225 * L0.InstanceOf statements and tags related to its input resource. If the
226 * copied resource has a value attached, it will also be copied.
228 * @param graph database write access
229 * @param source the resource to start the copy from
230 * @param advisor <code>null</code> or a custom advisor to guide whether or
231 * not to copy relations that are not inherited from L0.IsComposedOf.
232 * This advisor cannot be used to say that non-composing relations
233 * should perform recursive copy, only whether to copy the tested
234 * statement of or not.
235 * @return the copied resource
236 * @throws DatabaseException
238 public static Resource copy(WriteGraph graph, Resource source, BiFunction<ReadGraph, Statement, Boolean> advisor) throws DatabaseException {
239 return copy(graph, source, 0, advisor, new THashMap<Object, Object>());
243 * See {@link #copy(WriteGraph, Resource, BinaryFunction)}.
248 * @param copyMap a map for storing the correspondences between original and
249 * copied objects. This is used to output data from the copy process.
251 * @throws DatabaseException
253 public static Resource copy(WriteGraph graph, Resource source, BiFunction<ReadGraph, Statement, Boolean> advisor, Map<Object, Object> copyMap) throws DatabaseException {
254 return copy(graph, source, 0, advisor, copyMap);
257 private static Resource copy(WriteGraph graph, Resource source, int level, BiFunction<ReadGraph, Statement, Boolean> advisor, Map<Object, Object> copyMap) throws DatabaseException {
259 System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")");
261 Resource copy = (Resource) copyMap.get(source);
264 System.out.println("[" + level + "] already copied: " + NameUtils.getSafeName(graph, source) + " -> " + NameUtils.getSafeName(graph, copy));
268 Layer0 L0 = Layer0.getInstance(graph);
269 copy = graph.newResource();
270 copyMap.put(source, copy);
271 for (Resource type : graph.getObjects(source, L0.InstanceOf))
272 graph.claim(copy, L0.InstanceOf, null, type);
274 if (graph.hasValue(source)) {
275 Datatype dt = graph.getRelatedValue(source, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));
276 Binding b = Bindings.getBinding(dt);
277 graph.claimValue(copy, graph.<Object>getValue(source, b), b);
281 for (Statement stm : graph.getStatements(source, L0.IsWeaklyRelatedTo)) {
282 if (stm.isAsserted(source))
284 if (graph.isInstanceOf(stm.getPredicate(), L0.Tag)) {
286 System.out.println("[" + level + "]\tcopying tag ("
287 + NameUtils.getSafeName(graph, stm.getSubject()) + ", "
288 + NameUtils.getSafeName(graph, stm.getPredicate()) + ")");
289 graph.claim(copy, stm.getPredicate(), stm.getPredicate(), copy);
293 for (Statement stm : graph.getStatements(source, L0.IsRelatedTo)) {
294 Resource relation = stm.getPredicate();
296 // InstanceOf statements are handled separately, silently ignore them here.
297 if (L0.InstanceOf.equals(relation))
300 // Don't copy asserted relations!
301 if (stm.isAsserted(source)) {
303 System.out.println("[" + level + "]\t\tSkipping asserted statement");
308 System.out.println("[" + level + "]\tprocessing statement (" + NameUtils.toString(graph, stm) + ")");
310 Resource subject = stm.getSubject();
311 Resource inverse = graph.getPossibleInverse(relation);
312 boolean addInverse = false;
313 Resource obj = stm.getObject();
314 Resource propType = graph.getPossibleType(obj, L0.Literal);
316 // §1 only L0.IsComposedOf and its subrelations can be considered to be copied automatically
318 if (propType != null || graph.isSubrelationOf(relation, L0.IsComposedOf)) {
319 if (propType != null && graph.hasStatement(propType, L0.Enumeration, propType)) {
321 System.out.println("[" + level + "]\t\tclaim enumeration statement");
322 graph.claim(copy, relation, null, obj);
325 addInverse = graph.hasStatement(obj, inverse, subject);
327 // Copy instantiated properties, not asserted ones.
329 System.out.println("[" + level + "]\t\tcopy whole object");
330 Resource clone = copy(graph, obj, level + 1, advisor, copyMap);
331 graph.claim(copy, relation, clone);
334 if (advisor != null) {
335 Boolean result = advisor.apply(graph, stm);
336 if (Boolean.TRUE.equals(result)) {
337 // Don't clone the object, just add relation to the same object.
339 addInverse = graph.hasStatement(obj, inverse, subject);
342 System.out.println("[" + level + "]\t\tCopyAdvisorUtil.copy.claim(" + NameUtils.getSafeName(graph, copy) + ", "
343 + NameUtils.getSafeName(graph, relation) + ", "
344 + (addInverse ? NameUtils.getSafeName(graph, inverse) : null) + ", "
345 + NameUtils.getSafeName(graph, obj));
348 graph.claim(copy, relation, addInverse ? inverse : null, obj);
352 System.out.println("[" + level + "]\t\tskipping statement");
360 * Creates and returns a copy of the specified source resource based on the
361 * standard Layer0 relation hierarchy by recursively including all resources
362 * in the copy that the source and is composed of (see
363 * {@link Layer0#IsComposedOf}). A customizable advisor function can be used
364 * to guide the copy process. The routine will always copy at least all
365 * L0.InstanceOf statements and tags related to its input resource. If the
366 * copied resource has a value attached, it will also be copied.
368 * @param graph database write access
369 * @param source the resource to start the copy from
370 * @param advisor <code>null</code> or a custom advisor to guide the copy
371 * process according to the specifications of
372 * {@link StatementEvaluation}. Every copied statement besides
373 * L0.InstanceOf and tags will be evaluated by this advisor.
374 * @return the copied resource
375 * @throws DatabaseException
377 public static Resource copy2(WriteGraph graph, Resource source,
378 BiFunction<ReadGraph, Statement, StatementEvaluation> advisor)
379 throws DatabaseException
381 return copy2(graph, source, advisor, new THashMap<>());
385 * See {@link #copy2(WriteGraph, Resource, BinaryFunction)}.
390 * @param copyMap a map for storing the correspondences between original and
391 * copied objects. This is used to output data from the copy process.
393 * @throws DatabaseException
395 public static Resource copy2(WriteGraph graph, Resource source,
396 BiFunction<ReadGraph, Statement, StatementEvaluation> advisor,
397 Map<Object, Object> copyMap)
398 throws DatabaseException
400 Set<Statement4> pendingStatements = new THashSet<>();
401 Resource result = copy2(graph, source, 0, advisor, copyMap, pendingStatements);
402 postProcessStatements(graph, copyMap, pendingStatements);
407 * Post-process pending statement
409 * Rule: If both the subject and object of a pending source statement have
410 * been copied, then the pending statement should also be copied.
412 private static void postProcessStatements(
414 Map<Object, Object> copyMap,
415 Set<Statement4> pendingStatements)
416 throws DatabaseException
418 if (pendingStatements.isEmpty())
422 System.out.println("post processing " + pendingStatements.size() + " pending statements");
423 for (Statement4 srcStm : pendingStatements) {
424 // At this point, it is certain that srcStm subject has been copied
425 // but test it anyway.
426 Resource subjectCopy = (Resource) copyMap.get(srcStm.stm.getSubject());
427 Resource objectCopy = (Resource) copyMap.get(srcStm.stm.getObject());
428 if (subjectCopy == null || objectCopy == null) {
430 System.out.println("skipping pending statement: " + NameUtils.toString(graph, srcStm.stm));
434 System.out.println("copying pending statement: " + NameUtils.toString(graph, srcStm.stm));
435 graph.claim(subjectCopy, srcStm.stm.getPredicate(), srcStm.inverse, objectCopy);
439 private static Resource copy2(final WriteGraph graph, final Resource source, final int level,
440 BiFunction<ReadGraph, Statement, StatementEvaluation> advisor, Map<Object, Object> copyMap,
441 Set<Statement4> pendingSourceStatements)
442 throws DatabaseException {
444 System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")");
446 Resource copy = (Resource) copyMap.get(source);
449 System.out.println("[" + level + "] already copied: " + NameUtils.getSafeName(graph, source) + " -> " + NameUtils.getSafeName(graph, copy));
453 Layer0 L0 = Layer0.getInstance(graph);
454 copy = graph.newResource();
455 copyMap.put(source, copy);
456 for (Resource type : graph.getObjects(source, L0.InstanceOf))
457 graph.claim(copy, L0.InstanceOf, null, type);
459 if (graph.hasValue(source)) {
460 Datatype dt = graph.getRelatedValue(source, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));
461 Binding b = Bindings.getBinding(dt);
462 graph.claimValue(copy, graph.<Object>getValue(source, b), b);
466 for (Statement stm : graph.getStatements(source, L0.IsWeaklyRelatedTo)) {
467 if (stm.isAsserted(source))
469 if (graph.isInstanceOf(stm.getPredicate(), L0.Tag)) {
471 System.out.println("[" + level + "]\tcopying tag ("
472 + NameUtils.getSafeName(graph, stm.getSubject()) + ", "
473 + NameUtils.getSafeName(graph, stm.getPredicate()) + ")");
474 graph.claim(copy, stm.getPredicate(), stm.getPredicate(), copy);
478 for (Statement stm : graph.getStatements(source, L0.IsRelatedTo)) {
479 Resource relation = stm.getPredicate();
481 // InstanceOf statements are handled separately, silently ignore them here.
482 if (L0.InstanceOf.equals(relation))
485 // Don't copy asserted relations!
486 if (stm.isAsserted(source)) {
488 System.out.println("[" + level + "]\tskipping asserted statement (" + NameUtils.toString(graph, stm) + ")");
493 System.out.println("[" + level + "]\tprocessing statement (" + NameUtils.toString(graph, stm) + ")");
495 Resource subject = stm.getSubject();
496 Resource inverse = graph.getPossibleInverse(relation);
497 Resource obj = stm.getObject();
498 Resource propType = graph.getPossibleType(obj, L0.Literal);
499 boolean addInverse = false;
500 boolean forceIncludeAndFollow = false;
502 switch (evaluate(graph, stm, advisor)) {
505 System.out.println("[" + level + "]\t\tskipping statement");
510 // Don't clone the object, just add relation to the same object.
512 addInverse = graph.hasStatement(obj, inverse, subject);
515 System.out.println("[" + level + "]\t\tCopyAdvisorUtil.copy2.claim(" + NameUtils.getSafeName(graph, copy) + ", "
516 + NameUtils.getSafeName(graph, relation) + ", "
517 + (addInverse ? NameUtils.getSafeName(graph, inverse) : null) + ", "
518 + NameUtils.getSafeName(graph, obj));
521 graph.claim(copy, relation, addInverse ? inverse : null, obj);
525 case INCLUDE_AND_FOLLOW:
526 // Force follow-through in the default copy logic
527 forceIncludeAndFollow = true;
528 // NOTE: intentional fall-through here
532 if (forceIncludeAndFollow || propType != null || graph.isSubrelationOf(relation, L0.IsComposedOf)) {
533 if (propType != null && graph.hasStatement(propType, L0.Enumeration, propType)) {
534 // This logic is applied only for enumeration property
535 // statements that should not have an inverse in any case.
537 System.out.println("[" + level + "]\t\tclaim enumeration statement("
538 + NameUtils.getSafeName(graph, copy) + ", "
539 + NameUtils.getSafeName(graph, relation)+ ", null, "
540 + NameUtils.getSafeName(graph, obj));
542 graph.claim(copy, relation, null, obj);
544 // This logic is applied for other properties besides enumerations
546 addInverse = graph.hasStatement(obj, inverse, subject);
548 // Copy instantiated properties, not asserted ones.
550 System.out.println("[" + level + "]\t\tcopy whole object");
552 Resource clone = copy2(graph, obj, level + 1, advisor, copyMap, pendingSourceStatements);
553 graph.claim(copy, relation, inverse, clone);
556 if (graph.isSubrelationOf(relation, L0.IsRelatedTo)) {
558 System.out.println("[" + level + "]\t\tmarking statement as pending for post-processing");
559 pendingSourceStatements.add(new Statement4(stm, inverse));
562 System.out.println("[" + level + "]\t\tskipping weak statement");
572 * Creates and returns a copy of the specified source resource based on the
573 * standard Layer0 relation hierarchy by recursively including all resources
574 * in the copy that the source and is composed of (see
575 * {@link Layer0#IsComposedOf}). A customizable advisor function can be used
576 * to guide the copy process. The routine will always copy at least all
577 * L0.InstanceOf statements and tags related to its input resource. If the
578 * copied resource has a value attached, it will also be copied.
580 * Works exactly like {@link #copy2(WriteGraph, Resource, BinaryFunction)}
581 * but uses the <code>model</code> argument to make sure that the copy
582 * process does not propagate outside of the model. Any references that go
583 * to resources with URIs that are not in the model's namespace are copied
586 * @param graph database write access
587 * @param source the resource to start the copy from
588 * @param model the model containing the source object, used to keep the
589 * copy process model-local
590 * @param advisor <code>null</code> or a custom advisor to guide the copy
591 * process according to the specifications of
592 * {@link StatementEvaluation}. Every copied statement besides
593 * L0.InstanceOf and tags will be evaluated by this advisor.
594 * @return the copied resource
595 * @throws DatabaseException
597 public static Resource copy3(WriteGraph graph, Resource source, Resource model,
598 BiFunction<ReadGraph, Statement, StatementEvaluation> advisor) throws DatabaseException {
599 String modelURI = graph.getURI(model);
600 return copy3(graph, modelURI, source, 0, advisor, new THashMap<Object, Object>());
604 * See {@link #copy3(WriteGraph, Resource, Resource, BinaryFunction)}.
610 * @param copyMap a map for storing the correspondences between original and
611 * copied objects. This is used to output data from the copy process.
613 * @throws DatabaseException
615 public static Resource copy3(WriteGraph graph, Resource source, Resource model,
616 BiFunction<ReadGraph, Statement, StatementEvaluation> advisor, Map<Object, Object> copyMap) throws DatabaseException {
617 String modelURI = graph.getURI(model);
618 return copy3(graph, modelURI, source, 0, advisor, copyMap);
621 private static Resource copy3(WriteGraph graph, String modelURI, Resource source, int level,
622 BiFunction<ReadGraph, Statement, StatementEvaluation> advisor, Map<Object, Object> copyMap)
623 throws DatabaseException {
625 System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")");
627 Resource copy = (Resource) copyMap.get(source);
630 System.out.println("[" + level + "] already copied: " + NameUtils.getSafeName(graph, source) + " -> " + NameUtils.getSafeName(graph, copy));
634 Layer0 L0 = Layer0.getInstance(graph);
635 copy = graph.newResource();
636 copyMap.put(source, copy);
637 for (Resource type : graph.getObjects(source, L0.InstanceOf))
638 graph.claim(copy, L0.InstanceOf, null, type);
640 if (graph.hasValue(source)) {
641 Datatype dt = graph.getRelatedValue(source, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));
642 Binding b = Bindings.getBinding(dt);
643 graph.claimValue(copy, graph.<Object>getValue(source, b), b);
647 for (Statement stm : graph.getStatements(source, L0.IsWeaklyRelatedTo)) {
648 if (stm.isAsserted(source))
650 if (graph.isInstanceOf(stm.getPredicate(), L0.Tag)) {
652 System.out.println("[" + level + "]\tcopying tag ("
653 + NameUtils.getSafeName(graph, stm.getSubject()) + ", "
654 + NameUtils.getSafeName(graph, stm.getPredicate()) + ")");
655 graph.claim(copy, stm.getPredicate(), stm.getPredicate(), copy);
659 for (Statement stm : graph.getStatements(source, L0.IsRelatedTo)) {
660 Resource relation = stm.getPredicate();
662 // InstanceOf statements are handled separately, silently ignore them here.
663 if (L0.InstanceOf.equals(relation))
666 // Don't copy asserted relations!
667 if (stm.isAsserted(source)) {
669 System.out.println("[" + level + "]\tskipping asserted statement (" + NameUtils.toString(graph, stm) + ")");
674 System.out.println("[" + level + "]\tprocessing statement (" + NameUtils.toString(graph, stm) + ")");
676 Resource subject = stm.getSubject();
677 Resource inverse = graph.getPossibleInverse(relation);
678 Resource obj = stm.getObject();
679 Resource propType = graph.getPossibleType(obj, L0.Literal);
680 boolean addInverse = false;
681 boolean forceIncludeAndFollow = false;
683 switch (evaluate(graph, stm, advisor)) {
686 System.out.println("[" + level + "]\t\tskipping statement");
691 // Don't clone the object, just add relation to the same object.
693 addInverse = graph.hasStatement(obj, inverse, subject);
696 System.out.println("[" + level + "]\t\tCopyAdvisorUtil.copy2.claim(" + NameUtils.getSafeName(graph, copy) + ", "
697 + NameUtils.getSafeName(graph, relation) + ", "
698 + (addInverse ? NameUtils.getSafeName(graph, inverse) : null) + ", "
699 + NameUtils.getSafeName(graph, obj));
702 graph.claim(copy, relation, addInverse ? inverse : null, obj);
706 case INCLUDE_AND_FOLLOW:
707 // Force follow-through in the default copy logic
708 forceIncludeAndFollow = true;
709 // NOTE: intentional fall-through here
713 if (forceIncludeAndFollow || propType != null || graph.isSubrelationOf(relation, L0.IsComposedOf)) {
714 if (propType != null && graph.hasStatement(propType, L0.Enumeration, propType)) {
715 // This logic is applied only for enumeration property
716 // statements that should not have an inverse in any case.
718 System.out.println("[" + level + "]\t\tclaim enumeration statement("
719 + NameUtils.getSafeName(graph, copy) + ", "
720 + NameUtils.getSafeName(graph, relation)+ ", null, "
721 + NameUtils.getSafeName(graph, obj));
723 graph.claim(copy, relation, null, obj);
725 // This logic is applied for other properties besides enumerations
727 addInverse = graph.hasStatement(obj, inverse, subject);
729 String objectURI = graph.getPossibleURI(obj);
731 if(objectURI != null && !objectURI.startsWith(modelURI)) {
734 System.out.println("[" + level + "]\t\tclaim ontological reference");
736 graph.claim(copy, relation, null, obj);
740 // Copy instantiated properties, not asserted ones.
742 System.out.println("[" + level + "]\t\tcopy whole object");
744 Resource clone = copy3(graph, modelURI, obj, level + 1, advisor, copyMap);
745 graph.claim(copy, relation, inverse, clone);
752 System.out.println("[" + level + "]\t\tskipping statement");
760 protected static StatementEvaluation evaluate(ReadGraph graph, Statement stm, BiFunction<ReadGraph, Statement, StatementEvaluation> tester) {
762 return StatementEvaluation.USE_DEFAULT;
763 return tester.apply(graph, stm);
768 * <code>copy4(graph, source, graph.adapt(source, CopyHandler.class)))</code>
771 * @param graph database write access
772 * @param source the resource to start the copy from
773 * @return the copied resource
774 * @throws DatabaseException
776 public static Resource copy4(WriteGraph graph, Resource source) throws DatabaseException {
777 CopyHandler handler = graph.adapt(source, CopyHandler.class);
778 return copy4(graph, source, handler);
782 * Creates and returns a copy of the specified source resource based on
783 * transferable graph export and import. The TG representation shall be
784 * generated by the specified {@link CopyHandler} into a
785 * {@link SimanticsClipboard} instance from where it is read back as
786 * {@link TransferableGraph1} and imported into the database through
787 * {@link TransferableGraphs#importGraph1(WriteGraph, TransferableGraph1, org.simantics.graph.db.IImportAdvisor)}.
789 * @param graph database write access
790 * @param source the resource to start the copy from
791 * @param copyHandler the handler to use for generating the transferable
792 * graph representation from the copied resource
793 * @return the copied resource
794 * @throws DatabaseException
796 public static Resource copy4(WriteGraph graph, Resource source, CopyHandler copyHandler) throws DatabaseException {
797 SimanticsClipboardImpl builder = new SimanticsClipboardImpl();
798 copyHandler.copyToClipboard(graph, builder, new NullProgressMonitor());
800 for(Set<Representation> object : builder.getContents()) {
801 TransferableGraph1 tg = ClipboardUtils.accept(graph, object, SimanticsKeys.KEY_TRANSFERABLE_GRAPH);
803 FixedRootImportAdvisor advisor = new FixedRootImportAdvisor();
804 TransferableGraphs.importGraph1(graph, tg, advisor);
805 return advisor.getRoot();
809 String uri = graph.getPossibleURI(source);
810 throw new DatabaseException("Failed to copy resource " + NameUtils.getSafeName(graph, source, true)
811 + (uri != null ? " with URI " + uri : ""));