Author: justin
Date: Thu Feb 25 19:38:07 2010
New Revision: 916419

URL: http://svn.apache.org/viewvc?rev=916419&view=rev
Log:
SLING-1412 - adding support for @IgnoreBlanks

Added:
    
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/RequestPropertyTest.java
Modified:
    sling/trunk/bundles/servlets/post/pom.xml
    
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
    
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java
    
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/SlingPropertyValueHandler.java
    
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ModifyOperation.java

Modified: sling/trunk/bundles/servlets/post/pom.xml
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/pom.xml?rev=916419&r1=916418&r2=916419&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/post/pom.xml (original)
+++ sling/trunk/bundles/servlets/post/pom.xml Thu Feb 25 19:38:07 2010
@@ -102,6 +102,7 @@
         <dependency>
             <groupId>javax.jcr</groupId>
             <artifactId>jcr</artifactId>
+            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
@@ -143,6 +144,12 @@
             <version>2.0.4-incubator</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+               <groupId>junit-addons</groupId>
+               <artifactId>junit-addons</artifactId>
+               <version>1.4</version>
+               <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>

Modified: 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java?rev=916419&r1=916418&r2=916419&view=diff
==============================================================================
--- 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
 (original)
+++ 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
 Thu Feb 25 19:38:07 2010
@@ -108,7 +108,7 @@
      * to indicate how to send the actual response status.
      */
     public static final String OPERATION_NOP = "nop";
-    
+
     /**
      * Name of the request parameter used to indicate the resource to apply the
      * operation to (value is ":applyTo").
@@ -189,7 +189,7 @@
     /**
      * Prefix for properties addressing repository items with an absolute path
      * (value is "/").
-     * 
+     *
      * @see #ITEM_PREFIX_RELATIVE_CURRENT
      */
     public static final String ITEM_PREFIX_ABSOLUTE = "/";
@@ -214,7 +214,7 @@
     /**
      * Prefix for properties addressing repository items with a path relative 
to
      * the parent of the request item (value is "../").
-     * 
+     *
      * @see #ITEM_PREFIX_RELATIVE_CURRENT
      */
     public static final String ITEM_PREFIX_RELATIVE_PARENT = "../";
@@ -239,7 +239,7 @@
      * standard HTTP status codes. This value is assumed as the default value
      * for the {...@link #RP_STATUS} parameter if the parameter is missing or 
not
      * any of the two supported values.
-     * 
+     *
      * @see #RP_STATUS
      * @see #STATUS_VALUE_BROWSER
      */
@@ -250,7 +250,7 @@
      * requesting to not report success or failure of request processing using
      * standard HTTP status codes but instead alwas set the status to 200/OK 
and
      * only report the real success or failure status in the XHTML response.
-     * 
+     *
      * @see #RP_STATUS
      * @see #STATUS_VALUE_STANDARD
      */
@@ -264,21 +264,21 @@
      * valid HTTP status code. If this parameter is missing or the parameter
      * value cannot be converted to a HTTP status code (integer in the range
      * [100..999]), the default status code 200/OK is returned.
-     * 
+     *
      * @see #OPERATION_NOP
      * @see #RP_STATUS
      */
     public static final String RP_NOP_STATUS = RP_PREFIX + "nopstatus";
-    
+
     /**
      * The default response status sent back by a {...@link #OPERATION_NOP} if 
the
      * {...@link #RP_NOP_STATUS} parameter is not provided or the parameter 
value
      * cannot be converted into a valid response status code (value is 200).
-     * 
+     *
      * @see #RP_NOP_STATUS
      */
     public static final int NOPSTATUS_VALUE_DEFAULT = 200;
-    
+
     /**
      * Optional request parameter: if provided, added at the end of the 
computed
      * (or supplied) redirect URL
@@ -334,4 +334,10 @@
      * parameter.
      */
     public static final String SUFFIX_COPY_FROM = "@CopyFrom";
+
+    /**
+     * Suffix indicating that blank value or values for this property will be
+     * ignored.
+     */
+    public static final String SUFFIX_IGNORE_BLANKS = "@IgnoreBlanks";
 }

