You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bval.apache.org by mb...@apache.org on 2010/09/07 21:47:07 UTC
svn commit: r993503 - in /incubator/bval/sandbox/lang3-work/bval-jsr303/src:
main/java/ main/java/org/apache/bval/jsr303/
test/java/org/apache/bval/jsr303/
Author: mbenson
Date: Tue Sep 7 19:47:07 2010
New Revision: 993503
URL: http://svn.apache.org/viewvc?rev=993503&view=rev
Log:
add CascadingPropertyValidator interface to allow cascading property/value validations; make ClassValidator implement this new interface, and test it
Added:
incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/CascadingPropertyValidator.java (with props)
Modified:
incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/ (props changed)
incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java
incubator/bval/sandbox/lang3-work/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidationTest.java
Propchange: incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Tue Sep 7 19:47:07 2010
@@ -1 +1 @@
-/incubator/bval/trunk/bval-jsr303/src/main/java:992330-992353,992401,992406,992412,992510
+/incubator/bval/trunk/bval-jsr303/src/main/java:992330-992353,992401,992406,992412,992510,992648,993404-993438
Added: incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/CascadingPropertyValidator.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/CascadingPropertyValidator.java?rev=993503&view=auto
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/CascadingPropertyValidator.java (added)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/CascadingPropertyValidator.java Tue Sep 7 19:47:07 2010
@@ -0,0 +1,69 @@
+/**
+ * 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.bval.jsr303;
+
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Valid;
+import javax.validation.Validator;
+
+/**
+ * Per the bean validation spec, {@link Valid} is not honored by the
+ * {@link #validateProperty(Object, String, Class...)} and
+ * {@link #validateValue(Class, String, Object, Class...)} methods. The
+ * {@link CascadingPropertyValidator} interface thus defines a {@link Validator} that
+ * provides corresponding methods that <em>may</em> honor {@link Valid}.
+ * It should be noted that {@link Validator#validateProperty(Object, String, Class...)}
+ * and {@link Validator#validateValue(Class, String, Object, Class...)} are assumed
+ * semantically equivalent to calling the {@link CascadingPropertyValidator}-defined
+ * methods with <code>cascade == false</code>.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface CascadingPropertyValidator extends Validator {
+
+ /**
+ * Validates all constraints placed on <code>object</code>'s
+ * <code>propertyName</code> property, with optional validation cascading.
+ *
+ * @param <T>
+ * @param object
+ * @param propertyName
+ * @param cascade
+ * @param groups
+ * @return the resulting {@link Set} of {@link ConstraintViolation}s.
+ */
+ <T extends Object> java.util.Set<javax.validation.ConstraintViolation<T>> validateProperty(T object,
+ String propertyName, boolean cascade, java.lang.Class<?>... groups);
+
+ /**
+ * Validates all constraints placed on <code>object</code>'s
+ * <code>propertyName</code> property, with optional validation cascading,
+ * given a hypothetical property <code>value</code>.
+ *
+ * @param <T>
+ * @param beanType
+ * @param propertyName
+ * @param value
+ * @param cascade
+ * @param groups
+ * @return the resulting {@link Set} of {@link ConstraintViolation}s.
+ */
+ <T extends Object> java.util.Set<javax.validation.ConstraintViolation<T>> validateValue(
+ java.lang.Class<T> beanType, String propertyName, Object value, boolean cascade, java.lang.Class<?>... groups);
+}
Propchange: incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/CascadingPropertyValidator.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java?rev=993503&r1=993502&r2=993503&view=diff
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java (original)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java Tue Sep 7 19:47:07 2010
@@ -24,7 +24,6 @@ import java.util.List;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ValidationException;
-import javax.validation.Validator;
import javax.validation.groups.Default;
import javax.validation.metadata.BeanDescriptor;
import org.apache.bval.MetaBeanFinder;
@@ -54,9 +53,9 @@ import org.apache.commons.lang3.ClassUti
* API class
*
* @author Roman Stumm
- * @author Carlos Vara <br/>
+ * @author Carlos Vara
*/
-public class ClassValidator implements Validator {
+public class ClassValidator implements CascadingPropertyValidator {
/**
* {@link ApacheFactoryContext} used
*/
@@ -66,8 +65,7 @@ public class ClassValidator implements V
*/
protected final GroupsComputer groupsComputer = new GroupsComputer();
- private final NestedPathNavigator nestedPathNavigator =
- new NestedPathNavigator();
+ private final NestedPathNavigator nestedPathNavigator = new NestedPathNavigator();
/**
* Create a new ClassValidator instance.
@@ -120,8 +118,7 @@ public class ClassValidator implements V
*/
// @Override - not allowed in 1.5 for Interface methods
@SuppressWarnings("unchecked")
- public <T> Set<ConstraintViolation<T>> validate(T object,
- Class<?>... groups) {
+ public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
if (object == null)
throw new IllegalArgumentException("cannot validate null");
checkGroups(groups);
@@ -129,13 +126,10 @@ public class ClassValidator implements V
try {
Class<T> objectClass = (Class<T>) object.getClass();
- MetaBean objectMetaBean =
- factoryContext.getMetaBeanFinder().findForClass(objectClass);
+ MetaBean objectMetaBean = factoryContext.getMetaBeanFinder().findForClass(objectClass);
- final GroupValidationContext<T> context =
- createContext(objectMetaBean, object, objectClass, groups);
- final ConstraintValidationListener<T> result =
- context.getListener();
+ final GroupValidationContext<T> context = createContext(objectMetaBean, object, objectClass, groups);
+ final ConstraintValidationListener<T> result = context.getListener();
final Groups sequence = context.getGroups();
// 1. process groups
@@ -189,9 +183,17 @@ public class ClassValidator implements V
* process
*/
// @Override - not allowed in 1.5 for Interface methods
- @SuppressWarnings("unchecked")
- public <T> Set<ConstraintViolation<T>> validateProperty(T object,
- String propertyName, Class<?>... groups) {
+ public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups) {
+ return validateProperty(object, propertyName, false, groups);
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, boolean cascade,
+ Class<?>... groups) {
+
if (object == null)
throw new IllegalArgumentException("cannot validate null");
@@ -199,13 +201,11 @@ public class ClassValidator implements V
checkGroups(groups);
try {
-
+ @SuppressWarnings("unchecked")
Class<T> objectClass = (Class<T>) object.getClass();
- MetaBean objectMetaBean =
- factoryContext.getMetaBeanFinder().findForClass(objectClass);
+ MetaBean objectMetaBean = factoryContext.getMetaBeanFinder().findForClass(objectClass);
- GroupValidationContext<T> context =
- createContext(objectMetaBean, object, objectClass, groups);
+ GroupValidationContext<T> context = createContext(objectMetaBean, object, objectClass, groups);
ConstraintValidationListener<T> result = context.getListener();
nestedPathNavigator.navigate(context, propertyName);
Groups sequence = context.getGroups();
@@ -213,14 +213,22 @@ public class ClassValidator implements V
// 1. process groups
for (Group current : sequence.getGroups()) {
context.setCurrentGroup(current);
- validatePropertyInGroup(context);
+ if (cascade) {
+ validateBeanNet(context);
+ } else {
+ validatePropertyInGroup(context);
+ }
}
// 2. process sequences
for (List<Group> eachSeq : sequence.getSequences()) {
for (Group current : eachSeq) {
context.setCurrentGroup(current);
- validatePropertyInGroup(context);
+ if (cascade) {
+ validateBeanNet(context);
+ } else {
+ validatePropertyInGroup(context);
+ }
/**
* if one of the group process in the sequence leads to one
* or more validation failure, the groups following in the
@@ -268,33 +276,50 @@ public class ClassValidator implements V
* process
*/
// @Override - not allowed in 1.5 for Interface methods
- public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType,
- String propertyName, Object value, Class<?>... groups) {
+ public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value,
+ Class<?>... groups) {
+ return validateValue(beanType, propertyName, value, false, groups);
+ }
+ /**
+ * {@inheritDoc}
+ */
+ public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value,
+ boolean cascade, Class<?>... groups) {
checkBeanType(beanType);
checkPropertyName(propertyName);
checkGroups(groups);
try {
- MetaBean metaBean =
- factoryContext.getMetaBeanFinder().findForClass(beanType);
- GroupValidationContext<T> context =
- createContext(metaBean, null, beanType, groups);
+ MetaBean metaBean = factoryContext.getMetaBeanFinder().findForClass(beanType);
+ GroupValidationContext<T> context = createContext(metaBean, null, beanType, groups);
ConstraintValidationListener<T> result = context.getListener();
nestedPathNavigator.navigate(context, propertyName);
- context.setFixedValue(value);
+ if (context.getMetaProperty() == null) {
+ context.setBean(value);
+ } else {
+ context.setFixedValue(value);
+ }
Groups sequence = context.getGroups();
// 1. process groups
for (Group current : sequence.getGroups()) {
context.setCurrentGroup(current);
- validatePropertyInGroup(context);
+ if (cascade) {
+ validateBeanNet(context);
+ } else {
+ validatePropertyInGroup(context);
+ }
}
// 2. process sequences
for (List<Group> eachSeq : sequence.getSequences()) {
for (Group current : eachSeq) {
context.setCurrentGroup(current);
- validatePropertyInGroup(context);
+ if (cascade) {
+ validateBeanNet(context);
+ } else {
+ validatePropertyInGroup(context);
+ }
// if one of the group process in the sequence leads to one
// or more validation failure,
// the groups following in the sequence must not be
@@ -333,18 +358,15 @@ public class ClassValidator implements V
throw new IllegalArgumentException("Class cannot be null");
}
try {
- MetaBean metaBean =
- factoryContext.getMetaBeanFinder().findForClass(clazz);
- BeanDescriptorImpl edesc =
- metaBean.getFeature(Jsr303Features.Bean.BEAN_DESCRIPTOR);
+ MetaBean metaBean = factoryContext.getMetaBeanFinder().findForClass(clazz);
+ BeanDescriptorImpl edesc = metaBean.getFeature(Jsr303Features.Bean.BEAN_DESCRIPTOR);
if (edesc == null) {
edesc = createBeanDescriptor(metaBean);
metaBean.putFeature(Jsr303Features.Bean.BEAN_DESCRIPTOR, edesc);
}
return edesc;
} catch (RuntimeException ex) {
- throw new ValidationException("error retrieving constraints for "
- + clazz, ex);
+ throw new ValidationException("error retrieving constraints for " + clazz, ex);
}
}
@@ -368,20 +390,16 @@ public class ClassValidator implements V
@SuppressWarnings("unchecked")
final T result = (T) this;
return result;
- } else if (!(type.isInterface() || Modifier.isAbstract(type
- .getModifiers()))) {
- return SecureActions.newInstance(type,
- new Class[] { ApacheFactoryContext.class },
+ } else if (!(type.isInterface() || Modifier.isAbstract(type.getModifiers()))) {
+ return SecureActions.newInstance(type, new Class[] { ApacheFactoryContext.class },
new Object[] { factoryContext });
} else {
try {
Class<?> cls = ClassUtils.getClass(type.getName() + "Impl");
if (type.isAssignableFrom(cls)) {
@SuppressWarnings("unchecked")
- final Class<? extends T> implClass =
- (Class<? extends T>) cls;
- return SecureActions.newInstance(implClass,
- new Class[] { ApacheFactoryContext.class },
+ final Class<? extends T> implClass = (Class<? extends T>) cls;
+ return SecureActions.newInstance(implClass, new Class[] { ApacheFactoryContext.class },
new Object[] { factoryContext });
}
} catch (ClassNotFoundException e) {
@@ -422,8 +440,7 @@ public class ClassValidator implements V
if (context.getCurrentGroup().isDefault()) {
List<Group> defaultGroups = expandDefaultGroup(context);
- final ConstraintValidationListener<?> result =
- (ConstraintValidationListener<?>) context.getListener();
+ final ConstraintValidationListener<?> result = (ConstraintValidationListener<?>) context.getListener();
// If the rootBean defines a GroupSequence
if (defaultGroups.size() > 1) {
@@ -449,8 +466,7 @@ public class ClassValidator implements V
// Obtain the full class hierarchy
List<Class<?>> classHierarchy = new ArrayList<Class<?>>();
- ClassHelper.fillFullClassHierarchyAsList(classHierarchy,
- context.getMetaBean().getBeanClass());
+ ClassHelper.fillFullClassHierarchyAsList(classHierarchy, context.getMetaBean().getBeanClass());
Class<?> initialOwner = context.getCurrentOwner();
// For each owner in the hierarchy
@@ -462,8 +478,7 @@ public class ClassValidator implements V
// Obtain the group sequence of the owner, and use it for
// the constraints that belong to it
List<Group> ownerDefaultGroups =
- context.getMetaBean().getFeature(
- "{GroupSequence:" + owner.getCanonicalName() + "}");
+ context.getMetaBean().getFeature("{GroupSequence:" + owner.getCanonicalName() + "}");
for (Group each : ownerDefaultGroups) {
context.setCurrentGroup(each);
ValidationHelper.validateBean(context);
@@ -501,10 +516,8 @@ public class ClassValidator implements V
* @param prop
* The property to cascade from (in case it is possible).
*/
- private void validateCascadedBean(GroupValidationContext<?> context,
- MetaProperty prop) {
- AccessStrategy[] access =
- prop.getFeature(Features.Property.REF_CASCADE);
+ private void validateCascadedBean(GroupValidationContext<?> context, MetaProperty prop) {
+ AccessStrategy[] access = prop.getFeature(Features.Property.REF_CASCADE);
if (access != null) { // different accesses to relation
// save old values from context
final Object bean = context.getBean();
@@ -514,9 +527,8 @@ public class ClassValidator implements V
// modify context state for relationship-target bean
context.moveDown(prop, each);
// Now, if the related bean is an instance of Map/Array/etc,
- ValidationHelper.validateContext(context,
- new Jsr303ValidationCallback(context),
- treatMapsLikeBeans);
+ ValidationHelper
+ .validateContext(context, new Jsr303ValidationCallback(context), treatMapsLikeBeans);
// restore old values in context
context.moveUp(bean, mbean);
}
@@ -538,8 +550,7 @@ public class ClassValidator implements V
* @return <code>true</code> if the validator can access the related bean,
* <code>false</code> otherwise.
*/
- private boolean isCascadable(GroupValidationContext<?> context,
- MetaProperty prop, AccessStrategy access) {
+ private boolean isCascadable(GroupValidationContext<?> context, MetaProperty prop, AccessStrategy access) {
PathImpl beanPath = context.getPropertyPath();
NodeImpl node = new NodeImpl(prop.getName());
@@ -547,27 +558,19 @@ public class ClassValidator implements V
beanPath = PathImpl.create(null);
}
try {
- if (!context.getTraversableResolver().isReachable(
- context.getBean(), node,
- context.getRootMetaBean().getBeanClass(), beanPath,
- access.getElementType()))
+ if (!context.getTraversableResolver().isReachable(context.getBean(), node,
+ context.getRootMetaBean().getBeanClass(), beanPath, access.getElementType()))
return false;
} catch (RuntimeException e) {
- throw new ValidationException(
- "Error in TraversableResolver.isReachable() for "
- + context.getBean(), e);
+ throw new ValidationException("Error in TraversableResolver.isReachable() for " + context.getBean(), e);
}
try {
- if (!context.getTraversableResolver().isCascadable(
- context.getBean(), node,
- context.getRootMetaBean().getBeanClass(), beanPath,
- access.getElementType()))
+ if (!context.getTraversableResolver().isCascadable(context.getBean(), node,
+ context.getRootMetaBean().getBeanClass(), beanPath, access.getElementType()))
return false;
} catch (RuntimeException e) {
- throw new ValidationException(
- "Error TraversableResolver.isCascadable() for "
- + context.getBean(), e);
+ throw new ValidationException("Error TraversableResolver.isCascadable() for " + context.getBean(), e);
}
return true;
@@ -583,12 +586,9 @@ public class ClassValidator implements V
private List<Group> expandDefaultGroup(GroupValidationContext<?> context) {
if (context.getCurrentGroup().isDefault()) {
// mention if metaBean redefines the default group
- List<Group> groupSeq =
- context.getMetaBean().getFeature(
- Jsr303Features.Bean.GROUP_SEQUENCE);
+ List<Group> groupSeq = context.getMetaBean().getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
if (groupSeq != null) {
- context.getGroups().assertDefaultGroupSequenceIsExpandable(
- groupSeq);
+ context.getGroups().assertDefaultGroupSequenceIsExpandable(groupSeq);
}
return groupSeq;
} else {
@@ -603,8 +603,7 @@ public class ClassValidator implements V
* @param object
* @return a {@link RuntimeException} of the appropriate type
*/
- protected static RuntimeException unrecoverableValidationError(
- RuntimeException ex, Object object) {
+ protected static RuntimeException unrecoverableValidationError(RuntimeException ex, Object object) {
if (ex instanceof UnknownPropertyException) {
// Convert to IllegalArgumentException
return new IllegalArgumentException(ex.getMessage(), ex);
@@ -612,24 +611,38 @@ public class ClassValidator implements V
return ex; // do not wrap specific ValidationExceptions (or
// instances from subclasses)
} else {
- return new ValidationException("error during validation of "
- + object, ex);
+ return new ValidationException("error during validation of " + object, ex);
}
}
- private void validatePropertyInGroup(GroupValidationContext<?> context) {
+ private void validatePropertyInGroup(final GroupValidationContext<?> context) {
+ final Runnable helper;
+ if (context.getMetaProperty() == null) {
+ helper = new Runnable() {
+
+ public void run() {
+ ValidationHelper.validateBean(context);
+ }
+ };
+ } else {
+ helper = new Runnable() {
+
+ public void run() {
+ ValidationHelper.validateProperty(context);
+ }
+ };
+ }
Group currentGroup = context.getCurrentGroup();
List<Group> defaultGroups = expandDefaultGroup(context);
if (defaultGroups != null) {
for (Group each : defaultGroups) {
context.setCurrentGroup(each);
- ValidationHelper.validateProperty(context);
- // continue validation, even if errors already found: if
- // (!result.isEmpty())
+ helper.run();
+ // continue validation, even if errors already found
}
context.setCurrentGroup(currentGroup); // restore
} else {
- ValidationHelper.validateProperty(context);
+ helper.run();
}
}
@@ -643,14 +656,12 @@ public class ClassValidator implements V
* @param groups
* @return {@link GroupValidationContext} instance
*/
- protected <T> GroupValidationContext<T> createContext(MetaBean metaBean,
- T object, Class<T> objectClass, Class<?>[] groups) {
- ConstraintValidationListener<T> listener =
- new ConstraintValidationListener<T>(object, objectClass);
+ protected <T> GroupValidationContext<T> createContext(MetaBean metaBean, T object, Class<T> objectClass,
+ Class<?>[] groups) {
+ ConstraintValidationListener<T> listener = new ConstraintValidationListener<T>(object, objectClass);
GroupValidationContextImpl<T> context =
- new GroupValidationContextImpl<T>(listener, this.factoryContext
- .getMessageInterpolator(), this.factoryContext
- .getTraversableResolver(), metaBean);
+ new GroupValidationContextImpl<T>(listener, this.factoryContext.getMessageInterpolator(),
+ this.factoryContext.getTraversableResolver(), metaBean);
context.setBean(object, metaBean);
context.setGroups(groupsComputer.computeGroups(groups));
return context;
@@ -663,8 +674,7 @@ public class ClassValidator implements V
* @return {@link BeanDescriptorImpl} instance
*/
protected BeanDescriptorImpl createBeanDescriptor(MetaBean metaBean) {
- return new BeanDescriptorImpl(factoryContext, metaBean, metaBean
- .getValidations());
+ return new BeanDescriptorImpl(factoryContext, metaBean, metaBean.getValidations());
}
private boolean treatMapsLikeBeans = false;
@@ -712,8 +722,7 @@ public class ClassValidator implements V
*/
private void checkPropertyName(String propertyName) {
if (propertyName == null || propertyName.trim().length() == 0) {
- throw new IllegalArgumentException(
- "Property path cannot be null or empty.");
+ throw new IllegalArgumentException("Property path cannot be null or empty.");
}
}
@@ -735,8 +744,7 @@ public class ClassValidator implements V
* {@link ClassValidator#validateBeanNet(GroupValidationContext)} with the
* current context set.
*/
- protected class Jsr303ValidationCallback implements
- ValidationHelper.ValidateCallback {
+ protected class Jsr303ValidationCallback implements ValidationHelper.ValidateCallback {
private final GroupValidationContext<?> context;
Modified: incubator/bval/sandbox/lang3-work/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidationTest.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidationTest.java?rev=993503&r1=993502&r2=993503&view=diff
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidationTest.java (original)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidationTest.java Tue Sep 7 19:47:07 2010
@@ -332,6 +332,49 @@ public class ValidationTest extends Test
Assert.assertEquals(0, iv.size());
}
+ public void testValidateCascadingNestedPropertyPath() throws InvocationTargetException, NoSuchMethodException,
+ IllegalAccessException {
+ final String propPath = "addresses[0]";
+
+ CascadingPropertyValidator v = getValidator().unwrap(CascadingPropertyValidator.class);
+ Author author = new Author();
+ author.setAddresses(new ArrayList<Address>());
+ Address adr = new Address();
+ author.getAddresses().add(adr);
+ Country country = new Country();
+ adr.setCity("dark");
+ adr.setCountry(country);
+
+ Set<ConstraintViolation<Author>> iv = v.validateProperty(author, propPath);
+ Assert.assertEquals(1, iv.size()); // null address line 1 (no cascade)
+
+ country.setISO2Code("too_long");
+ iv = v.validateProperty(author, propPath, true);
+ Assert.assertEquals(3, iv.size()); // null address line 1 + null
+ // country.name + too long
+ // country.iso2code
+
+ country.setISO2Code("23");
+ iv = v.validateProperty(author, propPath, true);
+ Assert.assertEquals(2, iv.size()); // null address line 1 + null
+ // country.name, country.iso2code
+ // fixed
+
+ Address value = new Address();
+ value.setCity("whatever");
+ value.setAddressline1("1 address line");
+ iv = v.validateValue(Author.class, propPath, value, true);
+ Assert.assertEquals(1, iv.size()); // null country
+
+ value.setCountry(new Country());
+ iv = v.validateValue(Author.class, propPath, value, true);
+ Assert.assertEquals(1, iv.size()); // null country.name
+
+ value.getCountry().setName("NWO");
+ iv = v.validateValue(Author.class, propPath, value, true);
+ Assert.assertEquals(0, iv.size());
+ }
+
public void testMetadataAPI() {
Validator bookValidator = getValidator();
BeanDescriptor bookBeanDescriptor =