/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.diagram.synchronization.graph; import java.util.Map; import java.util.Set; import java.util.function.BiFunction; import org.eclipse.core.runtime.NullProgressMonitor; import org.simantics.databoard.Bindings; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.type.Datatype; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Statement; import org.simantics.db.WriteGraph; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.adapter.CopyHandler; import org.simantics.db.layer0.adapter.impl.FixedRootImportAdvisor; import org.simantics.db.layer0.util.ClipboardUtils; import org.simantics.db.layer0.util.SimanticsClipboard; import org.simantics.db.layer0.util.SimanticsClipboard.Representation; import org.simantics.db.layer0.util.SimanticsClipboardImpl; import org.simantics.db.layer0.util.SimanticsKeys; import org.simantics.diagram.adapter.GraphToDiagramSynchronizer; import org.simantics.diagram.internal.DebugPolicy; import org.simantics.diagram.synchronization.CopyAdvisor; import org.simantics.diagram.synchronization.CopyAdvisor.Evaluation; import org.simantics.diagram.synchronization.ErrorHandler; import org.simantics.diagram.synchronization.IModifiableSynchronizationContext; import org.simantics.diagram.synchronization.StatementEvaluation; import org.simantics.diagram.synchronization.SynchronizationException; import org.simantics.diagram.synchronization.SynchronizationHints; import org.simantics.graph.db.TransferableGraphs; import org.simantics.graph.representation.TransferableGraph1; import org.simantics.layer0.Layer0; import org.simantics.utils.datastructures.BinaryFunction; import gnu.trove.map.hash.THashMap; import gnu.trove.set.hash.THashSet; /** * This class contains utility methods for the basic cut/copy operations * performed related to cut-copy-pasting diagram/configuration component and * composites. * *

* Methods * {@link #cut(IModifiableSynchronizationContext, WriteGraph, CopyAdvisor, Resource, Resource, Resource)} * and * {@link #copy(IModifiableSynchronizationContext, WriteGraph, CopyAdvisor, Resource, Resource, Resource)} * are available for making it easier to properly invoke * {@link CopyAdvisor#cut(org.simantics.diagram.synchronization.ISynchronizationContext, Object, Object, Object)} * and * {@link CopyAdvisor#copy(org.simantics.diagram.synchronization.ISynchronizationContext, Object, Object, Object)} * operations in a diagram/graph transaction context. * *

* Methods {@link #copy(WriteGraph, Resource, BinaryFunction)}, * {@link #copy2(WriteGraph, Resource, BinaryFunction)}, * {@link #copy3(WriteGraph, Resource, Resource, BinaryFunction)}, * {@link #copy4(WriteGraph, Resource)} and * {@link #copy4(WriteGraph, Resource, CopyHandler)} offer differently * functioning versions of copying a single resource in the graph that are * mainly tuned for copying diagram elements and configuration components. * *

* IMPORTANT: Note that copy, copy2 and copy3 cannot handle copying of ordered sets * properly. * * @author Tuukka Lehtonen */ public class CopyAdvisorUtil { public static final boolean DEBUG_COPY = DebugPolicy.DEBUG_COPY_PASTE; private static class Statement4 { public final Statement stm; public final Resource inverse; public Statement4(Statement stm, Resource inverse) { this.stm = stm; this.inverse = inverse; } } /** * @param context a synchronization context instance, such as * {@link GraphToDiagramSynchronizer} * @param g handle for graph writing * @param ca the advisor for the copy operation * @param cut the resource that is about to be cut * @param sourceContainer the container from which the cut argument is about * to be removed * @param targetContainer the container into which the cut argument is about * to be moved * @return the result of * {@link CopyAdvisor#cut(org.simantics.diagram.synchronization.ISynchronizationContext, Object, Object, Object)} * @throws DatabaseException */ public static Object cut(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca, Resource cut, Resource sourceContainer, Resource targetContainer) throws DatabaseException { if (DEBUG_COPY) System.out.println("Attempting to cut component " + NameUtils.getSafeName(g, cut, true)); try { context.set(GraphSynchronizationHints.READ_TRANSACTION, g); context.set(GraphSynchronizationHints.WRITE_TRANSACTION, g); return ca.cut(context, cut, sourceContainer, targetContainer); } catch (SynchronizationException e) { ErrorHandler eh = context.get(SynchronizationHints.ERROR_HANDLER); eh.error(e.getMessage(), e); } finally { context.set(GraphSynchronizationHints.READ_TRANSACTION, null); context.set(GraphSynchronizationHints.WRITE_TRANSACTION, null); } return null; } /** * @param context a synchronization context instance, such as * {@link GraphToDiagramSynchronizer} * @param g handle for graph writing * @param ca the advisor for the copy operation * @param copyOf the resource that is about to be copied * @param sourceContainer the container of the resource that will be copied * @param targetContainer the to-be container of the copied resource instance * @return the copied resource * @throws DatabaseException */ public static Resource copy(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca, Resource copyOf, Resource sourceContainer, Resource targetContainer) throws DatabaseException { Resource resource = null; if (DEBUG_COPY) System.out.println("Attempting to copy component " + NameUtils.getSafeName(g, copyOf, true)); try { context.set(GraphSynchronizationHints.READ_TRANSACTION, g); context.set(GraphSynchronizationHints.WRITE_TRANSACTION, g); Evaluation eval = ca.canCopy(context, copyOf, sourceContainer, targetContainer); if (DEBUG_COPY) System.out.println(" CopyAdvisor(" + ca + ").canCopy evaluation result: " + eval); if (CopyAdvisor.SUPPORTED.contains(eval)) { Object copy = ca.copy(context, copyOf, sourceContainer, targetContainer); if (DEBUG_COPY) System.out.println(" CopyAdvisor(" + ca + ").copy result: " + copy); if (copy instanceof Resource) { resource = (Resource) copy; } else { throw new UnsupportedOperationException("Cannot copy element " + copyOf); } } } catch (SynchronizationException e) { ErrorHandler eh = context.get(SynchronizationHints.ERROR_HANDLER); eh.error(e.getMessage(), e); // throwing exception allows canceling failed copy! throw new DatabaseException(e); } finally { context.set(GraphSynchronizationHints.READ_TRANSACTION, null); context.set(GraphSynchronizationHints.WRITE_TRANSACTION, null); } return resource; } /** * @param context a synchronization context instance, such as * {@link GraphToDiagramSynchronizer} * @param g handle for graph writing * @param ca the advisor for the copy operation * @param copyOf the resource that is about to be copied * @param sourceContainer the container of the resource that will be copied * @param targetContainer the to-be container of the copied resource instance * @param map a map for storing the correspondences between original and * copied objects. This is used to output data from the copy process. * @return the copied resource * @throws DatabaseException */ public static Resource copy(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca, Resource copyOf, Resource sourceContainer, Resource targetContainer, Map map) throws DatabaseException { Resource resource = null; if (DEBUG_COPY) System.out.println("Attempting to copy component " + NameUtils.getSafeName(g, copyOf, true)); try { context.set(GraphSynchronizationHints.READ_TRANSACTION, g); context.set(GraphSynchronizationHints.WRITE_TRANSACTION, g); Evaluation eval = ca.canCopy(context, copyOf, sourceContainer, targetContainer); if (DEBUG_COPY) System.out.println(" CopyAdvisor(" + ca + ").canCopy evaluation result: " + eval); if (CopyAdvisor.SUPPORTED.contains(eval)) { Object copy = ca.copy(context, copyOf, sourceContainer, targetContainer, map); if (DEBUG_COPY) { System.out.println(" CopyAdvisor(" + ca + ").copy result: " + copy); System.out.println(" CopyAdvisor(" + ca + ").copy result map: " + map); } if (copy instanceof Resource) { resource = (Resource) copy; } else { throw new UnsupportedOperationException("Cannot copy element " + copyOf); } } } catch (SynchronizationException e) { ErrorHandler eh = context.get(SynchronizationHints.ERROR_HANDLER); eh.error(e.getMessage(), e); // throwing exception allows canceling failed copy! throw new DatabaseException(e); } finally { context.set(GraphSynchronizationHints.READ_TRANSACTION, null); context.set(GraphSynchronizationHints.WRITE_TRANSACTION, null); } return resource; } /** * Creates and returns a copy of the specified source resource based on the * standard Layer0 relation hierarchy by recursively including all resources * in the copy that the source and is composed of (see * {@link Layer0#IsComposedOf}). The routine will always copy at least all * L0.InstanceOf statements and tags related to its input resource. If the * copied resource has a value attached, it will also be copied. * * @param graph database write access * @param source the resource to start the copy from * @param advisor null or a custom advisor to guide whether or * not to copy relations that are not inherited from L0.IsComposedOf. * This advisor cannot be used to say that non-composing relations * should perform recursive copy, only whether to copy the tested * statement of or not. * @return the copied resource * @throws DatabaseException */ public static Resource copy(WriteGraph graph, Resource source, BiFunction advisor) throws DatabaseException { return copy(graph, source, 0, advisor, new THashMap()); } /** * See {@link #copy(WriteGraph, Resource, BinaryFunction)}. * * @param graph * @param source * @param advisor * @param copyMap a map for storing the correspondences between original and * copied objects. This is used to output data from the copy process. * @return * @throws DatabaseException */ public static Resource copy(WriteGraph graph, Resource source, BiFunction advisor, Map copyMap) throws DatabaseException { return copy(graph, source, 0, advisor, copyMap); } private static Resource copy(WriteGraph graph, Resource source, int level, BiFunction advisor, Map copyMap) throws DatabaseException { if (DEBUG_COPY) System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")"); Resource copy = (Resource) copyMap.get(source); if (copy != null) { if (DEBUG_COPY) System.out.println("[" + level + "] already copied: " + NameUtils.getSafeName(graph, source) + " -> " + NameUtils.getSafeName(graph, copy)); return copy; } Layer0 L0 = Layer0.getInstance(graph); copy = graph.newResource(); copyMap.put(source, copy); for (Resource type : graph.getObjects(source, L0.InstanceOf)) graph.claim(copy, L0.InstanceOf, null, type); if (graph.hasValue(source)) { Datatype dt = graph.getRelatedValue(source, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class)); Binding b = Bindings.getBinding(dt); graph.claimValue(copy, graph.getValue(source, b), b); } // Copy tags for (Statement stm : graph.getStatements(source, L0.IsWeaklyRelatedTo)) { if (stm.isAsserted(source)) continue; if (graph.isInstanceOf(stm.getPredicate(), L0.Tag)) { if (DEBUG_COPY) System.out.println("[" + level + "]\tcopying tag (" + NameUtils.getSafeName(graph, stm.getSubject()) + ", " + NameUtils.getSafeName(graph, stm.getPredicate()) + ")"); graph.claim(copy, stm.getPredicate(), stm.getPredicate(), copy); } } for (Statement stm : graph.getStatements(source, L0.IsRelatedTo)) { Resource relation = stm.getPredicate(); // InstanceOf statements are handled separately, silently ignore them here. if (L0.InstanceOf.equals(relation)) continue; // Don't copy asserted relations! if (stm.isAsserted(source)) { if (DEBUG_COPY) System.out.println("[" + level + "]\t\tSkipping asserted statement"); continue; } if (DEBUG_COPY) System.out.println("[" + level + "]\tprocessing statement (" + NameUtils.toString(graph, stm) + ")"); Resource subject = stm.getSubject(); Resource inverse = graph.getPossibleInverse(relation); boolean addInverse = false; Resource obj = stm.getObject(); Resource propType = graph.getPossibleType(obj, L0.Literal); // ยง1 only L0.IsComposedOf and its subrelations can be considered to be copied automatically if (propType != null || graph.isSubrelationOf(relation, L0.IsComposedOf)) { if (propType != null && graph.hasStatement(propType, L0.Enumeration, propType)) { if (DEBUG_COPY) System.out.println("[" + level + "]\t\tclaim enumeration statement"); graph.claim(copy, relation, null, obj); } else { if (inverse != null) addInverse = graph.hasStatement(obj, inverse, subject); // Copy instantiated properties, not asserted ones. if (DEBUG_COPY) System.out.println("[" + level + "]\t\tcopy whole object"); Resource clone = copy(graph, obj, level + 1, advisor, copyMap); graph.claim(copy, relation, clone); } } else { if (advisor != null) { Boolean result = advisor.apply(graph, stm); if (Boolean.TRUE.equals(result)) { // Don't clone the object, just add relation to the same object. if (inverse != null) addInverse = graph.hasStatement(obj, inverse, subject); if (DEBUG_COPY) { System.out.println("[" + level + "]\t\tCopyAdvisorUtil.copy.claim(" + NameUtils.getSafeName(graph, copy) + ", " + NameUtils.getSafeName(graph, relation) + ", " + (addInverse ? NameUtils.getSafeName(graph, inverse) : null) + ", " + NameUtils.getSafeName(graph, obj)); } graph.claim(copy, relation, addInverse ? inverse : null, obj); } } else { if (DEBUG_COPY) System.out.println("[" + level + "]\t\tskipping statement"); } } } return copy; } /** * Creates and returns a copy of the specified source resource based on the * standard Layer0 relation hierarchy by recursively including all resources * in the copy that the source and is composed of (see * {@link Layer0#IsComposedOf}). A customizable advisor function can be used * to guide the copy process. The routine will always copy at least all * L0.InstanceOf statements and tags related to its input resource. If the * copied resource has a value attached, it will also be copied. * * @param graph database write access * @param source the resource to start the copy from * @param advisor null or a custom advisor to guide the copy * process according to the specifications of * {@link StatementEvaluation}. Every copied statement besides * L0.InstanceOf and tags will be evaluated by this advisor. * @return the copied resource * @throws DatabaseException */ public static Resource copy2(WriteGraph graph, Resource source, BiFunction advisor) throws DatabaseException { return copy2(graph, source, advisor, new THashMap<>()); } /** * See {@link #copy2(WriteGraph, Resource, BinaryFunction)}. * * @param graph * @param source * @param advisor * @param copyMap a map for storing the correspondences between original and * copied objects. This is used to output data from the copy process. * @return * @throws DatabaseException */ public static Resource copy2(WriteGraph graph, Resource source, BiFunction advisor, Map copyMap) throws DatabaseException { Set pendingStatements = new THashSet<>(); Resource result = copy2(graph, source, 0, advisor, copyMap, pendingStatements); postProcessStatements(graph, copyMap, pendingStatements); return result; } /** * Post-process pending statement * * Rule: If both the subject and object of a pending source statement have * been copied, then the pending statement should also be copied. */ private static void postProcessStatements( WriteGraph graph, Map copyMap, Set pendingStatements) throws DatabaseException { if (pendingStatements.isEmpty()) return; if (DEBUG_COPY) System.out.println("post processing " + pendingStatements.size() + " pending statements"); for (Statement4 srcStm : pendingStatements) { // At this point, it is certain that srcStm subject has been copied // but test it anyway. Resource subjectCopy = (Resource) copyMap.get(srcStm.stm.getSubject()); Resource objectCopy = (Resource) copyMap.get(srcStm.stm.getObject()); if (subjectCopy == null || objectCopy == null) { if (DEBUG_COPY) System.out.println("skipping pending statement: " + NameUtils.toString(graph, srcStm.stm)); continue; } if (DEBUG_COPY) System.out.println("copying pending statement: " + NameUtils.toString(graph, srcStm.stm)); graph.claim(subjectCopy, srcStm.stm.getPredicate(), srcStm.inverse, objectCopy); } } private static Resource copy2(final WriteGraph graph, final Resource source, final int level, BiFunction advisor, Map copyMap, Set pendingSourceStatements) throws DatabaseException { if (DEBUG_COPY) System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")"); Resource copy = (Resource) copyMap.get(source); if (copy != null) { if (DEBUG_COPY) System.out.println("[" + level + "] already copied: " + NameUtils.getSafeName(graph, source) + " -> " + NameUtils.getSafeName(graph, copy)); return copy; } Layer0 L0 = Layer0.getInstance(graph); copy = graph.newResource(); copyMap.put(source, copy); for (Resource type : graph.getObjects(source, L0.InstanceOf)) graph.claim(copy, L0.InstanceOf, null, type); if (graph.hasValue(source)) { Datatype dt = graph.getRelatedValue(source, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class)); Binding b = Bindings.getBinding(dt); graph.claimValue(copy, graph.getValue(source, b), b); } // Copy tags for (Statement stm : graph.getStatements(source, L0.IsWeaklyRelatedTo)) { if (stm.isAsserted(source)) continue; if (graph.isInstanceOf(stm.getPredicate(), L0.Tag)) { if (DEBUG_COPY) System.out.println("[" + level + "]\tcopying tag (" + NameUtils.getSafeName(graph, stm.getSubject()) + ", " + NameUtils.getSafeName(graph, stm.getPredicate()) + ")"); graph.claim(copy, stm.getPredicate(), stm.getPredicate(), copy); } } for (Statement stm : graph.getStatements(source, L0.IsRelatedTo)) { Resource relation = stm.getPredicate(); // InstanceOf statements are handled separately, silently ignore them here. if (L0.InstanceOf.equals(relation)) continue; // Don't copy asserted relations! if (stm.isAsserted(source)) { if (DEBUG_COPY) System.out.println("[" + level + "]\tskipping asserted statement (" + NameUtils.toString(graph, stm) + ")"); continue; } if (DEBUG_COPY) System.out.println("[" + level + "]\tprocessing statement (" + NameUtils.toString(graph, stm) + ")"); Resource subject = stm.getSubject(); Resource inverse = graph.getPossibleInverse(relation); Resource obj = stm.getObject(); Resource propType = graph.getPossibleType(obj, L0.Literal); boolean addInverse = false; boolean forceIncludeAndFollow = false; switch (evaluate(graph, stm, advisor)) { case SKIP: if (DEBUG_COPY) System.out.println("[" + level + "]\t\tskipping statement"); break; case INCLUDE: { // Don't clone the object, just add relation to the same object. if (inverse != null) addInverse = graph.hasStatement(obj, inverse, subject); if (DEBUG_COPY) { System.out.println("[" + level + "]\t\tCopyAdvisorUtil.copy2.claim(" + NameUtils.getSafeName(graph, copy) + ", " + NameUtils.getSafeName(graph, relation) + ", " + (addInverse ? NameUtils.getSafeName(graph, inverse) : null) + ", " + NameUtils.getSafeName(graph, obj)); } graph.claim(copy, relation, addInverse ? inverse : null, obj); break; } case INCLUDE_AND_FOLLOW: // Force follow-through in the default copy logic forceIncludeAndFollow = true; // NOTE: intentional fall-through here case USE_DEFAULT: { if (forceIncludeAndFollow || propType != null || graph.isSubrelationOf(relation, L0.IsComposedOf)) { if (propType != null && graph.hasStatement(propType, L0.Enumeration, propType)) { // This logic is applied only for enumeration property // statements that should not have an inverse in any case. if (DEBUG_COPY) { System.out.println("[" + level + "]\t\tclaim enumeration statement(" + NameUtils.getSafeName(graph, copy) + ", " + NameUtils.getSafeName(graph, relation)+ ", null, " + NameUtils.getSafeName(graph, obj)); } graph.claim(copy, relation, null, obj); } else { // This logic is applied for other properties besides enumerations if (inverse != null) addInverse = graph.hasStatement(obj, inverse, subject); // Copy instantiated properties, not asserted ones. if (DEBUG_COPY) System.out.println("[" + level + "]\t\tcopy whole object"); Resource clone = copy2(graph, obj, level + 1, advisor, copyMap, pendingSourceStatements); graph.claim(copy, relation, inverse, clone); } } else { if (graph.isSubrelationOf(relation, L0.IsRelatedTo)) { if (DEBUG_COPY) System.out.println("[" + level + "]\t\tmarking statement as pending for post-processing"); pendingSourceStatements.add(new Statement4(stm, inverse)); } else { if (DEBUG_COPY) System.out.println("[" + level + "]\t\tskipping weak statement"); } } } } } return copy; } /** * Creates and returns a copy of the specified source resource based on the * standard Layer0 relation hierarchy by recursively including all resources * in the copy that the source and is composed of (see * {@link Layer0#IsComposedOf}). A customizable advisor function can be used * to guide the copy process. The routine will always copy at least all * L0.InstanceOf statements and tags related to its input resource. If the * copied resource has a value attached, it will also be copied. * * Works exactly like {@link #copy2(WriteGraph, Resource, BinaryFunction)} * but uses the model argument to make sure that the copy * process does not propagate outside of the model. Any references that go * to resources with URIs that are not in the model's namespace are copied * as unidirectional. * * @param graph database write access * @param source the resource to start the copy from * @param model the model containing the source object, used to keep the * copy process model-local * @param advisor null or a custom advisor to guide the copy * process according to the specifications of * {@link StatementEvaluation}. Every copied statement besides * L0.InstanceOf and tags will be evaluated by this advisor. * @return the copied resource * @throws DatabaseException */ public static Resource copy3(WriteGraph graph, Resource source, Resource model, BiFunction advisor) throws DatabaseException { String modelURI = graph.getURI(model); return copy3(graph, modelURI, source, 0, advisor, new THashMap()); } /** * See {@link #copy3(WriteGraph, Resource, Resource, BinaryFunction)}. * * @param graph * @param source * @param model * @param advisor * @param copyMap a map for storing the correspondences between original and * copied objects. This is used to output data from the copy process. * @return * @throws DatabaseException */ public static Resource copy3(WriteGraph graph, Resource source, Resource model, BiFunction advisor, Map copyMap) throws DatabaseException { String modelURI = graph.getURI(model); return copy3(graph, modelURI, source, 0, advisor, copyMap); } private static Resource copy3(WriteGraph graph, String modelURI, Resource source, int level, BiFunction advisor, Map copyMap) throws DatabaseException { if (DEBUG_COPY) System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")"); Resource copy = (Resource) copyMap.get(source); if (copy != null) { if (DEBUG_COPY) System.out.println("[" + level + "] already copied: " + NameUtils.getSafeName(graph, source) + " -> " + NameUtils.getSafeName(graph, copy)); return copy; } Layer0 L0 = Layer0.getInstance(graph); copy = graph.newResource(); copyMap.put(source, copy); for (Resource type : graph.getObjects(source, L0.InstanceOf)) graph.claim(copy, L0.InstanceOf, null, type); if (graph.hasValue(source)) { Datatype dt = graph.getRelatedValue(source, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class)); Binding b = Bindings.getBinding(dt); graph.claimValue(copy, graph.getValue(source, b), b); } // Copy tags for (Statement stm : graph.getStatements(source, L0.IsWeaklyRelatedTo)) { if (stm.isAsserted(source)) continue; if (graph.isInstanceOf(stm.getPredicate(), L0.Tag)) { if (DEBUG_COPY) System.out.println("[" + level + "]\tcopying tag (" + NameUtils.getSafeName(graph, stm.getSubject()) + ", " + NameUtils.getSafeName(graph, stm.getPredicate()) + ")"); graph.claim(copy, stm.getPredicate(), stm.getPredicate(), copy); } } for (Statement stm : graph.getStatements(source, L0.IsRelatedTo)) { Resource relation = stm.getPredicate(); // InstanceOf statements are handled separately, silently ignore them here. if (L0.InstanceOf.equals(relation)) continue; // Don't copy asserted relations! if (stm.isAsserted(source)) { if (DEBUG_COPY) System.out.println("[" + level + "]\tskipping asserted statement (" + NameUtils.toString(graph, stm) + ")"); continue; } if (DEBUG_COPY) System.out.println("[" + level + "]\tprocessing statement (" + NameUtils.toString(graph, stm) + ")"); Resource subject = stm.getSubject(); Resource inverse = graph.getPossibleInverse(relation); Resource obj = stm.getObject(); Resource propType = graph.getPossibleType(obj, L0.Literal); boolean addInverse = false; boolean forceIncludeAndFollow = false; switch (evaluate(graph, stm, advisor)) { case SKIP: if (DEBUG_COPY) System.out.println("[" + level + "]\t\tskipping statement"); break; case INCLUDE: { // Don't clone the object, just add relation to the same object. if (inverse != null) addInverse = graph.hasStatement(obj, inverse, subject); if (DEBUG_COPY) { System.out.println("[" + level + "]\t\tCopyAdvisorUtil.copy2.claim(" + NameUtils.getSafeName(graph, copy) + ", " + NameUtils.getSafeName(graph, relation) + ", " + (addInverse ? NameUtils.getSafeName(graph, inverse) : null) + ", " + NameUtils.getSafeName(graph, obj)); } graph.claim(copy, relation, addInverse ? inverse : null, obj); break; } case INCLUDE_AND_FOLLOW: // Force follow-through in the default copy logic forceIncludeAndFollow = true; // NOTE: intentional fall-through here case USE_DEFAULT: { if (forceIncludeAndFollow || propType != null || graph.isSubrelationOf(relation, L0.IsComposedOf)) { if (propType != null && graph.hasStatement(propType, L0.Enumeration, propType)) { // This logic is applied only for enumeration property // statements that should not have an inverse in any case. if (DEBUG_COPY) { System.out.println("[" + level + "]\t\tclaim enumeration statement(" + NameUtils.getSafeName(graph, copy) + ", " + NameUtils.getSafeName(graph, relation)+ ", null, " + NameUtils.getSafeName(graph, obj)); } graph.claim(copy, relation, null, obj); } else { // This logic is applied for other properties besides enumerations if (inverse != null) addInverse = graph.hasStatement(obj, inverse, subject); String objectURI = graph.getPossibleURI(obj); if(objectURI != null && !objectURI.startsWith(modelURI)) { if (DEBUG_COPY) System.out.println("[" + level + "]\t\tclaim ontological reference"); graph.claim(copy, relation, null, obj); } else { // Copy instantiated properties, not asserted ones. if (DEBUG_COPY) System.out.println("[" + level + "]\t\tcopy whole object"); Resource clone = copy3(graph, modelURI, obj, level + 1, advisor, copyMap); graph.claim(copy, relation, inverse, clone); } } } else { if (DEBUG_COPY) System.out.println("[" + level + "]\t\tskipping statement"); } } } } return copy; } protected static StatementEvaluation evaluate(ReadGraph graph, Statement stm, BiFunction tester) { if (tester == null) return StatementEvaluation.USE_DEFAULT; return tester.apply(graph, stm); } /** * Equal to * copy4(graph, source, graph.adapt(source, CopyHandler.class))) * . * * @param graph database write access * @param source the resource to start the copy from * @return the copied resource * @throws DatabaseException */ public static Resource copy4(WriteGraph graph, Resource source) throws DatabaseException { CopyHandler handler = graph.adapt(source, CopyHandler.class); return copy4(graph, source, handler); } /** * Creates and returns a copy of the specified source resource based on * transferable graph export and import. The TG representation shall be * generated by the specified {@link CopyHandler} into a * {@link SimanticsClipboard} instance from where it is read back as * {@link TransferableGraph1} and imported into the database through * {@link TransferableGraphs#importGraph1(WriteGraph, TransferableGraph1, org.simantics.graph.db.IImportAdvisor)}. * * @param graph database write access * @param source the resource to start the copy from * @param copyHandler the handler to use for generating the transferable * graph representation from the copied resource * @return the copied resource * @throws DatabaseException */ public static Resource copy4(WriteGraph graph, Resource source, CopyHandler copyHandler) throws DatabaseException { SimanticsClipboardImpl builder = new SimanticsClipboardImpl(); copyHandler.copyToClipboard(graph, builder, new NullProgressMonitor()); for(Set object : builder.getContents()) { TransferableGraph1 tg = ClipboardUtils.accept(graph, object, SimanticsKeys.KEY_TRANSFERABLE_GRAPH); if(tg != null) { FixedRootImportAdvisor advisor = new FixedRootImportAdvisor(); TransferableGraphs.importGraph1(graph, tg, advisor); return advisor.getRoot(); } } String uri = graph.getPossibleURI(source); throw new DatabaseException("Failed to copy resource " + NameUtils.getSafeName(graph, source, true) + (uri != null ? " with URI " + uri : "")); } }