Modified: 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java?rev=916419&r1=916418&r2=916419&view=diff
==============================================================================
--- 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java
 (original)
+++ 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java
 Thu Feb 25 19:38:07 2010
@@ -16,6 +16,10 @@
  */
 package org.apache.sling.servlets.post.impl.helper;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 import org.apache.sling.api.request.RequestParameter;
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.servlets.post.SlingPostConstants;
@@ -56,6 +60,8 @@
 
     private boolean isRepositoryResourceMove;
 
+    private boolean ignoreBlanks;
+
     public RequestProperty(String path) {
         assert path.startsWith("/");
         this.path = ResourceUtil.normalize(path);
@@ -94,7 +100,11 @@
     }
 
     public boolean hasValues() {
-        return values != null;
+        if (ignoreBlanks) {
+            return values != null && getStringValues().length > 0;
+        } else {
+            return values != null;
+        }
     }
 
     public RequestParameter[] getValues() {
@@ -155,23 +165,31 @@
             if (values.length > 1) {
                 // 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++) {
-                    stringValues[i] = values[i].getString();
+                List<String> stringValueList = new 
ArrayList<String>(values.length);
+                for (int i = 0; i < values.length; i++) {
+                    String value = values[i].getString();
+                    if ((!ignoreBlanks) || value.length() > 0) {
+                        stringValueList.add(value);
+                    }
                 }
+                stringValues = stringValueList.toArray(new 
String[stringValueList.size()]);
             } else {
                 String value = values[0].getString();
                 if (value.equals("")) {
-                    if (defaultValues.length == 1) {
-                        String defValue = defaultValues[0].getString();
-                        if (defValue.equals(DEFAULT_IGNORE)) {
-                            // ignore means, do not create empty values
-                            return new String[0];
-                        } else if (defValue.equals(DEFAULT_NULL)) {
-                            // null means, remove property if exist
-                            return null;
+                    if (ignoreBlanks) {
+                        return new String[0];
+                    } else {
+                        if (defaultValues.length == 1) {
+                            String defValue = defaultValues[0].getString();
+                            if (defValue.equals(DEFAULT_IGNORE)) {
+                                // ignore means, do not create empty values
+                                return new String[0];
+                            } else if (defValue.equals(DEFAULT_NULL)) {
+                                // null means, remove property if exist
+                                return null;
+                            }
+                            value = defValue;
                         }
-                        value = defValue;
                     }
                 }
                 stringValues = new String[] { value };
@@ -252,4 +270,8 @@
     public String getRepositorySource() {
         return repositoryResourcePath;
     }
+
+    public void setIgnoreBlanks(boolean b) {
+        ignoreBlanks = b;
+    }
 }
\ No newline at end of file

Modified: 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/SlingPropertyValueHandler.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/SlingPropertyValueHandler.java?rev=916419&r1=916418&r2=916419&view=diff
==============================================================================
--- 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/SlingPropertyValueHandler.java
 (original)
+++ 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/SlingPropertyValueHandler.java
 Thu Feb 25 19:38:07 2010
@@ -17,6 +17,7 @@
 
 package org.apache.sling.servlets.post.impl.helper;
 
+import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.HashMap;
 import java.util.List;
@@ -201,7 +202,7 @@
                 // ignore
             }
         }
-        final String[] values = prop.getStringValues();
+        String[] values = prop.getStringValues();
         if ( type == PropertyType.UNDEFINED && values != null && values.length 
> 0 ) {
             if ( parent.hasProperty(prop.getName()) ) {
                 type = parent.getProperty(prop.getName()).getType();
@@ -284,6 +285,7 @@
             }
         } else {
             removePropertyIfExists(parent, prop.getName());
+
             if (type == PropertyType.DATE) {
                 // try conversion
                 ValueFactory valFac = parent.getSession().getValueFactory();
@@ -316,6 +318,17 @@
         }
     }
 
+    private String[] filterBlanks(String[] values) {
+        List<String> filteredValues = new ArrayList<String>(values.length);
+        for (String s : values) {
+            if (s.trim().length() > 0) {
+                filteredValues.add(s);
+            }
+        }
+        return filteredValues.toArray(new String[filteredValues.size()]);
+    }
+
+
     private boolean isReferencePropertyType(int propertyType) {
         return propertyType == PropertyType.REFERENCE || propertyType == 
PROPERTY_TYPE_WEAKREFERENCE;
     }

Modified: 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ModifyOperation.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ModifyOperation.java?rev=916419&r1=916418&r2=916419&view=diff
==============================================================================
--- 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ModifyOperation.java
 (original)
+++ 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ModifyOperation.java
 Thu Feb 25 19:38:07 2010
@@ -570,6 +570,26 @@
                 continue;
             }
 
