Author: fmeschbe
Date: Mon May 19 23:53:30 2008
New Revision: 658124

URL: http://svn.apache.org/viewvc?rev=658124&view=rev
Log:
SLING-455 Implement @CopyFrom and @MoveFrom with integration tests

Added:
    
incubator/sling/trunk/launchpad/webapp/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletAtCopyTest.java
    
incubator/sling/trunk/launchpad/webapp/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletAtMoveTest.java
Modified:
    
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
    
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java
    
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CopyOperation.java
    
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ModifyOperation.java

Added: 
incubator/sling/trunk/launchpad/webapp/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletAtCopyTest.java
URL: 
http://svn.apache.org/viewvc/incubator/sling/trunk/launchpad/webapp/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletAtCopyTest.java?rev=658124&view=auto
==============================================================================
--- 
incubator/sling/trunk/launchpad/webapp/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletAtCopyTest.java
 (added)
+++ 
incubator/sling/trunk/launchpad/webapp/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletAtCopyTest.java
 Mon May 19 23:53:30 2008
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.launchpad.webapp.integrationtest.servlets.post;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.launchpad.webapp.integrationtest.HttpTestBase;
+import 
org.apache.sling.launchpad.webapp.integrationtest.helpers.HttpStatusCodeException;
+import org.apache.sling.servlets.post.SlingPostConstants;
+
+/** Test item copy support by @CopyFrom suffix (SLING-455) */
+public class PostServletAtCopyTest extends HttpTestBase {
+
+    public static final String TEST_BASE_PATH = "/sling-at-copy-tests";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public void testCopyNodeAbsolute() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/abs/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("text", "Hello");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/src", props);
+        
+        // assert content at source location
+        final String oldContent = getContent(HTTP_BASE_URL + testPath + 
"/src.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", oldContent, "out.println(data.text)");
+        
+        // create dest with text set from src/text
+        props.clear();
+        props.put("[EMAIL PROTECTED]", testPath + "/src");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest", props);
+        
+        // assert content at new location
+        String content = getContent(HTTP_BASE_URL + testPath + 
"/dest.-1.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", content, "out.println(data.src.text)");
+        
+        // assert content at old location
+        String contentOld = getContent(HTTP_BASE_URL + testPath + 
"/src.-1.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", contentOld, "out.println(data.text)");
+    }
+    
+    public void testCopyNodeRelative() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/rel/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("text", "Hello");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/src", props);
+        
+        // assert content at source location
+        final String oldContent = getContent(HTTP_BASE_URL + testPath + 
"/src.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", oldContent, "out.println(data.text)");
+        
+        // create dest with text set from src/text
+        props.clear();
+        props.put("[EMAIL PROTECTED]", "../src");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest", props);
+        
+        // assert content at new location
+        String content = getContent(HTTP_BASE_URL + testPath + 
"/dest.-1.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", content, "out.println(data.src.text)");
+        
+        // assert content at old location
+        String contentOld = getContent(HTTP_BASE_URL + testPath + 
"/src.-1.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", contentOld, "out.println(data.text)");
+    }
+    
+    public void testCopyPropertyAbsolute() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/abs/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("text", "Hello");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/src", props);
+        
+        // assert content at source location
+        final String oldContent = getContent(HTTP_BASE_URL + testPath + 
"/src.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", oldContent, "out.println(data.text)");
+        
+        // create dest with text set from src/text
+        props.clear();
+        props.put("[EMAIL PROTECTED]", testPath + "/src/text");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest", props);
+        
+        // assert content at new location
+        String content = getContent(HTTP_BASE_URL + testPath + "/dest.json", 
CONTENT_TYPE_JSON);
+        assertJavascript("Hello", content, "out.println(data.text)");
+        
+        // assert content at old location
+        String contentOld = getContent(HTTP_BASE_URL + testPath + "/src.json", 
CONTENT_TYPE_JSON);
+        assertJavascript("Hello", contentOld, "out.println(data.text)");
+    }
+    
+    public void testCopyPropertyRelative() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/rel/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("text", "Hello");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/src", props);
+
+        // assert content at source location
+        final String oldContent = getContent(HTTP_BASE_URL + testPath + 
"/src.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", oldContent, "out.println(data.text)");
+        
+        // create dest with text set from src/text
+        props.clear();
+        props.put("[EMAIL PROTECTED]", "../src/text");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest", props);
+        
+        // assert content at new location
+        String content = getContent(HTTP_BASE_URL + testPath + "/dest.json", 
CONTENT_TYPE_JSON);
+        assertJavascript("Hello", content, "out.println(data.text)");
+        
+        // assert content at old location
+        String contentOld = getContent(HTTP_BASE_URL + testPath + "/src.json", 
CONTENT_TYPE_JSON);
+        assertJavascript("Hello", contentOld, "out.println(data.text)");
+    }
+
+    public void testCopyNodeSourceMissing() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/exist/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+
+        // create dest node
+        props.clear();
+        props.put("text", "Hello Destination");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest/src", props);
+
+        props.clear();
+        props.put("[EMAIL PROTECTED]", testPath + "/non_existing_source");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest", props);
+
+        // expect unmodified dest
+        String content = getContent(HTTP_BASE_URL + testPath + 
"/dest.-1.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello Destination", content, 
"out.println(data.src.text)");
+    }
+
+    public void testCopyNodeExistingReplace() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/replace/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("text", "Hello");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/src", props);
+
+        // create dest node
+        props.clear();
+        props.put("text", "Hello Destination");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest/src", props);
+
+        props.clear();
+        props.put("[EMAIL PROTECTED]", "../src");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest", props);
+
+        // expect unmodified dest
+        String content = getContent(HTTP_BASE_URL + testPath + 
"/dest.-1.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", content, "out.println(data.src.text)");
+    }
+
+    public void testCopyNodeDeepRelative() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/new/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("text", "Hello");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/src", props);
+
+        props.clear();
+        props.put("deep/[EMAIL PROTECTED]", "../../src");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest", props);
+
+        // expect new data
+        String content = getContent(HTTP_BASE_URL + testPath + 
"/dest.-1.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", content, 
"out.println(data.deep['new'].text)");
+    }
+
+    public void testCopyNodeDeepAbsolute() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/new_fail/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("text", "Hello");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/src", props);
+
+        props.clear();
+        props.put(testPath + "/some/not/existing/[EMAIL PROTECTED]", testPath 
+ "/src");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/*", props);
+
+        // expect new data
+        String content = getContent(HTTP_BASE_URL + testPath + 
"/some/not/existing/structure.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", content, "out.println(data.text)");
+    }
+
+ }
\ No newline at end of file

Added: 
incubator/sling/trunk/launchpad/webapp/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletAtMoveTest.java
URL: 
http://svn.apache.org/viewvc/incubator/sling/trunk/launchpad/webapp/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletAtMoveTest.java?rev=658124&view=auto
==============================================================================
--- 
incubator/sling/trunk/launchpad/webapp/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletAtMoveTest.java
 (added)
+++ 
incubator/sling/trunk/launchpad/webapp/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletAtMoveTest.java
 Mon May 19 23:53:30 2008
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.launchpad.webapp.integrationtest.servlets.post;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.launchpad.webapp.integrationtest.HttpTestBase;
+import 
org.apache.sling.launchpad.webapp.integrationtest.helpers.HttpStatusCodeException;
+import org.apache.sling.servlets.post.SlingPostConstants;
+
+/** Test item move support by @MoveFrom suffix (SLING-455) */
+public class PostServletAtMoveTest extends HttpTestBase {
+
+    public static final String TEST_BASE_PATH = "/sling-at-move-tests";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public void testMoveNodeAbsolute() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/abs/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("text", "Hello");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/src", props);
+        
+        // assert content at source location
+        final String oldContent = getContent(HTTP_BASE_URL + testPath + 
"/src.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", oldContent, "out.println(data.text)");
+        
+        // create dest with text set from src/text
+        props.clear();
+        props.put("[EMAIL PROTECTED]", testPath + "/src");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest", props);
+        
+        // assert content at new location
+        String content = getContent(HTTP_BASE_URL + testPath + 
"/dest.-1.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", content, "out.println(data.src.text)");
+        
+        // assert no content at old location
+        assertHttpStatus(HTTP_BASE_URL + testPath + "/src.json",
+            HttpServletResponse.SC_NOT_FOUND,
+        "Expected Not_Found for old content");
+    }
+    
+    public void testMoveNodeRelative() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/rel/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("text", "Hello");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/src", props);
+        
+        // assert content at source location
+        final String oldContent = getContent(HTTP_BASE_URL + testPath + 
"/src.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", oldContent, "out.println(data.text)");
+        
+        // create dest with text set from src/text
+        props.clear();
+        props.put("[EMAIL PROTECTED]", "../src");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest", props);
+        
+        // assert content at new location
+        String content = getContent(HTTP_BASE_URL + testPath + 
"/dest.-1.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", content, "out.println(data.src.text)");
+        
+        // assert no content at old location
+        assertHttpStatus(HTTP_BASE_URL + testPath + "/src.json",
+            HttpServletResponse.SC_NOT_FOUND,
+        "Expected Not_Found for old content");
+    }
+    
+    public void testMovePropertyAbsolute() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/abs/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("text", "Hello");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/src", props);
+        
+        // assert content at source location
+        final String oldContent = getContent(HTTP_BASE_URL + testPath + 
"/src.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", oldContent, "out.println(data.text)");
+        
+        // create dest with text set from src/text
+        props.clear();
+        props.put("[EMAIL PROTECTED]", testPath + "/src/text");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest", props);
+        
+        // assert content at new location
+        String content = getContent(HTTP_BASE_URL + testPath + "/dest.json", 
CONTENT_TYPE_JSON);
+        assertJavascript("Hello", content, "out.println(data.text)");
+        
+        // assert no content at old location
+        assertHttpStatus(HTTP_BASE_URL + testPath + "/src.json",
+            HttpServletResponse.SC_OK, "Expected source parent existing");
+        assertHttpStatus(HTTP_BASE_URL + testPath + "/src/text.json",
+            HttpServletResponse.SC_NOT_FOUND,
+            "Expected Not_Found for old content");
+    }
+    
+    public void testMovePropertyRelative() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/rel/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("text", "Hello");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/src", props);
+
+        // assert content at source location
+        final String oldContent = getContent(HTTP_BASE_URL + testPath + 
"/src.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", oldContent, "out.println(data.text)");
+        
+        // create dest with text set from src/text
+        props.clear();
+        props.put("[EMAIL PROTECTED]", "../src/text");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest", props);
+        
+        // assert content at new location
+        String content = getContent(HTTP_BASE_URL + testPath + "/dest.json", 
CONTENT_TYPE_JSON);
+        assertJavascript("Hello", content, "out.println(data.text)");
+        
+        // assert no content at old location
+        assertHttpStatus(HTTP_BASE_URL + testPath + "/src.json",
+            HttpServletResponse.SC_OK, "Expected source parent existing");
+        assertHttpStatus(HTTP_BASE_URL + testPath + "/src/text.json",
+            HttpServletResponse.SC_NOT_FOUND,
+            "Expected Not_Found for old content");
+    }
+
+    public void testMoveNodeSourceMissing() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/exist/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+
+        // create dest node
+        props.clear();
+        props.put("text", "Hello Destination");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest/src", props);
+
+        props.clear();
+        props.put("[EMAIL PROTECTED]", testPath + "/non_existing_source");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest", props);
+
+        // expect unmodified dest
+        String content = getContent(HTTP_BASE_URL + testPath + 
"/dest.-1.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello Destination", content, 
"out.println(data.src.text)");
+    }
+
+    public void testMoveNodeExistingReplace() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/replace/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("text", "Hello");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/src", props);
+
+        // create dest node
+        props.clear();
+        props.put("text", "Hello Destination");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest/src", props);
+
+        props.clear();
+        props.put("[EMAIL PROTECTED]", "../src");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest", props);
+
+        // expect unmodified dest
+        String content = getContent(HTTP_BASE_URL + testPath + 
"/dest.-1.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", content, "out.println(data.src.text)");
+    }
+
+    public void testMoveNodeDeepRelative() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/new/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("text", "Hello");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/src", props);
+
+        props.clear();
+        props.put("deep/[EMAIL PROTECTED]", "../../src");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/dest", props);
+
+        // expect new data
+        String content = getContent(HTTP_BASE_URL + testPath + 
"/dest.-1.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", content, 
"out.println(data.deep['new'].text)");
+    }
+
+    public void testMoveNodeDeepAbsolute() throws IOException {
+        final String testPath = TEST_BASE_PATH + "/new_fail/" + 
System.currentTimeMillis();
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("text", "Hello");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/src", props);
+
+        props.clear();
+        props.put(testPath + "/some/not/existing/[EMAIL PROTECTED]", testPath 
+ "/src");
+        testClient.createNode(HTTP_BASE_URL + testPath + "/*", props);
+
+        // expect new data
+        String content = getContent(HTTP_BASE_URL + testPath + 
"/some/not/existing/structure.json", CONTENT_TYPE_JSON);
+        assertJavascript("Hello", content, "out.println(data.text)");
+    }
+
+ }
\ No newline at end of file

