-/*******************************************************************************\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 java.util.Map;
+import java.util.Set;
+import java.util.function.BiFunction;
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Statement;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.adapter.CopyHandler;
+import org.simantics.db.layer0.adapter.impl.FixedRootImportAdvisor;
+import org.simantics.db.layer0.util.ClipboardUtils;
+import org.simantics.db.layer0.util.SimanticsClipboard;
+import org.simantics.db.layer0.util.SimanticsClipboard.Representation;
+import org.simantics.db.layer0.util.SimanticsClipboardImpl;
+import org.simantics.db.layer0.util.SimanticsKeys;
+import org.simantics.diagram.adapter.GraphToDiagramSynchronizer;
+import org.simantics.diagram.internal.DebugPolicy;
+import org.simantics.diagram.synchronization.CopyAdvisor;
+import org.simantics.diagram.synchronization.CopyAdvisor.Evaluation;
+import org.simantics.diagram.synchronization.ErrorHandler;
+import org.simantics.diagram.synchronization.IModifiableSynchronizationContext;
+import org.simantics.diagram.synchronization.StatementEvaluation;
+import org.simantics.diagram.synchronization.SynchronizationException;
+import org.simantics.diagram.synchronization.SynchronizationHints;
+import org.simantics.graph.db.TransferableGraphs;
+import org.simantics.graph.representation.TransferableGraph1;
+import org.simantics.layer0.Layer0;
+import org.simantics.utils.datastructures.BinaryFunction;
+
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.set.hash.THashSet;
+
+/**
+ * This class contains utility methods for the basic cut/copy operations
+ * performed related to cut-copy-pasting diagram/configuration component and
+ * composites.
+ *
+ * <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;
+
+ private static class Statement4 {
+ public final Statement stm;
+ public final Resource inverse;
+ public Statement4(Statement stm, Resource inverse) {
+ this.stm = stm;
+ this.inverse = inverse;
+ }
+ }
+
+ /**
+ * @param context a synchronization context instance, such as
+ * {@link GraphToDiagramSynchronizer}
+ * @param g handle for graph writing
+ * @param ca the advisor for the copy operation
+ * @param cut the resource that is about to be cut
+ * @param sourceContainer the container from which the cut argument is about
+ * to be removed
+ * @param targetContainer the container into which the cut argument is about
+ * to be moved
+ * @return the result of
+ * {@link CopyAdvisor#cut(org.simantics.diagram.synchronization.ISynchronizationContext, Object, Object, Object)}
+ * @throws DatabaseException
+ */
+ public static Object cut(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca, Resource cut, Resource sourceContainer, Resource targetContainer) throws DatabaseException {
+ if (DEBUG_COPY)
+ System.out.println("Attempting to cut component " + NameUtils.getSafeName(g, cut, true));
+ try {
+ context.set(GraphSynchronizationHints.READ_TRANSACTION, g);
+ context.set(GraphSynchronizationHints.WRITE_TRANSACTION, g);
+ return ca.cut(context, cut, sourceContainer, targetContainer);
+ } catch (SynchronizationException e) {
+ ErrorHandler eh = context.get(SynchronizationHints.ERROR_HANDLER);
+ eh.error(e.getMessage(), e);
+ } finally {
+ context.set(GraphSynchronizationHints.READ_TRANSACTION, null);
+ context.set(GraphSynchronizationHints.WRITE_TRANSACTION, null);
+ }
+ return null;
+ }
+
+ /**
+ * @param context a synchronization context instance, such as
+ * {@link GraphToDiagramSynchronizer}
+ * @param g handle for graph writing
+ * @param ca the advisor for the copy operation
+ * @param copyOf the resource that is about to be copied
+ * @param sourceContainer the container of the resource that will be copied
+ * @param targetContainer the to-be container of the copied resource instance
+ * @return the copied resource
+ * @throws DatabaseException
+ */
+ public static Resource copy(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca,
+ Resource copyOf, Resource sourceContainer, Resource targetContainer) throws DatabaseException {
+ Resource resource = null;
+ if (DEBUG_COPY)
+ System.out.println("Attempting to copy component " + NameUtils.getSafeName(g, copyOf, true));
+ try {
+ context.set(GraphSynchronizationHints.READ_TRANSACTION, g);
+ context.set(GraphSynchronizationHints.WRITE_TRANSACTION, g);
+ Evaluation eval = ca.canCopy(context, copyOf, sourceContainer, targetContainer);
+ if (DEBUG_COPY)
+ System.out.println(" CopyAdvisor(" + ca + ").canCopy evaluation result: " + eval);
+ if (CopyAdvisor.SUPPORTED.contains(eval)) {
+ Object copy = ca.copy(context, copyOf, sourceContainer, targetContainer);
+ if (DEBUG_COPY)
+ System.out.println(" CopyAdvisor(" + ca + ").copy result: " + copy);
+ if (copy instanceof Resource) {
+ resource = (Resource) copy;
+ } else {
+ throw new UnsupportedOperationException("Cannot copy element " + copyOf);
+ }
+ }
+ } catch (SynchronizationException e) {
+ ErrorHandler eh = context.get(SynchronizationHints.ERROR_HANDLER);
+ eh.error(e.getMessage(), e);
+ // throwing exception allows canceling failed copy!
+ throw new DatabaseException(e);
+ } finally {
+ context.set(GraphSynchronizationHints.READ_TRANSACTION, null);
+ context.set(GraphSynchronizationHints.WRITE_TRANSACTION, null);
+ }
+ return resource;
+ }
+
+ /**
+ * @param context a synchronization context instance, such as
+ * {@link GraphToDiagramSynchronizer}
+ * @param g handle for graph writing
+ * @param ca the advisor for the copy operation
+ * @param copyOf the resource that is about to be copied
+ * @param sourceContainer the container of the resource that will be copied
+ * @param targetContainer the to-be container of the copied resource instance
+ * @param map a map for storing the correspondences between original and
+ * copied objects. This is used to output data from the copy process.
+ * @return the copied resource
+ * @throws DatabaseException
+ */
+ public static Resource copy(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca,
+ Resource copyOf, Resource sourceContainer, Resource targetContainer, Map<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, advisor, new THashMap<>());
+ }
+
+ /**
+ * See {@link #copy2(WriteGraph, Resource, BinaryFunction)}.
+ *
+ * @param graph
+ * @param source
+ * @param advisor
+ * @param copyMap a map for storing the correspondences between original and
+ * copied objects. This is used to output data from the copy process.
+ * @return
+ * @throws DatabaseException
+ */
+ public static Resource copy2(WriteGraph graph, Resource source,
+ BiFunction<ReadGraph, Statement, StatementEvaluation> advisor,
+ Map<Object, Object> copyMap)
+ throws DatabaseException
+ {
+ Set<Statement4> pendingStatements = new THashSet<>();
+ Resource result = copy2(graph, source, 0, advisor, copyMap, pendingStatements);
+ postProcessStatements(graph, copyMap, pendingStatements);
+ return result;
+ }
+
+ /**
+ * Post-process pending statement
+ *
+ * Rule: If both the subject and object of a pending source statement have
+ * been copied, then the pending statement should also be copied.
+ */
+ private static void postProcessStatements(
+ WriteGraph graph,
+ Map<Object, Object> copyMap,
+ Set<Statement4> pendingStatements)
+ throws DatabaseException
+ {
+ if (pendingStatements.isEmpty())
+ return;
+
+ if (DEBUG_COPY)
+ System.out.println("post processing " + pendingStatements.size() + " pending statements");
+ for (Statement4 srcStm : pendingStatements) {
+ // At this point, it is certain that srcStm subject has been copied
+ // but test it anyway.
+ Resource subjectCopy = (Resource) copyMap.get(srcStm.stm.getSubject());
+ Resource objectCopy = (Resource) copyMap.get(srcStm.stm.getObject());
+ if (subjectCopy == null || objectCopy == null) {
+ if (DEBUG_COPY)
+ System.out.println("skipping pending statement: " + NameUtils.toString(graph, srcStm.stm));
+ continue;
+ }
+ if (DEBUG_COPY)
+ System.out.println("copying pending statement: " + NameUtils.toString(graph, srcStm.stm));
+ graph.claim(subjectCopy, srcStm.stm.getPredicate(), srcStm.inverse, objectCopy);
+ }
+ }
+
+ private static Resource copy2(final WriteGraph graph, final Resource source, final int level,
+ BiFunction<ReadGraph, Statement, StatementEvaluation> advisor, Map<Object, Object> copyMap,
+ Set<Statement4> pendingSourceStatements)
+ throws DatabaseException {
+ if (DEBUG_COPY)
+ System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")");
+
+ Resource copy = (Resource) copyMap.get(source);
+ if (copy != null) {
+ if (DEBUG_COPY)
+ System.out.println("[" + level + "] already copied: " + NameUtils.getSafeName(graph, source) + " -> " + NameUtils.getSafeName(graph, copy));
+ return copy;
+ }
+
+ Layer0 L0 = Layer0.getInstance(graph);
+ copy = graph.newResource();
+ copyMap.put(source, copy);
+ for (Resource type : graph.getObjects(source, L0.InstanceOf))
+ graph.claim(copy, L0.InstanceOf, null, type);
+
+ if (graph.hasValue(source)) {
+ Datatype dt = graph.getRelatedValue(source, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));
+ Binding b = Bindings.getBinding(dt);
+ graph.claimValue(copy, graph.<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, pendingSourceStatements);
+ graph.claim(copy, relation, inverse, clone);
+ }
+ } else {
+ if (graph.isSubrelationOf(relation, L0.IsRelatedTo)) {
+ if (DEBUG_COPY)
+ System.out.println("[" + level + "]\t\tmarking statement as pending for post-processing");
+ pendingSourceStatements.add(new Statement4(stm, inverse));
+ } else {
+ if (DEBUG_COPY)
+ System.out.println("[" + level + "]\t\tskipping weak statement");
+ }
+ }
+ }
+ }
+ }
+ return copy;
+ }
+
+ /**
+ * Creates and returns a copy of the specified source resource based on the
+ * standard Layer0 relation hierarchy by recursively including all resources
+ * in the copy that the source and is composed of (see
+ * {@link Layer0#IsComposedOf}). A customizable advisor function can be used
+ * to guide the copy process. The routine will always copy at least all
+ * L0.InstanceOf statements and tags related to its input resource. If the
+ * copied resource has a value attached, it will also be copied.
+ *
+ * Works exactly like {@link #copy2(WriteGraph, Resource, BinaryFunction)}
+ * but uses the <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, new NullProgressMonitor());
+
+ 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 : ""));
+ }
+
+}