+            // SLING-1412: @IgnoreBlanks
+            // @Ignore example:
+            // <input name="./Text" type="hidden" value="test" />
+            // <input name="./Text" type="hidden" value="" />
+            // <input name="./t...@string[]" type="hidden" value="true" />
+            // <input name="./t...@ignoreblanks" type="hidden" value="true" />
+            // causes the JCR Text property to be set by copying the /tmp/path
+            // property to Text.
+            if (propPath.endsWith(SlingPostConstants.SUFFIX_IGNORE_BLANKS)) {
+                RequestProperty prop = getOrCreateRequestProperty(
+                    reqProperties, propPath,
+                    SlingPostConstants.SUFFIX_IGNORE_BLANKS);
+
+                if (e.getValue().length == 1) {
+                    prop.setIgnoreBlanks(true);
+                }
+
+                continue;
+            }
+
             // plain property, create from values
             RequestProperty prop = getOrCreateRequestProperty(reqProperties,
                 propPath, null);

Added: 
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/RequestPropertyTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/RequestPropertyTest.java?rev=916419&view=auto
==============================================================================
--- 
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/RequestPropertyTest.java
 (added)
+++ 
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/RequestPropertyTest.java
 Thu Feb 25 19:38:07 2010
@@ -0,0 +1,282 @@
+/*
+ * 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.servlets.post.impl;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+import junitx.util.PrivateAccessor;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.request.RequestParameter;
+import org.apache.sling.api.request.RequestParameterMap;
+import org.apache.sling.api.servlets.HtmlResponse;
+import org.apache.sling.servlets.post.impl.helper.RequestProperty;
+import org.apache.sling.servlets.post.impl.operations.ModifyOperation;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * This is primary a series of tests of the hasValues(), providesValues(), and 
getStringValues() methods of
+ * RequestProperty. It uses the collectContent() method of ModifyOperation to 
make the test cases more readable.
+ */
+...@runwith(JMock.class)
+public class RequestPropertyTest {
+
+    private Mockery context = new JUnit4Mockery();
+
+    @Test
+    public void testSingleValue() throws Throwable {
+        Map<String, RequestProperty> props = collectContent(p("./param", 
"true"));
+
+        assertEquals(1, props.size());
+        assertTrue(props.get("/test/path/param").hasValues());
+        assertTrue(props.get("/test/path/param").providesValue());
+        assertEquals(1, 
props.get("/test/path/param").getStringValues().length);
+        assertEquals("true", 
props.get("/test/path/param").getStringValues()[0]);
+    }
+
+    @Test
+    public void testSingleValueWithBlank() throws Throwable {
+        Map<String, RequestProperty> props = collectContent(p("./param", ""));
+
+        assertEquals(1, props.size());
+        RequestProperty prop = props.get("/test/path/param");
+        assertTrue(prop.hasValues());
+        assertFalse(prop.providesValue());
+        assertEquals(1, prop.getStringValues().length);
+        assertEquals("", prop.getStringValues()[0]);
+    }
+
+    @Test
+    public void testNullSingleValueWithDefaultToIgnore() throws Throwable {
+        Map<String, RequestProperty> props = 
collectContent(p("./pa...@defaultvalue", ":ignore"));
+
+        assertEquals(1, props.size());
+        RequestProperty prop = props.get("/test/path/param");
+        assertFalse(prop.hasValues());
+    }
+
+    @Test
+    public void testSingleValueWithDefaultToIgnore() throws Throwable {
+        Map<String, RequestProperty> props = collectContent(p("./param", ""), 
p("./pa...@defaultvalue", ":ignore"));
+
+        assertEquals(1, props.size());
+        RequestProperty prop = props.get("/test/path/param");
+        assertTrue(prop.hasValues());
+        assertFalse(prop.providesValue());
+        assertEquals(0, prop.getStringValues().length);
+    }
+
+    @Test
+    public void testSingleValueWithDefaultToNull() throws Throwable {
+        Map<String, RequestProperty> props = collectContent(p("./param", ""), 
p("./pa...@defaultvalue", ":null"));
+
+        assertEquals(1, props.size());
+        RequestProperty prop = props.get("/test/path/param");
+        assertTrue(prop.hasValues());
+        assertTrue(prop.providesValue());
+        assertNull(prop.getStringValues());
+    }
+
+    @Test
+    public void testSingleValueIgnoringBlanks() throws Throwable {
+        Map<String, RequestProperty> props = collectContent(p("./param", ""), 
p("./pa...@ignoreblanks", "true"));
+
+        assertEquals(1, props.size());
+        RequestProperty prop = props.get("/test/path/param");
+        assertFalse(prop.hasValues());
+    }
+
+    @Test
+    public void testMultiValue() throws Throwable {
+        Map<String, RequestProperty> props = collectContent(p("./param", 
"true", "false"));
+
+        assertEquals(1, props.size());
+        RequestProperty prop = props.get("/test/path/param");
+        assertTrue(prop.hasValues());
+        assertTrue(prop.providesValue());
+        assertEquals(2, prop.getStringValues().length);
+        assertEquals("true", prop.getStringValues()[0]);
+        assertEquals("false", prop.getStringValues()[1]);
+    }
+
+    @Test
+    public void testMultiValueWithBlank() throws Throwable {
+        Map<String, RequestProperty> props = collectContent(p("./param", 
"true", ""));
+
+        assertEquals(1, props.size());
+        RequestProperty prop = props.get("/test/path/param");
+        assertTrue(prop.hasValues());
+        assertTrue(prop.providesValue());
+        assertEquals(2, prop.getStringValues().length);
+        assertEquals("true", prop.getStringValues()[0]);
+        assertEquals("", prop.getStringValues()[1]);
+    }
+
+    @Test
+    public void testMultiValueWithBlanks() throws Throwable {
+        Map<String, RequestProperty> props = collectContent(p("./param", 
"true", "", ""));
+
+        assertEquals(1, props.size());
+        RequestProperty prop = props.get("/test/path/param");
+        assertTrue(prop.hasValues());
+        assertTrue(prop.providesValue());
+        assertEquals(3, prop.getStringValues().length);
+        assertEquals("true", prop.getStringValues()[0]);
+        assertEquals("", prop.getStringValues()[1]);
+        assertEquals("", prop.getStringValues()[2]);
+    }
+
+    @Test
+    public void testMultiValueWithAllBlanks() throws Throwable {
+        Map<String, RequestProperty> props = collectContent(p("./param", "", 
"", ""));
+
+        assertEquals(1, props.size());
+        RequestProperty prop = props.get("/test/path/param");
+        assertTrue(prop.hasValues());
+        assertFalse(prop.providesValue());
+        assertEquals(3, prop.getStringValues().length);
+        assertEquals("", prop.getStringValues()[0]);
+        assertEquals("", prop.getStringValues()[1]);
+        assertEquals("", prop.getStringValues()[2]);
+    }
+
+    @Test
+    public void testMultiValueWithBlankIgnoringBlanks() throws Throwable {
+        Map<String, RequestProperty> props = collectContent(p("./param", 
"true", ""));
+
+        assertEquals(1, props.size());
+        RequestProperty prop = props.get("/test/path/param");
+        assertTrue(prop.hasValues());
+        assertTrue(prop.providesValue());
+        assertEquals(2, prop.getStringValues().length);
+        assertEquals("true", prop.getStringValues()[0]);
+        assertEquals("", prop.getStringValues()[1]);
+    }
+
+    @Test
+    public void testMultiValueWithBlanksIgnoringBlanks() throws Throwable {
+        Map<String, RequestProperty> props = collectContent(p("./param", 
"true", "", ""), p("./pa...@ignoreblanks", "true"));
+
+        assertEquals(1, props.size());
+        RequestProperty prop = props.get("/test/path/param");
+        assertTrue(prop.hasValues());
+        assertTrue(prop.providesValue());
+        assertEquals(1, prop.getStringValues().length);
+        assertEquals("true", prop.getStringValues()[0]);
+    }
+
+    @Test
+    public void testMultiValueWithAllBlanksIgnoringBlanks() throws Throwable {
+        Map<String, RequestProperty> props = collectContent(p("./param", "", 
"", ""), p("./pa...@ignoreblanks", "true"));
+
+        assertEquals(1, props.size());
+        RequestProperty prop = props.get("/test/path/param");
+        assertFalse(prop.hasValues());
+    }
+
+    private static final Class[] COLLECT_CLASSES = new Class[] { 
SlingHttpServletRequest.class, HtmlResponse.class };
+
+    private class Param {
+        String key;
+        String[] value;
+    }
+
+    private Param p(String key, String... value) {
+        Param kv = new Param();
+        kv.key = key;
+        kv.value = value;
+        return kv;
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map<String, RequestProperty> collectContent(Param... kvs) throws 
Throwable {
+        final List<Map.Entry<String, RequestParameter>> params = new 
ArrayList<Map.Entry<String, RequestParameter>>();
+        for (final Param kv : kvs) {
+            final RequestParameter[] param = new 
RequestParameter[kv.value.length];
+            for (int i = 0; i < kv.value.length; i++) {
+                final String strValue = kv.value[i];
+                final RequestParameter aparam = 
context.mock(RequestParameter.class);
+                context.checking(new Expectations() {
+                    {
+                        allowing(aparam).getString();
+                        will(returnValue(strValue));
+                    }
+                });
+                param[i] = aparam;
+            }
+            final Map.Entry<String, RequestParameter> entry = 
context.mock(Map.Entry.class);
+            context.checking(new Expectations() {
+                {
+                    allowing(entry).getKey();
+                    will(returnValue(kv.key));
+                    allowing(entry).getValue();
+                    will(returnValue(param));
+
+                }
+            });
+            params.add(entry);
+        }
+
+        final Set set = context.mock(Set.class);
+        context.checking(new Expectations() {
+            {
+                one(set).iterator();
+                will(returnValue(params.iterator()));
+            }
+        });
+
+        final RequestParameterMap map = 
context.mock(RequestParameterMap.class);
+        context.checking(new Expectations() {
+            {
+                one(map).entrySet();
+                will(returnValue(set));
+
+            }
+        });
+
+        final SlingHttpServletRequest request = 
context.mock(SlingHttpServletRequest.class);
+        context.checking(new Expectations() {
+            {
+                Vector names = new Vector();
+                names.add("./param");
+
+                one(request).getParameterNames();
+                will(returnValue(names.elements()));
+                one(request).getRequestParameterMap();
+                will(returnValue(map));
+
+            }
+        });
+        final HtmlResponse response = new HtmlResponse();
+        response.setPath("/test/path");
+
+        Map<String, RequestProperty> props = (Map<String, RequestProperty>) 
PrivateAccessor.invoke(new ModifyOperation(
+                null, null, null), "collectContent", COLLECT_CLASSES, new 
Object[] { request, response });
+        return props;
+    }
+}


Reply via email to