Modified: 
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
URL: 
http://svn.apache.org/viewvc/incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java?rev=658124&r1=658123&r2=658124&view=diff
==============================================================================
--- 
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
 (original)
+++ 
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
 Mon May 19 23:53:30 2008
@@ -254,4 +254,30 @@
      * applying any new content (value is "@Delete").
      */
     public static final String SUFFIX_DELETE = "@Delete";
+    
+    /**
+     * Suffix indicating that the named item is to be set from an item whose
+     * absolute or relative path is given in the parameter's value (value is
+     * "@MoveFrom").
+     * <p>
+     * This suffix is similar to the [EMAIL PROTECTED] #VALUE_FROM_SUFFIX} in 
that the
+     * value for the item is not taken from the request parameter itself but
+     * from somewhere else. In this case the value is set by moving another
+     * repository item (in the same workspace) to the location addressed by the
+     * parameter.
+     */
+    public static final String SUFFIX_MOVE_FROM = "@MoveFrom";
+
+    /**
+     * Suffix indicating that the named item is to be set from an item whose
+     * absolute or relative path is given in the parameter's value (value is
+     * "@CopyFrom").
+     * <p>
+     * This suffix is similar to the [EMAIL PROTECTED] #VALUE_FROM_SUFFIX} in 
that the
+     * value for the item is not taken from the request parameter itself but
+     * from somewhere else. In this case the value is set by copying another
+     * repository item (in the same workspace) to the location addressed by the
+     * parameter.
+     */
+    public static final String SUFFIX_COPY_FROM = "@CopyFrom";
 }

