OCM-54 do copy in session state only and persist on save

git-svn-id: https://svn.apache.org/repos/asf/jackrabbit/commons/ocm/trunk@1357259 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/jackrabbit/ocm/manager/ObjectContentManager.java b/src/main/java/org/apache/jackrabbit/ocm/manager/ObjectContentManager.java
index 8fe6aa6..31fe86c 100644
--- a/src/main/java/org/apache/jackrabbit/ocm/manager/ObjectContentManager.java
+++ b/src/main/java/org/apache/jackrabbit/ocm/manager/ObjectContentManager.java
@@ -508,14 +508,21 @@
             throws ObjectContentManagerException;
 
     /**
-     * Copy an object
+     * Copy an object from <code>scrPath</code> to <code>destPath</code>. When there does not exist a node yet at
+     * <code>destPath</code> a new node will be created there if the parent {@link javax.jcr.Node} exists already. If 
+     * the parent does not exist,  an {@link ObjectContentManagerException} will be thrown
+     * 
+     * If there already exists a node at <code>destPath</code>, we
+     * try to add the {@link javax.jcr.Node} from <code>srcPath</code>. If not possible, an {@link ObjectContentManagerException}
+     * will be thrown.
      *
      * @param srcPath
      *            path of the object to copy
      * @param destPath
      *            destination path
      *
-     * @throws ObjectContentManagerException
+     * @throws ObjectContentManagerException when the copy did not succeed for example because there exists no {@link javax.jcr.Node} at
+     * <code>srcPath</code> the parent of <code>destPath</code> does not exist or there already exists an (incompatible) node at <code>destPath</code>
      */
     public void copy(String srcPath, String destPath)
             throws ObjectContentManagerException;
diff --git a/src/main/java/org/apache/jackrabbit/ocm/manager/impl/ObjectContentManagerImpl.java b/src/main/java/org/apache/jackrabbit/ocm/manager/impl/ObjectContentManagerImpl.java
index 7b5b7a2..4642614 100644
--- a/src/main/java/org/apache/jackrabbit/ocm/manager/impl/ObjectContentManagerImpl.java
+++ b/src/main/java/org/apache/jackrabbit/ocm/manager/impl/ObjectContentManagerImpl.java
@@ -29,13 +29,16 @@
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.UnsupportedRepositoryOperationException;
-import javax.jcr.Workspace;
 import javax.jcr.lock.LockException;
 import javax.jcr.lock.LockManager;
 import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.PropertyDefinition;
 import javax.jcr.query.InvalidQueryException;
 import javax.jcr.query.QueryResult;
 import javax.jcr.version.VersionHistory;
@@ -60,7 +63,6 @@
 import org.apache.jackrabbit.ocm.mapper.Mapper;
 import org.apache.jackrabbit.ocm.mapper.impl.digester.DigesterMapperImpl;
 import org.apache.jackrabbit.ocm.mapper.model.ClassDescriptor;
-import org.apache.jackrabbit.ocm.query.Filter;
 import org.apache.jackrabbit.ocm.query.Query;
 import org.apache.jackrabbit.ocm.query.QueryManager;
 import org.apache.jackrabbit.ocm.query.impl.QueryManagerImpl;
@@ -1028,10 +1030,33 @@
         }
     }
 
