You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bval.apache.org by rm...@apache.org on 2013/07/01 12:06:21 UTC

svn commit: r1498347 [3/7] - in /bval/branches/bval-11: ./ bval-core/src/main/java/org/apache/bval/ bval-core/src/main/java/org/apache/bval/model/ bval-core/src/main/java/org/apache/bval/util/ bval-extras/ bval-guice/ bval-guice/src/main/java/org/apach...

Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java?rev=1498347&r1=1498346&r2=1498347&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java (original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java Mon Jul  1 10:06:18 2013
@@ -18,20 +18,6 @@
  */
 package org.apache.bval.jsr303;
 
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Modifier;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-import javax.validation.ConstraintViolation;
-import javax.validation.ValidationException;
-import javax.validation.groups.Default;
-import javax.validation.metadata.BeanDescriptor;
-
 import org.apache.bval.DynamicMetaBean;
 import org.apache.bval.MetaBeanFinder;
 import org.apache.bval.jsr303.groups.Group;
@@ -41,17 +27,47 @@ import org.apache.bval.jsr303.util.Class
 import org.apache.bval.jsr303.util.NodeImpl;
 import org.apache.bval.jsr303.util.PathImpl;
 import org.apache.bval.jsr303.util.PathNavigation;
+import org.apache.bval.jsr303.util.Proxies;
 import org.apache.bval.jsr303.util.ValidationContextTraversal;
 import org.apache.bval.model.Features;
 import org.apache.bval.model.FeaturesCapable;
 import org.apache.bval.model.MetaBean;
 import org.apache.bval.model.MetaProperty;
+import org.apache.bval.model.Validation;
 import org.apache.bval.util.AccessStrategy;
 import org.apache.bval.util.ValidationHelper;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.reflect.TypeUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+
+import javax.validation.ConstraintDeclarationException;
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.ConstraintTarget;
+import javax.validation.ConstraintViolation;
+import javax.validation.ElementKind;
+import javax.validation.ValidationException;
+import javax.validation.executable.ExecutableValidator;
+import javax.validation.groups.Default;
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+import javax.validation.metadata.ParameterDescriptor;
+import javax.validation.metadata.PropertyDescriptor;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 // TODO: centralize treatMapsLikeBeans
 
@@ -67,7 +83,7 @@ import org.apache.commons.lang3.reflect.
  * @author Roman Stumm
  * @author Carlos Vara
  */
-public class ClassValidator implements CascadingPropertyValidator {
+public class ClassValidator implements CascadingPropertyValidator, ExecutableValidator {
     private static final Object VALIDATE_PROPERTY = new Object() {
         public String toString() {
             return "VALIDATE_PROPERTY";
@@ -130,44 +146,49 @@ public class ClassValidator implements C
     // @Override - not allowed in 1.5 for Interface methods
     @SuppressWarnings("unchecked")
     public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
-        if (object == null)
+        if (object == null) {
             throw new IllegalArgumentException("cannot validate null");
+        }
         checkGroups(groups);
 
         try {
 
-            Class<T> objectClass = (Class<T>) object.getClass();
-            MetaBean objectMetaBean = getMetaBeanFinder().findForClass(objectClass);
-
+            final Class<T> objectClass = (Class<T>) object.getClass();
+            final MetaBean objectMetaBean = getMetaBeanFinder().findForClass(objectClass);
             final GroupValidationContext<T> context = createContext(objectMetaBean, object, objectClass, groups);
-            final ConstraintValidationListener<T> result = context.getListener();
-            final Groups sequence = context.getGroups();
+            return validateBeanWithGroups(context, context.getGroups());
+        } catch (final RuntimeException ex) {
+            throw unrecoverableValidationError(ex, object);
+        }
+    }
 
-            // 1. process groups
-            for (Group current : sequence.getGroups()) {
+    private <T> Set<ConstraintViolation<T>> validateBeanWithGroups(final GroupValidationContext<T> context, final Groups sequence) {
+        final ConstraintValidationListener<T> result = context.getListener();
+
+        // 1. process groups
+        for (final Group current : sequence.getGroups()) {
+            context.setCurrentGroup(current);
+            validateBeanNet(context);
+        }
+
+        // 2. process sequences
+        for (final List<Group> eachSeq : sequence.getSequences()) {
+            for (final Group current : eachSeq) {
                 context.setCurrentGroup(current);
                 validateBeanNet(context);
-            }
-
-            // 2. process sequences
-            for (List<Group> eachSeq : sequence.getSequences()) {
-                for (Group current : eachSeq) {
-                    context.setCurrentGroup(current);
-                    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
-                    if (!result.isEmpty())
-                        break;
-                }
-                if (!result.isEmpty())
+                // 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
+                if (!result.isEmpty()) {
                     break;
+                }
+            }
+            if (!result.isEmpty()) {
+                break;
             }
-            return result.getConstraintViolations();
-        } catch (RuntimeException ex) {
-            throw unrecoverableValidationError(ex, object);
         }
+        return result.getConstraintViolations();
     }
 
     /**
@@ -255,14 +276,18 @@ public class ClassValidator implements C
             throw new IllegalArgumentException("Class cannot be null");
         }
         try {
-            MetaBean metaBean = getMetaBeanFinder().findForClass(clazz);
+            MetaBean metaBean = getMetaBeanFinder().findForClass(clazz); // don't throw an exception because of a missing validator here
             BeanDescriptorImpl edesc = metaBean.getFeature(Jsr303Features.Bean.BEAN_DESCRIPTOR);
             if (edesc == null) {
                 edesc = createBeanDescriptor(metaBean);
                 metaBean.putFeature(Jsr303Features.Bean.BEAN_DESCRIPTOR, edesc);
             }
             return edesc;
-        } catch (RuntimeException ex) {
+        } catch (final ConstraintDefinitionException definitionEx) {
+            throw definitionEx;
+        } catch (final ConstraintDeclarationException declarationEx) {
+            throw declarationEx;
+        } catch (final RuntimeException ex) {
             throw new ValidationException("error retrieving constraints for " + clazz, ex);
         }
     }
@@ -303,6 +328,10 @@ public class ClassValidator implements C
         }
     }
 
+    public ExecutableValidator forExecutables() {
+        return this;
+    }
+
     private <T> T newInstance(final Class<T> cls) {
         return AccessController.doPrivileged(new PrivilegedAction<T>() {
             public T run() {
@@ -327,7 +356,7 @@ public class ClassValidator implements C
      * <p/>
      * Special code is present to manage the {@link Default} group.
      *
-     * @param validationContext The current context of this validation call. Must have its
+     * @param context The current context of this validation call. Must have its
      *                          {@link GroupValidationContext#getCurrentGroup()} field set.
      */
     protected void validateBeanNet(GroupValidationContext<?> context) {
@@ -349,7 +378,7 @@ public class ClassValidator implements C
         if (context.getCurrentGroup().isDefault()) {
 
             List<Group> defaultGroups = expandDefaultGroup(context);
-            final ConstraintValidationListener<?> result = (ConstraintValidationListener<?>) context.getListener();
+            final ConstraintValidationListener<?> result = context.getListener();
 
             // If the rootBean defines a GroupSequence
             if (defaultGroups != null && defaultGroups.size() > 1) {
@@ -357,10 +386,13 @@ public class ClassValidator implements C
                 int numViolations = result.violationsSize();
 
                 // Validate the bean for each group in the sequence
-                Group currentGroup = context.getCurrentGroup();
-                for (Group each : defaultGroups) {
+                final Group currentGroup = context.getCurrentGroup();
+                for (final Group each : defaultGroups) {
                     context.setCurrentGroup(each);
-                    ValidationHelper.validateBean(context);
+
+                    // ValidationHelper.validateBean(context);, doesn't match anymore because of @ConvertGroup
+                    validateBean(context);
+
                     // Spec 3.4.3 - Stop validation if errors already found
                     if (result.violationsSize() > numViolations) {
                         break;
@@ -374,23 +406,22 @@ public class ClassValidator implements C
                 // to the GroupSequence defined in the same class
 
                 // Obtain the full class hierarchy
-                List<Class<?>> classHierarchy = new ArrayList<Class<?>>();
+                final List<Class<?>> classHierarchy = new ArrayList<Class<?>>();
                 ClassHelper.fillFullClassHierarchyAsList(classHierarchy, context.getMetaBean().getBeanClass());
-                Class<?> initialOwner = context.getCurrentOwner();
+                final Class<?> initialOwner = context.getCurrentOwner();
 
                 // For each owner in the hierarchy
-                for (Class<?> owner : classHierarchy) {
+                for (final 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() + "}");
+                    final List<Group> ownerDefaultGroups = context.getMetaBean().getFeature("{GroupSequence:" + owner.getCanonicalName() + "}");
                     for (Group each : ownerDefaultGroups) {
                         context.setCurrentGroup(each);
-                        ValidationHelper.validateBean(context);
+                        validateBean(context);
                         // Spec 3.4.3 - Stop validation if errors already found
                         if (result.violationsSize() > numViolations) {
                             break;
@@ -406,14 +437,59 @@ public class ClassValidator implements C
         }
         // if not the default group, proceed as normal
         else {
-            ValidationHelper.validateBean(context);
+            validateBean(context);
         }
 
         // ### Then, the cascaded beans (@Valid)
-        for (MetaProperty prop : context.getMetaBean().getProperties()) {
-            validateCascadedBean(context, prop);
+        for (final MetaProperty prop : context.getMetaBean().getProperties()) {
+            final Group group = context.getCurrentGroup();
+            final Group mappedGroup;
+
+            final Object feature = prop.getFeature(Jsr303Features.Property.PropertyDescriptor);
+            if (feature != null) {
+                mappedGroup = PropertyDescriptorImpl.class.cast(feature).mapGroup(group);
+            } else {
+                mappedGroup = group;
+            }
+
+
+            if (group != mappedGroup) {
+                final Groups propertyGroup = groupsComputer.computeGroups(new Class<?>[]{ mappedGroup.getGroup() });
+                validateCascadedBean(context, prop, propertyGroup);
+            } else {
+                validateCascadedBean(context, prop, null);
+            }
+
+            context.setCurrentGroup(group);
+        }
+
+    }
+
+    // TODO: maybe add a GroupMapper to bval-core to ease this kind of thing and void to fork this method from ValidationHelper
+    private void validateBean(final GroupValidationContext<?> context) {
+        // execute all property level validations
+        for (final PropertyDescriptor prop : getConstraintsForClass(context.getMetaBean().getBeanClass()).getConstrainedProperties()) {
+            final PropertyDescriptorImpl impl = PropertyDescriptorImpl.class.cast(prop);
+            checkValidationAppliesTo(impl.getConstraintDescriptors(), ConstraintTarget.PARAMETERS);
+            checkValidationAppliesTo(impl.getConstraintDescriptors(), ConstraintTarget.RETURN_VALUE);
+
+            final MetaProperty metaProperty = context.getMetaBean().getProperty(prop.getPropertyName());
+            context.setMetaProperty(metaProperty);
+            final Group current = context.getCurrentGroup();
+            context.setCurrentGroup(impl.mapGroup(current));
+            ValidationHelper.validateProperty(context);
+            context.setCurrentGroup(current);
         }
 
+        // execute all bean level validations
+        context.setMetaProperty(null);
+        for (final Validation validation : context.getMetaBean().getValidations()) {
+            if (ConstraintValidation.class.isInstance(validation)) {
+                checkValidationAppliesTo(ConstraintValidation.class.cast(validation).getValidationAppliesTo(), ConstraintTarget.PARAMETERS);
+                checkValidationAppliesTo(ConstraintValidation.class.cast(validation).getValidationAppliesTo(), ConstraintTarget.RETURN_VALUE);
+            }
+            validation.validate(context);
+        }
     }
 
     /**
@@ -422,7 +498,7 @@ public class ClassValidator implements C
      * @param context The current validation context.
      * @param prop    The property to cascade from (in case it is possible).
      */
-    private void validateCascadedBean(GroupValidationContext<?> context, MetaProperty prop) {
+    private void validateCascadedBean(final GroupValidationContext<?> context, final MetaProperty prop, final Groups groups) {
         AccessStrategy[] access = prop.getFeature(Features.Property.REF_CASCADE);
         if (access != null) { // different accesses to relation
             // save old values from context
@@ -430,13 +506,20 @@ public class ClassValidator implements C
             final MetaBean mbean = context.getMetaBean();
             // TODO implement Validation.groups support on related bean
 //            Class[] groups = prop.getFeature(Jsr303Features.Property.REF_GROUPS);
-            for (AccessStrategy each : access) {
+            for (final AccessStrategy each : access) {
                 if (isCascadable(context, prop, each)) {
                     // modify context state for relationship-target bean
                     context.moveDown(prop, each);
-                    // Now, if the related bean is an instance of Map/Array/etc,
-                    ValidationHelper
-                        .validateContext(context, new Jsr303ValidationCallback(context), treatMapsLikeBeans);
+                    // validate
+                    if (groups == null) {
+                        ValidationHelper.validateContext(context, new Jsr303ValidationCallback(context), treatMapsLikeBeans);
+                    } else {
+                        ValidationHelper.validateContext(context, new ValidationHelper.ValidateCallback() {
+                            public void validate() {
+                                validateBeanWithGroups(context, groups);
+                            }
+                        }, treatMapsLikeBeans);
+                    }
                     // restore old values in context
                     context.moveUp(bean, mbean);
                 }
@@ -456,14 +539,15 @@ public class ClassValidator implements C
     private boolean isCascadable(GroupValidationContext<?> context, MetaProperty prop, AccessStrategy access) {
 
         PathImpl beanPath = context.getPropertyPath();
-        NodeImpl node = new NodeImpl(prop.getName());
+        final NodeImpl node = new NodeImpl.PropertyNodeImpl(prop.getName());
         if (beanPath == null) {
-            beanPath = PathImpl.create(null);
+            beanPath = PathImpl.create();
         }
         try {
             if (!context.getTraversableResolver().isReachable(context.getBean(), node,
-                context.getRootMetaBean().getBeanClass(), beanPath, access.getElementType()))
+                context.getRootMetaBean().getBeanClass(), beanPath, access.getElementType())) {
                 return false;
+            }
         } catch (RuntimeException e) {
             throw new ValidationException("Error in TraversableResolver.isReachable() for " + context.getBean(), e);
         }
@@ -570,12 +654,11 @@ public class ClassValidator implements C
      * @param groups
      * @return {@link GroupValidationContext} instance
      */
-    protected <T> GroupValidationContext<T> createContext(MetaBean metaBean, T object, Class<T> objectClass,
-        Class<?>... groups) {
-        ConstraintValidationListener<T> listener = new ConstraintValidationListener<T>(object, objectClass);
-        GroupValidationContextImpl<T> context =
+    protected <T> GroupValidationContext<T> createContext(MetaBean metaBean, T object, Class<T> objectClass, Class<?>... groups) {
+        final ConstraintValidationListener<T> listener = new ConstraintValidationListener<T>(object, objectClass);
+        final GroupValidationContextImpl<T> context =
             new GroupValidationContextImpl<T>(listener, this.factoryContext.getMessageInterpolator(),
-                this.factoryContext.getTraversableResolver(), metaBean);
+                this.factoryContext.getTraversableResolver(), factoryContext.getParameterNameProvider(), metaBean);
         context.setBean(object, metaBean);
         context.setGroups(groupsComputer.computeGroups(groups));
         return context;
@@ -587,6 +670,11 @@ public class ClassValidator implements C
      * @param metaBean
      * @return {@link BeanDescriptorImpl} instance
      */
+    /*
+    protected BeanDescriptorImpl createBeanDescriptor(MetaBean metaBean) {
+        return new BeanDescriptorImpl(factoryContext, metaBean);
+    }
+    */
     protected BeanDescriptorImpl createBeanDescriptor(MetaBean metaBean) {
         return new BeanDescriptorImpl(factoryContext, metaBean);
     }
@@ -660,6 +748,331 @@ public class ClassValidator implements C
         if (groups == null) {
             throw new IllegalArgumentException("Groups cannot be null.");
         }
+        for (final Class<?> c : groups) {
+            if (c == null) {
+                throw new IllegalArgumentException("Group cannot be null.");
+            }
+        }
+    }
+
+    public <T> Set<ConstraintViolation<T>> validateConstructorParameters(Constructor<? extends T> constructor, Object[] parameterValues, Class<?>... gps) {
+        notNull("Constructor", constructor);
+        notNull("Groups", gps);
+        notNull("Parameters", parameterValues);
+
+        final Class<?> declaringClass = constructor.getDeclaringClass();
+        final ConstructorDescriptorImpl constructorDescriptor = ConstructorDescriptorImpl.class.cast(getConstraintsForClass(declaringClass).getConstraintsForConstructor(constructor.getParameterTypes()));
+        if (constructorDescriptor == null) { // no constraint
+            return Collections.emptySet();
+        }
+
+        // validations
+        if (parameterValues.length > 0) {
+            checkValidationAppliesTo(Collections.singleton(constructorDescriptor.getCrossParameterDescriptor()), ConstraintTarget.IMPLICIT);
+            checkValidationAppliesTo(constructorDescriptor.getParameterDescriptors(), ConstraintTarget.IMPLICIT);
+        } else {
+            checkValidationAppliesTo(Collections.singleton(constructorDescriptor.getCrossParameterDescriptor()), ConstraintTarget.PARAMETERS);
+            checkValidationAppliesTo(constructorDescriptor.getParameterDescriptors(), ConstraintTarget.PARAMETERS);
+        }
+
+        final Set<ConstraintViolation<T>> violations = new HashSet<ConstraintViolation<T>>();
+        final ImmutablePair<Set<ConstraintViolation<Object>>, Group> result;
+        {
+            final GroupValidationContext<ConstraintValidationListener<?>> context = createContext(constructorDescriptor.getMetaBean(), null, Class.class.cast(constructor.getDeclaringClass()), gps);
+            context.moveDown(new NodeImpl.ConstructorNodeImpl(declaringClass.getSimpleName(), Arrays.asList(constructor.getParameterTypes())));
+            result = validateParameters(context, constructorDescriptor.getParameterDescriptors(), parameterValues);
+            violations.addAll(Set.class.cast(result.getLeft()));
+            context.moveUp(null, null);
+        }
+        {
+            final GroupValidationContext<Object> crossParameterContext;
+            if (result.getRight() == null) {
+                crossParameterContext = createContext(constructorDescriptor.getMetaBean(), parameterValues, Class.class.cast(constructor.getDeclaringClass()), gps);
+            } else {
+                crossParameterContext = createContext(constructorDescriptor.getMetaBean(), parameterValues, Class.class.cast(constructor.getDeclaringClass()), result.getRight().getGroup());
+            }
+
+            crossParameterContext.setBean(parameterValues);
+            crossParameterContext.moveDown(new NodeImpl.ConstructorNodeImpl(declaringClass.getSimpleName(), Arrays.asList(constructor.getParameterTypes())));
+            crossParameterContext.moveDown("<cross-parameter>");
+            crossParameterContext.setKind(ElementKind.CROSS_PARAMETER);
+            validateElementInContext(crossParameterContext, constructorDescriptor.getCrossParameterDescriptor());
+            crossParameterContext.moveUp(null, null);
+            crossParameterContext.moveUp(null, null);
+
+            violations.addAll(Set.class.cast(crossParameterContext.getListener().getConstraintViolations()));
+        }
+
+        return violations;
+    }
+
+    private static void checkValidationAppliesTo(final Collection<? extends ElementDescriptor> descriptors, final ConstraintTarget forbidden) {
+        for (final ElementDescriptor descriptor : descriptors) {
+            for (final ConstraintDescriptor<?> consDesc : descriptor.getConstraintDescriptors()) {
+                checkValidationAppliesTo(consDesc.getValidationAppliesTo(), forbidden);
+            }
+        }
+    }
+
+    private static void checkValidationAppliesTo(final Set<ConstraintDescriptor<?>> constraintDescriptors, final ConstraintTarget forbidden) {
+        for (final ConstraintDescriptor<?> descriptor : constraintDescriptors) {
+            checkValidationAppliesTo(descriptor.getValidationAppliesTo(), forbidden);
+        }
+    }
+
+    private static void checkValidationAppliesTo(final ConstraintTarget configured, final ConstraintTarget forbidden) {
+        if (forbidden.equals(configured)) {
+            throw new ConstraintDeclarationException(forbidden.name() + " forbidden here");
+        }
+    }
+
+    public <T> Set<ConstraintViolation<T>> validateConstructorReturnValue(final Constructor<? extends T> constructor, final T createdObject, final Class<?>... groups) {
+        if (constructor == null) {
+            throw new IllegalArgumentException("constructor shouldn't be null");
+        }
+        if (createdObject == null) {
+            throw new IllegalArgumentException("returned value shouldn't be null");
+        }
+
+        final ConstructorDescriptorImpl methodDescriptor = ConstructorDescriptorImpl.class.cast(getConstraintsForClass(constructor.getDeclaringClass()).getConstraintsForConstructor(constructor.getParameterTypes()));
+        if (methodDescriptor == null) {
+            throw new ValidationException("Constructor " + constructor + " doesn't belong to class " + constructor.getDeclaringClass());
+        }
+
+        final GroupValidationContext<T> context = createContext(methodDescriptor.getMetaBean(), createdObject, (Class<T>) Proxies.classFor(createdObject.getClass()), groups);
+        context.moveDown(new NodeImpl.ConstructorNodeImpl(constructor.getDeclaringClass().getSimpleName(), Arrays.asList(constructor.getParameterTypes())));
+        context.moveDown(new NodeImpl.ReturnValueNodeImpl());
+        context.setReturnValue(createdObject);
+        validateElementInContext(context, methodDescriptor.getReturnValueDescriptor());
+        final Set<ConstraintViolation<T>> constraintViolations = context.getListener().getConstraintViolations();
+        context.moveUp(null, null);
+        context.moveUp(null, null);
+
+        if (methodDescriptor.isCascaded()) {
+            context.moveDown(new NodeImpl.ConstructorNodeImpl(constructor.getDeclaringClass().getSimpleName(), Arrays.asList(constructor.getParameterTypes())));
+            context.moveDown(new NodeImpl.ReturnValueNodeImpl());
+            context.setReturnValue(createdObject);
+            context.setBean(createdObject);
+            for (final Group group : context.getGroups().getGroups()) {
+                context.setCurrentGroup(methodDescriptor.mapGroup(group));
+                validateBeanNet(context);
+            }
+            for (final List<Group> sequence : context.getGroups().getSequences()) {
+                for (final Group group : sequence) {
+                    context.setCurrentGroup(methodDescriptor.mapGroup(group));
+                    validateBeanNet(context);
+                }
+            }
+            constraintViolations.addAll(Set.class.cast(context.getListener().getConstraintViolations()));
+            context.moveUp(null, null);
+            context.moveUp(null, null);
+        }
+
+        return constraintViolations;
+    }
+
+    public <T> Set<ConstraintViolation<T>> validateParameters(T object, Method method, Object[] parameterValues, Class<?>... groups) {
+        {
+            notNull("Object", object);
+            notNull("Parameters", parameterValues);
+            notNull("Method", method);
+            notNull("Groups", groups);
+            for (final Class<?> g : groups) {
+                notNull("Each group", g);
+            }
+        }
+
+        final MethodDescriptorImpl methodDescriptor = findMethodDescriptor(object, method);
+        if (methodDescriptor == null) { // no constraint
+            return Collections.emptySet();
+        }
+
+        if (method.getParameterTypes().length > 0 && method.getReturnType() != Void.TYPE) {
+            checkValidationAppliesTo(Collections.singleton(methodDescriptor.getCrossParameterDescriptor()), ConstraintTarget.IMPLICIT);
+            checkValidationAppliesTo(methodDescriptor.getParameterDescriptors(), ConstraintTarget.IMPLICIT);
+        } else if (method.getParameterTypes().length == 0) {
+            checkValidationAppliesTo(Collections.singleton(methodDescriptor.getCrossParameterDescriptor()), ConstraintTarget.PARAMETERS);
+            checkValidationAppliesTo(methodDescriptor.getParameterDescriptors(), ConstraintTarget.PARAMETERS);
+        }
+
+        final Set<ConstraintViolation<T>> violations = new HashSet<ConstraintViolation<T>>();
+
+        final ImmutablePair<Set<ConstraintViolation<Object>>, Group> result;
+        { // parameter validations
+            final GroupValidationContext<ConstraintValidationListener<?>> context = createContext(methodDescriptor.getMetaBean(), object, Class.class.cast(object.getClass()), groups);
+            context.setMethod(method);
+            context.moveDown(new NodeImpl.MethodNodeImpl(method.getName(), Arrays.asList(method.getParameterTypes())));
+            result = validateParameters(context, methodDescriptor.getParameterDescriptors(), parameterValues);
+            violations.addAll(Set.class.cast(result.getLeft()));
+            context.moveUp(null, null);
+        }
+
+        { // cross parameters validation
+            final GroupValidationContext<Object[]> context;
+            if (result.getRight() == null) {
+                context = createContext(methodDescriptor.getMetaBean(), object, Class.class.cast(Object[].class), groups);
+            } else {
+                context = createContext(methodDescriptor.getMetaBean(), object, Class.class.cast(Object[].class), result.getRight().getGroup());
+            }
+            context.setMethod(method);
+            context.setBean(parameterValues);
+            context.moveDown(new NodeImpl.MethodNodeImpl(method.getName(), Arrays.asList(method.getParameterTypes())));
+            context.moveDown(new NodeImpl.CrossParameterNodeImpl());
+            validateElementInContext(context, methodDescriptor.getCrossParameterDescriptor());
+            violations.addAll(Set.class.cast(context.getListener().getConstraintViolations()));
+            context.moveUp(null, null);
+            context.moveUp(null, null);
+        }
+
+        return violations;
+    }
+
+    private static void notNull(final String entity, final Object shouldntBeNull) {
+        if (shouldntBeNull == null) {
+            throw new IllegalArgumentException(entity + " shouldn't be null");
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> Set<ConstraintViolation<T>> validateReturnValue(T object, Method method, Object returnValue, Class<?>... groups) {
+        notNull("object", object);
+        notNull("method", method);
+        notNull("groups", groups);
+
+        MethodDescriptorImpl methodDescriptor = findMethodDescriptor(object, method);
+        if (methodDescriptor == null) {
+            throw new ValidationException("Method " + method + " doesn't belong to class " + object.getClass());
+        }
+
+        if (method.getReturnType() == Void.TYPE) {
+            checkValidationAppliesTo(methodDescriptor.getReturnValueDescriptor().getConstraintDescriptors(), ConstraintTarget.RETURN_VALUE);
+        }
+
+        final GroupValidationContext<?> context = createContext(methodDescriptor.getMetaBean(), returnValue, Class.class.cast(Proxies.classFor(object.getClass())), groups);
+        context.moveDown(new NodeImpl.MethodNodeImpl(method.getName(), Arrays.asList(method.getParameterTypes())));
+        context.moveDown(new NodeImpl.ReturnValueNodeImpl());
+        context.setReturnValue(object); // switched with returnValue when creating violation, avoid to modify the validation logic
+        initMetaBean(context, factoryContext.getMetaBeanFinder(), method.getReturnType());
+        validateElementInContext(context, methodDescriptor.getReturnValueDescriptor());
+        final ConstraintValidationListener<T> result = (ConstraintValidationListener<T>) context.getListener();
+        context.moveUp(null, null);
+        context.moveUp(null, null);
+
+        return result.getConstraintViolations();
+    }
+
+    private <T> MethodDescriptorImpl findMethodDescriptor(final T object, final Method method) {
+        return MethodDescriptorImpl.class.cast(getConstraintsForClass(Proxies.classFor(object.getClass())).getConstraintsForMethod(method.getName(), method.getParameterTypes()));
+    }
+
+    private <T> ImmutablePair<Set<ConstraintViolation<T>>, Group> validateParameters(GroupValidationContext<ConstraintValidationListener<?>> context,
+                                                                                         List<ParameterDescriptor> paramDescriptors, Object[] parameters) {
+        if (parameters == null) {
+            throw new IllegalArgumentException("cannot validate null");
+        }
+        if (parameters.length > 0) {
+            try {
+                Group gp = null;
+                for (int i = 0; i < parameters.length; i++) {
+                    final ParameterDescriptorImpl paramDesc = (ParameterDescriptorImpl) paramDescriptors.get(i);
+                    context.setBean(parameters[i]);
+                    context.setParameters(parameters);
+                    context.moveDown(new NodeImpl.ParameterNodeImpl(paramDesc.getName(), i));
+                    final Group current = validateElementInContext(context, paramDesc);
+                    if (current != null) {
+                        gp = current;
+                    }
+                    context.moveUp(null, null);
+                }
+
+                final ConstraintValidationListener<T> result = (ConstraintValidationListener<T>) context.getListener();
+                return new ImmutablePair<Set<ConstraintViolation<T>>, Group>(result.getConstraintViolations(), gp);
+            } catch (final RuntimeException ex) {
+                throw unrecoverableValidationError(ex, parameters);
+            }
+        } else {
+            return new ImmutablePair<Set<ConstraintViolation<T>>, Group>(Collections.<ConstraintViolation<T>> emptySet(), null);
+        }
+    }
+
+    private <T> void initMetaBean(final GroupValidationContext<T> context, final MetaBeanFinder metaBeanFinder, final Class<?> directValueClass) {
+        final boolean collection = Collection.class.isAssignableFrom(directValueClass);
+        final boolean map = Map.class.isAssignableFrom(directValueClass);
+        if (!directValueClass.isArray()
+                && (!collection || Collection.class.cast(context.getValidatedValue()).isEmpty())
+                && (!map || Map.class.cast(context.getValidatedValue()).isEmpty())) {
+            context.setMetaBean(metaBeanFinder.findForClass(directValueClass));
+        } else if (collection) {
+            context.setMetaBean(metaBeanFinder.findForClass(Collection.class.cast(context.getValidatedValue()).iterator().next().getClass()));
+        } else if (map) {
+            context.setMetaBean(metaBeanFinder.findForClass(Map.class.cast(context.getValidatedValue()).values().iterator().next().getClass()));
+        } else {
+            context.setMetaBean(metaBeanFinder.findForClass(directValueClass.getComponentType()));
+        }
+    }
+
+    private <T> Group validateElementInContext(final GroupValidationContext<T> context,
+                                              final ElementDescriptor eltDescriptor) {
+
+        final ElementDescriptorImpl impl = ElementDescriptorImpl.class.cast(eltDescriptor);
+
+        final Groups groups = context.getGroups();
+
+        Group breakOnGroup = null;
+
+        for (final ConstraintDescriptor<?> consDesc : eltDescriptor.getConstraintDescriptors()) {
+            final ConstraintValidation<?> validation = (ConstraintValidation<?>) consDesc;
+
+            // 1. process groups
+            for (final Group current : groups.getGroups()) {
+                context.setCurrentGroup(current);
+                validation.validate(context);
+            }
+            // 2. process sequences
+            for (final List<Group> eachSeq : groups.getSequences()) {
+                for (final Group current : eachSeq) {
+                    context.setCurrentGroup(current);
+                    validation.validate(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
+                     */
+                    if (!context.getListener().isEmpty()) {
+                        breakOnGroup = current;
+                        break;
+                    }
+                }
+            }
+        }
+        if (impl.isCascaded() && context.getValidatedValue() != null) {
+            initMetaBean(context, factoryContext.getMetaBeanFinder(), context.getValidatedValue().getClass());
+
+            // 1. process groups
+            for (final Group current : groups.getGroups()) {
+                context.setCurrentGroup(impl.mapGroup(current));
+                ValidationHelper.validateContext(context, new Jsr303ValidationCallback(context), isTreatMapsLikeBeans());
+            }
+            // 2. process sequences
+            for (final List<Group> eachSeq : groups.getSequences()) {
+                for (final Group current : eachSeq) {
+                    context.setCurrentGroup(impl.mapGroup(current));
+                    ValidationHelper.validateContext(context, new Jsr303ValidationCallback(context), isTreatMapsLikeBeans());
+                    /**
+                     * 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
+                     */
+                    if (!context.getListener().isEmpty()) {
+                        breakOnGroup = current;
+                        break;
+                    }
+                }
+            }
+        }
+        return breakOnGroup;
     }
 
     /**
@@ -813,5 +1226,4 @@ public class ClassValidator implements C
             throw unrecoverableValidationError(ex, ObjectUtils.defaultIfNull(object, value));
         }
     }
-
 }

Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConfigurationImpl.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConfigurationImpl.java?rev=1498347&r1=1498346&r2=1498347&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConfigurationImpl.java (original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConfigurationImpl.java Mon Jul  1 10:06:18 2013
@@ -19,18 +19,34 @@
 package org.apache.bval.jsr303;
 
 
+import org.apache.bval.jsr303.parameter.DefaultParameterNameProvider;
 import org.apache.bval.jsr303.resolver.DefaultTraversableResolver;
+import org.apache.bval.jsr303.util.IOs;
 import org.apache.bval.jsr303.util.SecureActions;
 import org.apache.bval.jsr303.xml.ValidationParser;
 
-import javax.validation.*;
+import javax.validation.BootstrapConfiguration;
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.MessageInterpolator;
+import javax.validation.ParameterNameProvider;
+import javax.validation.TraversableResolver;
+import javax.validation.ValidationException;
+import javax.validation.ValidationProviderResolver;
+import javax.validation.ValidatorFactory;
+import javax.validation.executable.ExecutableType;
 import javax.validation.spi.BootstrapState;
 import javax.validation.spi.ConfigurationState;
 import javax.validation.spi.ValidationProvider;
+import java.io.Closeable;
 import java.io.InputStream;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.util.*;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.logging.Logger;
 
 /**
@@ -61,37 +77,38 @@ public class ConfigurationImpl implement
     /**
      * Configured {@link MessageInterpolator}
      */
-    protected MessageInterpolator messageInterpolator;
+    protected MessageInterpolator defaultMessageInterpolator = new DefaultMessageInterpolator();
+    protected MessageInterpolator messageInterpolator = defaultMessageInterpolator;
 
     /**
      * Configured {@link ConstraintValidatorFactory}
      */
-    protected ConstraintValidatorFactory constraintValidatorFactory;
+    protected ConstraintValidatorFactory defaultConstraintValidatorFactory = new DefaultConstraintValidatorFactory();
+    protected ConstraintValidatorFactory constraintValidatorFactory = defaultConstraintValidatorFactory;
 
-    private TraversableResolver traversableResolver;
+    private TraversableResolver defaultTraversableResolver = new DefaultTraversableResolver();
+    private TraversableResolver traversableResolver = defaultTraversableResolver;
+
+    protected ParameterNameProvider defaultParameterNameProvider = new DefaultParameterNameProvider();
+    protected ParameterNameProvider parameterNameProvider = defaultParameterNameProvider;
+
+    protected BootstrapConfiguration  bootstrapConfiguration;
+
+    protected Collection<ExecutableType> executableValidation;
 
     // BEGIN DEFAULTS
     /**
      * false = dirty flag (to prevent from multiple parsing validation.xml)
      */
     private boolean prepared = false;
-    private final TraversableResolver defaultTraversableResolver =
-          new DefaultTraversableResolver();
-
-    /**
-     * Default {@link MessageInterpolator}
-     */
-    protected final MessageInterpolator defaultMessageInterpolator =
-          new DefaultMessageInterpolator();
-
-    private final ConstraintValidatorFactory defaultConstraintValidatorFactory =
-          new DefaultConstraintValidatorFactory();
     // END DEFAULTS
 
     private Set<InputStream> mappingStreams = new HashSet<InputStream>();
     private Map<String, String> properties = new HashMap<String, String>();
     private boolean ignoreXmlConfiguration = false;
 
+    private ValidationParser parser;
+
     /**
      * Create a new ConfigurationImpl instance.
      * @param aState
@@ -153,6 +170,11 @@ public class ConfigurationImpl implement
         return this;
     }
 
+    public ApacheValidatorConfiguration parameterNameProvider(ParameterNameProvider parameterNameProvider) {
+        this.parameterNameProvider = parameterNameProvider;
+        return this;
+    }
+
     /**
      * {@inheritDoc}
      * Add a stream describing constraint mapping in the Bean Validation
@@ -161,7 +183,7 @@ public class ConfigurationImpl implement
      * @return this
      */
     public ApacheValidatorConfiguration addMapping(InputStream stream) {
-        mappingStreams.add(stream);
+        mappingStreams.add(IOs.convertToMarkableInputStream(stream));
         return this;
     }
 
@@ -178,6 +200,22 @@ public class ConfigurationImpl implement
         return this;
     }
 
+    public MessageInterpolator getDefaultMessageInterpolator() {
+        return defaultMessageInterpolator;
+    }
+
+    public TraversableResolver getDefaultTraversableResolver() {
+        return defaultTraversableResolver;
+    }
+
+    public ConstraintValidatorFactory getDefaultConstraintValidatorFactory() {
+        return defaultConstraintValidatorFactory;
+    }
+
+    public ParameterNameProvider getDefaultParameterNameProvider() {
+        return defaultParameterNameProvider;
+    }
+
     /**
      * {@inheritDoc}
      * Return a map of non type-safe custom properties.
@@ -213,25 +251,16 @@ public class ConfigurationImpl implement
         return messageInterpolator;
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    public MessageInterpolator getDefaultMessageInterpolator() {
-        return defaultMessageInterpolator;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public TraversableResolver getDefaultTraversableResolver() {
-        return defaultTraversableResolver;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public ConstraintValidatorFactory getDefaultConstraintValidatorFactory() {
-        return defaultConstraintValidatorFactory;
+    public BootstrapConfiguration getBootstrapConfiguration() {
+        if (bootstrapConfiguration == null) {
+            synchronized (this) {
+                if (bootstrapConfiguration == null) {
+                    parser = parseValidationXml();
+                    bootstrapConfiguration = parser.getBootstrap();
+                }
+            }
+        }
+        return bootstrapConfiguration;
     }
 
     /**
@@ -246,6 +275,7 @@ public class ConfigurationImpl implement
 
     public ValidatorFactory doPrivBuildValidatorFactory() {
         prepare();
+        parser.ensureValidatorFactoryCanBeBuilt();
         if (provider != null) {
             return provider.buildValidatorFactory(this);
         } else {
@@ -253,34 +283,33 @@ public class ConfigurationImpl implement
         }
     }
 
-    private void prepare() {
-        if (prepared) return;
-        parseValidationXml();
-        applyDefaults();
-        prepared = true;
-    }
-
-    /** Check whether a validation.xml file exists and parses it with JAXB */
-    private void parseValidationXml() {
-        if (isIgnoreXmlConfiguration()) {
-            log.config("ignoreXmlConfiguration == true");
-        } else {
-            new ValidationParser(getProperties().get(Properties.VALIDATION_XML_PATH))
-                  .processValidationConfig(this);
+    public ConfigurationImpl prepare() {
+        if (prepared) {
+            return this;
         }
-    }
 
-    private void applyDefaults() {
-        // make sure we use the defaults in case they haven't been provided yet
-        if (traversableResolver == null) {
-            traversableResolver = getDefaultTraversableResolver();
-        }
-        if (messageInterpolator == null) {
-            messageInterpolator = getDefaultMessageInterpolator();
+        if (parser == null) {
+            parser = parseValidationXml(); // already done if BootstrapConfiguration already looked up
+            synchronized (this) { // this synchro should be done in a better way
+                if (bootstrapConfiguration == null) {
+                    bootstrapConfiguration = parser.getBootstrap();
+                }
+            }
         }
-        if (constraintValidatorFactory == null) {
-            constraintValidatorFactory = getDefaultConstraintValidatorFactory();
+        parser.applyConfigWithInstantiation(this); // instantiate the config if needed
+
+        // TODO: maybe find a better way to communicate between validator factory and config
+        if (getBootstrapConfiguration().isExecutableValidationEnabled()) {
+            getProperties().put(Properties.EXECUTABLE_VALIDATION_TYPES, executableValidationTypesAsString());
         }
+
+        prepared = true;
+        return this;
+    }
+
+    /** Check whether a validation.xml file exists and parses it with JAXB */
+    private ValidationParser parseValidationXml() {
+        return ValidationParser.processValidationConfig(getProperties().get(Properties.VALIDATION_XML_PATH), this, ignoreXmlConfiguration);
     }
 
     /**
@@ -298,6 +327,10 @@ public class ConfigurationImpl implement
         return traversableResolver;
     }
 
+    public ParameterNameProvider getParameterNameProvider() {
+        return parameterNameProvider;
+    }
+
     /**
      * Get the configured {@link ValidationProvider}.
      * @return {@link ValidationProvider}
@@ -337,4 +370,37 @@ public class ConfigurationImpl implement
             return action.run();
         }
     }
+
+    public void setExecutableValidation(final Collection<ExecutableType> executableValidation) {
+        this.executableValidation = executableValidation;
+    }
+
+    public Collection<ExecutableType> getExecutableValidation() {
+        return executableValidation;
+    }
+
+    private String executableValidationTypesAsString() {
+        if (executableValidation == null || executableValidation.isEmpty()) {
+            return "";
+        }
+
+        final StringBuilder builder = new StringBuilder();
+        for (final ExecutableType type : executableValidation) {
+            builder.append(type.name()).append(",");
+        }
+        final String s = builder.toString();
+        return s.substring(0, s.length() - 1);
+    }
+
+    public Closeable getClosable() {
+        return parser;
+    }
+
+    public ValidationParser getParser() {
+        return parser;
+    }
+
+    public void setParser(ValidationParser parser) {
+        this.parser = parser;
+    }
 }

Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java?rev=1498347&r1=1498346&r2=1498347&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java (original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java Mon Jul  1 10:06:18 2013
@@ -16,23 +16,22 @@
  */
 package org.apache.bval.jsr303;
 
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.ConstraintTarget;
+import javax.validation.Payload;
+import javax.validation.ValidationException;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Array;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.util.Locale;
 import java.util.Map;
 
-import javax.validation.Constraint;
-import javax.validation.ConstraintDefinitionException;
-import javax.validation.Payload;
-import javax.validation.ValidationException;
-
-import org.apache.bval.jsr303.util.SecureActions;
-import org.apache.commons.lang3.reflect.TypeUtils;
-
 /**
  * Defines the well-known attributes of {@link Constraint} annotations.
  * 
@@ -42,22 +41,27 @@ public enum ConstraintAnnotationAttribut
     /**
      * "message"
      */
-    MESSAGE,
+    MESSAGE(false, false, "message"),
 
     /**
      * "groups"
      */
-    GROUPS,
+    GROUPS(false, false, "groups"),
 
     /**
      * "payload"
      */
-    PAYLOAD,
+    PAYLOAD(false, false, "payload"),
+
+    /**
+     * "validationAppliesTo"
+     */
+    VALIDATION_APPLIES_TO(true, true, "validationAppliesTo"),
 
     /**
      * "value" for multi-valued constraints
      */
-    VALUE(true);
+    VALUE(false, true, "value");
 
     @SuppressWarnings("unused")
     private static class Types {
@@ -65,17 +69,18 @@ public enum ConstraintAnnotationAttribut
         Class<?>[] groups;
         Class<? extends Payload>[] payload;
         Annotation[] value;
+        ConstraintTarget validationAppliesTo;
     }
 
-    private Type type;
-    private boolean permitNullDefaultValue;
-
-    private ConstraintAnnotationAttributes() {
-        this(false);
-    }
+    private final Type type;
+    private final boolean permitNullDefaultValue;
+    private final String attributeName;
+    private final boolean quiet;
 
-    private ConstraintAnnotationAttributes(boolean permitNullDefaultValue) {
+    private ConstraintAnnotationAttributes(final boolean quiet, final boolean permitNullDefaultValue, final String name) {
         this.permitNullDefaultValue = permitNullDefaultValue;
+        this.quiet = quiet;
+        this.attributeName = name;
         try {
             this.type = Types.class.getDeclaredField(getAttributeName()).getGenericType();
         } catch (Exception e) {
@@ -99,7 +104,7 @@ public enum ConstraintAnnotationAttribut
      * @return String
      */
     public String getAttributeName() {
-        return name().toLowerCase(Locale.US);
+        return attributeName;
     }
 
     /**
@@ -141,7 +146,7 @@ public enum ConstraintAnnotationAttribut
      * @throws ConstraintDefinitionException
      */
     public <A extends Annotation> void validateOn(Class<A> type) {
-        new Worker<A>(type);
+        new Worker<A>(type, quiet);
     }
 
     /**
@@ -178,9 +183,7 @@ public enum ConstraintAnnotationAttribut
      * @return Object
      */
     public <T, A extends Annotation> T getDefaultValue(Class<A> type) {
-        @SuppressWarnings("unchecked")
-        final T result = (T) new Worker<A>(type).defaultValue;
-        return result;
+        return  (T) new Worker<A>(type, quiet).defaultValue;
     }
 
     private static <T> T doPrivileged(final PrivilegedAction<T> action) {

Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDescriptorImpl.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDescriptorImpl.java?rev=1498347&r1=1498346&r2=1498347&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDescriptorImpl.java (original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDescriptorImpl.java Mon Jul  1 10:06:18 2013
@@ -18,6 +18,7 @@
  */
 package org.apache.bval.jsr303;
 
+import javax.validation.ConstraintTarget;
 import javax.validation.Payload;
 import javax.validation.metadata.ConstraintDescriptor;
 import java.io.Serializable;
@@ -44,16 +45,18 @@ public class ConstraintDescriptorImpl<T 
     private final Map<String, Object> attributes;
     private final Set<ConstraintDescriptor<?>> composingConstraints;
     private final boolean reportAsSingleViolation;
+    private final ConstraintTarget validationAppliesTo;
+    private final String template;
 
     /**
      * Create a new ConstraintDescriptorImpl instance.
      * 
      * @param descriptor
      */
-    public ConstraintDescriptorImpl(ConstraintDescriptor<T> descriptor) {
+    public ConstraintDescriptorImpl(final ConstraintDescriptor<T> descriptor) {
         this(descriptor.getAnnotation(), descriptor.getGroups(), descriptor.getPayload(), descriptor
             .getConstraintValidatorClasses(), descriptor.getAttributes(), descriptor.getComposingConstraints(),
-            descriptor.isReportAsSingleViolation());
+            descriptor.isReportAsSingleViolation(), descriptor.getValidationAppliesTo(), descriptor.getMessageTemplate());
     }
 
     /**
@@ -71,7 +74,7 @@ public class ConstraintDescriptorImpl<T 
         Set<Class<? extends javax.validation.Payload>> payload,
         List<java.lang.Class<? extends javax.validation.ConstraintValidator<T, ?>>> constraintValidatorClasses,
         Map<String, Object> attributes, Set<ConstraintDescriptor<?>> composingConstraints,
-        boolean reportAsSingleViolation) {
+        boolean reportAsSingleViolation, ConstraintTarget validationAppliesTo, String messageTemplate) {
         this.annotation = annotation;
         this.groups = groups;
         this.payload = payload;
@@ -79,6 +82,8 @@ public class ConstraintDescriptorImpl<T 
         this.attributes = attributes;
         this.composingConstraints = composingConstraints;
         this.reportAsSingleViolation = reportAsSingleViolation;
+        this.validationAppliesTo = validationAppliesTo;
+        this.template = messageTemplate;
     }
 
     /**
@@ -88,6 +93,10 @@ public class ConstraintDescriptorImpl<T 
         return annotation;
     }
 
+    public String getMessageTemplate() {
+        return template;
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -102,6 +111,10 @@ public class ConstraintDescriptorImpl<T 
         return payload;
     }
 
+    public ConstraintTarget getValidationAppliesTo() {
+        return validationAppliesTo;
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -129,4 +142,36 @@ public class ConstraintDescriptorImpl<T 
     public boolean isReportAsSingleViolation() {
         return reportAsSingleViolation;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ConstraintDescriptorImpl that = (ConstraintDescriptorImpl) o;
+
+        if (reportAsSingleViolation != that.reportAsSingleViolation) return false;
+        if (!annotation.annotationType().equals(that.annotation.annotationType())) return false;
+        if (composingConstraints != null ? !composingConstraints.equals(that.composingConstraints) : that.composingConstraints != null)
+            return false;
+        if (constraintValidatorClasses != null ? !constraintValidatorClasses.equals(that.constraintValidatorClasses) : that.constraintValidatorClasses != null)
+            return false;
+        if (payload != null ? !payload.equals(that.payload) : that.payload != null) return false;
+        if (template != null ? !template.equals(that.template) : that.template != null) return false;
+        if (validationAppliesTo != that.validationAppliesTo) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = annotation != null ? annotation.annotationType().hashCode() : 0;
+        result = 31 * result + (payload != null ? payload.hashCode() : 0);
+        result = 31 * result + (constraintValidatorClasses != null ? constraintValidatorClasses.hashCode() : 0);
+        result = 31 * result + (composingConstraints != null ? composingConstraints.hashCode() : 0);
+        result = 31 * result + (reportAsSingleViolation ? 1 : 0);
+        result = 31 * result + (validationAppliesTo != null ? validationAppliesTo.hashCode() : 0);
+        result = 31 * result + (template != null ? template.hashCode() : 0);
+        return result;
+    }
 }

Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintFinderImpl.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintFinderImpl.java?rev=1498347&r1=1498346&r2=1498347&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintFinderImpl.java (original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintFinderImpl.java Mon Jul  1 10:06:18 2013
@@ -149,8 +149,10 @@ final class ConstraintFinderImpl impleme
      * {@inheritDoc}
      */
     public Set<ConstraintDescriptor<?>> getConstraintDescriptors() {
-        return constraintDescriptors.isEmpty() ? Collections.<ConstraintDescriptor<?>> emptySet() : Collections
-            .<ConstraintDescriptor<?>> unmodifiableSet(constraintDescriptors);
+        if (constraintDescriptors.isEmpty()) {
+            return Collections.emptySet();
+        }
+        return Collections.<ConstraintDescriptor<?>>unmodifiableSet(constraintDescriptors);
     }
 
     /**

Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java?rev=1498347&r1=1498346&r2=1498347&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java (original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java Mon Jul  1 10:06:18 2013
@@ -27,6 +27,7 @@ import org.apache.bval.util.AccessStrate
 import org.apache.commons.lang3.ArrayUtils;
 
 import javax.validation.ConstraintDefinitionException;
+import javax.validation.ConstraintTarget;
 import javax.validation.ConstraintValidator;
 import javax.validation.Payload;
 import javax.validation.ValidationException;
@@ -41,6 +42,7 @@ import java.util.*;
  */
 public class ConstraintValidation<T extends Annotation> implements Validation, ConstraintDescriptor<T> {
     private final ConstraintValidator<T, ?> validator;
+    private final RuntimeException missingValidatorException;
     private T annotation; // for metadata request API
     private final AccessStrategy access;
     private final boolean reportFromComposite;
@@ -56,25 +58,27 @@ public class ConstraintValidation<T exte
     private Set<Class<?>> groups;
     private Set<Class<? extends Payload>> payload;
     private Class<? extends ConstraintValidator<T, ?>>[] validatorClasses;
+    private ConstraintTarget validationAppliesTo = null;
 
     /**
      * Create a new ConstraintValidation instance.
-     * 
+     *
      * @param validatorClasses
      * @param validator
      *            - the constraint validator
      * @param annotation
-     *            - the annotation of the constraint
+ *            - the annotation of the constraint
      * @param owner
-     *            - the type where the annotated element is placed (class,
-     *            interface, annotation type)
+*            - the type where the annotated element is placed (class,
+*            interface, annotation type)
      * @param access
-     *            - how to access the value
+*            - how to access the value
      * @param reportFromComposite
+     * @param missingValidatorException
      */
     public ConstraintValidation(Class<? extends ConstraintValidator<T, ?>>[] validatorClasses,
-        ConstraintValidator<T, ?> validator, T annotation, Class<?> owner, AccessStrategy access,
-        boolean reportFromComposite) {
+                                ConstraintValidator<T, ?> validator, T annotation, Class<?> owner, AccessStrategy access,
+                                boolean reportFromComposite, ConstraintTarget target, RuntimeException missingValidatorException) {
         this.attributes = new HashMap<String, Object>();
         this.validatorClasses = ArrayUtils.clone(validatorClasses);
         this.validator = validator;
@@ -82,6 +86,8 @@ public class ConstraintValidation<T exte
         this.owner = owner;
         this.access = access;
         this.reportFromComposite = reportFromComposite;
+        this.validationAppliesTo = target;
+        this.missingValidatorException = missingValidatorException;
     }
 
     /**
@@ -148,6 +154,10 @@ public class ConstraintValidation<T exte
      *            root
      */
     public void validate(GroupValidationContext<?> context) {
+        if (missingValidatorException != null) {
+            throw missingValidatorException;
+        }
+
         context.setConstraintValidation(this);
         /**
          * execute unless the given validation constraint has already been
@@ -231,11 +241,11 @@ public class ConstraintValidation<T exte
     }
 
     private boolean isReachable(GroupValidationContext<?> context) {
-        PathImpl path = context.getPropertyPath();
-        NodeImpl node = path.getLeafNode();
+        final PathImpl path = context.getPropertyPath();
+        final NodeImpl node = path.getLeafNode();
         PathImpl beanPath = path.getPathWithoutLeafNode();
         if (beanPath == null) {
-            beanPath = PathImpl.create(null);
+            beanPath = PathImpl.create();
         }
         try {
             if (!context.getTraversableResolver().isReachable(context.getBean(), node,
@@ -368,6 +378,10 @@ public class ConstraintValidation<T exte
         return payload;
     }
 
+    public ConstraintTarget getValidationAppliesTo() {
+        return validationAppliesTo;
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -378,4 +392,7 @@ public class ConstraintValidation<T exte
         return Arrays.asList(validatorClasses);
     }
 
+    public void setValidationAppliesTo(final ConstraintTarget validationAppliesTo) {
+        this.validationAppliesTo = validationAppliesTo;
+    }
 }

Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java?rev=1498347&r1=1498346&r2=1498347&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java (original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java Mon Jul  1 10:06:18 2013
@@ -22,11 +22,13 @@ import org.apache.bval.model.ValidationC
 import org.apache.bval.model.ValidationListener;
 
 import javax.validation.ConstraintViolation;
+import javax.validation.ElementKind;
 import javax.validation.MessageInterpolator;
 import javax.validation.Path;
 import javax.validation.metadata.ConstraintDescriptor;
 import java.lang.annotation.ElementType;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Set;
 
 /**
@@ -102,9 +104,82 @@ public final class ConstraintValidationL
             descriptor = null;
         }
         ElementType elementType = (context.getAccess() != null) ? context.getAccess().getElementType() : null;
-        ConstraintViolationImpl<T> ic = new ConstraintViolationImpl<T>(messageTemplate,
-              message, rootBean, context.getBean(), propPath, value, descriptor, rootBeanType, elementType);
-        constraintViolations.add(ic);
+
+        final Object[] parameters;
+        Object leaf;
+        Object returnValue;
+        T rootBean;
+        if (GroupValidationContext.class.isInstance(context)) { // TODO: clean up it but it would need to rework completely our context - get rid of it would be the best
+            final GroupValidationContext<T> ctx = GroupValidationContext.class.cast(context);
+            final ElementKind elementKind = ctx.getElementKind();
+            final Iterator<Path.Node> it = propPath.iterator();
+            final ElementKind kind = propPath.iterator().next().getKind();
+
+            returnValue = ctx.getReturnValue();
+
+            if (ElementKind.CONSTRUCTOR.equals(kind)) {
+                rootBean = null;
+                leaf = context.getBean();
+            } else if (ElementKind.METHOD.equals(kind)) {
+                if (ElementKind.RETURN_VALUE.equals(elementKind)) { // switch back return value and rootBean
+                    rootBean = (T) returnValue;
+                    if (kindOf(propPath, ElementKind.RETURN_VALUE)) {
+                        leaf = returnValue;
+                        returnValue = this.rootBean;
+                    } else {
+                        leaf = this.rootBean;
+                        returnValue = this.rootBean;
+                    }
+                } else {
+                    rootBean = this.rootBean;
+                    if (kindOf(propPath, ElementKind.PARAMETER, ElementKind.CROSS_PARAMETER)) {
+                        leaf = rootBean;
+                    } else {
+                        leaf = context.getBean();
+                    }
+                }
+            } else {
+                rootBean = this.rootBean;
+                leaf = context.getBean();
+            }
+
+            if (ElementKind.CONSTRUCTOR.equals(kind)
+                    && (ElementKind.CROSS_PARAMETER.equals(elementKind)
+                        || ElementKind.PARAMETER.equals(elementKind))
+                    && (it.hasNext() && it.next() != null && it.hasNext() && it.next() != null && !it.hasNext())) { // means inherited validation use real value
+                leaf = null;
+            }
+
+            parameters = ctx.getParameters();
+        } else {
+            leaf = context.getBean();
+            returnValue = null;
+            parameters = null;
+            rootBean = this.rootBean;
+        }
+
+        constraintViolations.add(new ConstraintViolationImpl<T>(
+                messageTemplate, message,
+                rootBean, leaf,
+                propPath, value, descriptor,
+                rootBeanType,
+                elementType, returnValue, parameters));
+    }
+
+    private static boolean kindOf(final Path propPath, final ElementKind... kinds) {
+        final Iterator<Path.Node> node = propPath.iterator();
+        boolean isParam = false;
+        while (node.hasNext()) {
+            final ElementKind current = node.next().getKind();
+            isParam = false;
+            for (final ElementKind k : kinds) {
+                if (k.equals(current)) {
+                    isParam = true;
+                    break;
+                }
+            }
+        }
+        return isParam;
     }
 
     /**

Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorContextImpl.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorContextImpl.java?rev=1498347&r1=1498346&r2=1498347&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorContextImpl.java (original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorContextImpl.java Mon Jul  1 10:06:18 2013
@@ -19,6 +19,7 @@
 package org.apache.bval.jsr303;
 
 
+import org.apache.bval.jsr303.util.LeafNodeBuilderCustomizableContextImpl;
 import org.apache.bval.jsr303.util.NodeBuilderDefinedContextImpl;
 import org.apache.bval.jsr303.util.NodeImpl;
 import org.apache.bval.jsr303.util.PathImpl;
@@ -26,8 +27,11 @@ import org.apache.bval.model.ValidationL
 
 import javax.validation.ConstraintValidator;
 import javax.validation.ConstraintValidatorContext;
+import javax.validation.ElementKind;
+import javax.validation.ParameterNameProvider;
 import javax.validation.Path;
 import javax.validation.ValidationException;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
@@ -75,8 +79,14 @@ public class ConstraintValidatorContextI
      */
     public ConstraintViolationBuilder buildConstraintViolationWithTemplate(
           String messageTemplate) {
-        return new ConstraintViolationBuilderImpl(this, messageTemplate,
-              validationContext.getPropertyPath());
+        return new ConstraintViolationBuilderImpl(this, messageTemplate, validationContext.getPropertyPath());
+    }
+
+    public <T> T unwrap(Class<T> type) {
+        if (type.isInstance(this)) {
+            return type.cast(this);
+        }
+        throw new ValidationException("Type " + type + " not supported");
     }
 
     private static final class ConstraintViolationBuilderImpl
@@ -104,7 +114,8 @@ public class ConstraintValidatorContextI
         public NodeBuilderDefinedContext addNode(String name) {
             PathImpl path;
             if (propertyPath.isRootPath()) {
-                path = PathImpl.create(name);
+                path = PathImpl.create();
+                path.getLeafNode().setName(name);
             } else {
                 path = PathImpl.copy(propertyPath);
                 path.addNode(new NodeImpl(name));
@@ -112,6 +123,44 @@ public class ConstraintValidatorContextI
             return new NodeBuilderDefinedContextImpl(parent, messageTemplate, path);
         }
 
+        public NodeBuilderCustomizableContext addPropertyNode(String name) {
+            final NodeImpl node;
+            if (!propertyPath.isRootPath()) {
+                if (propertyPath.getLeafNode().getKind() != null) {
+                    node = new NodeImpl.PropertyNodeImpl(name);
+                    propertyPath.addNode(node);
+                } else {
+                    node = propertyPath.getLeafNode();
+                }
+            } else {
+                node = new NodeImpl.PropertyNodeImpl(name);
+                propertyPath.addNode(node);
+            }
+            node.setName(name);
+            node.setKind(ElementKind.PROPERTY); // enforce it
+            return new NodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath);
+        }
+
+        public LeafNodeBuilderCustomizableContext addBeanNode() {
+            final NodeImpl node = new NodeImpl.BeanNodeImpl();
+            node.setKind(ElementKind.BEAN);
+            propertyPath.addNode(node);
+            return new LeafNodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath);
+        }
+
+        public NodeBuilderDefinedContext addParameterNode(int index) {
+            final Method method = parent.validationContext.getMethod();
+            final List<String> parameters = parent.validationContext.getParameterNameProvider().getParameterNames(method);
+            final NodeImpl node = new NodeImpl.ParameterNodeImpl(parameters.get(index), index);
+            node.setParameterIndex(index);
+            node.setKind(ElementKind.PARAMETER);
+            if (!propertyPath.isRootPath()) {
+                propertyPath.removeLeafNode();
+            }
+            propertyPath.addNode(node);
+            return new NodeBuilderDefinedContextImpl(parent, messageTemplate, propertyPath);
+        }
+
         /**
          * {@inheritDoc}
          */

Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintViolationImpl.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintViolationImpl.java?rev=1498347&r1=1498346&r2=1498347&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintViolationImpl.java (original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintViolationImpl.java Mon Jul  1 10:06:18 2013
@@ -18,9 +18,11 @@ package org.apache.bval.jsr303;
 
 import javax.validation.ConstraintViolation;
 import javax.validation.Path;
+import javax.validation.ValidationException;
 import javax.validation.metadata.ConstraintDescriptor;
 import java.io.Serializable;
 import java.lang.annotation.ElementType;
+import java.util.Arrays;
 
 /**
  * Description: Describe a constraint validation defect.<br/>
@@ -41,10 +43,12 @@ class ConstraintViolationImpl<T> impleme
     private final Path propertyPath;
     private final ElementType elementType;
     private final ConstraintDescriptor<?> constraintDescriptor;
-    
+    private final Object returnValue;
+    private final Object[] parameters;
+
     /**
      * Create a new ConstraintViolationImpl instance.
-     * @param messageTemplate - message reason (raw message) 
+     * @param messageTemplate - message reason (raw message)
      * @param message - interpolated message (locale specific)
      * @param rootBean
      * @param leafBean
@@ -53,10 +57,13 @@ class ConstraintViolationImpl<T> impleme
      * @param constraintDescriptor
      * @param rootBeanClass
      * @param elementType
+     * @param returnValue
+     * @param parameters
      */
     public ConstraintViolationImpl(String messageTemplate, String message, T rootBean, Object leafBean,
                                    Path propertyPath, Object value,
-                                   ConstraintDescriptor<?> constraintDescriptor, Class<T> rootBeanClass, ElementType elementType) {
+                                   ConstraintDescriptor<?> constraintDescriptor, Class<T> rootBeanClass,
+                                   ElementType elementType, Object returnValue, Object[] parameters) {
         this.messageTemplate = messageTemplate;
         this.message = message;
         this.rootBean = rootBean;
@@ -66,6 +73,8 @@ class ConstraintViolationImpl<T> impleme
         this.value = value;
         this.constraintDescriptor = constraintDescriptor;
         this.elementType = elementType;
+        this.returnValue = returnValue;
+        this.parameters = parameters;
     }
 
     /**
@@ -106,6 +115,14 @@ class ConstraintViolationImpl<T> impleme
         return leafBean;
     }
 
+    public Object[] getExecutableParameters() {
+        return parameters;
+    }
+
+    public Object getExecutableReturnValue() {
+        return returnValue;
+    }
+
     /**
      * {@inheritDoc}
      * @return The value failing to pass the constraint
@@ -130,6 +147,13 @@ class ConstraintViolationImpl<T> impleme
         return constraintDescriptor;
     }
 
+    public <U> U unwrap(Class<U> type) {
+        if (type.isInstance(this)) {
+            return type.cast(this);
+        }
+        throw new ValidationException("Type " + type + " is not supported");
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -140,102 +164,45 @@ class ConstraintViolationImpl<T> impleme
               leafBean + ", value=" + value + '}';
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result
-                + ((this.leafBean == null) ? 0 : this.leafBean.hashCode());
-        result = prime * result
-                + ((this.message == null) ? 0 : this.message.hashCode());
-        result = prime
-                * result
-                + ((this.propertyPath == null) ? 0 : this.propertyPath
-                        .hashCode());
-        result = prime * result
-                + ((this.rootBean == null) ? 0 : this.rootBean.hashCode());
-        result = prime * result
-                + ((this.value == null) ? 0 : this.value.hashCode());
-        result = prime * result
-                + ((this.elementType == null) ? 0 : this.elementType.hashCode());
-        return result;
-    }
-
-    /**
-     * {@inheritDoc}
-     * NOTE: Needed to avoid duplication in the reported violations.
-     * 
-     * @param   obj   the reference object with which to compare.
-     * @return  <code>true</code> if this object is the same as the obj
-     *          argument; <code>false</code> otherwise.
-     */
     @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (!(obj instanceof ConstraintViolationImpl<?>)) {
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ConstraintViolationImpl that = (ConstraintViolationImpl) o;
+
+        if (constraintDescriptor != null ? !constraintDescriptor.equals(that.constraintDescriptor) : that.constraintDescriptor != null)
+            return false;
+        if (elementType != that.elementType) return false;
+        if (leafBean != null ? !leafBean.equals(that.leafBean) : that.leafBean != null) return false;
+        if (message != null ? !message.equals(that.message) : that.message != null) return false;
+        if (messageTemplate != null ? !messageTemplate.equals(that.messageTemplate) : that.messageTemplate != null)
+            return false;
+        // Probably incorrect - comparing Object[] arrays with Arrays.equals
+        if (!Arrays.equals(parameters, that.parameters)) return false;
+        if (propertyPath != null ? !propertyPath.equals(that.propertyPath) : that.propertyPath != null) return false;
+        if (returnValue != null ? !returnValue.equals(that.returnValue) : that.returnValue != null) return false;
+        if (rootBean != null ? !rootBean.equals(that.rootBean) : that.rootBean != null) return false;
+        if (rootBeanClass != null ? !rootBeanClass.equals(that.rootBeanClass) : that.rootBeanClass != null)
             return false;
-        }
-
-        ConstraintViolationImpl<?> other = (ConstraintViolationImpl<?>) obj;
+        if (value != null ? !value.equals(that.value) : that.value != null) return false;
 
-        if (this.leafBean == null) {
-            if (other.leafBean != null) {
-                return false;
-            }
-        } else if (!this.leafBean.equals(other.leafBean)) {
-            return false;
-        }
-        
-        if (this.message == null) {
-            if (other.message != null) {
-                return false;
-            }
-        } else if (!this.message.equals(other.message)) {
-            return false;
-        }
-        
-        if (this.propertyPath == null) {
-            if (other.propertyPath != null) {
-                return false;
-            }
-        } else if (!this.propertyPath.equals(other.propertyPath)) {
-            return false;
-        }
-        
-        if (this.rootBean == null) {
-            if (other.rootBean != null) {
-                return false;
-            }
-        } else if (!this.rootBean.equals(other.rootBean)) {
-            return false;
-        }
-        
-        if (this.rootBeanClass != other.rootBeanClass) {
-            return false;
-        }
-        
-        if (this.value == null) {
-            if (other.value != null) {
-                return false;
-            }
-        } else if (!this.value.equals(other.value)) {
-            return false;
-        }
-        
-        if (this.elementType != other.elementType) {
-            return false;
-        }
-        
         return true;
     }
-    
-    
+
+    @Override
+    public int hashCode() {
+        int result = messageTemplate != null ? messageTemplate.hashCode() : 0;
+        result = 31 * result + (message != null ? message.hashCode() : 0);
+        result = 31 * result + (rootBean != null ? rootBean.hashCode() : 0);
+        result = 31 * result + (rootBeanClass != null ? rootBeanClass.hashCode() : 0);
+        result = 31 * result + (leafBean != null ? leafBean.hashCode() : 0);
+        result = 31 * result + (value != null ? value.hashCode() : 0);
+        result = 31 * result + (propertyPath != null ? propertyPath.hashCode() : 0);
+        result = 31 * result + (elementType != null ? elementType.hashCode() : 0);
+        result = 31 * result + (constraintDescriptor != null ? constraintDescriptor.hashCode() : 0);
+        result = 31 * result + (returnValue != null ? returnValue.hashCode() : 0);
+        result = 31 * result + (parameters != null ? Arrays.hashCode(parameters) : 0);
+        return result;
+    }
 }

Added: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstructorDescriptorImpl.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstructorDescriptorImpl.java?rev=1498347&view=auto
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstructorDescriptorImpl.java (added)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstructorDescriptorImpl.java Mon Jul  1 10:06:18 2013
@@ -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;
+
+
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaConstructor;
+import org.apache.bval.model.Validation;
+
+import javax.validation.metadata.ConstructorDescriptor;
+
+/**
+ * Description: {@link javax.validation.metadata.ConstructorDescriptor} implementation.<br/>
+ */
+public class ConstructorDescriptorImpl extends InvocableElementDescriptor
+      implements ConstructorDescriptor, ProcedureDescriptor {
+    /**
+     * Create a new ConstructorDescriptorImpl instance.
+     * @param metaBean
+     * @param validations
+     */
+    protected ConstructorDescriptorImpl(MetaBean metaBean, Validation[] validations) {
+        super(metaBean, metaBean.getBeanClass(), validations);
+    }
+
+    public ConstructorDescriptorImpl(final MetaBean metaBean, final MetaConstructor metaMethod) {
+        super(metaBean, metaBean.getBeanClass(), new Validation[0]);
+        setCascaded(false);
+    }
+
+    public String getName() {
+        return elementClass.getSimpleName();
+    }
+
+    @Override
+    public boolean hasConstraints() {
+        return false;
+    }
+
+    public boolean hasConstrainedParameters() {
+        return super.hasConstrainedParameters();
+    }
+
+    public boolean hasConstrainedReturnValue() {
+        return super.hasConstrainedReturnValue();
+    }
+}

Added: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/CrossParameterDescriptorImpl.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/CrossParameterDescriptorImpl.java?rev=1498347&view=auto
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/CrossParameterDescriptorImpl.java (added)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/CrossParameterDescriptorImpl.java Mon Jul  1 10:06:18 2013
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import org.apache.bval.model.MetaBean;
+
+import javax.validation.metadata.CrossParameterDescriptor;
+import java.util.Collection;
+
+public class CrossParameterDescriptorImpl extends ElementDescriptorImpl implements CrossParameterDescriptor {
+    public CrossParameterDescriptorImpl(final MetaBean bean, final Collection<ConstraintValidation<?>> list) {
+        super(bean, Object[].class, list.toArray(new ConstraintValidation<?>[list.size()]));
+    }
+
+    public boolean hasConstraints() {
+        return !getConstraintDescriptors().isEmpty();
+    }
+}