Modified: 
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java
URL: 
http://svn.apache.org/viewvc/incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java?rev=658124&r1=658123&r2=658124&view=diff
==============================================================================
--- 
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java
 (original)
+++ 
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java
 Mon May 19 23:53:30 2008
@@ -28,9 +28,11 @@
 
     private static final RequestParameter[] EMPTY_PARAM_ARRAY = new 
RequestParameter[0];
 
-    public static final String DEFAULT_IGNORE = SlingPostConstants.RP_PREFIX + 
"ignore";
+    public static final String DEFAULT_IGNORE = SlingPostConstants.RP_PREFIX
+        + "ignore";
 
-    public static final String DEFAULT_NULL = SlingPostConstants.RP_PREFIX + 
"null";
+    public static final String DEFAULT_NULL = SlingPostConstants.RP_PREFIX
+        + "null";
 
     private final String path;
 
@@ -47,7 +49,11 @@
     private RequestParameter[] defaultValues = EMPTY_PARAM_ARRAY;
 
     private boolean isDelete;
-    
+
+    private String repositoryResourcePath;
+
+    private boolean isRepositoryResourceMove;
+
     public RequestProperty(String path) {
         assert path.startsWith("/");
         this.path = ResourceUtil.normalize(path);
@@ -59,7 +65,6 @@
         return typeHint;
     }
 