-    public void copy(String srcPath, String destPath) {
-        Workspace workspace = session.getWorkspace();
+    public void copy(String srcPath, String destPath) throws ObjectContentManagerException {
         try {
-            workspace.copy(srcPath, destPath);
+            if(StringUtils.isBlank(srcPath) || StringUtils.isBlank(destPath) || !srcPath.startsWith("/") || !destPath.startsWith("/")) {
+                throw new ObjectContentManagerException("scrPath " + srcPath + " or destPath " + destPath + " is not valid");
+            }
+            // no check for existence needed, as handled by exceptions
+            Node srcNode = session.getNode(srcPath);
+            Node destNode;
+            if (session.nodeExists(destPath)) {
+                destNode = session.getNode(destPath);
+            } else {
+                // if parentDestNode cannot be found, just a PathNotFoundException is thrown
+                while (destPath.endsWith("/")) {
+                    destPath = destPath.substring(0, destPath.length()-1);
+                }
+                int indexOfLastSlash = destPath.lastIndexOf("/");
+                String parentDestPath = destPath.substring(0, indexOfLastSlash);
+                String destNodeName = destPath.substring(indexOfLastSlash + 1);
+                Node parentDestNode;
+                if (StringUtils.isBlank(parentDestPath)) {
+                    parentDestNode = session.getRootNode();
+                } else {
+                    parentDestNode = session.getNode(parentDestPath);
+                }
+                destNode = parentDestNode.addNode(destNodeName, srcNode.getPrimaryNodeType().getName());
+            }
+            copy(srcNode, destNode);
         } catch (javax.jcr.nodetype.ConstraintViolationException cve) {
             throw new ObjectContentManagerException("Cannot copy the object from " + srcPath + " to " + destPath + "."
                     + "Violation of a nodetype or attempt to copy under property detected ", cve);
@@ -1044,7 +1069,7 @@
             throw new ObjectContentManagerException("Cannot copy the object from " + srcPath + " to " + destPath + "." + " Session does not have access permissions", ade);
 
         } catch (javax.jcr.PathNotFoundException pnf) {
-            throw new ObjectContentManagerException("Cannot copy the object from " + srcPath + " to " + destPath + "." + "Node at source or destination does not exist ", pnf);
+            throw new ObjectContentManagerException("Cannot copy the object from " + srcPath + " to " + destPath + "." + "Node at source or parent of destination does not exist ", pnf);
 
         } catch (javax.jcr.ItemExistsException ie) {
             throw new ObjectContentManagerException("Cannot copy the object from " + srcPath + " to " + destPath + "." + "It might already exist at destination path.", ie);
@@ -1065,4 +1090,43 @@
         return session.getWorkspace().getVersionManager();
     }
 
+    /**
+     * Helper for copying scrNode (including properties & descendants) to destNode
+     *
+     * @param srcNode
+     * @param destNode
+     * @throws RepositoryException
+     */
+    private void copy(Node srcNode, Node destNode) throws RepositoryException {
+
+        NodeType[] mixinNodeTypes = srcNode.getMixinNodeTypes();
+        for (int i = 0; i < mixinNodeTypes.length; i++) {
+            destNode.addMixin(mixinNodeTypes[i].getName());
+        }
+
+        for (PropertyIterator iter = srcNode.getProperties(); iter.hasNext(); ) {
+            Property property = iter.nextProperty();
+            PropertyDefinition definition = property.getDefinition();
+            if (!definition.isProtected()) {
+                if (definition.isMultiple()) {
+                    destNode.setProperty(property.getName(), property.getValues(), property.getType());
+                } else {
+                    destNode.setProperty(property.getName(), property.getValue());
+                }
+            }
+        }
+
+        for (NodeIterator iter = srcNode.getNodes(); iter.hasNext(); ) {
+            Node node = iter.nextNode();
+            Node child;
+            // check if the subnode is autocreated
+            if (!node.getDefinition().isAutoCreated() && destNode.hasNode(node.getName())) {
+                child = destNode.getNode(node.getName());
+            } else {
+                child = destNode.addNode(node.getName(), node.getPrimaryNodeType().getName());
+            }
+            copy(node, child);
+        }
+    }
+    
 }
diff --git a/src/test/java/org/apache/jackrabbit/ocm/manager/basic/AnnotationCopyMoveTest.java b/src/test/java/org/apache/jackrabbit/ocm/manager/basic/AnnotationCopyMoveTest.java
index fe5ff6d..d9fc649 100644
--- a/src/test/java/org/apache/jackrabbit/ocm/manager/basic/AnnotationCopyMoveTest.java
+++ b/src/test/java/org/apache/jackrabbit/ocm/manager/basic/AnnotationCopyMoveTest.java
@@ -97,7 +97,7 @@
         // Copy the object
         // --------------------------------------------------------------------------------
         ocm.copy("/test", "/test2");
-
+        ocm.save();
         // --------------------------------------------------------------------------------
         // Get the object
         // --------------------------------------------------------------------------------
diff --git a/src/test/java/org/apache/jackrabbit/ocm/manager/basic/DigesterCopyMoveTest.java b/src/test/java/org/apache/jackrabbit/ocm/manager/basic/DigesterCopyMoveTest.java
index 233546f..2943db0 100644
--- a/src/test/java/org/apache/jackrabbit/ocm/manager/basic/DigesterCopyMoveTest.java
+++ b/src/test/java/org/apache/jackrabbit/ocm/manager/basic/DigesterCopyMoveTest.java
@@ -98,7 +98,7 @@
         // Copy the object
         // --------------------------------------------------------------------------------
         ocm.copy("/test", "/test2");
-
+        ocm.save();
         // --------------------------------------------------------------------------------
         // Get the object
         // --------------------------------------------------------------------------------