]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/CopyAdvisorUtil.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / synchronization / graph / CopyAdvisorUtil.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.diagram.synchronization.graph;\r
13 \r
14 import gnu.trove.map.hash.THashMap;\r
15 \r
16 import java.util.Map;\r
17 import java.util.Set;\r
18 \r
19 import org.simantics.databoard.Bindings;\r
20 import org.simantics.databoard.binding.Binding;\r
21 import org.simantics.databoard.type.Datatype;\r
22 import org.simantics.db.ReadGraph;\r
23 import org.simantics.db.Resource;\r
24 import org.simantics.db.Statement;\r
25 import org.simantics.db.WriteGraph;\r
26 import org.simantics.db.common.utils.NameUtils;\r
27 import org.simantics.db.exception.DatabaseException;\r
28 import org.simantics.db.layer0.adapter.CopyHandler;\r
29 import org.simantics.db.layer0.adapter.impl.FixedRootImportAdvisor;\r
30 import org.simantics.db.layer0.util.ClipboardUtils;\r
31 import org.simantics.db.layer0.util.SimanticsClipboard;\r
32 import org.simantics.db.layer0.util.SimanticsClipboard.Representation;\r
33 import org.simantics.db.layer0.util.SimanticsClipboardImpl;\r
34 import org.simantics.db.layer0.util.SimanticsKeys;\r
35 import org.simantics.diagram.adapter.GraphToDiagramSynchronizer;\r
36 import org.simantics.diagram.internal.DebugPolicy;\r
37 import org.simantics.diagram.synchronization.CopyAdvisor;\r
38 import org.simantics.diagram.synchronization.CopyAdvisor.Evaluation;\r
39 import org.simantics.diagram.synchronization.ErrorHandler;\r
40 import org.simantics.diagram.synchronization.IModifiableSynchronizationContext;\r
41 import org.simantics.diagram.synchronization.StatementEvaluation;\r
42 import org.simantics.diagram.synchronization.SynchronizationException;\r
43 import org.simantics.diagram.synchronization.SynchronizationHints;\r
44 import org.simantics.graph.db.TransferableGraphs;\r
45 import org.simantics.graph.representation.TransferableGraph1;\r
46 import org.simantics.layer0.Layer0;\r
47 import org.simantics.utils.datastructures.BinaryFunction;\r
48 \r
49 /**\r
50  * This class contains utility methods for the basic cut/copy operations\r
51  * performed related to cut-copy-pasting diagram/configuration component and\r
52  * composites.\r
53  * \r
54  * <p>\r
55  * Methods\r
56  * {@link #cut(IModifiableSynchronizationContext, WriteGraph, CopyAdvisor, Resource, Resource, Resource)}\r
57  * and\r
58  * {@link #copy(IModifiableSynchronizationContext, WriteGraph, CopyAdvisor, Resource, Resource, Resource)}\r
59  * are available for making it easier to properly invoke\r
60  * {@link CopyAdvisor#cut(org.simantics.diagram.synchronization.ISynchronizationContext, Object, Object, Object)}\r
61  * and\r
62  * {@link CopyAdvisor#copy(org.simantics.diagram.synchronization.ISynchronizationContext, Object, Object, Object)}\r
63  * operations in a diagram/graph transaction context.\r
64  * \r
65  * <p>\r
66  * Methods {@link #copy(WriteGraph, Resource, BinaryFunction)},\r
67  * {@link #copy2(WriteGraph, Resource, BinaryFunction)},\r
68  * {@link #copy3(WriteGraph, Resource, Resource, BinaryFunction)},\r
69  * {@link #copy4(WriteGraph, Resource)} and\r
70  * {@link #copy4(WriteGraph, Resource, CopyHandler)} offer differently\r
71  * functioning versions of copying a single resource in the graph that are\r
72  * mainly tuned for copying diagram elements and configuration components.\r
73  * \r
74  * <p>\r
75  * <b>IMPORTANT:</b> Note that copy, copy2 and copy3 cannot handle copying of ordered sets\r
76  * properly.\r
77  * \r
78  * @author Tuukka Lehtonen\r
79  */\r
80 public class CopyAdvisorUtil {\r
81 \r
82     public static final boolean DEBUG_COPY = DebugPolicy.DEBUG_COPY_PASTE;\r
83 \r
84     /**\r
85      * @param context a synchronization context instance, such as\r
86      *        {@link GraphToDiagramSynchronizer}\r
87      * @param g handle for graph writing\r
88      * @param ca the advisor for the copy operation\r
89      * @param cut the resource that is about to be cut\r
90      * @param sourceContainer the container from which the cut argument is about\r
91      *        to be removed\r
92      * @param targetContainer the container into which the cut argument is about\r
93      *        to be moved\r
94      * @return the result of\r
95      *         {@link CopyAdvisor#cut(org.simantics.diagram.synchronization.ISynchronizationContext, Object, Object, Object)}\r
96      * @throws DatabaseException\r
97      */\r
98     public static Object cut(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca, Resource cut, Resource sourceContainer, Resource targetContainer) throws DatabaseException {\r
99         if (DEBUG_COPY)\r
100             System.out.println("Attempting to cut component " + NameUtils.getSafeName(g, cut, true));\r
101         try {\r
102             context.set(GraphSynchronizationHints.READ_TRANSACTION, g);\r
103             context.set(GraphSynchronizationHints.WRITE_TRANSACTION, g);\r
104             return ca.cut(context, cut, sourceContainer, targetContainer);\r
105         } catch (SynchronizationException e) {\r
106             ErrorHandler eh = context.get(SynchronizationHints.ERROR_HANDLER);\r
107             eh.error(e.getMessage(), e);\r
108         } finally {\r
109             context.set(GraphSynchronizationHints.READ_TRANSACTION, null);\r
110             context.set(GraphSynchronizationHints.WRITE_TRANSACTION, null);\r
111         }\r
112         return null;\r
113     }\r
114 \r
115     /**\r
116      * @param context a synchronization context instance, such as\r
117      *        {@link GraphToDiagramSynchronizer}\r
118      * @param g handle for graph writing\r
119      * @param ca the advisor for the copy operation\r
120      * @param copyOf the resource that is about to be copied\r
121      * @param sourceContainer the container of the resource that will be copied\r
122      * @param targetContainer the to-be container of the copied resource instance\r
123      * @return the copied resource\r
124      * @throws DatabaseException\r
125      */\r
126     public static Resource copy(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca,\r
127             Resource copyOf, Resource sourceContainer, Resource targetContainer) throws DatabaseException {\r
128         Resource resource = null;\r
129         if (DEBUG_COPY)\r
130             System.out.println("Attempting to copy component " + NameUtils.getSafeName(g, copyOf, true));\r
131         try {\r
132             context.set(GraphSynchronizationHints.READ_TRANSACTION, g);\r
133             context.set(GraphSynchronizationHints.WRITE_TRANSACTION, g);\r
134             Evaluation eval = ca.canCopy(context, copyOf, sourceContainer, targetContainer);\r
135             if (DEBUG_COPY)\r
136                 System.out.println("  CopyAdvisor(" + ca + ").canCopy evaluation result: " + eval);\r
137             if (CopyAdvisor.SUPPORTED.contains(eval)) {\r
138                 Object copy = ca.copy(context, copyOf, sourceContainer, targetContainer);\r
139                 if (DEBUG_COPY)\r
140                     System.out.println("  CopyAdvisor(" + ca + ").copy result: " + copy);\r
141                 if (copy instanceof Resource) {\r
142                     resource = (Resource) copy;\r
143                 } else {\r
144                     throw new UnsupportedOperationException("Cannot copy element " + copyOf);\r
145                 }\r
146             }\r
147         } catch (SynchronizationException e) {\r
148             ErrorHandler eh = context.get(SynchronizationHints.ERROR_HANDLER);\r
149             eh.error(e.getMessage(), e);\r
150             // throwing exception allows canceling failed copy!\r
151             throw new DatabaseException(e);\r
152         } finally {\r
153             context.set(GraphSynchronizationHints.READ_TRANSACTION, null);\r
154             context.set(GraphSynchronizationHints.WRITE_TRANSACTION, null);\r
155         }\r
156         return resource;\r
157     }\r
158 \r
159     /**\r
160      * @param context a synchronization context instance, such as\r
161      *        {@link GraphToDiagramSynchronizer}\r
162      * @param g handle for graph writing\r
163      * @param ca the advisor for the copy operation\r
164      * @param copyOf the resource that is about to be copied\r
165      * @param sourceContainer the container of the resource that will be copied\r
166      * @param targetContainer the to-be container of the copied resource instance\r
167      * @param map a map for storing the correspondences between original and\r
168      *        copied objects. This is used to output data from the copy process.\r
169      * @return the copied resource\r
170      * @throws DatabaseException\r
171      */\r
172     public static Resource copy(IModifiableSynchronizationContext context, WriteGraph g, CopyAdvisor ca,\r
173             Resource copyOf, Resource sourceContainer, Resource targetContainer, Map<Object, Object> map)\r
174             throws DatabaseException {\r
175         Resource resource = null;\r
176         if (DEBUG_COPY)\r
177             System.out.println("Attempting to copy component " + NameUtils.getSafeName(g, copyOf, true));\r
178         try {\r
179             context.set(GraphSynchronizationHints.READ_TRANSACTION, g);\r
180             context.set(GraphSynchronizationHints.WRITE_TRANSACTION, g);\r
181             Evaluation eval = ca.canCopy(context, copyOf, sourceContainer, targetContainer);\r
182             if (DEBUG_COPY)\r
183                 System.out.println("  CopyAdvisor(" + ca + ").canCopy evaluation result: " + eval);\r
184             if (CopyAdvisor.SUPPORTED.contains(eval)) {\r
185                 Object copy = ca.copy(context, copyOf, sourceContainer, targetContainer, map);\r
186                 if (DEBUG_COPY) {\r
187                     System.out.println("  CopyAdvisor(" + ca + ").copy result: " + copy);\r
188                     System.out.println("  CopyAdvisor(" + ca + ").copy result map: " + map);\r
189                 }\r
190                 if (copy instanceof Resource) {\r
191                     resource = (Resource) copy;\r
192                 } else {\r
193                     throw new UnsupportedOperationException("Cannot copy element " + copyOf);\r
194                 }\r
195             }\r
196         } catch (SynchronizationException e) {\r
197             ErrorHandler eh = context.get(SynchronizationHints.ERROR_HANDLER);\r
198             eh.error(e.getMessage(), e);\r
199             // throwing exception allows canceling failed copy!\r
200             throw new DatabaseException(e);\r
201         } finally {\r
202             context.set(GraphSynchronizationHints.READ_TRANSACTION, null);\r
203             context.set(GraphSynchronizationHints.WRITE_TRANSACTION, null);\r
204         }\r
205         return resource;\r
206     }\r
207 \r
208     /**\r
209      * Creates and returns a copy of the specified source resource based on the\r
210      * standard Layer0 relation hierarchy by recursively including all resources\r
211      * in the copy that the source and is composed of (see\r
212      * {@link Layer0#IsComposedOf}). The routine will always copy at least all\r
213      * L0.InstanceOf statements and tags related to its input resource. If the\r
214      * copied resource has a value attached, it will also be copied.\r
215      * \r
216      * @param graph database write access\r
217      * @param source the resource to start the copy from\r
218      * @param advisor <code>null</code> or a custom advisor to guide whether or\r
219      *        not to copy relations that are not inherited from L0.IsComposedOf.\r
220      *        This advisor cannot be used to say that non-composing relations\r
221      *        should perform recursive copy, only whether to copy the tested\r
222      *        statement of or not.\r
223      * @return the copied resource\r
224      * @throws DatabaseException\r
225      */\r
226     public static Resource copy(WriteGraph graph, Resource source, BinaryFunction<Boolean, ReadGraph, Statement> advisor) throws DatabaseException {\r
227         return copy(graph, source, 0, advisor, new THashMap<Object, Object>());\r
228     }\r
229 \r
230     /**\r
231      * See {@link #copy(WriteGraph, Resource, BinaryFunction)}.\r
232      * \r
233      * @param graph\r
234      * @param source\r
235      * @param advisor\r
236      * @param copyMap a map for storing the correspondences between original and\r
237      *        copied objects. This is used to output data from the copy process.\r
238      * @return\r
239      * @throws DatabaseException\r
240      */\r
241     public static Resource copy(WriteGraph graph, Resource source, BinaryFunction<Boolean, ReadGraph, Statement> advisor, Map<Object, Object> copyMap) throws DatabaseException {\r
242         return copy(graph, source, 0, advisor, copyMap);\r
243     }\r
244 \r
245     private static Resource copy(WriteGraph graph, Resource source, int level, BinaryFunction<Boolean, ReadGraph, Statement> advisor, Map<Object, Object> copyMap) throws DatabaseException {\r
246         if (DEBUG_COPY)\r
247             System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")");\r
248 \r
249         Resource copy = (Resource) copyMap.get(source);\r
250         if (copy != null) {\r
251             if (DEBUG_COPY)\r
252                 System.out.println("[" + level + "] already copied: " + NameUtils.getSafeName(graph, source) + " -> " + NameUtils.getSafeName(graph, copy));\r
253             return copy;\r
254         }\r
255 \r
256         Layer0 L0 = Layer0.getInstance(graph);\r
257         copy = graph.newResource();\r
258         copyMap.put(source, copy);\r
259         for (Resource type : graph.getObjects(source, L0.InstanceOf))\r
260             graph.claim(copy, L0.InstanceOf, null, type);\r
261 \r
262         if (graph.hasValue(source)) {\r
263             Datatype dt = graph.getRelatedValue(source, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));\r
264             Binding b = Bindings.getBinding(dt);\r
265             graph.claimValue(copy, graph.<Object>getValue(source, b), b);\r
266         }\r
267 \r
268         // Copy tags\r
269         for (Statement stm : graph.getStatements(source, L0.IsWeaklyRelatedTo)) {\r
270             if (stm.isAsserted(source))\r
271                 continue;\r
272             if (graph.isInstanceOf(stm.getPredicate(), L0.Tag)) {\r
273                 if (DEBUG_COPY)\r
274                     System.out.println("[" + level + "]\tcopying tag ("\r
275                             + NameUtils.getSafeName(graph, stm.getSubject()) + ", "\r
276                             + NameUtils.getSafeName(graph, stm.getPredicate()) + ")");\r
277                 graph.claim(copy, stm.getPredicate(), stm.getPredicate(), copy);\r
278             }\r
279         }\r
280 \r
281         for (Statement stm : graph.getStatements(source, L0.IsRelatedTo)) {\r
282             Resource relation = stm.getPredicate();\r
283 \r
284             // InstanceOf statements are handled separately, silently ignore them here.\r
285             if (L0.InstanceOf.equals(relation))\r
286                 continue;\r
287 \r
288             // Don't copy asserted relations!\r
289             if (stm.isAsserted(source)) {\r
290                 if (DEBUG_COPY)\r
291                     System.out.println("[" + level + "]\t\tSkipping asserted statement");\r
292                 continue;\r
293             }\r
294 \r
295             if (DEBUG_COPY)\r
296                 System.out.println("[" + level + "]\tprocessing statement (" + NameUtils.toString(graph, stm) + ")");\r
297 \r
298             Resource subject = stm.getSubject();\r
299             Resource inverse = graph.getPossibleInverse(relation);\r
300             boolean addInverse = false;\r
301             Resource obj = stm.getObject();\r
302             Resource propType = graph.getPossibleType(obj, L0.Literal);\r
303 \r
304             // Â§1 only L0.IsComposedOf and its subrelations can be considered to be copied automatically\r
305 \r
306             if (propType != null || graph.isSubrelationOf(relation, L0.IsComposedOf)) {\r
307                 if (propType != null && graph.hasStatement(propType, L0.Enumeration, propType)) {\r
308                     if (DEBUG_COPY)\r
309                         System.out.println("[" + level + "]\t\tclaim enumeration statement");\r
310                     graph.claim(copy, relation, null, obj);\r
311                 } else {\r
312                     if (inverse != null)\r
313                         addInverse = graph.hasStatement(obj, inverse, subject);\r
314 \r
315                     // Copy instantiated properties, not asserted ones.\r
316                     if (DEBUG_COPY)\r
317                         System.out.println("[" + level + "]\t\tcopy whole object");\r
318                     Resource clone = copy(graph, obj, level + 1, advisor, copyMap);\r
319                     graph.claim(copy, relation, clone);\r
320                 }\r
321             } else {\r
322                 if (advisor != null) {\r
323                     Boolean result = advisor.call(graph, stm);\r
324                     if (Boolean.TRUE.equals(result)) {\r
325                         // Don't clone the object, just add relation to the same object.\r
326                         if (inverse != null)\r
327                             addInverse = graph.hasStatement(obj, inverse, subject);\r
328 \r
329                         if (DEBUG_COPY) {\r
330                             System.out.println("[" + level + "]\t\tCopyAdvisorUtil.copy.claim(" + NameUtils.getSafeName(graph, copy) + ", "\r
331                                     + NameUtils.getSafeName(graph, relation) + ", "\r
332                                     + (addInverse ? NameUtils.getSafeName(graph, inverse) : null) + ", "\r
333                                     + NameUtils.getSafeName(graph, obj));\r
334                         }\r
335 \r
336                         graph.claim(copy, relation, addInverse ? inverse : null, obj);\r
337                     }\r
338                 } else {\r
339                     if (DEBUG_COPY)\r
340                         System.out.println("[" + level + "]\t\tskipping statement");\r
341                 }\r
342             }\r
343         }\r
344         return copy;\r
345     }\r
346 \r
347     /**\r
348      * Creates and returns a copy of the specified source resource based on the\r
349      * standard Layer0 relation hierarchy by recursively including all resources\r
350      * in the copy that the source and is composed of (see\r
351      * {@link Layer0#IsComposedOf}). A customizable advisor function can be used\r
352      * to guide the copy process. The routine will always copy at least all\r
353      * L0.InstanceOf statements and tags related to its input resource. If the\r
354      * copied resource has a value attached, it will also be copied.\r
355      * \r
356      * @param graph database write access\r
357      * @param source the resource to start the copy from\r
358      * @param advisor <code>null</code> or a custom advisor to guide the copy\r
359      *        process according to the specifications of\r
360      *        {@link StatementEvaluation}. Every copied statement besides\r
361      *        L0.InstanceOf and tags will be evaluated by this advisor.\r
362      * @return the copied resource\r
363      * @throws DatabaseException\r
364      */\r
365     public static Resource copy2(WriteGraph graph, Resource source,\r
366             BinaryFunction<StatementEvaluation, ReadGraph, Statement> advisor) throws DatabaseException {\r
367         return copy2(graph, source, 0, advisor, new THashMap<Object, Object>());\r
368     }\r
369 \r
370     /**\r
371      * See {@link #copy2(WriteGraph, Resource, BinaryFunction)}.\r
372      * \r
373      * @param graph\r
374      * @param source\r
375      * @param advisor\r
376      * @param copyMap a map for storing the correspondences between original and\r
377      *        copied objects. This is used to output data from the copy process.\r
378      * @return\r
379      * @throws DatabaseException \r
380      */\r
381     public static Resource copy2(WriteGraph graph, Resource source,\r
382             BinaryFunction<StatementEvaluation, ReadGraph, Statement> advisor, Map<Object, Object> copyMap)\r
383             throws DatabaseException {\r
384         return copy2(graph, source, 0, advisor, copyMap);\r
385     }\r
386 \r
387     private static Resource copy2(final WriteGraph graph, final Resource source, final int level,\r
388             BinaryFunction<StatementEvaluation, ReadGraph, Statement> advisor, Map<Object, Object> copyMap)\r
389     throws DatabaseException {\r
390         if (DEBUG_COPY)\r
391             System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")");\r
392 \r
393         Resource copy = (Resource) copyMap.get(source);\r
394         if (copy != null) {\r
395             if (DEBUG_COPY)\r
396                 System.out.println("[" + level + "] already copied: " + NameUtils.getSafeName(graph, source) + " -> " + NameUtils.getSafeName(graph, copy));\r
397             return copy;\r
398         }\r
399 \r
400         Layer0 L0 = Layer0.getInstance(graph);\r
401         copy = graph.newResource();\r
402         copyMap.put(source, copy);\r
403         for (Resource type : graph.getObjects(source, L0.InstanceOf))\r
404             graph.claim(copy, L0.InstanceOf, null, type);\r
405 \r
406         if (graph.hasValue(source)) {\r
407             Datatype dt = graph.getRelatedValue(source, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));\r
408             Binding b = Bindings.getBinding(dt);\r
409             graph.claimValue(copy, graph.<Object>getValue(source, b), b);\r
410         }\r
411 \r
412         // Copy tags\r
413         for (Statement stm : graph.getStatements(source, L0.IsWeaklyRelatedTo)) {\r
414             if (stm.isAsserted(source))\r
415                 continue;\r
416             if (graph.isInstanceOf(stm.getPredicate(), L0.Tag)) {\r
417                 if (DEBUG_COPY)\r
418                     System.out.println("[" + level + "]\tcopying tag ("\r
419                             + NameUtils.getSafeName(graph, stm.getSubject()) + ", "\r
420                             + NameUtils.getSafeName(graph, stm.getPredicate()) + ")");\r
421                 graph.claim(copy, stm.getPredicate(), stm.getPredicate(), copy);\r
422             }\r
423         }\r
424 \r
425         for (Statement stm : graph.getStatements(source, L0.IsRelatedTo)) {\r
426             Resource relation = stm.getPredicate();\r
427 \r
428             // InstanceOf statements are handled separately, silently ignore them here.\r
429             if (L0.InstanceOf.equals(relation))\r
430                 continue;\r
431 \r
432             // Don't copy asserted relations!\r
433             if (stm.isAsserted(source)) {\r
434                 if (DEBUG_COPY)\r
435                     System.out.println("[" + level + "]\tskipping asserted statement (" + NameUtils.toString(graph, stm) + ")");\r
436                 continue;\r
437             }\r
438 \r
439             if (DEBUG_COPY)\r
440                 System.out.println("[" + level + "]\tprocessing statement (" + NameUtils.toString(graph, stm) + ")");\r
441 \r
442             Resource subject = stm.getSubject();\r
443             Resource inverse = graph.getPossibleInverse(relation);\r
444             Resource obj = stm.getObject();\r
445             Resource propType = graph.getPossibleType(obj, L0.Literal);\r
446             boolean addInverse = false;\r
447             boolean forceIncludeAndFollow = false;\r
448 \r
449             switch (evaluate(graph, stm, advisor)) {\r
450                 case SKIP:\r
451                     if (DEBUG_COPY)\r
452                         System.out.println("[" + level + "]\t\tskipping statement");\r
453                     break;\r
454 \r
455                 case INCLUDE:\r
456                 {\r
457                     // Don't clone the object, just add relation to the same object.\r
458                     if (inverse != null)\r
459                         addInverse = graph.hasStatement(obj, inverse, subject);\r
460 \r
461                     if (DEBUG_COPY) {\r
462                         System.out.println("[" + level + "]\t\tCopyAdvisorUtil.copy2.claim(" + NameUtils.getSafeName(graph, copy) + ", "\r
463                                 + NameUtils.getSafeName(graph, relation) + ", "\r
464                                 + (addInverse ? NameUtils.getSafeName(graph, inverse) : null) + ", "\r
465                                 + NameUtils.getSafeName(graph, obj));\r
466                     }\r
467 \r
468                     graph.claim(copy, relation, addInverse ? inverse : null, obj);\r
469                     break;\r
470                 }\r
471 \r
472                 case INCLUDE_AND_FOLLOW:\r
473                     // Force follow-through in the default copy logic\r
474                     forceIncludeAndFollow = true;\r
475                     // NOTE: intentional fall-through here\r
476 \r
477                 case USE_DEFAULT:\r
478                 {\r
479                     if (forceIncludeAndFollow || propType != null || graph.isSubrelationOf(relation, L0.IsComposedOf)) {\r
480                         if (propType != null && graph.hasStatement(propType, L0.Enumeration, propType)) {\r
481                             // This logic is applied only for enumeration property\r
482                             // statements that should not have an inverse in any case.\r
483                             if (DEBUG_COPY) {\r
484                                 System.out.println("[" + level + "]\t\tclaim enumeration statement("\r
485                                         + NameUtils.getSafeName(graph, copy) + ", "\r
486                                         + NameUtils.getSafeName(graph, relation)+ ", null, "\r
487                                         + NameUtils.getSafeName(graph, obj));\r
488                             }\r
489                             graph.claim(copy, relation, null, obj);\r
490                         } else {\r
491                             // This logic is applied for other properties besides enumerations\r
492                             if (inverse != null)\r
493                                 addInverse = graph.hasStatement(obj, inverse, subject);\r
494 \r
495                             // Copy instantiated properties, not asserted ones.\r
496                             if (DEBUG_COPY)\r
497                                 System.out.println("[" + level + "]\t\tcopy whole object");\r
498 \r
499                             Resource clone = copy2(graph, obj, level + 1, advisor, copyMap);\r
500                             graph.claim(copy, relation, inverse, clone);\r
501                         }\r
502                     } else {\r
503                         if (DEBUG_COPY)\r
504                             System.out.println("[" + level + "]\t\tskipping statement");\r
505                     }\r
506                 }\r
507             }\r
508         }\r
509         return copy;\r
510     }\r
511 \r
512     /**\r
513      * Creates and returns a copy of the specified source resource based on the\r
514      * standard Layer0 relation hierarchy by recursively including all resources\r
515      * in the copy that the source and is composed of (see\r
516      * {@link Layer0#IsComposedOf}). A customizable advisor function can be used\r
517      * to guide the copy process. The routine will always copy at least all\r
518      * L0.InstanceOf statements and tags related to its input resource. If the\r
519      * copied resource has a value attached, it will also be copied.\r
520      * \r
521      * Works exactly like {@link #copy2(WriteGraph, Resource, BinaryFunction)}\r
522      * but uses the <code>model</code> argument to make sure that the copy\r
523      * process does not propagate outside of the model. Any references that go\r
524      * to resources with URIs that are not in the model's namespace are copied\r
525      * as unidirectional.\r
526      * \r
527      * @param graph database write access\r
528      * @param source the resource to start the copy from\r
529      * @param model the model containing the source object, used to keep the\r
530      *        copy process model-local\r
531      * @param advisor <code>null</code> or a custom advisor to guide the copy\r
532      *        process according to the specifications of\r
533      *        {@link StatementEvaluation}. Every copied statement besides\r
534      *        L0.InstanceOf and tags will be evaluated by this advisor.\r
535      * @return the copied resource\r
536      * @throws DatabaseException\r
537      */\r
538     public static Resource copy3(WriteGraph graph, Resource source, Resource model,\r
539             BinaryFunction<StatementEvaluation, ReadGraph, Statement> advisor) throws DatabaseException {\r
540         String modelURI = graph.getURI(model);\r
541         return copy3(graph, modelURI, source, 0, advisor, new THashMap<Object, Object>());\r
542     }\r
543 \r
544     /**\r
545      * See {@link #copy3(WriteGraph, Resource, Resource, BinaryFunction)}.\r
546      * \r
547      * @param graph\r
548      * @param source\r
549      * @param model\r
550      * @param advisor\r
551      * @param copyMap a map for storing the correspondences between original and\r
552      *        copied objects. This is used to output data from the copy process.\r
553      * @return\r
554      * @throws DatabaseException\r
555      */\r
556     public static Resource copy3(WriteGraph graph, Resource source, Resource model,\r
557             BinaryFunction<StatementEvaluation, ReadGraph, Statement> advisor, Map<Object, Object> copyMap) throws DatabaseException {\r
558         String modelURI = graph.getURI(model);\r
559         return copy3(graph, modelURI, source, 0, advisor, copyMap);\r
560     }\r
561 \r
562     private static Resource copy3(WriteGraph graph, String modelURI, Resource source, int level,\r
563             BinaryFunction<StatementEvaluation, ReadGraph, Statement> advisor, Map<Object, Object> copyMap)\r
564     throws DatabaseException {\r
565         if (DEBUG_COPY)\r
566             System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")");\r
567 \r
568         Resource copy = (Resource) copyMap.get(source);\r
569         if (copy != null) {\r
570             if (DEBUG_COPY)\r
571                 System.out.println("[" + level + "] already copied: " + NameUtils.getSafeName(graph, source) + " -> " + NameUtils.getSafeName(graph, copy));\r
572             return copy;\r
573         }\r
574 \r
575         Layer0 L0 = Layer0.getInstance(graph);\r
576         copy = graph.newResource();\r
577         copyMap.put(source, copy);\r
578         for (Resource type : graph.getObjects(source, L0.InstanceOf))\r
579             graph.claim(copy, L0.InstanceOf, null, type);\r
580 \r
581         if (graph.hasValue(source)) {\r
582             Datatype dt = graph.getRelatedValue(source, L0.HasDataType, Bindings.getBindingUnchecked(Datatype.class));\r
583             Binding b = Bindings.getBinding(dt);\r
584             graph.claimValue(copy, graph.<Object>getValue(source, b), b);\r
585         }\r
586 \r
587         // Copy tags\r
588         for (Statement stm : graph.getStatements(source, L0.IsWeaklyRelatedTo)) {\r
589             if (stm.isAsserted(source))\r
590                 continue;\r
591             if (graph.isInstanceOf(stm.getPredicate(), L0.Tag)) {\r
592                 if (DEBUG_COPY)\r
593                     System.out.println("[" + level + "]\tcopying tag ("\r
594                             + NameUtils.getSafeName(graph, stm.getSubject()) + ", "\r
595                             + NameUtils.getSafeName(graph, stm.getPredicate()) + ")");\r
596                 graph.claim(copy, stm.getPredicate(), stm.getPredicate(), copy);\r
597             }\r
598         }\r
599 \r
600         for (Statement stm : graph.getStatements(source, L0.IsRelatedTo)) {\r
601             Resource relation = stm.getPredicate();\r
602 \r
603             // InstanceOf statements are handled separately, silently ignore them here.\r
604             if (L0.InstanceOf.equals(relation))\r
605                 continue;\r
606 \r
607             // Don't copy asserted relations!\r
608             if (stm.isAsserted(source)) {\r
609                 if (DEBUG_COPY)\r
610                     System.out.println("[" + level + "]\tskipping asserted statement (" + NameUtils.toString(graph, stm) + ")");\r
611                 continue;\r
612             }\r
613 \r
614             if (DEBUG_COPY)\r
615                 System.out.println("[" + level + "]\tprocessing statement (" + NameUtils.toString(graph, stm) + ")");\r
616 \r
617             Resource subject = stm.getSubject();\r
618             Resource inverse = graph.getPossibleInverse(relation);\r
619             Resource obj = stm.getObject();\r
620             Resource propType = graph.getPossibleType(obj, L0.Literal);\r
621             boolean addInverse = false;\r
622             boolean forceIncludeAndFollow = false;\r
623 \r
624             switch (evaluate(graph, stm, advisor)) {\r
625                 case SKIP:\r
626                     if (DEBUG_COPY)\r
627                         System.out.println("[" + level + "]\t\tskipping statement");\r
628                     break;\r
629 \r
630                 case INCLUDE:\r
631                 {\r
632                     // Don't clone the object, just add relation to the same object.\r
633                     if (inverse != null)\r
634                         addInverse = graph.hasStatement(obj, inverse, subject);\r
635 \r
636                     if (DEBUG_COPY) {\r
637                         System.out.println("[" + level + "]\t\tCopyAdvisorUtil.copy2.claim(" + NameUtils.getSafeName(graph, copy) + ", "\r
638                                 + NameUtils.getSafeName(graph, relation) + ", "\r
639                                 + (addInverse ? NameUtils.getSafeName(graph, inverse) : null) + ", "\r
640                                 + NameUtils.getSafeName(graph, obj));\r
641                     }\r
642 \r
643                     graph.claim(copy, relation, addInverse ? inverse : null, obj);\r
644                     break;\r
645                 }\r
646 \r
647                 case INCLUDE_AND_FOLLOW:\r
648                     // Force follow-through in the default copy logic\r
649                     forceIncludeAndFollow = true;\r
650                     // NOTE: intentional fall-through here\r
651 \r
652                 case USE_DEFAULT:\r
653                 {\r
654                     if (forceIncludeAndFollow || propType != null || graph.isSubrelationOf(relation, L0.IsComposedOf)) {\r
655                         if (propType != null && graph.hasStatement(propType, L0.Enumeration, propType)) {\r
656                             // This logic is applied only for enumeration property\r
657                             // statements that should not have an inverse in any case.\r
658                             if (DEBUG_COPY) {\r
659                                 System.out.println("[" + level + "]\t\tclaim enumeration statement("\r
660                                         + NameUtils.getSafeName(graph, copy) + ", "\r
661                                         + NameUtils.getSafeName(graph, relation)+ ", null, "\r
662                                         + NameUtils.getSafeName(graph, obj));\r
663                             }\r
664                             graph.claim(copy, relation, null, obj);\r
665                         } else {\r
666                             // This logic is applied for other properties besides enumerations\r
667                             if (inverse != null)\r
668                                 addInverse = graph.hasStatement(obj, inverse, subject);\r
669 \r
670                             String objectURI = graph.getPossibleURI(obj);\r
671 \r
672                             if(objectURI != null && !objectURI.startsWith(modelURI)) {\r
673 \r
674                                 if (DEBUG_COPY)\r
675                                     System.out.println("[" + level + "]\t\tclaim ontological reference");\r
676 \r
677                                 graph.claim(copy, relation, null, obj);\r
678 \r
679                             } else {\r
680 \r
681                                 // Copy instantiated properties, not asserted ones.\r
682                                 if (DEBUG_COPY)\r
683                                     System.out.println("[" + level + "]\t\tcopy whole object");\r
684 \r
685                                 Resource clone = copy3(graph, modelURI, obj, level + 1, advisor, copyMap);\r
686                                 graph.claim(copy, relation, inverse, clone);\r
687 \r
688                             }\r
689 \r
690                         }\r
691                     } else {\r
692                         if (DEBUG_COPY)\r
693                             System.out.println("[" + level + "]\t\tskipping statement");\r
694                     }\r
695                 }\r
696             }\r
697         }\r
698         return copy;\r
699     }\r
700 \r
701     protected static StatementEvaluation evaluate(ReadGraph graph, Statement stm, BinaryFunction<StatementEvaluation, ReadGraph, Statement> tester) {\r
702         if (tester == null)\r
703             return StatementEvaluation.USE_DEFAULT;\r
704         return tester.call(graph, stm);\r
705     }\r
706 \r
707     /**\r
708      * Equal to\r
709      * <code>copy4(graph, source, graph.adapt(source, CopyHandler.class)))</code>\r
710      * .\r
711      * \r
712      * @param graph database write access\r
713      * @param source the resource to start the copy from\r
714      * @return the copied resource\r
715      * @throws DatabaseException\r
716      */\r
717     public static Resource copy4(WriteGraph graph, Resource source) throws DatabaseException {\r
718         CopyHandler handler = graph.adapt(source, CopyHandler.class);\r
719         return copy4(graph, source, handler);\r
720     }\r
721 \r
722     /**\r
723      * Creates and returns a copy of the specified source resource based on\r
724      * transferable graph export and import. The TG representation shall be\r
725      * generated by the specified {@link CopyHandler} into a\r
726      * {@link SimanticsClipboard} instance from where it is read back as\r
727      * {@link TransferableGraph1} and imported into the database through\r
728      * {@link TransferableGraphs#importGraph1(WriteGraph, TransferableGraph1, org.simantics.graph.db.IImportAdvisor)}.\r
729      * \r
730      * @param graph database write access\r
731      * @param source the resource to start the copy from\r
732      * @param copyHandler the handler to use for generating the transferable\r
733      *        graph representation from the copied resource\r
734      * @return the copied resource\r
735      * @throws DatabaseException\r
736      */\r
737     public static Resource copy4(WriteGraph graph, Resource source, CopyHandler copyHandler) throws DatabaseException {\r
738         SimanticsClipboardImpl builder = new SimanticsClipboardImpl();\r
739         copyHandler.copyToClipboard(graph, builder);\r
740 \r
741         for(Set<Representation> object : builder.getContents()) {\r
742             TransferableGraph1 tg = ClipboardUtils.accept(graph, object, SimanticsKeys.KEY_TRANSFERABLE_GRAPH);\r
743             if(tg != null) {\r
744                 FixedRootImportAdvisor advisor = new FixedRootImportAdvisor();\r
745                 TransferableGraphs.importGraph1(graph, tg, advisor);\r
746                 return advisor.getRoot();\r
747             }\r
748         }\r
749 \r
750         String uri = graph.getPossibleURI(source);\r
751         throw new DatabaseException("Failed to copy resource " + NameUtils.getSafeName(graph, source, true)\r
752                 + (uri != null ? " with URI " + uri : ""));\r
753     }\r
754 \r
755 }\r