-
     public void setTypeHint(String typeHint) {
         this.typeHint = typeHint;
     }
@@ -79,7 +84,7 @@
     public boolean hasValues() {
         return values != null;
     }
-    
+
     public RequestParameter[] getValues() {
         return values;
     }
@@ -87,7 +92,7 @@
     public void setValues(RequestParameter[] values) {
         this.values = values;
     }
-    
+
     public RequestParameter[] getDefaultValues() {
         return defaultValues;
     }
@@ -105,9 +110,9 @@
     }
 
     /**
-     * Checks if this property provides any values. this is the case if
-     * one of the values is not empty or if the default handling is not
-     * 'ignore'
+     * Checks if this property provides any values. this is the case if one of
+     * the values is not empty or if the default handling is not 'ignore'
+     * 
      * @return <code>true</code> if this property provides values
      */
     public boolean providesValue() {
@@ -118,7 +123,7 @@
             // get auto-create values
             return true;
         }
-        for (String s: sv) {
+        for (String s : sv) {
             if (!s.equals("")) {
                 return true;
             }
@@ -127,17 +132,19 @@
     }
 
     /**
-     * Returns the assembled string array out of the provided request values
-     * and default values.
+     * Returns the assembled string array out of the provided request values 
and
+     * default values.
+     * 
      * @return a String array or <code>null</code> if the property needs to be
      *         removed.
      */
     public String[] getStringValues() {
         if (stringValues == null) {
             if (values.length > 1) {
-                // TODO: how the default values work for MV props is not very 
clear
+                // TODO: how the default values work for MV props is not very
+                // clear
                 stringValues = new String[values.length];
-                for (int i=0; i<stringValues.length; i++) {
+                for (int i = 0; i < stringValues.length; i++) {
                     stringValues[i] = values[i].getString();
                 }
             } else {
@@ -155,17 +162,82 @@
                         value = defValue;
                     }
                 }
-                stringValues = new String[]{value};
+                stringValues = new String[] { value };
             }
         }
         return stringValues;
     }
 
