You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bval.apache.org by ro...@apache.org on 2010/05/19 11:25:47 UTC
svn commit: r946095 - in /incubator/bval/trunk:
bval-core/src/main/java/org/apache/bval/
bval-jsr303/src/main/java/org/apache/bval/jsr303/
bval-jsr303/src/main/java/org/apache/bval/jsr303/util/
bval-jsr303/src/test/java/org/apache/bval/jsr303/ bval-jsr...
Author: romanstumm
Date: Wed May 19 09:25:47 2010
New Revision: 946095
URL: http://svn.apache.org/viewvc?rev=946095&view=rev
Log:
BVAL-47- Correctly follow GroupSequence definitions when validating
applied patch by Carlos Varla,
plus one minor change by Roman Stumm: using variable BeanValidationContext.validatedObjects instead of a new variable in GroupValidationContextImpl.validatedBeans
Added:
incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GraphBeanIdentity.java
incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ClassHelper.java
incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceIsolationTest.java
Modified:
incubator/bval/trunk/bval-core/src/main/java/org/apache/bval/BeanValidationContext.java
incubator/bval/trunk/bval-core/src/main/java/org/apache/bval/BeanValidator.java
incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java
incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java
incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java
incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContext.java
incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContextImpl.java
incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java
incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintValidatorContextTest.java
incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CollectionValidationTest.java
Modified: incubator/bval/trunk/bval-core/src/main/java/org/apache/bval/BeanValidationContext.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-core/src/main/java/org/apache/bval/BeanValidationContext.java?rev=946095&r1=946094&r2=946095&view=diff
==============================================================================
--- incubator/bval/trunk/bval-core/src/main/java/org/apache/bval/BeanValidationContext.java (original)
+++ incubator/bval/trunk/bval-core/src/main/java/org/apache/bval/BeanValidationContext.java Wed May 19 09:25:47 2010
@@ -21,6 +21,7 @@ import org.apache.bval.util.AccessStrate
import org.apache.bval.util.PropertyAccess;
import java.util.IdentityHashMap;
+import java.util.Map;
/**
* Description: Context during validation to help the {@link org.apache.bval.model.Validation}
@@ -52,7 +53,7 @@ public class BeanValidationContext<T ext
private AccessStrategy access;
/** set of objects already validated to avoid endless loops. */
- protected IdentityHashMap<Object, Object> validatedObjects = new IdentityHashMap<Object, Object>();
+ protected Map validatedObjects;
/**
* true when value is fixed, so that it will NOT be dynamically
@@ -66,7 +67,12 @@ public class BeanValidationContext<T ext
private T listener;
public BeanValidationContext(T listener) {
+ this(listener, new IdentityHashMap());
+ }
+
+ protected BeanValidationContext(T listener, Map validatedMap) {
this.listener = listener;
+ this.validatedObjects = validatedMap;
}
public T getListener() {
Modified: incubator/bval/trunk/bval-core/src/main/java/org/apache/bval/BeanValidator.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-core/src/main/java/org/apache/bval/BeanValidator.java?rev=946095&r1=946094&r2=946095&view=diff
==============================================================================
--- incubator/bval/trunk/bval-core/src/main/java/org/apache/bval/BeanValidator.java (original)
+++ incubator/bval/trunk/bval-core/src/main/java/org/apache/bval/BeanValidator.java Wed May 19 09:25:47 2010
@@ -215,7 +215,7 @@ public class BeanValidator<T extends Val
}
}
- private <VL extends ValidationListener> void validateBeanInContext(ValidationContext<VL> context) {
+ protected <VL extends ValidationListener> void validateBeanInContext(ValidationContext<VL> context) {
if (getDynamicMetaBean(context) != null) {
context.setMetaBean(
getDynamicMetaBean(context).resolveMetaBean(context.getBean()));
@@ -223,7 +223,7 @@ public class BeanValidator<T extends Val
validateBeanNet(context);
}
- private <VL extends ValidationListener> void validateArrayInContext(ValidationContext<VL> context) {
+ protected <VL extends ValidationListener> void validateArrayInContext(ValidationContext<VL> context) {
int index = 0;
DynamicMetaBean dyn = getDynamicMetaBean(context);
for (Object each : ((Object[]) context.getBean())) {
@@ -244,7 +244,7 @@ public class BeanValidator<T extends Val
}
/** Any object implementing java.lang.Iterable is supported */
- private <VL extends ValidationListener> void validateIteratableInContext(ValidationContext<VL> context) {
+ protected <VL extends ValidationListener> void validateIteratableInContext(ValidationContext<VL> context) {
Iterator<?> it = ((Iterable<?>) context.getBean()).iterator();
int index = 0;
// jsr303 spec: Each object provided by the iterator is validated.
@@ -263,7 +263,7 @@ public class BeanValidator<T extends Val
}
}
- private <VL extends ValidationListener> void validateNonPositionalIteratableInContext(ValidationContext<VL> context) {
+ protected <VL extends ValidationListener> void validateNonPositionalIteratableInContext(ValidationContext<VL> context) {
Iterator<?> it = ((Iterable<?>) context.getBean()).iterator();
// jsr303 spec: Each object provided by the iterator is validated.
context.setCurrentIndex(null);
@@ -281,7 +281,7 @@ public class BeanValidator<T extends Val
}
}
- private <VL extends ValidationListener> void validateMapInContext(ValidationContext<VL> context) {
+ protected <VL extends ValidationListener> void validateMapInContext(ValidationContext<VL> context) {
// jsr303 spec: For Map, the value of each Map.Entry is validated (key is not validated).
Iterator<Map.Entry<Object, Object>> it = ((Map<Object, Object>) context.getBean()).entrySet().iterator();
final DynamicMetaBean dyn = getDynamicMetaBean(context);
@@ -309,7 +309,7 @@ public class BeanValidator<T extends Val
}
}
- private <VL extends ValidationListener> void validateRelatedBean(ValidationContext<VL> context, MetaProperty prop) {
+ protected <VL extends ValidationListener> void validateRelatedBean(ValidationContext<VL> context, MetaProperty prop) {
AccessStrategy[] access = prop.getFeature(Features.Property.REF_CASCADE);
if (access == null && prop.getMetaBean() != null) { // single property access strategy
// save old values from context
Modified: incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java?rev=946095&r1=946094&r2=946095&view=diff
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java (original)
+++ incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java Wed May 19 09:25:47 2010
@@ -23,16 +23,24 @@ import org.apache.bval.BeanValidator;
import org.apache.bval.jsr303.groups.Group;
import org.apache.bval.jsr303.groups.Groups;
import org.apache.bval.jsr303.groups.GroupsComputer;
+import org.apache.bval.jsr303.util.ClassHelper;
import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.model.Features;
import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
import org.apache.bval.model.ValidationContext;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.PropertyAccess;
import org.apache.commons.lang.ClassUtils;
import javax.validation.ConstraintViolation;
import javax.validation.ValidationException;
import javax.validation.Validator;
+import javax.validation.groups.Default;
import javax.validation.metadata.BeanDescriptor;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -76,13 +84,13 @@ public class ClassValidator extends Bean
// 1. process groups
for (Group current : groups.getGroups()) {
context.setCurrentGroup(current);
- validateContext(context);
+ validateBeanNet(context);
}
// 2. process sequences
for (List<Group> eachSeq : groups.getSequences()) {
for (Group current : eachSeq) {
context.setCurrentGroup(current);
- validateContext(context);
+ validateBeanNet(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 processed
@@ -97,26 +105,171 @@ public class ClassValidator extends Bean
}
}
+ /**
+ * Validates a bean and all its cascaded related beans for the currently
+ * defined group.
+ *
+ * Special code is present to manage the {@link Default} group.
+ *
+ * TODO: More descriptive name and don't override method from BeanValidator.
+ *
+ * @param ValidationContext
+ * The current context of this validation call.
+ */
@Override
- public void validateBeanNet(ValidationContext vcontext) {
- GroupValidationContext context = (GroupValidationContext) vcontext;
- List<Group> defaultGroups = expandDefaultGroup(context);
- final ConstraintValidationListener result = (ConstraintValidationListener) vcontext.getListener();
- if (defaultGroups != null) {
- Group currentGroup = context.getCurrentGroup();
- for (Group each : defaultGroups) {
- context.setCurrentGroup(each);
- super.validateBeanNet(context);
- // Spec 3.4.3 - Stop validation if errors already found
- if ( !result.isEmpty() ) {
- break;
+ protected void validateBeanNet(ValidationContext vcontext) {
+
+ GroupValidationContext<?> context = (GroupValidationContext<?>)vcontext;
+
+ // If reached a cascaded bean which is null
+ if ( context.getBean() == null ) {
+ return;
+ }
+
+ // If reached a cascaded bean which has already been validated for the current group
+ if ( !context.collectValidated() ) {
+ return;
+ }
+
+
+ // ### First, validate the bean
+
+ // Default is a special case
+ if ( context.getCurrentGroup().isDefault() ) {
+
+ List<Group> defaultGroups = expandDefaultGroup(context);
+ final ConstraintValidationListener result = (ConstraintValidationListener) context.getListener();
+
+ // If the rootBean defines a GroupSequence
+ if ( defaultGroups.size() > 1 ) {
+
+ int numViolations = result.violationsSize();
+
+ // Validate the bean for each group in the sequence
+ Group currentGroup = context.getCurrentGroup();
+ for (Group each : defaultGroups) {
+ context.setCurrentGroup(each);
+ super.validateBean(context);
+ // Spec 3.4.3 - Stop validation if errors already found
+ if ( result.violationsSize() > numViolations ) {
+ break;
+ }
}
+ context.setCurrentGroup(currentGroup);
+ }
+ else {
+
+ // For each class in the hierarchy of classes of rootBean,
+ // validate the constraints defined in that class according
+ // to the GroupSequence defined in the same class
+
+ // Obtain the full class hierarchy
+ List<Class<?>> classHierarchy = new ArrayList<Class<?>>();
+ ClassHelper.fillFullClassHierarchyAsList(classHierarchy, context.getMetaBean().getBeanClass());
+ Class<?> initialOwner = context.getCurrentOwner();
+
+ // For each owner in the hierarchy
+ for ( Class<?> owner : classHierarchy ) {
+ context.setCurrentOwner(owner);
+
+ int numViolations = result.violationsSize();
+
+ // 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()+"}");
+ for (Group each : ownerDefaultGroups) {
+ context.setCurrentGroup(each);
+ super.validateBean(context);
+ // Spec 3.4.3 - Stop validation if errors already found
+ if ( result.violationsSize() > numViolations ) {
+ break;
+ }
+ }
+
+ }
+ context.setCurrentOwner(initialOwner);
+ context.setCurrentGroup(Group.DEFAULT);
+
+ }
+
+ }
+ // if not the default group, proceed as normal
+ else {
+ super.validateBean(context);
+ }
+
+
+ // ### Then, the cascaded beans (@Valid)
+ for (MetaProperty prop : context.getMetaBean().getProperties()) {
+ validateCascadedBean(context, prop);
+ }
+
+ }
+
+ /**
+ * TODO: Currently, almost the same code as super.validateRelatedBean, but
+ * as it is being called at a different time, I have explicitly added the
+ * code here with a different method name.
+ *
+ * @param context
+ * The current context
+ * @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);
+ if (access == null && prop.getMetaBean() != null) { // single property access strategy
+ // save old values from context
+ final Object bean = context.getBean();
+ final MetaBean mbean = context.getMetaBean();
+ // modify context state for relationship-target bean
+ context.moveDown(prop, new PropertyAccess(bean.getClass(), prop.getName()));
+ followCascadedConstraint(context);
+ // restore old values in context
+ context.moveUp(bean, mbean);
+ } else if (access != null) { // different accesses to relation
+ // save old values from context
+ final Object bean = context.getBean();
+ final MetaBean mbean = context.getMetaBean();
+ for (AccessStrategy each : access) {
+ // modify context state for relationship-target bean
+ context.moveDown(prop, each);
+ // Now, if the related bean is an instance of Map/Array/etc,
+ followCascadedConstraint(context);
+ // restore old values in context
+ context.moveUp(bean, mbean);
+ }
+ }
+ }
+
+
+ /**
+ * TODO: Currently almost the same code as super.validateContext, but as it
+ * is being called at a different time, I have explicitly added the code
+ * here with a different method name.
+ *
+ * Methods defined in {@link BeanValidator} take care of setting the path
+ * and current bean correctly and call
+ * {@link #validateBeanNet(ValidationContext)} for each individual bean.
+ *
+ * @param context
+ * The current validation context.
+ */
+ private void followCascadedConstraint(GroupValidationContext<?> context) {
+ if ( context.getBean() != null ) {
+ if (context.getBean() instanceof Map<?, ?>) {
+ validateMapInContext(context);
+ } else if (context.getBean() instanceof List<?>) {
+ validateIteratableInContext(context);
+ } else if (context.getBean() instanceof Iterable<?>) {
+ validateNonPositionalIteratableInContext(context);
+ } else if (context.getBean() instanceof Object[]) {
+ validateArrayInContext(context);
+ } else { // to One Bean (or Map like Bean)
+ validateBeanInContext(context);
}
- context.setCurrentGroup(currentGroup); // restore (finally{} not required)
- } else {
- super.validateBeanNet(context);
}
}
+
/**
* in case of a default group return the list of groups
Modified: incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java?rev=946095&r1=946094&r2=946095&view=diff
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java (original)
+++ incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java Wed May 19 09:25:47 2010
@@ -40,249 +40,254 @@ import java.util.*;
* this instance is immutable!<br/>
*/
public class ConstraintValidation<T extends Annotation>
- implements Validation, ConstraintDescriptor<T> {
- private static final String ANNOTATION_MESSAGE = "message";
- private final ConstraintValidator validator;
- private T annotation; // for metadata request API
- private final AccessStrategy access;
- private final boolean reportFromComposite;
- private final Map<String, Object> attributes;
-
- private Set<ConstraintValidation<?>> composedConstraints;
+ implements Validation, ConstraintDescriptor<T> {
+ private static final String ANNOTATION_MESSAGE = "message";
+ private final ConstraintValidator validator;
+ private T annotation; // for metadata request API
+ private final AccessStrategy access;
+ private final boolean reportFromComposite;
+ private final Map<String, Object> attributes;
+
+ private Set<ConstraintValidation<?>> composedConstraints;
+
+ /**
+ * the owner is the type where the validation comes from.
+ * it is used to support implicit grouping.
+ */
+ private final Class owner;
+ private Set<Class<?>> groups;
+ private Set<Class<? extends Payload>> payload;
+ private Class<? extends ConstraintValidator<T, ?>>[] validatorClasses;
+
+ /**
+ * @param validator - the constraint validator
+ * @param annotation - the annotation of the constraint
+ * @param owner - the type where the annotated element is placed
+ * (class, interface, annotation type)
+ * @param access - how to access the value
+ */
+ public ConstraintValidation(
+ Class<? extends ConstraintValidator<T, ?>>[] validatorClasses,
+ ConstraintValidator validator, T annotation, Class owner,
+ AccessStrategy access, boolean reportFromComposite) {
+ this.attributes = new HashMap();
+ this.validatorClasses = validatorClasses;
+ this.validator = validator;
+ this.annotation = annotation;
+ this.owner = owner;
+ this.access = access;
+ this.reportFromComposite = reportFromComposite;
+ }
+
+ public ConstraintDescriptor<T> asSerializableDescriptor() {
+ return new ConstraintDescriptorImpl(this);
+ }
+
+ void setGroups(Set<Class<?>> groups) {
+ this.groups = groups;
+ this.attributes.put("groups", groups.toArray(new Class[groups.size()]));
+ }
+
+ void setPayload(Set<Class<? extends Payload>> payload) {
+ this.payload = payload;
+ this.attributes.put("payload", payload.toArray(new Class[payload.size()]));
+ }
+
+ public boolean isReportAsSingleViolation() {
+ return reportFromComposite;
+ }
+
+ public void addComposed(ConstraintValidation aConstraintValidation) {
+ if (composedConstraints == null) {
+ composedConstraints = new HashSet();
+ }
+ composedConstraints.add(aConstraintValidation);
+ }
+
+ public void validate(ValidationContext context) {
+ validate((GroupValidationContext) context);
+ }
+ public void validate(GroupValidationContext context) {
+ context.setConstraintValidation(this);
/**
- * the owner is the type where the validation comes from.
- * it is used to support implicit grouping.
+ * execute unless the given validation constraint has already been processed
+ * during this validation routine (as part of a previous group match)
*/
- private final Class owner;
- private Set<Class<?>> groups;
- private Set<Class<? extends Payload>> payload;
- private Class<? extends ConstraintValidator<T, ?>>[] validatorClasses;
-
- /**
- * @param validator - the constraint validator
- * @param annotation - the annotation of the constraint
- * @param owner - the type where the annotated element is placed
- * (class, interface, annotation type)
- * @param access - how to access the value
- */
- public ConstraintValidation(
- Class<? extends ConstraintValidator<T, ?>>[] validatorClasses,
- ConstraintValidator validator, T annotation, Class owner,
- AccessStrategy access, boolean reportFromComposite) {
- this.attributes = new HashMap();
- this.validatorClasses = validatorClasses;
- this.validator = validator;
- this.annotation = annotation;
- this.owner = owner;
- this.access = access;
- this.reportFromComposite = reportFromComposite;
- }
-
- public ConstraintDescriptor<T> asSerializableDescriptor() {
- return new ConstraintDescriptorImpl(this);
- }
-
- void setGroups(Set<Class<?>> groups) {
- this.groups = groups;
- this.attributes.put("groups", groups.toArray(new Class[groups.size()]));
- }
-
- void setPayload(Set<Class<? extends Payload>> payload) {
- this.payload = payload;
- this.attributes.put("payload", payload.toArray(new Class[payload.size()]));
- }
-
- public boolean isReportAsSingleViolation() {
- return reportFromComposite;
- }
-
- public void addComposed(ConstraintValidation aConstraintValidation) {
- if (composedConstraints == null) {
- composedConstraints = new HashSet();
- }
- composedConstraints.add(aConstraintValidation);
- }
-
- public void validate(ValidationContext context) {
- validate((GroupValidationContext) context);
- }
-
- public void validate(GroupValidationContext context) {
- context.setConstraintValidation(this);
- /**
- * execute unless the given validation constraint has already been processed
- * during this validation routine (as part of a previous group match)
- */
- if (!isMemberOf(context.getCurrentGroup().getGroup())) {
- return; // do not validate in the current group
- }
- if (validator != null && !context.collectValidated(validator))
- return; // already done
-
- if (context.getMetaProperty() != null && !isCascadeEnabled(context)) {
- return;
- }
-
- // process composed constraints
- if (isReportAsSingleViolation()) {
- BeanValidationContext gctx = (BeanValidationContext) context;
- ConstraintValidationListener oldListener =
- ((ConstraintValidationListener) gctx.getListener());
- ConstraintValidationListener listener =
- new ConstraintValidationListener(oldListener.getRootBean(), oldListener.getRootBeanType());
- gctx.setListener(listener);
- try {
- for (ConstraintValidation composed : getComposingValidations()) {
- composed.validate(context);
- }
- } finally {
- gctx.setListener(oldListener);
- }
-
- // Restore current constraint validation
- context.setConstraintValidation(this);
-
- // stop validating when already failed and ReportAsSingleInvalidConstraint = true ?
- if (!listener.getConstaintViolations().isEmpty()) {
- // TODO RSt - how should the composed constraint error report look like?
- ConstraintValidatorContextImpl jsrContext =
- new ConstraintValidatorContextImpl(context, this);
- addErrors(context, jsrContext); // add defaultErrorMessage only*/
- return;
- }
- } else {
- for (ConstraintValidation composed : getComposingValidations()) {
- composed.validate(context);
- }
-
- // Restore current constraint validation
- context.setConstraintValidation(this);
- }
-
- if (validator != null) {
- ConstraintValidatorContextImpl jsrContext =
- new ConstraintValidatorContextImpl(context, this);
- if (!validator.isValid(context.getValidatedValue(), jsrContext)) {
- addErrors(context, jsrContext);
- }
- }
- }
-
- /**
- * Initialize the validator (if not <code>null</code>) with the stored
- * annotation.
- */
- public void initialize() {
- if (null != validator) {
- try {
- validator.initialize(annotation);
- } catch (RuntimeException e) {
- // Either a "legit" problem initializing the validator or a
- // ClassCastException if the validator associated annotation is
- // not a supertype of the validated annotation.
- throw new ConstraintDefinitionException("Incorrect validator [" + validator.getClass().getCanonicalName() + "] for annotation " + annotation.annotationType().getCanonicalName(), e);
- }
- }
- }
-
- private boolean isCascadeEnabled(GroupValidationContext context) {
- PathImpl path = context.getPropertyPath();
- NodeImpl node = path.getLeafNode();
- PathImpl beanPath = path.getPathWithoutLeafNode();
- if (beanPath == null) {
- beanPath = PathImpl.create(null);
- }
- try {
- 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);
- }
-
- try {
- 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);
- }
-
- return true;
+ if (!isMemberOf(context.getCurrentGroup().getGroup())) {
+ return; // do not validate in the current group
}
-
- private void addErrors(GroupValidationContext context,
- ConstraintValidatorContextImpl jsrContext) {
- for (ValidationListener.Error each : jsrContext.getErrorMessages()) {
- context.getListener().addError(each, context);
- }
- }
-
- public String toString() {
- return "ConstraintValidation{" + validator + '}';
- }
-
- public String getMessageTemplate() {
- return (String) attributes.get(ANNOTATION_MESSAGE);
- }
-
- public ConstraintValidator getValidator() {
- return validator;
- }
-
- protected boolean isMemberOf(Class<?> reqGroup) {
- return groups.contains(reqGroup);
- }
-
- public Class getOwner() {
- return owner;
- }
-
- public T getAnnotation() {
- return annotation;
+ if (context.getCurrentOwner() != null && this.owner != context.getCurrentOwner()) {
+ return;
}
+ if (validator != null && !context.collectValidated(validator))
+ return; // already done
- public AccessStrategy getAccess() {
- return access;
- }
-
- public void setAnnotation(T annotation) {
- this.annotation = annotation;
- }
-
- /////////////////////////// ConstraintDescriptor implementation
-
-
- public Map<String, Object> getAttributes() {
- return attributes;
- }
-
- public Set<ConstraintDescriptor<?>> getComposingConstraints() {
- return composedConstraints == null ? Collections.EMPTY_SET : composedConstraints;
- }
-
- Set<ConstraintValidation<?>> getComposingValidations() {
- return composedConstraints == null ? Collections.EMPTY_SET : composedConstraints;
- }
-
- public Set<Class<?>> getGroups() {
- return groups;
- }
-
- public Set<Class<? extends Payload>> getPayload() {
- return payload;
- }
-
- public List<Class<? extends ConstraintValidator<T, ?>>> getConstraintValidatorClasses() {
- if ( validatorClasses == null ) {
- return Collections.emptyList();
- }
- return Arrays.asList(validatorClasses);
+ if (context.getMetaProperty() != null && !isCascadeEnabled(context)) {
+ return;
+ }
+
+ // process composed constraints
+ if (isReportAsSingleViolation()) {
+ BeanValidationContext gctx = (BeanValidationContext) context;
+ ConstraintValidationListener oldListener =
+ ((ConstraintValidationListener) gctx.getListener());
+ ConstraintValidationListener listener =
+ new ConstraintValidationListener(oldListener.getRootBean(), oldListener.getRootBeanType());
+ gctx.setListener(listener);
+ try {
+ for (ConstraintValidation composed : getComposingValidations()) {
+ composed.validate(context);
+ }
+ } finally {
+ gctx.setListener(oldListener);
+ }
+
+ // Restore current constraint validation
+ context.setConstraintValidation(this);
+
+ // stop validating when already failed and ReportAsSingleInvalidConstraint = true ?
+ if (!listener.getConstaintViolations().isEmpty()) {
+ // TODO RSt - how should the composed constraint error report look like?
+ ConstraintValidatorContextImpl jsrContext =
+ new ConstraintValidatorContextImpl(context, this);
+ addErrors(context, jsrContext); // add defaultErrorMessage only*/
+ return;
+ }
+ } else {
+ for (ConstraintValidation composed : getComposingValidations()) {
+ composed.validate(context);
+ }
+
+ // Restore current constraint validation
+ context.setConstraintValidation(this);
+ }
+
+ if (validator != null) {
+ ConstraintValidatorContextImpl jsrContext =
+ new ConstraintValidatorContextImpl(context, this);
+ if (!validator.isValid(context.getValidatedValue(), jsrContext)) {
+ addErrors(context, jsrContext);
+ }
+ }
+ }
+
+ /**
+ * Initialize the validator (if not <code>null</code>) with the stored
+ * annotation.
+ */
+ public void initialize() {
+ if (null != validator) {
+ try {
+ validator.initialize(annotation);
+ } catch (RuntimeException e) {
+ // Either a "legit" problem initializing the validator or a
+ // ClassCastException if the validator associated annotation is
+ // not a supertype of the validated annotation.
+ throw new ConstraintDefinitionException(
+ "Incorrect validator [" + validator.getClass().getCanonicalName() + "] for annotation " +
+ annotation.annotationType().getCanonicalName(), e);
+ }
+ }
+ }
+
+ private boolean isCascadeEnabled(GroupValidationContext context) {
+ PathImpl path = context.getPropertyPath();
+ NodeImpl node = path.getLeafNode();
+ PathImpl beanPath = path.getPathWithoutLeafNode();
+ if (beanPath == null) {
+ beanPath = PathImpl.create(null);
+ }
+ try {
+ 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);
+ }
+
+ try {
+ 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);
+ }
+
+ return true;
+ }
+
+ private void addErrors(GroupValidationContext context,
+ ConstraintValidatorContextImpl jsrContext) {
+ for (ValidationListener.Error each : jsrContext.getErrorMessages()) {
+ context.getListener().addError(each, context);
+ }
+ }
+
+ public String toString() {
+ return "ConstraintValidation{" + validator + '}';
+ }
+
+ public String getMessageTemplate() {
+ return (String) attributes.get(ANNOTATION_MESSAGE);
+ }
+
+ public ConstraintValidator getValidator() {
+ return validator;
+ }
+
+ protected boolean isMemberOf(Class<?> reqGroup) {
+ return groups.contains(reqGroup);
+ }
+
+ public Class getOwner() {
+ return owner;
+ }
+
+ public T getAnnotation() {
+ return annotation;
+ }
+
+ public AccessStrategy getAccess() {
+ return access;
+ }
+
+ public void setAnnotation(T annotation) {
+ this.annotation = annotation;
+ }
+
+ /////////////////////////// ConstraintDescriptor implementation
+
+
+ public Map<String, Object> getAttributes() {
+ return attributes;
+ }
+
+ public Set<ConstraintDescriptor<?>> getComposingConstraints() {
+ return composedConstraints == null ? Collections.EMPTY_SET : composedConstraints;
+ }
+
+ Set<ConstraintValidation<?>> getComposingValidations() {
+ return composedConstraints == null ? Collections.EMPTY_SET : composedConstraints;
+ }
+
+ public Set<Class<?>> getGroups() {
+ return groups;
+ }
+
+ public Set<Class<? extends Payload>> getPayload() {
+ return payload;
+ }
+
+ public List<Class<? extends ConstraintValidator<T, ?>>> getConstraintValidatorClasses() {
+ if (validatorClasses == null) {
+ return Collections.emptyList();
}
+ return Arrays.asList(validatorClasses);
+ }
}
Modified: incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java?rev=946095&r1=946094&r2=946095&view=diff
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java (original)
+++ incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java Wed May 19 09:25:47 2010
@@ -103,4 +103,8 @@ public final class ConstraintValidationL
public Class<T> getRootBeanType() {
return rootBeanType;
}
+
+ public int violationsSize() {
+ return constaintViolations.size();
+ }
}
Added: incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GraphBeanIdentity.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GraphBeanIdentity.java?rev=946095&view=auto
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GraphBeanIdentity.java (added)
+++ incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GraphBeanIdentity.java Wed May 19 09:25:47 2010
@@ -0,0 +1,109 @@
+/*
+ * 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;
+
+/**
+ * Class that stores the needed properties to avoid circular paths when
+ * validating an object graph.
+ * <p>
+ * These properties are:
+ * <ul>
+ * <li>The ref of the bean to which the validation would be applied.</li>
+ * <li>The current group being validated.</li>
+ * </ul>
+ *
+ * FIXME: Owner is currently not used in identity checking, and probably won't
+ * never be. So it is likely to be deleted.
+ *
+ * @author Carlos Vara
+ */
+public class GraphBeanIdentity {
+
+ private final Object bean;
+ private final Class<?> group;
+ private final Class<?> owner;
+
+ public GraphBeanIdentity(Object bean, Class<?> group, Class<?> owner) {
+ this.bean = bean;
+ this.group = group;
+ this.owner = owner;
+ }
+
+ public Object getBean() {
+ return bean;
+ }
+
+ public Class<?> getGroup() {
+ return group;
+ }
+
+ public Class<?> getOwner() {
+ return owner;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null) {
+ return false;
+ }
+
+ if (!(obj instanceof GraphBeanIdentity)) {
+ return false;
+ }
+
+ GraphBeanIdentity other = (GraphBeanIdentity) obj;
+
+ // Bean ref must be the same
+ if (this.bean != other.bean) {
+ return false;
+ }
+
+ // Group ref must be the same
+ if (this.group != other.group) {
+ return false;
+ }
+
+// // Owner ref must be the same
+// if (this.owner != other.owner) {
+// return false;
+// }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((this.bean == null) ? 0 : this.bean.hashCode());
+ result = prime * result
+ + ((this.group == null) ? 0 : this.group.hashCode());
+// result = prime * result
+// + ((this.owner == null) ? 0 : this.owner.hashCode());
+ return result;
+ }
+
+
+}
Modified: incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContext.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContext.java?rev=946095&r1=946094&r2=946095&view=diff
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContext.java (original)
+++ incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContext.java Wed May 19 09:25:47 2010
@@ -57,5 +57,9 @@ public interface GroupValidationContext<
TraversableResolver getTraversableResolver();
boolean collectValidated(ConstraintValidator constraint);
+
+ Class<?> getCurrentOwner();
+
+ void setCurrentOwner(Class<?> currentOwner);
}
Modified: incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContextImpl.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContextImpl.java?rev=946095&r1=946094&r2=946095&view=diff
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContextImpl.java (original)
+++ incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContextImpl.java Wed May 19 09:25:47 2010
@@ -34,7 +34,6 @@ import javax.validation.TraversableResol
import javax.validation.metadata.ConstraintDescriptor;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Map;
import java.util.Set;
/**
@@ -56,19 +55,23 @@ final class GroupValidationContextImpl<T
*/
private Group currentGroup;
+ private Class<?> currentOwner;
+
/**
* contains the validation constraints that have already been processed during
* this validation routine (as part of a previous group match)
*/
private HashSet<ConstraintValidatorIdentity> validatedConstraints =
new HashSet<ConstraintValidatorIdentity>();
+
private ConstraintValidation constraintValidation;
private final TraversableResolver traversableResolver;
public GroupValidationContextImpl(T listener, MessageInterpolator aMessageResolver,
TraversableResolver traversableResolver,
MetaBean rootMetaBean) {
- super(listener);
+ // inherited variable 'validatedObjects' is of type: HashMap<GraphBeanIdentity, Set<PathImpl>> in this class
+ super(listener, new HashMap<GraphBeanIdentity, Set<PathImpl>>());
this.messageResolver = aMessageResolver;
this.traversableResolver = CachingTraversableResolver.cacheFor(traversableResolver);
this.rootMetaBean = rootMetaBean;
@@ -100,34 +103,33 @@ final class GroupValidationContextImpl<T
/**
* add the object in the current group to the collection of validated
* objects to keep track of them to avoid endless loops during validation.
+ * <p/>
+ * NOTE: No longer uses the inherited validatedObjects hashmap
*
* @return true when the object was not already validated in this context
*/
@Override
public boolean collectValidated() {
- Map<Group, Set<PathImpl>> groupMap = (Map<Group, Set<PathImpl>>) validatedObjects
- .get(getBean());
- if (groupMap == null) {
- groupMap = new HashMap<Group, Set<PathImpl>>();
- validatedObjects.put(getBean(), groupMap);
- }
- Set<PathImpl> validatedPathsForGroup = groupMap.get(getCurrentGroup());
- if (validatedPathsForGroup == null) {
- validatedPathsForGroup = new HashSet<PathImpl>();
- groupMap.put(getCurrentGroup(), validatedPathsForGroup);
+ // Combination of bean+group+owner (owner is currently ignored)
+ GraphBeanIdentity gbi = new GraphBeanIdentity(getBean(), getCurrentGroup().getGroup(), getCurrentOwner());
+
+ Set<PathImpl> validatedPathsForGBI = (Set<PathImpl>) validatedObjects.get(gbi);
+ if (validatedPathsForGBI == null) {
+ validatedPathsForGBI = new HashSet<PathImpl>();
+ validatedObjects.put(gbi, validatedPathsForGBI);
}
// If any of the paths is a subpath of the current path, there is a
// circular dependency, so return false
- for (PathImpl validatedPath : validatedPathsForGroup) {
+ for (PathImpl validatedPath : validatedPathsForGBI) {
if (path.isSubPathOf(validatedPath)) {
return false;
}
}
// Else, add the currentPath to the set of validatedPaths
- validatedPathsForGroup.add(PathImpl.copy(path));
+ validatedPathsForGBI.add(PathImpl.copy(path));
return true;
}
@@ -210,4 +212,13 @@ final class GroupValidationContextImpl<T
public TraversableResolver getTraversableResolver() {
return traversableResolver;
}
+
+
+ public Class<?> getCurrentOwner() {
+ return this.currentOwner;
+ }
+
+ public void setCurrentOwner(Class<?> currentOwner) {
+ this.currentOwner = currentOwner;
+ }
}
Modified: incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java?rev=946095&r1=946094&r2=946095&view=diff
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java (original)
+++ incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java Wed May 19 09:25:47 2010
@@ -21,6 +21,7 @@ package org.apache.bval.jsr303;
import org.apache.bval.MetaBeanFactory;
import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.util.ClassHelper;
import org.apache.bval.jsr303.util.ConstraintDefinitionValidator;
import org.apache.bval.jsr303.util.SecureActions;
import org.apache.bval.jsr303.util.TypeUtils;
@@ -40,7 +41,10 @@ import javax.validation.*;
import javax.validation.groups.Default;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* Description: process the class annotations for JSR303 constraint validations
@@ -76,7 +80,7 @@ public class Jsr303MetaBeanFactory imple
// process class, superclasses and interfaces
List<Class<?>> classSequence = new ArrayList<Class<?>>();
- fillFullClassHierarchyAsList(classSequence, beanClass);
+ ClassHelper.fillFullClassHierarchyAsList(classSequence, beanClass);
// start with superclasses and go down the hierarchy so that
// the child classes are processed last to have the chance to overwrite some declarations
@@ -84,13 +88,9 @@ public class Jsr303MetaBeanFactory imple
for (int i = classSequence.size() - 1; i >= 0; i--) {
Class<?> eachClass = classSequence.get(i);
processClass(eachClass, metabean);
+ processGroupSequence(eachClass, metabean, "{GroupSequence:"+eachClass.getCanonicalName()+"}");
}
- // JSR-303 2.3:
- // Groups from the main constraint annotation are inherited by the composing annotations.
- // Any groups definition on a composing annotation is ignored.
-// applyGroupInheritance(metabean);
-
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
} catch (InvocationTargetException e) {
@@ -98,77 +98,6 @@ public class Jsr303MetaBeanFactory imple
}
}
-// /**
-// * Traverses the metabean to set the groups of the composed constraints the
-// * same as the groups from its root constraint.
-// *
-// * @param metabean
-// * The metabean to which group inheritance will be applied.
-// */
-// private void applyGroupInheritance(MetaBean metabean) {
-// for ( MetaProperty prop : metabean.getProperties() ) {
-// for ( Validation val : prop.getValidations() ) {
-// if ( val instanceof ConstraintValidation<?> ) { // Should always be true?
-// ConstraintValidation<?> cv = (ConstraintValidation<?>) val;
-// Set<Class<?>> baseGroups = cv.getGroups();
-// for ( ConstraintValidation<?> composedVal : cv.getComposingValidations() ) {
-// overrideGroupsAndContinueRecursion(baseGroups, composedVal);
-// }
-// }
-// }
-// }
-// }
-//
-// /**
-// * Recursive method that sets the current {@link ConstraintValidation}
-// * groups as baseGroups, and continues the recursion with its composing
-// * validations (if any).
-// *
-// * @param baseGroups
-// * The groups to set in the current validation node.
-// * @param val
-// * The current validation node.
-// */
-// private void overrideGroupsAndContinueRecursion(Set<Class<?>> baseGroups, ConstraintValidation<?> val) {
-// val.setGroups(baseGroups);
-// for ( ConstraintValidation<?> composedVal : val.getComposingValidations() ) {
-// overrideGroupsAndContinueRecursion(baseGroups, composedVal);
-// }
-// }
-
- /**
- * Fill the list with the full class/interface hierarchy of the given class.
- * List is ordered from the most to less specific.
- *
- * @param allClasses
- * The current list of classes in the hierarchy.
- * @param clazz
- * The current class, root of the hierarchy to traverse.
- */
- private void fillFullClassHierarchyAsList(List<Class<?>> allClasses, Class<?> clazz) {
-
- if ( clazz == null || clazz == Object.class ) {
- return;
- }
-
- if ( allClasses.contains(clazz) ) {
- // No duplicates wanted, and if it is already in the list, then
- // its superclasses/interfaces will also be
- return;
- }
-
- allClasses.add(clazz);
-
- // Obtain the list of interfaces and superclass
- List<Class<?>> subClasses = new ArrayList<Class<?>>(Arrays.asList(clazz.getInterfaces()));
- subClasses.add(0, clazz.getSuperclass());
-
- for ( Class<?> subClass : subClasses ) {
- fillFullClassHierarchyAsList(allClasses, subClass);
- }
-
- }
-
/**
* process class annotations, field and method annotations
*
@@ -406,11 +335,15 @@ public class Jsr303MetaBeanFactory imple
}
private void processGroupSequence(Class<?> beanClass, MetaBean metabean) {
+ processGroupSequence(beanClass, metabean, Jsr303Features.Bean.GROUP_SEQUENCE);
+ }
+
+ private void processGroupSequence(Class<?> beanClass, MetaBean metabean, String key) {
GroupSequence annotation = beanClass.getAnnotation(GroupSequence.class);
- List<Group> groupSeq = metabean.getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
+ List<Group> groupSeq = metabean.getFeature(key);
if (groupSeq == null) {
groupSeq = new ArrayList<Group>(annotation == null ? 1 : annotation.value().length);
- metabean.putFeature(Jsr303Features.Bean.GROUP_SEQUENCE, groupSeq);
+ metabean.putFeature(key, groupSeq);
}
Class<?>[] groupClasses = factoryContext.getFactory().getDefaultSequence(beanClass);
if (groupClasses == null || groupClasses.length == 0) {
Added: incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ClassHelper.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ClassHelper.java?rev=946095&view=auto
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ClassHelper.java (added)
+++ incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ClassHelper.java Wed May 19 09:25:47 2010
@@ -0,0 +1,61 @@
+/*
+ * 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.util;
+
+import java.security.AccessController;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Common operations on classes that do not requiere an {@link AccessController}.
+ *
+ * @author Carlos Vara
+ */
+public class ClassHelper {
+
+ private ClassHelper() {
+ // No instances please
+ }
+
+ /**
+ * Fill the list with the full class/interface hierarchy of the given class.
+ * List is ordered from the most to less specific.
+ *
+ * @param allClasses
+ * The current list of classes in the hierarchy.
+ * @param clazz
+ * The current class, root of the hierarchy to traverse.
+ */
+ static public void fillFullClassHierarchyAsList(List<Class<?>> allClasses, Class<?> clazz) {
+ if ( clazz == null || clazz == Object.class ) {
+ return;
+ }
+ if ( allClasses.contains(clazz) ) {
+ return;
+ }
+ allClasses.add(clazz);
+ List<Class<?>> subClasses = new ArrayList<Class<?>>(Arrays.asList(clazz.getInterfaces()));
+ subClasses.add(0, clazz.getSuperclass());
+ for ( Class<?> subClass : subClasses ) {
+ fillFullClassHierarchyAsList(allClasses, subClass);
+ }
+ }
+
+}
Modified: incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintValidatorContextTest.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintValidatorContextTest.java?rev=946095&r1=946094&r2=946095&view=diff
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintValidatorContextTest.java (original)
+++ incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintValidatorContextTest.java Wed May 19 09:25:47 2010
@@ -18,15 +18,8 @@
*/
package org.apache.bval.jsr303;
-import javax.validation.ConstraintValidator;
-import javax.validation.ConstraintValidatorContext;
-import javax.validation.MessageInterpolator;
-import javax.validation.TraversableResolver;
-import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder;
-
import junit.framework.Assert;
import junit.framework.TestCase;
-
import org.apache.bval.jsr303.groups.Group;
import org.apache.bval.jsr303.groups.Groups;
import org.apache.bval.jsr303.util.PathImpl;
@@ -36,6 +29,12 @@ import org.apache.bval.model.ValidationL
import org.apache.bval.model.ValidationListener.Error;
import org.apache.bval.util.AccessStrategy;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+
/**
* Checks to validate the correct implementation of
@@ -223,6 +222,16 @@ public class ConstraintValidatorContextT
throw new IllegalStateException("Unexpected call");
}
+ @Override
+ public Class<?> getCurrentOwner() {
+ throw new IllegalStateException("Unexpected call");
+ }
+
+ @Override
+ public void setCurrentOwner(Class<?> currentOwner) {
+ throw new IllegalStateException("Unexpected call");
+ }
+
}
}
Modified: incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CollectionValidationTest.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CollectionValidationTest.java?rev=946095&r1=946094&r2=946095&view=diff
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CollectionValidationTest.java (original)
+++ incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CollectionValidationTest.java Wed May 19 09:25:47 2010
@@ -18,22 +18,13 @@
*/
package org.apache.bval.jsr303.groups;
-import org.apache.bval.jsr303.ApacheValidatorFactory;
import junit.framework.TestCase;
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.example.*;
+import org.apache.bval.jsr303.util.TestUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
-
-import org.apache.bval.jsr303.example.Address;
-import org.apache.bval.jsr303.example.Author;
-import org.apache.bval.jsr303.example.Book;
-import org.apache.bval.jsr303.example.Country;
-import org.apache.bval.jsr303.example.Customer;
-import org.apache.bval.jsr303.example.Employee;
-import org.apache.bval.jsr303.example.Library;
-import org.apache.bval.jsr303.example.Person;
-import org.apache.bval.jsr303.util.TestUtils;
-
import java.util.ArrayList;
import java.util.Set;
@@ -141,16 +132,15 @@ public class CollectionValidationTest ex
/*
This, by the way, tests redefined default group sequence behavior
on non-root-beans (Library.Book)!!
- So only 1 constraint violation expected
*/
violations = validator.validate(lib);
assertEquals(
- "redefined default group of Book not correctly validated from Library", 1,
+ "redefined default group of Book not correctly validated from Library", 3,
violations.size());
assertNotNull(TestUtils.getViolation(violations, "taggedBooks[politics].title"));
- assertNull(
+ assertNotNull(
TestUtils.getViolation(violations, "taggedBooks[humor].author.firstName"));
- assertNull(TestUtils.getViolation(violations,
+ assertNotNull(TestUtils.getViolation(violations,
"taggedBooks[science].author.addresses[0].city"));
}
Added: incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceIsolationTest.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceIsolationTest.java?rev=946095&view=auto
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceIsolationTest.java (added)
+++ incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceIsolationTest.java Wed May 19 09:25:47 2010
@@ -0,0 +1,166 @@
+/*
+ * 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.groups;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.DefaultMessageInterpolator;
+
+import javax.validation.*;
+import javax.validation.constraints.NotNull;
+import javax.validation.groups.Default;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Additional tests to check the correct processing of {@link GroupSequence}s
+ * by the validator.
+ *
+ * @author Carlos Vara
+ */
+public class GroupSequenceIsolationTest extends TestCase {
+
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator)factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ private Validator getValidator() {
+ return factory.getValidator();
+ }
+
+
+ /**
+ * When validating the {@link Default} group in a bean whose class doesn't
+ * define a {@link GroupSequence}, all the classes in the hierarchy must be
+ * checked for group sequence definitions and they must be evaluated in
+ * order for the constraints defined on those classes.
+ */
+ public void testGroupSequencesInHierarchyClasses() {
+ Validator validator = getValidator();
+
+ HolderWithNoGS h = new HolderWithNoGS();
+ Set<ConstraintViolation<HolderWithNoGS>> violations;
+
+ violations = validator.validate(h);
+ Assert.assertEquals("Unexpected number of violations", 2, violations.size());
+ for ( ConstraintViolation<HolderWithNoGS> violation : violations ) {
+ boolean good = violation.getPropertyPath().toString().equals("a1");
+ good |= violation.getPropertyPath().toString().equals("b2");
+ Assert.assertTrue("Wrong constraint", good);
+ }
+
+ h.a1 = "good";
+ violations = validator.validate(h);
+ Assert.assertEquals("Unexpected number of violations", 2, violations.size());
+ for ( ConstraintViolation<HolderWithNoGS> violation : violations ) {
+ boolean good = violation.getPropertyPath().toString().equals("a2");
+ good |= violation.getPropertyPath().toString().equals("b2");
+ Assert.assertTrue("Wrong constraint", good);
+ }
+
+ h.b2 = "good";
+ violations = validator.validate(h);
+ Assert.assertEquals("Unexpected number of violations", 2, violations.size());
+ for ( ConstraintViolation<HolderWithNoGS> violation : violations ) {
+ boolean good = violation.getPropertyPath().toString().equals("a2");
+ good |= violation.getPropertyPath().toString().equals("b1");
+ Assert.assertTrue("Wrong constraint", good);
+ }
+
+ h.b1 = "good";
+ violations = validator.validate(h);
+ Assert.assertEquals("Unexpected number of violations", 1, violations.size());
+ for ( ConstraintViolation<HolderWithNoGS> violation : violations ) {
+ boolean good = violation.getPropertyPath().toString().equals("a2");
+ Assert.assertTrue("Wrong constraint", good);
+ }
+ }
+
+ /**
+ * When validating the {@link Default} group in a bean whose class defines
+ * a group sequence, that group sequence is used for all the constraints.
+ */
+ public void testGroupSequenceOfBeanClass() {
+ Validator validator = getValidator();
+
+ HolderWithGS h = new HolderWithGS();
+ Set<ConstraintViolation<HolderWithGS>> violations;
+
+ violations = validator.validate(h);
+ Assert.assertEquals("Unexpected number of violations", 1, violations.size());
+ for ( ConstraintViolation<HolderWithGS> violation : violations ) {
+ boolean good = violation.getPropertyPath().toString().equals("a1");
+ Assert.assertTrue("Wrong constraint", good);
+ }
+
+ h.a1 = "good";
+ violations = validator.validate(h);
+ Assert.assertEquals("Unexpected number of violations", 2, violations.size());
+ for ( ConstraintViolation<HolderWithGS> violation : violations ) {
+ boolean good = violation.getPropertyPath().toString().equals("a2");
+ good |= violation.getPropertyPath().toString().equals("b2");
+ Assert.assertTrue("Wrong constraint", good);
+ }
+
+ h.a2 = "good";
+ h.b2 = "good";
+ violations = validator.validate(h);
+ Assert.assertEquals("Unexpected number of violations", 1, violations.size());
+ for ( ConstraintViolation<HolderWithGS> violation : violations ) {
+ boolean good = violation.getPropertyPath().toString().equals("b1");
+ Assert.assertTrue("Wrong constraint", good);
+ }
+ }
+
+ @GroupSequence({GroupA1.class, A.class})
+ public static class A {
+ @NotNull(groups={GroupA1.class})
+ public String a1;
+ @NotNull
+ public String a2;
+ }
+
+ public static interface GroupA1 {
+ }
+
+ @GroupSequence({B.class, GroupB1.class})
+ public static class B extends A {
+ @NotNull(groups={GroupB1.class})
+ public String b1;
+ @NotNull
+ public String b2;
+ }
+
+ public static interface GroupB1 {
+
+ }
+
+ // No group sequence definition
+ public static class HolderWithNoGS extends B {
+
+ }
+
+ @GroupSequence({GroupA1.class, HolderWithGS.class, GroupB1.class})
+ public static class HolderWithGS extends B {
+
+ }
+}