Author: hlship
Date: Wed Feb 24 00:52:43 2010
New Revision: 915625
URL: http://svn.apache.org/viewvc?rev=915625&view=rev
Log:
TAP5-1028: Validator Macros: Combine multiple common validators into a single
term
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/validator/
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/validator/ValidatorMacroImpl.java
(with props)
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/validator/ValidatorMacro.java
(with props)
Modified:
tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/beaneditor/Validate.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/FieldValidatorSourceImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/FieldValidatorSourceImplTest.java
Modified:
tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/beaneditor/Validate.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/beaneditor/Validate.java?rev=915625&r1=915624&r2=915625&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/beaneditor/Validate.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/beaneditor/Validate.java
Wed Feb 24 00:52:43 2010
@@ -1,10 +1,10 @@
-// Copyright 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2007, 2008, 2009, 2010 The Apache Software Foundation
//
// Licensed 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
+// 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,
@@ -14,29 +14,34 @@
package org.apache.tapestry5.beaneditor;
-import org.apache.tapestry5.ioc.annotations.UseWith;
-import org.apache.tapestry5.ioc.annotations.AnnotationUseContext;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.BEAN;
+import static
org.apache.tapestry5.ioc.annotations.AnnotationUseContext.COMPONENT;
+import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.MIXIN;
+import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.PAGE;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
-import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.*;
+import org.apache.tapestry5.ioc.annotations.UseWith;
/**
* Used to attach validation constraints directly to a property (either the
getter or the setter method). The annotation
* value is a comma separated list of <em>validation constraints</em>, each
one identifying a validator type (such as
* "required", "minlength") and optionally, a constraint value. Most
validators need a constraint value, which is
- * separated from the type by an equals size (i.e., "maxlength=30").
+ * separated from the type by an equals size (i.e., "maxlength=30"). In
addition, the value may include
+ * validator macros.
* <p/>
* May be placed on any getter or setter method, or on the matching field.
*/
-...@target({ElementType.FIELD, ElementType.METHOD})
+...@target(
+{ ElementType.FIELD, ElementType.METHOD })
@Retention(RUNTIME)
@Documented
-...@usewith({BEAN,COMPONENT,MIXIN,PAGE})
+...@usewith(
+{ BEAN, COMPONENT, MIXIN, PAGE })
public @interface Validate
{
String value();
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/FieldValidatorSourceImpl.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/FieldValidatorSourceImpl.java?rev=915625&r1=915624&r2=915625&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/FieldValidatorSourceImpl.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/FieldValidatorSourceImpl.java
Wed Feb 24 00:52:43 2010
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2010 The Apache Software Foundation
//
// Licensed 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
+// 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,
@@ -14,26 +14,30 @@
package org.apache.tapestry5.internal.services;
+import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newList;
+import static org.apache.tapestry5.ioc.internal.util.Defense.cast;
+import static org.apache.tapestry5.ioc.internal.util.Defense.notBlank;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.Field;
import org.apache.tapestry5.FieldValidator;
import org.apache.tapestry5.Validator;
import org.apache.tapestry5.ioc.MessageFormatter;
import org.apache.tapestry5.ioc.Messages;
-import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newList;
-import static org.apache.tapestry5.ioc.internal.util.Defense.cast;
-import static org.apache.tapestry5.ioc.internal.util.Defense.notBlank;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.services.TypeCoercer;
import org.apache.tapestry5.runtime.Component;
import org.apache.tapestry5.services.FieldValidatorSource;
import org.apache.tapestry5.services.FormSupport;
import org.apache.tapestry5.services.ValidationMessagesSource;
+import org.apache.tapestry5.validator.ValidatorMacro;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
+...@suppresswarnings("unchecked")
public class FieldValidatorSourceImpl implements FieldValidatorSource
{
private final ValidationMessagesSource messagesSource;
@@ -44,13 +48,16 @@
private final FormSupport formSupport;
+ private final ValidatorMacro validatorMacro;
+
public FieldValidatorSourceImpl(ValidationMessagesSource messagesSource,
TypeCoercer typeCoercer,
- FormSupport formSupport, Map<String,
Validator> validators)
+ FormSupport formSupport, Map<String, Validator> validators,
ValidatorMacro validatorMacro)
{
this.messagesSource = messagesSource;
this.typeCoercer = typeCoercer;
this.formSupport = formSupport;
this.validators = validators;
+ this.validatorMacro = validatorMacro;
}
public FieldValidator createValidator(Field field, String validatorType,
String constraintValue)
@@ -71,15 +78,15 @@
}
public FieldValidator createValidator(Field field, String validatorType,
String constraintValue, String overrideId,
- Messages overrideMessages, Locale
locale)
+ Messages overrideMessages, Locale locale)
{
notBlank(validatorType, "validatorType");
Validator validator = validators.get(validatorType);
if (validator == null)
- throw new IllegalArgumentException(
- ServicesMessages.unknownValidatorType(validatorType,
InternalUtils.sortedKeys(validators)));
+ throw new
IllegalArgumentException(ServicesMessages.unknownValidatorType(validatorType,
InternalUtils
+ .sortedKeys(validators)));
// I just have this thing about always treating parameters as finals,
so
// we introduce a second variable to treat a mutable.
@@ -87,66 +94,67 @@
String formValidationid = formSupport.getFormValidationId();
Object coercedConstraintValue = computeConstraintValue(validatorType,
validator, constraintValue,
-
formValidationid,
- overrideId,
-
overrideMessages);
+ formValidationid, overrideId, overrideMessages);
MessageFormatter formatter = findMessageFormatter(formValidationid,
overrideId, overrideMessages, locale,
- validatorType,
- validator);
+ validatorType, validator);
return new FieldValidatorImpl(field, coercedConstraintValue,
formatter, validator, formSupport);
}
private Object computeConstraintValue(String validatorType, Validator
validator, String constraintValue,
- String formId, String overrideId,
- Messages overrideMessages)
+ String formId, String overrideId, Messages overrideMessages)
{
Class constraintType = validator.getConstraintType();
String constraintText = findConstraintValue(validatorType,
constraintType, constraintValue, formId, overrideId,
- overrideMessages);
+ overrideMessages);
- if (constraintText == null) return null;
+ if (constraintText == null)
+ return null;
return typeCoercer.coerce(constraintText, constraintType);
}
private String findConstraintValue(String validatorType, Class
constraintType, String constraintValue,
- String formValidationId, String
overrideId,
- Messages overrideMessages)
+ String formValidationId, String overrideId, Messages
overrideMessages)
{
- if (constraintValue != null) return constraintValue;
+ if (constraintValue != null)
+ return constraintValue;
- if (constraintType == null) return null;
+ if (constraintType == null)
+ return null;
// If no constraint was provided, check to see if it is available via
a localized message
// key. This is really handy for complex validations such as patterns.
String perFormKey = formValidationId + "-" + overrideId + "-" +
validatorType;
- if (overrideMessages.contains(perFormKey)) return
overrideMessages.get(perFormKey);
+ if (overrideMessages.contains(perFormKey))
+ return overrideMessages.get(perFormKey);
String generalKey = overrideId + "-" + validatorType;
- if (overrideMessages.contains(generalKey)) return
overrideMessages.get(generalKey);
+ if (overrideMessages.contains(generalKey))
+ return overrideMessages.get(generalKey);
- throw new IllegalArgumentException(
- ServicesMessages.missingValidatorConstraint(validatorType,
constraintType, perFormKey, generalKey));
+ throw new
IllegalArgumentException(ServicesMessages.missingValidatorConstraint(validatorType,
constraintType,
+ perFormKey, generalKey));
}
private MessageFormatter findMessageFormatter(String formId, String
overrideId, Messages overrideMessages,
- Locale locale,
- String validatorType,
Validator validator)
+ Locale locale, String validatorType, Validator validator)
{
String overrideKey = formId + "-" + overrideId + "-" + validatorType +
"-message";
- if (overrideMessages.contains(overrideKey)) return
overrideMessages.getFormatter(overrideKey);
+ if (overrideMessages.contains(overrideKey))
+ return overrideMessages.getFormatter(overrideKey);
overrideKey = overrideId + "-" + validatorType + "-message";
- if (overrideMessages.contains(overrideKey)) return
overrideMessages.getFormatter(overrideKey);
+ if (overrideMessages.contains(overrideKey))
+ return overrideMessages.getFormatter(overrideKey);
Messages messages = messagesSource.getValidationMessages(locale);
@@ -157,21 +165,70 @@
public FieldValidator createValidators(Field field, String specification)
{
- List<ValidatorSpecification> specs = parse(specification);
+ List<ValidatorSpecification> specs =
toValidatorSpecifications(specification);
- List<FieldValidator> fieldValidators = newList();
+ List<FieldValidator> fieldValidators = CollectionFactory.newList();
for (ValidatorSpecification spec : specs)
{
- fieldValidators.add(createValidator(field,
spec.getValidatorType(), spec
- .getConstraintValue()));
+ fieldValidators.add(createValidator(field,
spec.getValidatorType(), spec.getConstraintValue()));
}
- if (fieldValidators.size() == 1) return fieldValidators.get(0);
+ if (fieldValidators.size() == 1)
+ return fieldValidators.get(0);
return new CompositeFieldValidator(fieldValidators);
}
+ List<ValidatorSpecification> toValidatorSpecifications(String
specification)
+ {
+ return expandMacros(parse(specification));
+ }
+
+ private List<ValidatorSpecification>
expandMacros(List<ValidatorSpecification> specs)
+ {
+ Map<String, Boolean> expandedMacros =
CollectionFactory.newCaseInsensitiveMap();
+ List<ValidatorSpecification> queue = CollectionFactory.newList(specs);
+ List<ValidatorSpecification> result = CollectionFactory.newList();
+
+ while (!queue.isEmpty())
+ {
+ ValidatorSpecification head = queue.remove(0);
+
+ String validatorType = head.getValidatorType();
+
+ String expanded = validatorMacro.valueForMacro(validatorType);
+ if (expanded != null)
+ {
+ if (head.getConstraintValue() != null)
+ throw new RuntimeException(String.format(
+ "'%s' is a validator macro, not a validator, and
can not have a constraint value.",
+ validatorType));
+
+ if (expandedMacros.containsKey(validatorType))
+ throw new RuntimeException(String.format("Validator macro
'%s' appears more than once.",
+ validatorType));
+
+ expandedMacros.put(validatorType, true);
+
+ List<ValidatorSpecification> parsed = parse(expanded);
+
+ // Add the new validator specifications to the front of the
queue, replacing the validator macro
+
+ for (int i = 0; i < parsed.size(); i++)
+ {
+ queue.add(i, parsed.get(i));
+ }
+ }
+ else
+ {
+ result.add(head);
+ }
+ }
+
+ return result;
+ }
+
/**
* A code defining what the parser is looking for.
*/
@@ -328,7 +385,7 @@
result.add(new ValidatorSpecification(type));
break;
- // Case when the specification ends with an equals sign.
+ // Case when the specification ends with an equals sign.
case VALUE_START:
result.add(new ValidatorSpecification(type, ""));
@@ -339,7 +396,7 @@
result.add(new ValidatorSpecification(type,
specification.substring(start)));
break;
- // For better or worse, ending the string with a comma is
valid.
+ // For better or worse, ending the string with a comma is valid.
default:
}
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/validator/ValidatorMacroImpl.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/validator/ValidatorMacroImpl.java?rev=915625&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/validator/ValidatorMacroImpl.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/validator/ValidatorMacroImpl.java
Wed Feb 24 00:52:43 2010
@@ -0,0 +1,35 @@
+// Copyright 2010 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.internal.validator;
+
+import java.util.Map;
+
+import org.apache.tapestry5.validator.ValidatorMacro;
+
+public class ValidatorMacroImpl implements ValidatorMacro
+{
+ private final Map<String, String> configuration;
+
+ public ValidatorMacroImpl(Map<String, String> configuration)
+ {
+ this.configuration = configuration;
+ }
+
+ public String valueForMacro(String validatorMacro)
+ {
+ return configuration.get(validatorMacro);
+ }
+
+}
Propchange:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/validator/ValidatorMacroImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=915625&r1=915624&r2=915625&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
Wed Feb 24 00:52:43 2010
@@ -16,7 +16,6 @@
import java.io.IOException;
import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
@@ -81,6 +80,7 @@
import org.apache.tapestry5.internal.util.PrimaryKeyEncoder2ValueEncoder;
import org.apache.tapestry5.internal.util.RenderableAsBlock;
import org.apache.tapestry5.internal.util.StringRenderable;
+import org.apache.tapestry5.internal.validator.ValidatorMacroImpl;
import org.apache.tapestry5.ioc.*;
import org.apache.tapestry5.ioc.annotations.*;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
@@ -104,6 +104,7 @@
import org.apache.tapestry5.validator.MinLength;
import org.apache.tapestry5.validator.Regexp;
import org.apache.tapestry5.validator.Required;
+import org.apache.tapestry5.validator.ValidatorMacro;
import org.slf4j.Logger;
/**
@@ -349,6 +350,7 @@
binder.bind(Dispatcher.class,
AssetProtectionDispatcher.class).withId("AssetProtectionDispatcher");
binder.bind(AssetPathAuthorizer.class,
WhitelistAuthorizer.class).withId("WhitelistAuthorizer");
binder.bind(AssetPathAuthorizer.class,
RegexAuthorizer.class).withId("RegexAuthorizer");
+ binder.bind(ValidatorMacro.class, ValidatorMacroImpl.class);
}
// ========================================================================
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/validator/ValidatorMacro.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/validator/ValidatorMacro.java?rev=915625&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/validator/ValidatorMacro.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/validator/ValidatorMacro.java
Wed Feb 24 00:52:43 2010
@@ -0,0 +1,35 @@
+// Copyright 2010 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.validator;
+
+import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
+
+/**
+ * Allows support for "validator macros", a simple-minded way of combining
several related valiations together under
+ * a single name. The service's configuration maps string keys (macro names)
to string values (validation constraints).
+ *
+ * @since 5.2.0
+ */
+...@usesmappedconfiguration(String.class)
+public interface ValidatorMacro
+{
+ /**
+ * Given a <em>potential</em> validator macro (a simple string name),
returns the value for that macro, a
+ * comma-separated list of validation constraints.
+ *
+ * @return constraints, or null if no such validator macro
+ */
+ String valueForMacro(String validatorMacro);
+}
Propchange:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/validator/ValidatorMacro.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/FieldValidatorSourceImplTest.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/FieldValidatorSourceImplTest.java?rev=915625&r1=915624&r2=915625&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/FieldValidatorSourceImplTest.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/FieldValidatorSourceImplTest.java
Wed Feb 24 00:52:43 2010
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2010 The Apache Software Foundation
//
// Licensed 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
+// 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,
@@ -22,11 +22,15 @@
import org.apache.tapestry5.ioc.MessageFormatter;
import org.apache.tapestry5.ioc.Messages;
import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newMap;
+
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.services.TypeCoercer;
import org.apache.tapestry5.runtime.Component;
import org.apache.tapestry5.services.FieldValidatorSource;
import org.apache.tapestry5.services.FormSupport;
import org.apache.tapestry5.services.ValidationMessagesSource;
+import org.apache.tapestry5.validator.ValidatorMacro;
+import org.easymock.EasyMock;
import org.testng.annotations.Test;
import java.util.Arrays;
@@ -63,7 +67,7 @@
replay();
- FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, null, map);
+ FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, null, map, null);
try
{
@@ -119,7 +123,7 @@
replay();
- FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map);
+ FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map, null);
FieldValidator fieldValidator = source.createValidator(field,
"required", null);
@@ -164,7 +168,7 @@
replay();
- FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map);
+ FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map, null);
FieldValidator fieldValidator = source.createValidator(field,
"required", null);
@@ -207,7 +211,7 @@
replay();
- FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map);
+ FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map, null);
FieldValidator fieldValidator = source.createValidator(field,
"required", null);
@@ -231,7 +235,6 @@
Messages containerMessages = mockMessages();
FormSupport fs = mockFormSupport();
-
Map<String, Validator> map = singletonMap("minlength", validator);
train_getConstraintType(validator, Integer.class);
@@ -262,9 +265,12 @@
train_getValueType(validator, Object.class);
validator.validate(field, 5, formatter, inputValue);
+ ValidatorMacro macro = mockValidatorMacro();
+ train_alwaysNull(macro);
+
replay();
- FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map);
+ FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map, macro);
FieldValidator fieldValidator = source.createValidators(field,
"minlength");
@@ -288,7 +294,6 @@
Messages containerMessages = mockMessages();
FormSupport fs = mockFormSupport();
-
Map<String, Validator> map = singletonMap("minlength", validator);
train_getConstraintType(validator, Integer.class);
@@ -318,9 +323,12 @@
train_getValueType(validator, Object.class);
validator.validate(field, 5, formatter, inputValue);
+ ValidatorMacro macro = mockValidatorMacro();
+ train_alwaysNull(macro);
+
replay();
- FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map);
+ FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map, macro);
FieldValidator fieldValidator = source.createValidators(field,
"minlength");
@@ -329,7 +337,6 @@
verify();
}
-
@SuppressWarnings("unchecked")
@Test
public void missing_field_validator_constraint() throws Exception
@@ -356,9 +363,12 @@
train_contains(containerMessages, "myform-fred-minlength", false);
train_contains(containerMessages, "fred-minlength", false);
+ ValidatorMacro macro = mockValidatorMacro();
+ train_alwaysNull(macro);
+
replay();
- FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map);
+ FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map, macro);
try
{
@@ -367,8 +377,9 @@
}
catch (IllegalArgumentException ex)
{
- assertEquals(ex.getMessage(),
- "Validator 'minlength' requires a validation
constraint (of type java.lang.Integer) but none was provided. The constraint
may be provided inside the @Validator annotaton on the property, or in the
associated component message catalog as key 'myform-fred-minlength' or key
'fred-minlength'. ");
+ assertEquals(
+ ex.getMessage(),
+ "Validator 'minlength' requires a validation constraint
(of type java.lang.Integer) but none was provided. The constraint may be
provided inside the @Validator annotaton on the property, or in the associated
component message catalog as key 'myform-fred-minlength' or key
'fred-minlength'. ");
}
verify();
@@ -413,9 +424,12 @@
train_getValueType(validator, Object.class);
validator.validate(field, null, formatter, inputValue);
+ ValidatorMacro macro = mockValidatorMacro();
+ train_alwaysNull(macro);
+
replay();
- FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map);
+ FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map, macro);
FieldValidator fieldValidator = source.createValidators(field,
"required");
@@ -424,6 +438,89 @@
verify();
}
+ private void train_alwaysNull(ValidatorMacro macro)
+ {
+
expect(macro.valueForMacro(EasyMock.isA(String.class))).andReturn(null).anyTimes();
+ }
+
+ private ValidatorMacro mockValidatorMacro()
+ {
+ return newMock(ValidatorMacro.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void simple_macro_expansion() throws Exception
+ {
+
+ ValidatorMacro macro = mockValidatorMacro();
+ expect(macro.valueForMacro("combo")).andReturn("required,minlength=5");
+ expect(macro.valueForMacro("required")).andReturn(null);
+ expect(macro.valueForMacro("minlength")).andReturn(null);
+
+ replay();
+
+ FieldValidatorSourceImpl source = new FieldValidatorSourceImpl(null,
null, null, null, macro);
+
+ List<ValidatorSpecification> specs =
source.toValidatorSpecifications("combo");
+
+ assertListsEquals(specs, new ValidatorSpecification("required"), new
ValidatorSpecification("minlength", "5"));
+
+ verify();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void macros_can_not_have_constraints() throws Exception
+ {
+
+ ValidatorMacro macro = mockValidatorMacro();
+ expect(macro.valueForMacro("combo")).andReturn("required,minlength=5");
+
+ replay();
+
+ FieldValidatorSourceImpl source = new FieldValidatorSourceImpl(null,
null, null, null, macro);
+
+ try
+ {
+ source.toValidatorSpecifications("combo=3");
+ unreachable();
+ }
+ catch (RuntimeException ex)
+ {
+ assertEquals(ex.getMessage(),
+ "'combo' is a validator macro, not a validator, and can
not have a constraint value.");
+ }
+
+ verify();
+ }
+
+ @Test
+ public void recursive_macros_are_caught()
+ {
+
+ ValidatorMacro macro = mockValidatorMacro();
+ expect(macro.valueForMacro("combo")).andReturn("required,combo");
+ expect(macro.valueForMacro("required")).andReturn(null);
+ expect(macro.valueForMacro("combo")).andReturn("required,combo");
+
+ replay();
+
+ FieldValidatorSourceImpl source = new FieldValidatorSourceImpl(null,
null, null, null, macro);
+
+ try
+ {
+ source.toValidatorSpecifications("combo");
+ unreachable();
+ }
+ catch (RuntimeException ex)
+ {
+ assertEquals(ex.getMessage(), "Validator macro 'combo' appears
more than once.");
+ }
+
+ verify();
+ }
+
@SuppressWarnings("unchecked")
@Test
public void multiple_validators_via_specification() throws Exception
@@ -481,9 +578,12 @@
train_getValueType(minLength, String.class);
minLength.validate(field, fifteen, minLengthFormatter, inputValue);
+ ValidatorMacro macro = mockValidatorMacro();
+ train_alwaysNull(macro);
+
replay();
- FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map);
+ FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map, macro);
FieldValidator fieldValidator = source.createValidators(field,
"required,minLength=15");
@@ -535,7 +635,7 @@
replay();
- FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map);
+ FieldValidatorSource source = new
FieldValidatorSourceImpl(messagesSource, coercer, fs, map, null);
FieldValidator fieldValidator = source.createValidator(field,
"minLength", "5");
@@ -571,22 +671,22 @@
@Test
public void ignore_whitespace_around_type_name()
{
- test(" required , email ", new ValidatorSpecification("required",
null),
- new ValidatorSpecification("email", null));
+ test(" required , email ", new ValidatorSpecification("required",
null), new ValidatorSpecification(
+ "email", null));
}
@Test
public void parse_simple_type_with_value()
{
test("minLength=5,sameAs=otherComponentId", new
ValidatorSpecification("minLength", "5"),
- new ValidatorSpecification("sameAs", "otherComponentId"));
+ new ValidatorSpecification("sameAs", "otherComponentId"));
}
@Test
public void whitespace_ignored_around_value()
{
test("minLength= 5 , sameAs = otherComponentId ", new
ValidatorSpecification("minLength", "5"),
- new ValidatorSpecification("sameAs", "otherComponentId"));
+ new ValidatorSpecification("sameAs", "otherComponentId"));
}
@Test
@@ -620,7 +720,7 @@
catch (RuntimeException ex)
{
assertEquals(ex.getMessage(),
- "Unexpected character '.' at position 13 of input
string: minLength=3 . email");
+ "Unexpected character '.' at position 13 of input string:
minLength=3 . email");
}
}
}