+    /**
+     * Specifies whether this property should be deleted before any new content
+     * is to be set according to the values stored.
+     * 
+     * @param isDelete <code>true</code> if the repository item described by
+     *            this is to be deleted before any other operation.
+     */
     public void setDelete(boolean isDelete) {
         this.isDelete = isDelete;
     }
 
+    /**
+     * Returns <code>true</code> if the repository item described by this is
+     * to be deleted before setting new content to it.
+     */
     public boolean isDelete() {
         return isDelete;
     }
+
+    /**
+     * Sets the path of the repository item from which the content for this
+     * property is to be copied or moved. The path may be relative in which 
case
+     * it will be resolved relative to the absolute path of this property.
+     * 
+     * @param path The path of the repository item to get the content from
+     * @param isMove <code>true</code> if the source content is to be moved,
+     *            otherwise the source content is copied from the repository
+     *            item.
+     */
+    public void setRepositorySource(String sourcePath, boolean isMove) {
+
+        // make source path absolute
+        if (!sourcePath.startsWith("/")) {
+            sourcePath = getParentPath() + "/" + sourcePath;
+            sourcePath = ResourceUtil.normalize(sourcePath);
+        }
+
+        this.repositoryResourcePath = sourcePath;
+        this.isRepositoryResourceMove = isMove;
+    }
+
+    /**
+     * Returns <code>true</code> if the content of this property is to be set
+     * by moving content from another repository item.
+     * 
+     * @see #getRepositorySource()
+     */
+    public boolean hasRepositoryMoveSource() {
+        return isRepositoryResourceMove;
+    }
+
+    /**
+     * Returns <code>true</code> if the content of this property is to be set
+     * by copying content from another repository item.
+     * 
+     * @see #getRepositorySource()
+     */
+    public boolean hasRepositoryCopySource() {
+        return getRepositorySource() != null && !hasRepositoryMoveSource();
+    }
+
+    /**
+     * Returns the absolute path of the repository item from which the content
+     * for this property is to be copied or moved.
+     * 
+     * @see #hasRepositoryCopySource()
+     * @see #hasRepositoryMoveSource()
+     * @see #setRepositorySource(String, boolean)
+     */
+    public String getRepositorySource() {
+        return repositoryResourcePath;
+    }
 }
