--- /dev/null
+/*******************************************************************************\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