X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fsynchronization%2Fgraph%2FCopyAdvisorUtil.java;h=60d4c56aeaa54d9c5d0559dcea7b189d188e6a06;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hp=86a673952b2560e46df62aef86f627f9b0e919d8;hpb=24e2b34260f219f0d1644ca7a138894980e25b14;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/CopyAdvisorUtil.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/CopyAdvisorUtil.java index 86a673952..60d4c56ae 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/CopyAdvisorUtil.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/CopyAdvisorUtil.java @@ -1,755 +1,755 @@ -/******************************************************************************* - * 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 gnu.trove.map.hash.THashMap; - -import java.util.Map; -import java.util.Set; - -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; - -/** - * 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; - - /** - * @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, BinaryFunction 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, BinaryFunction advisor, Map copyMap) throws DatabaseException { - return copy(graph, source, 0, advisor, copyMap); - } - - private static Resource copy(WriteGraph graph, Resource source, int level, BinaryFunction 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.call(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, - BinaryFunction advisor) throws DatabaseException { - return copy2(graph, source, 0, 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, - BinaryFunction advisor, Map copyMap) - throws DatabaseException { - return copy2(graph, source, 0, advisor, copyMap); - } - - private static Resource copy2(final WriteGraph graph, final Resource source, final int level, - BinaryFunction 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); - - // 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); - graph.claim(copy, relation, inverse, clone); - } - } 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. - * - * 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, - BinaryFunction 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, - BinaryFunction 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, - BinaryFunction 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, BinaryFunction tester) { - if (tester == null) - return StatementEvaluation.USE_DEFAULT; - return tester.call(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); - - 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 : "")); - } - -} +/******************************************************************************* + * 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 gnu.trove.map.hash.THashMap; + +import java.util.Map; +import java.util.Set; + +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; + +/** + * 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; + + /** + * @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, BinaryFunction 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, BinaryFunction advisor, Map copyMap) throws DatabaseException { + return copy(graph, source, 0, advisor, copyMap); + } + + private static Resource copy(WriteGraph graph, Resource source, int level, BinaryFunction 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.call(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, + BinaryFunction advisor) throws DatabaseException { + return copy2(graph, source, 0, 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, + BinaryFunction advisor, Map copyMap) + throws DatabaseException { + return copy2(graph, source, 0, advisor, copyMap); + } + + private static Resource copy2(final WriteGraph graph, final Resource source, final int level, + BinaryFunction 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); + + // 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); + graph.claim(copy, relation, inverse, clone); + } + } 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. + * + * 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, + BinaryFunction 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, + BinaryFunction 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, + BinaryFunction 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, BinaryFunction tester) { + if (tester == null) + return StatementEvaluation.USE_DEFAULT; + return tester.call(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); + + 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 : "")); + } + +}