\ No newline at end of file

Modified: 
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CopyOperation.java
URL: 
http://svn.apache.org/viewvc/incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CopyOperation.java?rev=658124&r1=658123&r2=658124&view=diff
==============================================================================
--- 
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CopyOperation.java
 (original)
+++ 
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/CopyOperation.java
 Mon May 19 23:53:30 2008
@@ -49,36 +49,83 @@
     @Override
     protected void execute(HtmlResponse response, Session session,
             String source, String dest) throws RepositoryException {
-        copyNode((Node) session.getItem(source),
+        copy((Node) session.getItem(source),
             (Node) session.getItem(ResourceUtil.getParent(dest)),
             ResourceUtil.getName(dest));
         response.onCopied(source, dest);
         log.debug("copy {} to {}", source, dest);
     }
 
-    private void copyNode(Node src, Node dstParent, String name)
+    /**
+     * Copy the <code>src</code> node into the <code>dstParent</code> node.
+     * The name of the newly created node is set to <code>name</code>.
+     * <p>
+     * This method does a recursive (deep) copy of the subtree rooted at the
+     * source node to the destination. Any protected child nodes and and
+     * properties are not copied.
+     * 
+     * @param src The node to copy to the new location
+     * @param dstParent The node into which the <code>src</code> node is to be
+     *            copied
+     * @param name The name of the newly created node. If this is
+     *            <code>null</code> the new node gets the same name as the
+     *            <code>src</code> node.
+     * @throws RepositoryException May be thrown in case of any problem copying
+     *             the content.
+     */
+    static void copy(Node src, Node dstParent, String name)
             throws RepositoryException {
+
+        if (name == null) {
+            name = src.getName();
+        }
+
         // create new node
         Node dst = dstParent.addNode(name, src.getPrimaryNodeType().getName());
         for (NodeType mix : src.getMixinNodeTypes()) {
             dst.addMixin(mix.getName());
         }
+
         // copy the properties
         for (PropertyIterator iter = src.getProperties(); iter.hasNext();) {
-            Property p = iter.nextProperty();
-            if (p.getDefinition().isProtected()) {
-                // skip
-            } else if (p.getDefinition().isMultiple()) {
-                dst.setProperty(p.getName(), p.getValues());
-            } else {
-                dst.setProperty(p.getName(), p.getValue());
-            }
+            copy(iter.nextProperty(), dst, null);
         }
+
         // copy the child nodes
         for (NodeIterator iter = src.getNodes(); iter.hasNext();) {
             Node n = iter.nextNode();
             if (!n.getDefinition().isProtected()) {
-                copyNode(n, dst, n.getName());
+                copy(n, dst, null);
+            }
+        }
+    }
+
+    /**
+     * Copy the <code>src</code> property into the <code>dstParent</code>
+     * node. The name of the newly created property is set to 
<code>name</code>.
+     * <p>
+     * If the source property is protected, this method does nothing.
+     * 
+     * @param src The property to copy to the new location
+     * @param dstParent The node into which the <code>src</code> property is
+     *            to be copied
+     * @param name The name of the newly created property. If this is
+     *            <code>null</code> the new property gets the same name as the
+     *            <code>src</code> property.
+     * @throws RepositoryException May be thrown in case of any problem copying
+     *             the content.
+     */
+    static void copy(Property src, Node dstParent, String name)
+            throws RepositoryException {
+        if (!src.getDefinition().isProtected()) {
+            if (name == null) {
+                name = src.getName();
+            }
+
+            if (src.getDefinition().isMultiple()) {
+                dstParent.setProperty(name, src.getValues());
+            } else {
+                dstParent.setProperty(name, src.getValue());
             }
         }
     }

Modified: 
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ModifyOperation.java
URL: 
http://svn.apache.org/viewvc/incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ModifyOperation.java?rev=658124&r1=658123&r2=658124&view=diff
==============================================================================
--- 
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ModifyOperation.java
 (original)
+++ 
incubator/sling/trunk/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ModifyOperation.java
 Mon May 19 23:53:30 2008
@@ -21,7 +21,9 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.jcr.Item;
 import javax.jcr.Node;
+import javax.jcr.Property;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.servlet.ServletContext;
@@ -81,10 +83,20 @@
         // do not change order unless you have a very good reason.
         Session session = request.getResourceResolver().adaptTo(Session.class);
 
+        // ensure root of new content
         processCreate(session, reqProperties, response);
+
+        // cleanup any old content (@Delete parameters)
         processDeletes(session, reqProperties, response);
+
+        // write content from existing content (@Move/CopyFrom parameters)
+        processMoves(session, reqProperties, response);
+        processCopies(session, reqProperties, response);
+
+        // write content from form
         writeContent(session, reqProperties, response);
 
+        // order content
         String path = response.getPath();
         orderNode(request, session.getItem(path));
     }
