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