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;
+ }
+}