]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/CopyAdvisorUtil.java
Merge branch 'feature/funcwrite'
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / synchronization / graph / CopyAdvisorUtil.java
index 86a673952b2560e46df62aef86f627f9b0e919d8..61d09acdd3cb18981d53c67e9300d255a3fc23d6 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.diagram.synchronization.graph;\r
-\r
-import gnu.trove.map.hash.THashMap;\r
-\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.binding.Binding;\r
-import org.simantics.databoard.type.Datatype;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.Statement;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.common.utils.NameUtils;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.layer0.adapter.CopyHandler;\r
-import org.simantics.db.layer0.adapter.impl.FixedRootImportAdvisor;\r
-import org.simantics.db.layer0.util.ClipboardUtils;\r
-import org.simantics.db.layer0.util.SimanticsClipboard;\r
-import org.simantics.db.layer0.util.SimanticsClipboard.Representation;\r
-import org.simantics.db.layer0.util.SimanticsClipboardImpl;\r
-import org.simantics.db.layer0.util.SimanticsKeys;\r
-import org.simantics.diagram.adapter.GraphToDiagramSynchronizer;\r
-import org.simantics.diagram.internal.DebugPolicy;\r
-import org.simantics.diagram.synchronization.CopyAdvisor;\r
-import org.simantics.diagram.synchronization.CopyAdvisor.Evaluation;\r
-import org.simantics.diagram.synchronization.ErrorHandler;\r
-import org.simantics.diagram.synchronization.IModifiableSynchronizationContext;\r
-import org.simantics.diagram.synchronization.StatementEvaluation;\r
-import org.simantics.diagram.synchronization.SynchronizationException;\r
-import org.simantics.diagram.synchronization.SynchronizationHints;\r
-import org.simantics.graph.db.TransferableGraphs;\r
-import org.simantics.graph.representation.TransferableGraph1;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.utils.datastructures.BinaryFunction;\r
-\r
-/**\r
- * This class contains utility methods for the basic cut/copy operations\r
- * performed related to cut-copy-pasting diagram/configuration component and\r
- * composites.\r
- * \r
- * <p>\r
- * Methods\r
- * {@link #cut(IModifiableSynchronizationContext, WriteGraph, CopyAdvisor, Resource, Resource, Resource)}\r
- * and\r
- * {@link #copy(IModifiableSynchronizationContext, WriteGraph, CopyAdvisor, Resource, Resource, Resource)}\r
- * are available for making it easier to properly invoke\r
- * {@link CopyAdvisor#cut(org.simantics.diagram.synchronization.ISynchronizationContext, Object, Object, Object)}\r
- * and\r
- * {@link CopyAdvisor#copy(org.simantics.diagram.synchronization.ISynchronizationContext, Object, Object, Object)}\r
- * operations in a diagram/graph transaction context.\r
- * \r
- * <p>\r
- * Methods {@link #copy(WriteGraph, Resource, BinaryFunction)},\r
- * {@link #copy2(WriteGraph, Resource, BinaryFunction)},\r
- * {@link #copy3(WriteGraph, Resource, Resource, BinaryFunction)},\r
- * {@link #copy4(WriteGraph, Resource)} and\r
- * {@link #copy4(WriteGraph, Resource, CopyHandler)} offer differently\r
- * functioning versions of copying a single resource in the graph that are\r
- * mainly tuned for copying diagram elements and configuration components.\r
- * \r
- * <p>\r
- * <b>IMPORTANT:</b> Note that copy, copy2 and copy3 cannot handle copying of ordered sets\r
- * properly.\r
- * \r
- * @author Tuukka Lehtonen\r
- */\r
-public class CopyAdvisorUtil {\r
-\r
-    public static final boolean DEBUG_COPY = DebugPolicy.DEBUG_COPY_PASTE;\r
-\r
-    /**\r
-     * @param context a synchronization context instance, such as\r
-     *        {@link GraphToDiagramSynchronizer}\r
-     * @param g handle for graph writing\r
-     * @param ca the advisor for the copy operation\r
-     * @param cut the resource that is about to be cut\r
-     * @param sourceContainer the container from which the cut argument is about\r
-     *        to be removed\r
-     * @param targetContainer the container into which the cut argument is about\r
-     *        to be moved\r
-     * @return the result of\r
-     *         {@link CopyAdvisor#cut(org.simantics.diagram.synchronization.ISynchronizationContext, Object, Object, Object)}\r
-     * @throws DatabaseException\r
-     */\r
-    public static Object cut(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca, Resource cut, Resource sourceContainer, Resource targetContainer) throws DatabaseException {\r
-        if (DEBUG_COPY)\r
-            System.out.println("Attempting to cut component " + NameUtils.getSafeName(g, cut, true));\r
-        try {\r
-            context.set(GraphSynchronizationHints.READ_TRANSACTION, g);\r
-            context.set(GraphSynchronizationHints.WRITE_TRANSACTION, g);\r
-            return ca.cut(context, cut, sourceContainer, targetContainer);\r
-        } catch (SynchronizationException e) {\r
-            ErrorHandler eh = context.get(SynchronizationHints.ERROR_HANDLER);\r
-            eh.error(e.getMessage(), e);\r
-        } finally {\r
-            context.set(GraphSynchronizationHints.READ_TRANSACTION, null);\r
-            context.set(GraphSynchronizationHints.WRITE_TRANSACTION, null);\r
-        }\r
-        return null;\r
-    }\r
-\r
-    /**\r
-     * @param context a synchronization context instance, such as\r
-     *        {@link GraphToDiagramSynchronizer}\r
-     * @param g handle for graph writing\r
-     * @param ca the advisor for the copy operation\r
-     * @param copyOf the resource that is about to be copied\r
-     * @param sourceContainer the container of the resource that will be copied\r
-     * @param targetContainer the to-be container of the copied resource instance\r
-     * @return the copied resource\r
-     * @throws DatabaseException\r
-     */\r
-    public static Resource copy(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca,\r
-            Resource copyOf, Resource sourceContainer, Resource targetContainer) throws DatabaseException {\r
-        Resource resource = null;\r
-        if (DEBUG_COPY)\r
-            System.out.println("Attempting to copy component " + NameUtils.getSafeName(g, copyOf, true));\r
-        try {\r
-            context.set(GraphSynchronizationHints.READ_TRANSACTION, g);\r
-            context.set(GraphSynchronizationHints.WRITE_TRANSACTION, g);\r
-            Evaluation eval = ca.canCopy(context, copyOf, sourceContainer, targetContainer);\r
-            if (DEBUG_COPY)\r
-                System.out.println("  CopyAdvisor(" + ca + ").canCopy evaluation result: " + eval);\r
-            if (CopyAdvisor.SUPPORTED.contains(eval)) {\r
-                Object copy = ca.copy(context, copyOf, sourceContainer, targetContainer);\r
-                if (DEBUG_COPY)\r
-                    System.out.println("  CopyAdvisor(" + ca + ").copy result: " + copy);\r
-                if (copy instanceof Resource) {\r
-                    resource = (Resource) copy;\r
-                } else {\r
-                    throw new UnsupportedOperationException("Cannot copy element " + copyOf);\r
-                }\r
-            }\r
-        } catch (SynchronizationException e) {\r
-            ErrorHandler eh = context.get(SynchronizationHints.ERROR_HANDLER);\r
-            eh.error(e.getMessage(), e);\r
-            // throwing exception allows canceling failed copy!\r
-            throw new DatabaseException(e);\r
-        } finally {\r
-            context.set(GraphSynchronizationHints.READ_TRANSACTION, null);\r
-            context.set(GraphSynchronizationHints.WRITE_TRANSACTION, null);\r
-        }\r
-        return resource;\r
-    }\r
-\r
-    /**\r
-     * @param context a synchronization context instance, such as\r
-     *        {@link GraphToDiagramSynchronizer}\r
-     * @param g handle for graph writing\r
-     * @param ca the advisor for the copy operation\r
-     * @param copyOf the resource that is about to be copied\r
-     * @param sourceContainer the container of the resource that will be copied\r
-     * @param targetContainer the to-be container of the copied resource instance\r
-     * @param map a map for storing the correspondences between original and\r
-     *        copied objects. This is used to output data from the copy process.\r
-     * @return the copied resource\r
-     * @throws DatabaseException\r
-     */\r
-    public static Resource copy(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca,\r
-            Resource copyOf, Resource sourceContainer, Resource targetContainer, Map<Object, Object> map)\r
-            throws DatabaseException {\r
-        Resource resource = null;\r
-        if (DEBUG_COPY)\r
-            System.out.println("Attempting to copy component " + NameUtils.getSafeName(g, copyOf, true));\r
-        try {\r
-            context.set(GraphSynchronizationHints.READ_TRANSACTION, g);\r
-            context.set(GraphSynchronizationHints.WRITE_TRANSACTION, g);\r
-            Evaluation eval = ca.canCopy(context, copyOf, sourceContainer, targetContainer);\r
-            if (DEBUG_COPY)\r
-                System.out.println("  CopyAdvisor(" + ca + ").canCopy evaluation result: " + eval);\r
-            if (CopyAdvisor.SUPPORTED.contains(eval)) {\r
-                Object copy = ca.copy(context, copyOf, sourceContainer, targetContainer, map);\r
-                if (DEBUG_COPY) {\r
-                    System.out.println("  CopyAdvisor(" + ca + ").copy result: " + copy);\r
-                    System.out.println("  CopyAdvisor(" + ca + ").copy result map: " + map);\r
-                }\r
-                if (copy instanceof Resource) {\r
-                    resource = (Resource) copy;\r
-                } else {\r
-                    throw new UnsupportedOperationException("Cannot copy element " + copyOf);\r
-                }\r
-            }\r
-        } catch (SynchronizationException e) {\r
-            ErrorHandler eh = context.get(SynchronizationHints.ERROR_HANDLER);\r
-            eh.error(e.getMessage(), e);\r
-            // throwing exception allows canceling failed copy!\r
-            throw new DatabaseException(e);\r
-        } finally {\r
-            context.set(GraphSynchronizationHints.READ_TRANSACTION, null);\r
-            context.set(GraphSynchronizationHints.WRITE_TRANSACTION, null);\r
-        }\r
-        return resource;\r
-    }\r
-\r
-    /**\r
-     * Creates and returns a copy of the specified source resource based on the\r
-     * standard Layer0 relation hierarchy by recursively including all resources\r
-     * in the copy that the source and is composed of (see\r
-     * {@link Layer0#IsComposedOf}). The routine will always copy at least all\r
-     * L0.InstanceOf statements and tags related to its input resource. If the\r
-     * copied resource has a value attached, it will also be copied.\r
-     * \r
-     * @param graph database write access\r
-     * @param source the resource to start the copy from\r
-     * @param advisor <code>null</code> or a custom advisor to guide whether or\r
-     *        not to copy relations that are not inherited from L0.IsComposedOf.\r
-     *        This advisor cannot be used to say that non-composing relations\r
-     *        should perform recursive copy, only whether to copy the tested\r
-     *        statement of or not.\r
-     * @return the copied resource\r
-     * @throws DatabaseException\r
-     */\r
-    public static Resource copy(WriteGraph graph, Resource source, BinaryFunction<Boolean, ReadGraph, Statement> advisor) throws DatabaseException {\r
-        return copy(graph, source, 0, advisor, new THashMap<Object, Object>());\r
-    }\r
-\r
-    /**\r
-     * See {@link #copy(WriteGraph, Resource, BinaryFunction)}.\r
-     * \r
-     * @param graph\r
-     * @param source\r
-     * @param advisor\r
-     * @param copyMap a map for storing the correspondences between original and\r
-     *        copied objects. This is used to output data from the copy process.\r
-     * @return\r
-     * @throws DatabaseException\r
-     */\r
-    public static Resource copy(WriteGraph graph, Resource source, BinaryFunction<Boolean, ReadGraph, Statement> advisor, Map<Object, Object> copyMap) throws DatabaseException {\r
-        return copy(graph, source, 0, advisor, copyMap);\r
-    }\r
-\r
-    private static Resource copy(WriteGraph graph, Resource source, int level, BinaryFunction<Boolean, ReadGraph, Statement> advisor, Map<Object, Object> copyMap) throws DatabaseException {\r
-        if (DEBUG_COPY)\r
-            System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")");\r
-\r
-        Resource copy = (Resource) copyMap.get(source);\r
-        if (copy != null) {\r
-            if (DEBUG_COPY)\r
-                System.out.println("[" + level + "] already copied: " + NameUtils.getSafeName(graph, source) + " -> " + NameUtils.getSafeName(graph, copy));\r
-            return copy;\r
-        }\r
-\r
-        Layer0 L0 = Layer0.getInstance(graph);\r
-        copy = graph.newResource();\r
-        copyMap.put(source, copy);\r
-        for (Resource type : graph.getObjects(source, L0.InstanceOf))\r
-            graph.claim(copy, L0.InstanceOf, null, type);\r
-\r
-        if (graph.hasValue(source)) {\r
-            Datatype dt = graph.getRelatedValue(source, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));\r
-            Binding b = Bindings.getBinding(dt);\r
-            graph.claimValue(copy, graph.<Object>getValue(source, b), b);\r
-        }\r
-\r
-        // Copy tags\r
-        for (Statement stm : graph.getStatements(source, L0.IsWeaklyRelatedTo)) {\r
-            if (stm.isAsserted(source))\r
-                continue;\r
-            if (graph.isInstanceOf(stm.getPredicate(), L0.Tag)) {\r
-                if (DEBUG_COPY)\r
-                    System.out.println("[" + level + "]\tcopying tag ("\r
-                            + NameUtils.getSafeName(graph, stm.getSubject()) + ", "\r
-                            + NameUtils.getSafeName(graph, stm.getPredicate()) + ")");\r
-                graph.claim(copy, stm.getPredicate(), stm.getPredicate(), copy);\r
-            }\r
-        }\r
-\r
-        for (Statement stm : graph.getStatements(source, L0.IsRelatedTo)) {\r
-            Resource relation = stm.getPredicate();\r
-\r
-            // InstanceOf statements are handled separately, silently ignore them here.\r
-            if (L0.InstanceOf.equals(relation))\r
-                continue;\r
-\r
-            // Don't copy asserted relations!\r
-            if (stm.isAsserted(source)) {\r
-                if (DEBUG_COPY)\r
-                    System.out.println("[" + level + "]\t\tSkipping asserted statement");\r
-                continue;\r
-            }\r
-\r
-            if (DEBUG_COPY)\r
-                System.out.println("[" + level + "]\tprocessing statement (" + NameUtils.toString(graph, stm) + ")");\r
-\r
-            Resource subject = stm.getSubject();\r
-            Resource inverse = graph.getPossibleInverse(relation);\r
-            boolean addInverse = false;\r
-            Resource obj = stm.getObject();\r
-            Resource propType = graph.getPossibleType(obj, L0.Literal);\r
-\r
-            // Â§1 only L0.IsComposedOf and its subrelations can be considered to be copied automatically\r
-\r
-            if (propType != null || graph.isSubrelationOf(relation, L0.IsComposedOf)) {\r
-                if (propType != null && graph.hasStatement(propType, L0.Enumeration, propType)) {\r
-                    if (DEBUG_COPY)\r
-                        System.out.println("[" + level + "]\t\tclaim enumeration statement");\r
-                    graph.claim(copy, relation, null, obj);\r
-                } else {\r
-                    if (inverse != null)\r
-                        addInverse = graph.hasStatement(obj, inverse, subject);\r
-\r
-                    // Copy instantiated properties, not asserted ones.\r
-                    if (DEBUG_COPY)\r
-                        System.out.println("[" + level + "]\t\tcopy whole object");\r
-                    Resource clone = copy(graph, obj, level + 1, advisor, copyMap);\r
-                    graph.claim(copy, relation, clone);\r
-                }\r
-            } else {\r
-                if (advisor != null) {\r
-                    Boolean result = advisor.call(graph, stm);\r
-                    if (Boolean.TRUE.equals(result)) {\r
-                        // Don't clone the object, just add relation to the same object.\r
-                        if (inverse != null)\r
-                            addInverse = graph.hasStatement(obj, inverse, subject);\r
-\r
-                        if (DEBUG_COPY) {\r
-                            System.out.println("[" + level + "]\t\tCopyAdvisorUtil.copy.claim(" + NameUtils.getSafeName(graph, copy) + ", "\r
-                                    + NameUtils.getSafeName(graph, relation) + ", "\r
-                                    + (addInverse ? NameUtils.getSafeName(graph, inverse) : null) + ", "\r
-                                    + NameUtils.getSafeName(graph, obj));\r
-                        }\r
-\r
-                        graph.claim(copy, relation, addInverse ? inverse : null, obj);\r
-                    }\r
-                } else {\r
-                    if (DEBUG_COPY)\r
-                        System.out.println("[" + level + "]\t\tskipping statement");\r
-                }\r
-            }\r
-        }\r
-        return copy;\r
-    }\r
-\r
-    /**\r
-     * Creates and returns a copy of the specified source resource based on the\r
-     * standard Layer0 relation hierarchy by recursively including all resources\r
-     * in the copy that the source and is composed of (see\r
-     * {@link Layer0#IsComposedOf}). A customizable advisor function can be used\r
-     * to guide the copy process. The routine will always copy at least all\r
-     * L0.InstanceOf statements and tags related to its input resource. If the\r
-     * copied resource has a value attached, it will also be copied.\r
-     * \r
-     * @param graph database write access\r
-     * @param source the resource to start the copy from\r
-     * @param advisor <code>null</code> or a custom advisor to guide the copy\r
-     *        process according to the specifications of\r
-     *        {@link StatementEvaluation}. Every copied statement besides\r
-     *        L0.InstanceOf and tags will be evaluated by this advisor.\r
-     * @return the copied resource\r
-     * @throws DatabaseException\r
-     */\r
-    public static Resource copy2(WriteGraph graph, Resource source,\r
-            BinaryFunction<StatementEvaluation, ReadGraph, Statement> advisor) throws DatabaseException {\r
-        return copy2(graph, source, 0, advisor, new THashMap<Object, Object>());\r
-    }\r
-\r
-    /**\r
-     * See {@link #copy2(WriteGraph, Resource, BinaryFunction)}.\r
-     * \r
-     * @param graph\r
-     * @param source\r
-     * @param advisor\r
-     * @param copyMap a map for storing the correspondences between original and\r
-     *        copied objects. This is used to output data from the copy process.\r
-     * @return\r
-     * @throws DatabaseException \r
-     */\r
-    public static Resource copy2(WriteGraph graph, Resource source,\r
-            BinaryFunction<StatementEvaluation, ReadGraph, Statement> advisor, Map<Object, Object> copyMap)\r
-            throws DatabaseException {\r
-        return copy2(graph, source, 0, advisor, copyMap);\r
-    }\r
-\r
-    private static Resource copy2(final WriteGraph graph, final Resource source, final int level,\r
-            BinaryFunction<StatementEvaluation, ReadGraph, Statement> advisor, Map<Object, Object> copyMap)\r
-    throws DatabaseException {\r
-        if (DEBUG_COPY)\r
-            System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")");\r
-\r
-        Resource copy = (Resource) copyMap.get(source);\r
-        if (copy != null) {\r
-            if (DEBUG_COPY)\r
-                System.out.println("[" + level + "] already copied: " + NameUtils.getSafeName(graph, source) + " -> " + NameUtils.getSafeName(graph, copy));\r
-            return copy;\r
-        }\r
-\r
-        Layer0 L0 = Layer0.getInstance(graph);\r
-        copy = graph.newResource();\r
-        copyMap.put(source, copy);\r
-        for (Resource type : graph.getObjects(source, L0.InstanceOf))\r
-            graph.claim(copy, L0.InstanceOf, null, type);\r
-\r
-        if (graph.hasValue(source)) {\r
-            Datatype dt = graph.getRelatedValue(source, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));\r
-            Binding b = Bindings.getBinding(dt);\r
-            graph.claimValue(copy, graph.<Object>getValue(source, b), b);\r
-        }\r
-\r
-        // Copy tags\r
-        for (Statement stm : graph.getStatements(source, L0.IsWeaklyRelatedTo)) {\r
-            if (stm.isAsserted(source))\r
-                continue;\r
-            if (graph.isInstanceOf(stm.getPredicate(), L0.Tag)) {\r
-                if (DEBUG_COPY)\r
-                    System.out.println("[" + level + "]\tcopying tag ("\r
-                            + NameUtils.getSafeName(graph, stm.getSubject()) + ", "\r
-                            + NameUtils.getSafeName(graph, stm.getPredicate()) + ")");\r
-                graph.claim(copy, stm.getPredicate(), stm.getPredicate(), copy);\r
-            }\r
-        }\r
-\r
-        for (Statement stm : graph.getStatements(source, L0.IsRelatedTo)) {\r
-            Resource relation = stm.getPredicate();\r
-\r
-            // InstanceOf statements are handled separately, silently ignore them here.\r
-            if (L0.InstanceOf.equals(relation))\r
-                continue;\r
-\r
-            // Don't copy asserted relations!\r
-            if (stm.isAsserted(source)) {\r
-                if (DEBUG_COPY)\r
-                    System.out.println("[" + level + "]\tskipping asserted statement (" + NameUtils.toString(graph, stm) + ")");\r
-                continue;\r
-            }\r
-\r
-            if (DEBUG_COPY)\r
-                System.out.println("[" + level + "]\tprocessing statement (" + NameUtils.toString(graph, stm) + ")");\r
-\r
-            Resource subject = stm.getSubject();\r
-            Resource inverse = graph.getPossibleInverse(relation);\r
-            Resource obj = stm.getObject();\r
-            Resource propType = graph.getPossibleType(obj, L0.Literal);\r
-            boolean addInverse = false;\r
-            boolean forceIncludeAndFollow = false;\r
-\r
-            switch (evaluate(graph, stm, advisor)) {\r
-                case SKIP:\r
-                    if (DEBUG_COPY)\r
-                        System.out.println("[" + level + "]\t\tskipping statement");\r
-                    break;\r
-\r
-                case INCLUDE:\r
-                {\r
-                    // Don't clone the object, just add relation to the same object.\r
-                    if (inverse != null)\r
-                        addInverse = graph.hasStatement(obj, inverse, subject);\r
-\r
-                    if (DEBUG_COPY) {\r
-                        System.out.println("[" + level + "]\t\tCopyAdvisorUtil.copy2.claim(" + NameUtils.getSafeName(graph, copy) + ", "\r
-                                + NameUtils.getSafeName(graph, relation) + ", "\r
-                                + (addInverse ? NameUtils.getSafeName(graph, inverse) : null) + ", "\r
-                                + NameUtils.getSafeName(graph, obj));\r
-                    }\r
-\r
-                    graph.claim(copy, relation, addInverse ? inverse : null, obj);\r
-                    break;\r
-                }\r
-\r
-                case INCLUDE_AND_FOLLOW:\r
-                    // Force follow-through in the default copy logic\r
-                    forceIncludeAndFollow = true;\r
-                    // NOTE: intentional fall-through here\r
-\r
-                case USE_DEFAULT:\r
-                {\r
-                    if (forceIncludeAndFollow || propType != null || graph.isSubrelationOf(relation, L0.IsComposedOf)) {\r
-                        if (propType != null && graph.hasStatement(propType, L0.Enumeration, propType)) {\r
-                            // This logic is applied only for enumeration property\r
-                            // statements that should not have an inverse in any case.\r
-                            if (DEBUG_COPY) {\r
-                                System.out.println("[" + level + "]\t\tclaim enumeration statement("\r
-                                        + NameUtils.getSafeName(graph, copy) + ", "\r
-                                        + NameUtils.getSafeName(graph, relation)+ ", null, "\r
-                                        + NameUtils.getSafeName(graph, obj));\r
-                            }\r
-                            graph.claim(copy, relation, null, obj);\r
-                        } else {\r
-                            // This logic is applied for other properties besides enumerations\r
-                            if (inverse != null)\r
-                                addInverse = graph.hasStatement(obj, inverse, subject);\r
-\r
-                            // Copy instantiated properties, not asserted ones.\r
-                            if (DEBUG_COPY)\r
-                                System.out.println("[" + level + "]\t\tcopy whole object");\r
-\r
-                            Resource clone = copy2(graph, obj, level + 1, advisor, copyMap);\r
-                            graph.claim(copy, relation, inverse, clone);\r
-                        }\r
-                    } else {\r
-                        if (DEBUG_COPY)\r
-                            System.out.println("[" + level + "]\t\tskipping statement");\r
-                    }\r
-                }\r
-            }\r
-        }\r
-        return copy;\r
-    }\r
-\r
-    /**\r
-     * Creates and returns a copy of the specified source resource based on the\r
-     * standard Layer0 relation hierarchy by recursively including all resources\r
-     * in the copy that the source and is composed of (see\r
-     * {@link Layer0#IsComposedOf}). A customizable advisor function can be used\r
-     * to guide the copy process. The routine will always copy at least all\r
-     * L0.InstanceOf statements and tags related to its input resource. If the\r
-     * copied resource has a value attached, it will also be copied.\r
-     * \r
-     * Works exactly like {@link #copy2(WriteGraph, Resource, BinaryFunction)}\r
-     * but uses the <code>model</code> argument to make sure that the copy\r
-     * process does not propagate outside of the model. Any references that go\r
-     * to resources with URIs that are not in the model's namespace are copied\r
-     * as unidirectional.\r
-     * \r
-     * @param graph database write access\r
-     * @param source the resource to start the copy from\r
-     * @param model the model containing the source object, used to keep the\r
-     *        copy process model-local\r
-     * @param advisor <code>null</code> or a custom advisor to guide the copy\r
-     *        process according to the specifications of\r
-     *        {@link StatementEvaluation}. Every copied statement besides\r
-     *        L0.InstanceOf and tags will be evaluated by this advisor.\r
-     * @return the copied resource\r
-     * @throws DatabaseException\r
-     */\r
-    public static Resource copy3(WriteGraph graph, Resource source, Resource model,\r
-            BinaryFunction<StatementEvaluation, ReadGraph, Statement> advisor) throws DatabaseException {\r
-        String modelURI = graph.getURI(model);\r
-        return copy3(graph, modelURI, source, 0, advisor, new THashMap<Object, Object>());\r
-    }\r
-\r
-    /**\r
-     * See {@link #copy3(WriteGraph, Resource, Resource, BinaryFunction)}.\r
-     * \r
-     * @param graph\r
-     * @param source\r
-     * @param model\r
-     * @param advisor\r
-     * @param copyMap a map for storing the correspondences between original and\r
-     *        copied objects. This is used to output data from the copy process.\r
-     * @return\r
-     * @throws DatabaseException\r
-     */\r
-    public static Resource copy3(WriteGraph graph, Resource source, Resource model,\r
-            BinaryFunction<StatementEvaluation, ReadGraph, Statement> advisor, Map<Object, Object> copyMap) throws DatabaseException {\r
-        String modelURI = graph.getURI(model);\r
-        return copy3(graph, modelURI, source, 0, advisor, copyMap);\r
-    }\r
-\r
-    private static Resource copy3(WriteGraph graph, String modelURI, Resource source, int level,\r
-            BinaryFunction<StatementEvaluation, ReadGraph, Statement> advisor, Map<Object, Object> copyMap)\r
-    throws DatabaseException {\r
-        if (DEBUG_COPY)\r
-            System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")");\r
-\r
-        Resource copy = (Resource) copyMap.get(source);\r
-        if (copy != null) {\r
-            if (DEBUG_COPY)\r
-                System.out.println("[" + level + "] already copied: " + NameUtils.getSafeName(graph, source) + " -> " + NameUtils.getSafeName(graph, copy));\r
-            return copy;\r
-        }\r
-\r
-        Layer0 L0 = Layer0.getInstance(graph);\r
-        copy = graph.newResource();\r
-        copyMap.put(source, copy);\r
-        for (Resource type : graph.getObjects(source, L0.InstanceOf))\r
-            graph.claim(copy, L0.InstanceOf, null, type);\r
-\r
-        if (graph.hasValue(source)) {\r
-            Datatype dt = graph.getRelatedValue(source, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));\r
-            Binding b = Bindings.getBinding(dt);\r
-            graph.claimValue(copy, graph.<Object>getValue(source, b), b);\r
-        }\r
-\r
-        // Copy tags\r
-        for (Statement stm : graph.getStatements(source, L0.IsWeaklyRelatedTo)) {\r
-            if (stm.isAsserted(source))\r
-                continue;\r
-            if (graph.isInstanceOf(stm.getPredicate(), L0.Tag)) {\r
-                if (DEBUG_COPY)\r
-                    System.out.println("[" + level + "]\tcopying tag ("\r
-                            + NameUtils.getSafeName(graph, stm.getSubject()) + ", "\r
-                            + NameUtils.getSafeName(graph, stm.getPredicate()) + ")");\r
-                graph.claim(copy, stm.getPredicate(), stm.getPredicate(), copy);\r
-            }\r
-        }\r
-\r
-        for (Statement stm : graph.getStatements(source, L0.IsRelatedTo)) {\r
-            Resource relation = stm.getPredicate();\r
-\r
-            // InstanceOf statements are handled separately, silently ignore them here.\r
-            if (L0.InstanceOf.equals(relation))\r
-                continue;\r
-\r
-            // Don't copy asserted relations!\r
-            if (stm.isAsserted(source)) {\r
-                if (DEBUG_COPY)\r
-                    System.out.println("[" + level + "]\tskipping asserted statement (" + NameUtils.toString(graph, stm) + ")");\r
-                continue;\r
-            }\r
-\r
-            if (DEBUG_COPY)\r
-                System.out.println("[" + level + "]\tprocessing statement (" + NameUtils.toString(graph, stm) + ")");\r
-\r
-            Resource subject = stm.getSubject();\r
-            Resource inverse = graph.getPossibleInverse(relation);\r
-            Resource obj = stm.getObject();\r
-            Resource propType = graph.getPossibleType(obj, L0.Literal);\r
-            boolean addInverse = false;\r
-            boolean forceIncludeAndFollow = false;\r
-\r
-            switch (evaluate(graph, stm, advisor)) {\r
-                case SKIP:\r
-                    if (DEBUG_COPY)\r
-                        System.out.println("[" + level + "]\t\tskipping statement");\r
-                    break;\r
-\r
-                case INCLUDE:\r
-                {\r
-                    // Don't clone the object, just add relation to the same object.\r
-                    if (inverse != null)\r
-                        addInverse = graph.hasStatement(obj, inverse, subject);\r
-\r
-                    if (DEBUG_COPY) {\r
-                        System.out.println("[" + level + "]\t\tCopyAdvisorUtil.copy2.claim(" + NameUtils.getSafeName(graph, copy) + ", "\r
-                                + NameUtils.getSafeName(graph, relation) + ", "\r
-                                + (addInverse ? NameUtils.getSafeName(graph, inverse) : null) + ", "\r
-                                + NameUtils.getSafeName(graph, obj));\r
-                    }\r
-\r
-                    graph.claim(copy, relation, addInverse ? inverse : null, obj);\r
-                    break;\r
-                }\r
-\r
-                case INCLUDE_AND_FOLLOW:\r
-                    // Force follow-through in the default copy logic\r
-                    forceIncludeAndFollow = true;\r
-                    // NOTE: intentional fall-through here\r
-\r
-                case USE_DEFAULT:\r
-                {\r
-                    if (forceIncludeAndFollow || propType != null || graph.isSubrelationOf(relation, L0.IsComposedOf)) {\r
-                        if (propType != null && graph.hasStatement(propType, L0.Enumeration, propType)) {\r
-                            // This logic is applied only for enumeration property\r
-                            // statements that should not have an inverse in any case.\r
-                            if (DEBUG_COPY) {\r
-                                System.out.println("[" + level + "]\t\tclaim enumeration statement("\r
-                                        + NameUtils.getSafeName(graph, copy) + ", "\r
-                                        + NameUtils.getSafeName(graph, relation)+ ", null, "\r
-                                        + NameUtils.getSafeName(graph, obj));\r
-                            }\r
-                            graph.claim(copy, relation, null, obj);\r
-                        } else {\r
-                            // This logic is applied for other properties besides enumerations\r
-                            if (inverse != null)\r
-                                addInverse = graph.hasStatement(obj, inverse, subject);\r
-\r
-                            String objectURI = graph.getPossibleURI(obj);\r
-\r
-                            if(objectURI != null && !objectURI.startsWith(modelURI)) {\r
-\r
-                                if (DEBUG_COPY)\r
-                                    System.out.println("[" + level + "]\t\tclaim ontological reference");\r
-\r
-                                graph.claim(copy, relation, null, obj);\r
-\r
-                            } else {\r
-\r
-                                // Copy instantiated properties, not asserted ones.\r
-                                if (DEBUG_COPY)\r
-                                    System.out.println("[" + level + "]\t\tcopy whole object");\r
-\r
-                                Resource clone = copy3(graph, modelURI, obj, level + 1, advisor, copyMap);\r
-                                graph.claim(copy, relation, inverse, clone);\r
-\r
-                            }\r
-\r
-                        }\r
-                    } else {\r
-                        if (DEBUG_COPY)\r
-                            System.out.println("[" + level + "]\t\tskipping statement");\r
-                    }\r
-                }\r
-            }\r
-        }\r
-        return copy;\r
-    }\r
-\r
-    protected static StatementEvaluation evaluate(ReadGraph graph, Statement stm, BinaryFunction<StatementEvaluation, ReadGraph, Statement> tester) {\r
-        if (tester == null)\r
-            return StatementEvaluation.USE_DEFAULT;\r
-        return tester.call(graph, stm);\r
-    }\r
-\r
-    /**\r
-     * Equal to\r
-     * <code>copy4(graph, source, graph.adapt(source, CopyHandler.class)))</code>\r
-     * .\r
-     * \r
-     * @param graph database write access\r
-     * @param source the resource to start the copy from\r
-     * @return the copied resource\r
-     * @throws DatabaseException\r
-     */\r
-    public static Resource copy4(WriteGraph graph, Resource source) throws DatabaseException {\r
-        CopyHandler handler = graph.adapt(source, CopyHandler.class);\r
-        return copy4(graph, source, handler);\r
-    }\r
-\r
-    /**\r
-     * Creates and returns a copy of the specified source resource based on\r
-     * transferable graph export and import. The TG representation shall be\r
-     * generated by the specified {@link CopyHandler} into a\r
-     * {@link SimanticsClipboard} instance from where it is read back as\r
-     * {@link TransferableGraph1} and imported into the database through\r
-     * {@link TransferableGraphs#importGraph1(WriteGraph, TransferableGraph1, org.simantics.graph.db.IImportAdvisor)}.\r
-     * \r
-     * @param graph database write access\r
-     * @param source the resource to start the copy from\r
-     * @param copyHandler the handler to use for generating the transferable\r
-     *        graph representation from the copied resource\r
-     * @return the copied resource\r
-     * @throws DatabaseException\r
-     */\r
-    public static Resource copy4(WriteGraph graph, Resource source, CopyHandler copyHandler) throws DatabaseException {\r
-        SimanticsClipboardImpl builder = new SimanticsClipboardImpl();\r
-        copyHandler.copyToClipboard(graph, builder);\r
-\r
-        for(Set<Representation> object : builder.getContents()) {\r
-            TransferableGraph1 tg = ClipboardUtils.accept(graph, object, SimanticsKeys.KEY_TRANSFERABLE_GRAPH);\r
-            if(tg != null) {\r
-                FixedRootImportAdvisor advisor = new FixedRootImportAdvisor();\r
-                TransferableGraphs.importGraph1(graph, tg, advisor);\r
-                return advisor.getRoot();\r
-            }\r
-        }\r
-\r
-        String uri = graph.getPossibleURI(source);\r
-        throw new DatabaseException("Failed to copy resource " + NameUtils.getSafeName(graph, source, true)\r
-                + (uri != null ? " with URI " + uri : ""));\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * 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 java.util.function.BiFunction;
+
+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;
+
+/**
+ * This class contains utility methods for the basic cut/copy operations
+ * performed related to cut-copy-pasting diagram/configuration component and
+ * composites.
+ * 
+ * <p>
+ * 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.
+ * 
+ * <p>
+ * 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.
+ * 
+ * <p>
+ * <b>IMPORTANT:</b> 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<Object, Object> 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 <code>null</code> 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<ReadGraph, Statement, Boolean> advisor) throws DatabaseException {
+        return copy(graph, source, 0, advisor, new THashMap<Object, Object>());
+    }
+
+    /**
+     * 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<ReadGraph, Statement, Boolean> advisor, Map<Object, Object> copyMap) throws DatabaseException {
+        return copy(graph, source, 0, advisor, copyMap);
+    }
+
+    private static Resource copy(WriteGraph graph, Resource source, int level, BiFunction<ReadGraph, Statement, Boolean> advisor, Map<Object, Object> 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.<Object>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 <code>null</code> 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<ReadGraph, Statement, StatementEvaluation> advisor) throws DatabaseException {
+        return copy2(graph, source, 0, advisor, new THashMap<Object, Object>());
+    }
+
+    /**
+     * 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<ReadGraph, Statement, StatementEvaluation> advisor, Map<Object, Object> copyMap)
+            throws DatabaseException {
+        return copy2(graph, source, 0, advisor, copyMap);
+    }
+
+    private static Resource copy2(final WriteGraph graph, final Resource source, final int level,
+            BiFunction<ReadGraph, Statement, StatementEvaluation> advisor, Map<Object, Object> 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.<Object>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 <code>model</code> 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 <code>null</code> 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<ReadGraph, Statement, StatementEvaluation> advisor) throws DatabaseException {
+        String modelURI = graph.getURI(model);
+        return copy3(graph, modelURI, source, 0, advisor, new THashMap<Object, Object>());
+    }
+
+    /**
+     * 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<ReadGraph, Statement, StatementEvaluation> advisor, Map<Object, Object> 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<ReadGraph, Statement, StatementEvaluation> advisor, Map<Object, Object> 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.<Object>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<ReadGraph, Statement, StatementEvaluation> tester) {
+        if (tester == null)
+            return StatementEvaluation.USE_DEFAULT;
+        return tester.apply(graph, stm);
+    }
+
+    /**
+     * Equal to
+     * <code>copy4(graph, source, graph.adapt(source, CopyHandler.class)))</code>
+     * .
+     * 
+     * @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<Representation> 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 : ""));
+    }
+
+}