@@ -204,6 +216,119 @@
     }
 
     /**
+     * Moves all repository content listed as repository move source in the
+     * request properties to the locations indicated by the resource 
properties.
+     */
+    private void processMoves(Session session,
+            Map<String, RequestProperty> reqProperties, HtmlResponse response)
+            throws RepositoryException {
+
+        for (RequestProperty property : reqProperties.values()) {
+            if (property.hasRepositoryMoveSource()) {
+                processMovesCopiesInternal(property, true, session,
+                    reqProperties, response);
+            }
+        }
+    }
+
+    /**
+     * Copies all repository content listed as repository copy source in the
+     * request properties to the locations indicated by the resource 
properties.
+     */
+    private void processCopies(Session session,
+            Map<String, RequestProperty> reqProperties, HtmlResponse response)
+            throws RepositoryException {
+
+        for (RequestProperty property : reqProperties.values()) {
+            if (property.hasRepositoryCopySource()) {
+                processMovesCopiesInternal(property, false, session,
+                    reqProperties, response);
+            }
+        }
+    }
+
+    /**
+     * Internal implementation of the
+     * [EMAIL PROTECTED] #processCopies(Session, Map, HtmlResponse)} and
+     * [EMAIL PROTECTED] #processMoves(Session, Map, HtmlResponse)} methods 
taking into
+     * account whether the source is actually a property or a node.
+     * <p>
+     * Any intermediary nodes to the destination as indicated by the
+     * <code>property</code> path are created using the
+     * <code>reqProperties</code> as indications for required node types.
+     * 
+     * @param property The [EMAIL PROTECTED] RequestProperty} identifying the 
source
+     *            content of the operation.
+     * @param isMove <code>true</code> if the source item is to be moved.
+     *            Otherwise the source item is just copied.
+     * @param session The repository session to use to access the content
+     * @param reqProperties All accepted request properties. This is used to
+     *            create intermediary nodes along the property path.
+     * @param response The <code>HtmlResponse</code> into which successfull
+     *            copies and moves as well as intermediary node creations are
+     *            recorded.
+     * @throws RepositoryException May be thrown if an error occurrs.
+     */
+    private void processMovesCopiesInternal(RequestProperty property,
+            boolean isMove, Session session,
+            Map<String, RequestProperty> reqProperties, HtmlResponse response)
+            throws RepositoryException {
+
+        String propPath = property.getPath();
+        String source = property.getRepositorySource();
+
+        // only continue here, if the source really exists
+        if (session.itemExists(source)) {
+
+            // if the destination item already exists, remove it
+            // first, otherwise ensure the parent location
+            if (session.itemExists(propPath)) {
+                session.getItem(propPath).remove();
+                response.onDeleted(propPath);
+            } else {
+                deepGetOrCreateNode(session, property.getParentPath(),
+                    reqProperties, response);
+            }
+
+            // move through the session and record operation
+            Item sourceItem = session.getItem(source);
+            if (sourceItem.isNode()) {
+
+                // node move/copy through session
+                if (isMove) {
+                    session.move(source, propPath);
+                } else {
+                    Node sourceNode = (Node) sourceItem;
+                    Node destParent = (Node) 
session.getItem(property.getParentPath());
+                    CopyOperation.copy(sourceNode, destParent,
+                        property.getName());
+                }
+
+            } else {
+
+                // property move manually
+                Property sourceProperty = (Property) sourceItem;
+
+                // create destination property
+                Node destParent = (Node) 
session.getItem(property.getParentPath());
+                CopyOperation.copy(sourceProperty, destParent, null);
+
+                // remove source property (if not just copying)
+                if (isMove) {
+                    sourceProperty.remove();
+                }
+            }
+
+            // record successful move
+            if (isMove) {
+                response.onMoved(source, propPath);
+            } else {
+                response.onCopied(source, propPath);
+            }
+        }
+    }
+
+    /**
      * Removes all properties listed as [EMAIL PROTECTED] 
RequestProperty#isDelete()} from
      * the repository.
      * 
@@ -335,8 +460,7 @@
                     reqProperties, propPath,
                     SlingPostConstants.VALUE_FROM_SUFFIX);
 
-                // @ValueFrom params must have exactly one value, else
-                // ignored
+                // @ValueFrom params must have exactly one value, else ignored
                 if (e.getValue().length == 1) {
                     String refName = e.getValue()[0].getString();
                     RequestParameter[] refValues = 
request.getRequestParameters(refName);
@@ -361,6 +485,44 @@
                 continue;
             }
 
+            // SLING-455: @MoveFrom means moving content to another location
+            // @MoveFrom example:
+            // <input name="./[EMAIL PROTECTED]" type="hidden" 
value="/tmp/path" />
+            // causes the JCR Text property to be set by moving the /tmp/path
+            // property to Text.
+            if (propPath.endsWith(SlingPostConstants.SUFFIX_MOVE_FROM)) {
+                RequestProperty prop = getOrCreateRequestProperty(
+                    reqProperties, propPath,
+                    SlingPostConstants.SUFFIX_MOVE_FROM);
+
+                // @MoveFrom params must have exactly one value, else ignored
+                if (e.getValue().length == 1) {
+                    String sourcePath = e.getValue()[0].getString();
+                    prop.setRepositorySource(sourcePath, true);
+                }
+
+                continue;
+            }
+
+            // SLING-455: @CopyFrom means moving content to another location
+            // @CopyFrom example:
+            // <input name="./[EMAIL PROTECTED]" type="hidden" 
value="/tmp/path" />
+            // causes the JCR Text property to be set by copying the /tmp/path
+            // property to Text.
+            if (propPath.endsWith(SlingPostConstants.SUFFIX_COPY_FROM)) {
+                RequestProperty prop = getOrCreateRequestProperty(
+                    reqProperties, propPath,
+                    SlingPostConstants.SUFFIX_COPY_FROM);
+
+                // @MoveFrom params must have exactly one value, else ignored
+                if (e.getValue().length == 1) {
+                    String sourcePath = e.getValue()[0].getString();
+                    prop.setRepositorySource(sourcePath, false);
+                }
+
+                continue;
+            }
+
             // plain property, create from values
             RequestProperty prop = getOrCreateRequestProperty(reqProperties,
                 propPath, null);


Reply via email to