bval-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rmannibu...@apache.org
Subject svn commit: r1514716 - in /bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303: AnnotationConstraintBuilder.java AnnotationProcessor.java BaseAppendValidation.java ConstraintValidation.java
Date Fri, 16 Aug 2013 14:00:45 GMT
Author: rmannibucau
Date: Fri Aug 16 14:00:45 2013
New Revision: 1514716

URL: http://svn.apache.org/r1514716
Log:
lazy creation of constraint validators - needed for CDI

Modified:
    bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationConstraintBuilder.java
    bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationProcessor.java
    bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/BaseAppendValidation.java
    bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java

Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationConstraintBuilder.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationConstraintBuilder.java?rev=1514716&r1=1514715&r2=1514716&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationConstraintBuilder.java
(original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationConstraintBuilder.java
Fri Aug 16 14:00:45 2013
@@ -27,6 +27,7 @@ import javax.validation.ConstraintDeclar
 import javax.validation.ConstraintDefinitionException;
 import javax.validation.ConstraintTarget;
 import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorFactory;
 import javax.validation.OverridesAttribute;
 import javax.validation.Payload;
 import javax.validation.ReportAsSingleViolation;
@@ -63,19 +64,15 @@ final class AnnotationConstraintBuilder<
      * Create a new AnnotationConstraintBuilder instance.
      * 
      * @param validatorClasses
-     * @param constraintValidator
      * @param annotation
      * @param owner
      * @param access
      */
-    public AnnotationConstraintBuilder(Class<? extends ConstraintValidator<A, ?>>[]
validatorClasses,
-        ConstraintValidator<A, ?> constraintValidator, A annotation, Class<?>
owner, AccessStrategy access,
-        ConstraintTarget target, RuntimeException missingValidatorException) {
+    public AnnotationConstraintBuilder(ConstraintValidatorFactory factory, Class<? extends
ConstraintValidator<A, ?>>[] validatorClasses,
+                                        A annotation, Class<?> owner, AccessStrategy
access, ConstraintTarget target) {
         boolean reportFromComposite =
             annotation != null && annotation.annotationType().isAnnotationPresent(ReportAsSingleViolation.class);
-        constraintValidation =
-            new ConstraintValidation<A>(validatorClasses, constraintValidator, annotation,
owner, access,
-                reportFromComposite, target, missingValidatorException);
+        constraintValidation = new ConstraintValidation<A>(factory, validatorClasses,
annotation, owner, access, reportFromComposite, target);
         buildFromAnnotation();
     }
 

Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationProcessor.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationProcessor.java?rev=1514716&r1=1514715&r2=1514716&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationProcessor.java
(original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationProcessor.java
Fri Aug 16 14:00:45 2013
@@ -252,15 +252,8 @@ public final class AnnotationProcessor {
             return false;
         }
 
-        RuntimeException missingValidatorException = null;
-        ConstraintValidator<A, ?> validator = null;
-        try {
-            validator = getConstraintValidator(annotation, constraintClasses, owner, access);
-        } catch (final RuntimeException e) {
-            missingValidatorException = e;
-        }
         final AnnotationConstraintBuilder<A> builder =
-            new AnnotationConstraintBuilder<A>(constraintClasses, validator, annotation,
owner, access, null, missingValidatorException);
+            new AnnotationConstraintBuilder<A>(factoryContext.getConstraintValidatorFactory(),
constraintClasses, annotation, owner, access, null);
 
         // JSR-303 3.4.4: Add implicit groups
         if (prop != null && prop.getParentMetaBean() != null) {
@@ -325,208 +318,4 @@ public final class AnnotationProcessor {
         return rawConstraintClasses;
     }
 
-    private <A extends Annotation, T> ConstraintValidator<A, ? super T> getConstraintValidator(A
annotation,
-        Class<? extends ConstraintValidator<A, ?>>[] constraintClasses, Class<?>
owner, AccessStrategy access) {
-        if (constraintClasses != null && constraintClasses.length > 0) {
-            Type type = determineTargetedType(owner, access);
-
-            /**
-             * spec says in chapter 3.5.3.: The ConstraintValidator chosen to
-             * validate a declared type T is the one where the type supported by
-             * the ConstraintValidator is a supertype of T and where there is no
-             * other ConstraintValidator whose supported type is a supertype of
-             * T and not a supertype of the chosen ConstraintValidator supported
-             * type.
-             */
-            final Map<Type, Collection<Class<? extends ConstraintValidator<A,
?>>>> validatorTypes = getValidatorsTypes(constraintClasses);
-            reduceTarget(validatorTypes, access);
-
-            final List<Type> assignableTypes = new ArrayList<Type>(constraintClasses.length);
-            fillAssignableTypes(type, validatorTypes.keySet(), assignableTypes);
-            reduceAssignableTypes(assignableTypes);
-            checkOneType(assignableTypes, type, owner, annotation, access);
-
-            if ((type == Object.class || type == Object[].class) && validatorTypes.containsKey(Object.class)
&& validatorTypes.containsKey(Object[].class)) {
-                throw new ConstraintDefinitionException("Only a validator for Object or Object[]
should be provided for cross parameter validators");
-            }
-
-            final Collection<Class<? extends ConstraintValidator<A, ?>>>
key = validatorTypes.get(assignableTypes.get(0));
-            if (key.size() > 1) {
-                final String message = "Factory returned " + key.size() + " validators";
-                if (ParametersAccess.class.isInstance(access)) { // cross parameter
-                    throw new ConstraintDefinitionException(message);
-                }
-                throw new UnexpectedTypeException(message);
-            }
-
-            @SuppressWarnings("unchecked")
-            final ConstraintValidator<A, ? super T> validator = (ConstraintValidator<A,
? super T>) factoryContext.getConstraintValidatorFactory().getInstance(key.iterator().next());
-            if (validator == null) {
-                throw new ValidationException("Factory returned null validator for: " + key);
-
-            }
-            return validator;
-            // NOTE: validator initialization deferred until append phase
-        }
-        return null;
-    }
-
-    private <A extends Annotation> void reduceTarget(final Map<Type, Collection<Class<?
extends ConstraintValidator<A, ?>>>> validator, final AccessStrategy access)
{
-        for (final Map.Entry<Type, Collection<Class<? extends ConstraintValidator<A,
?>>>> entry : validator.entrySet()) {
-            final Collection<Class<? extends ConstraintValidator<A, ?>>>
validators = entry.getValue();
-            final Iterator<Class<? extends ConstraintValidator<A, ?>>>
it = validators.iterator();
-            while (it.hasNext()) {
-                final Type v = it.next();
-                if (!Class.class.isInstance(v)) {
-                    continue; // TODO: handle this case
-                }
-
-                final Class<?> clazz = Class.class.cast(v);
-                final SupportedValidationTarget target = clazz.getAnnotation(SupportedValidationTarget.class);
-                if (target != null) {
-                    final Collection<ValidationTarget> targets = Arrays.asList(target.value());
-                    final boolean isParameter = ParameterAccess.class.isInstance(access)
|| ParametersAccess.class.isInstance(access);
-                    if ((isParameter && !targets.contains(ValidationTarget.PARAMETERS))
-                            || (!isParameter && !targets.contains(ValidationTarget.ANNOTATED_ELEMENT)))
{
-                        it.remove();
-                    }
-                }
-            }
-            if (validators.isEmpty()) {
-                validator.remove(entry.getKey());
-            }
-        }
-    }
-
-    private static void checkOneType(List<Type> types, Type targetType, Class<?>
owner, Annotation anno,
-        AccessStrategy access) {
-
-        if (types.isEmpty()) {
-            final String message = "No validator could be found for type " + stringForType(targetType)
-                    + ". See: @" + anno.annotationType().getSimpleName() + " at " + stringForLocation(owner,
access);
-            if (Object[].class.equals(targetType)) { // cross parameter
-                throw new ConstraintDefinitionException(message);
-            }
-            throw new UnexpectedTypeException(message);
-        } else if (types.size() > 1) {
-            StringBuilder buf = new StringBuilder();
-            buf.append("Ambiguous validators for type ");
-            buf.append(stringForType(targetType));
-            buf.append(". See: @").append(anno.annotationType().getSimpleName()).append("
at ").append(
-                stringForLocation(owner, access));
-            buf.append(". Validators are: ");
-            boolean comma = false;
-            for (Type each : types) {
-                if (comma)
-                    buf.append(", ");
-                comma = true;
-                buf.append(each);
-            }
-            throw new UnexpectedTypeException(buf.toString());
-        }
-    }
-
-    /** implements spec chapter 3.5.3. ConstraintValidator resolution algorithm. */
-    private static Type determineTargetedType(Class<?> owner, AccessStrategy access)
{
-        // if the constraint declaration is hosted on a class or an interface,
-        // the targeted type is the class or the interface.
-        if (access == null)
-            return owner;
-        Type type = access.getJavaType();
-        if (type == null)
-            return Object.class;
-        if (type instanceof Class<?>)
-            type = ClassUtils.primitiveToWrapper((Class<?>) type);
-        return type;
-    }
-
-    private static String stringForType(Type clazz) {
-        if (clazz instanceof Class<?>) {
-            if (((Class<?>) clazz).isArray()) {
-                return ((Class<?>) clazz).getComponentType().getName() + "[]";
-            } else {
-                return ((Class<?>) clazz).getName();
-            }
-        } else {
-            return clazz.toString();
-        }
-    }
-
-    private static String stringForLocation(Class<?> owner, AccessStrategy access)
{
-        if (access != null) {
-            return access.toString();
-        } else {
-            return owner.getName();
-        }
-    }
-
-    private static void fillAssignableTypes(Type type, Set<Type> validatorsTypes, List<Type>
suitableTypes) {
-        for (final Type validatorType : validatorsTypes) {
-            if (org.apache.commons.lang3.reflect.TypeUtils.isAssignable(type, validatorType)
-                && !suitableTypes.contains(validatorType)) {
-                suitableTypes.add(validatorType);
-            }
-        }
-    }
-
-    /**
-     * Tries to reduce all assignable classes down to a single class.
-     * 
-     * @param assignableTypes
-     *            The set of all classes which are assignable to the class of
-     *            the value to be validated and which are handled by at least
-     *            one of the validators for the specified constraint.
-     */
-    private static void reduceAssignableTypes(List<Type> assignableTypes) {
-        if (assignableTypes.size() <= 1) {
-            return; // no need to reduce
-        }
-        boolean removed;
-        do {
-            removed = false;
-            final Type type = assignableTypes.get(0);
-            for (int i = 1; i < assignableTypes.size(); i++) {
-                Type nextType = assignableTypes.get(i);
-                if (TypeUtils.isAssignable(nextType, type)) {
-                    assignableTypes.remove(0);
-                    i--;
-                    removed = true;
-                } else if (TypeUtils.isAssignable(type, nextType)) {
-                    assignableTypes.remove(i--);
-                    removed = true;
-                }
-            }
-        } while (removed && assignableTypes.size() > 1);
-
-    }
-
-    /**
-     * Given a set of {@link ConstraintValidator} implementation classes, map
-     * those to their target types.
-     * 
-     * @param constraintValidatorClasses
-     * @return {@link Map} of {@link Type} : {@link ConstraintValidator} subtype
-     */
-    private static <A extends Annotation> Map<Type, Collection<Class<? extends
ConstraintValidator<A, ?>>>> getValidatorsTypes(
-        Class<? extends ConstraintValidator<A, ?>>[] constraintValidatorClasses)
{
-        final Map<Type, Collection<Class<? extends ConstraintValidator<A, ?>>>>
validatorsTypes = new HashMap<Type, Collection<Class<? extends ConstraintValidator<A,
?>>>>();
-        for (Class<? extends ConstraintValidator<A, ?>> validatorType : constraintValidatorClasses)
{
-            Type validatedType = TypeUtils.getTypeArguments(validatorType, ConstraintValidator.class).get(ConstraintValidator.class.getTypeParameters()[1]);
-            if (validatedType == null) {
-                throw new ValidationException(String.format("Could not detect validated type
for %s", validatorType));
-            }
-            if (validatedType instanceof GenericArrayType) {
-                Type componentType = TypeUtils.getArrayComponentType(validatedType);
-                if (componentType instanceof Class<?>) {
-                    validatedType = Array.newInstance((Class<?>) componentType, 0).getClass();
-                }
-            }
-            if (!validatorsTypes.containsKey(validatedType)) {
-                validatorsTypes.put(validatedType, new ArrayList<Class<? extends ConstraintValidator<A,
?>>>());
-            }
-            validatorsTypes.get(validatedType).add(validatorType);
-        }
-        return validatorsTypes;
-    }
-
 }

Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/BaseAppendValidation.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/BaseAppendValidation.java?rev=1514716&r1=1514715&r2=1514716&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/BaseAppendValidation.java
(original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/BaseAppendValidation.java
Fri Aug 16 14:00:45 2013
@@ -34,7 +34,7 @@ public abstract class BaseAppendValidati
      * Append operation divided in 3 stages: pre & post processing and the
      * "real" append process.
      */
-    public final <T extends Annotation> void append(ConstraintValidation<T> validation)
{
+    public final <T extends Annotation> void append(final ConstraintValidation<T>
validation) {
         preProcessValidation(validation);
         performAppend(validation);
         postProcessValidation(validation);
@@ -50,7 +50,7 @@ public abstract class BaseAppendValidati
      * @param validation
      *            The validation to be appended.
      */
-    public abstract <T extends Annotation> void performAppend(ConstraintValidation<T>
validation);
+    public abstract <T extends Annotation> void performAppend(final ConstraintValidation<T>
validation);
 
     /**
      * Pre-process the validation before appending it.
@@ -60,21 +60,21 @@ public abstract class BaseAppendValidati
      * @param validation
      *            The validation to be appended.
      */
-    public <T extends Annotation> void preProcessValidation(ConstraintValidation<T>
validation) {
+    public <T extends Annotation> void preProcessValidation(final ConstraintValidation<T>
validation) {
         // No generic pre-processing
     }
     
     /**
-     * Post-process the validation once it has been appended.
+     * Post-process the validation once it postProcessValidationhas been appended.
      * 
      * @param <T>
      *            The type of the validation.
      * @param validation
      *            The validation to be appended.
      */
-    public <T extends Annotation> void postProcessValidation(ConstraintValidation<T>
validation) {
-        // Initialize the validator
-        validation.initialize();
+    public <T extends Annotation> void postProcessValidation(final ConstraintValidation<T>
validation) {
+        // done lazily to ensure CDI is available
+        // validation.initialize();
     }
     
 }

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=1514716&r1=1514715&r2=1514716&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
Fri Aug 16 14:00:45 2013
@@ -25,16 +25,27 @@ import org.apache.bval.model.ValidationC
 import org.apache.bval.model.ValidationListener;
 import org.apache.bval.util.AccessStrategy;
 import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.reflect.TypeUtils;
 
 import javax.validation.ConstraintDefinitionException;
 import javax.validation.ConstraintTarget;
 import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorFactory;
 import javax.validation.Payload;
+import javax.validation.UnexpectedTypeException;
 import javax.validation.ValidationException;
+import javax.validation.constraintvalidation.SupportedValidationTarget;
+import javax.validation.constraintvalidation.ValidationTarget;
 import javax.validation.metadata.ConstraintDescriptor;
 import java.io.Serializable;
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -48,12 +59,12 @@ import java.util.Set;
  * this instance is immutable!<br/>
  */
 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 ConstraintValidatorFactory factory;
     private final AccessStrategy access;
     private final boolean reportFromComposite;
     private final Map<String, Object> attributes;
+    private T annotation; // for metadata request API
+    private ConstraintValidator<T, ?> validator;
 
     private Set<ConstraintValidation<?>> composedConstraints;
 
@@ -69,51 +80,30 @@ public class ConstraintValidation<T exte
     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
-     * @param owner
-*            - the type where the annotated element is placed (class,
-*            interface, annotation type)
-     * @param access
-*            - 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, ConstraintTarget target, RuntimeException
missingValidatorException) {
+    public ConstraintValidation(ConstraintValidatorFactory factory,
+                                Class<? extends ConstraintValidator<T, ?>>[]
validatorClasses,
+                                T annotation, Class<?> owner, AccessStrategy access,
+                                boolean reportFromComposite, ConstraintTarget target) {
+        this.factory = factory;
         this.attributes = new HashMap<String, Object>();
         this.validatorClasses = ArrayUtils.clone(validatorClasses);
-        this.validator = validator;
         this.annotation = annotation;
         this.owner = owner;
         this.access = access;
         this.reportFromComposite = reportFromComposite;
         this.validationAppliesTo = target;
-        this.missingValidatorException = missingValidatorException;
     }
 
     /**
      * Return a {@link Serializable} {@link ConstraintDescriptor} capturing a
      * snapshot of current state.
-     * 
+     *
      * @return {@link ConstraintDescriptor}
      */
     public ConstraintDescriptor<T> asSerializableDescriptor() {
         return new ConstraintDescriptorImpl<T>(this);
     }
 
-    /**
-     * Set the applicable validation groups.
-     * 
-     * @param groups
-     */
     void setGroups(final Set<Class<?>> groups) {
         this.groups = groups;
         ConstraintAnnotationAttributes.GROUPS.put(attributes, groups.toArray(new Class<?>[groups.size()]));
@@ -125,11 +115,6 @@ public class ConstraintValidation<T exte
         ConstraintAnnotationAttributes.GROUPS.put(attributes, groups);
     }
 
-    /**
-     * Set the payload.
-     * 
-     * @param payload
-     */
     void setPayload(Set<Class<? extends Payload>> payload) {
         this.payload = payload;
         ConstraintAnnotationAttributes.PAYLOAD.put(attributes, payload.toArray(new Class[payload.size()]));
@@ -144,9 +129,8 @@ public class ConstraintValidation<T exte
 
     /**
      * Add a composing constraint.
-     * 
-     * @param aConstraintValidation
-     *            to add
+     *
+     * @param aConstraintValidation to add
      */
     public void addComposed(ConstraintValidation<?> aConstraintValidation) {
         if (composedConstraints == null) {
@@ -164,13 +148,26 @@ public class ConstraintValidation<T exte
 
     /**
      * Validate a {@link GroupValidationContext}.
-     * 
-     * @param context
-     *            root
-     */
-    public void validate(GroupValidationContext<?> context) {
-        if (missingValidatorException != null) {
-            throw missingValidatorException;
+     *
+     * @param context root
+     */
+    public void validate(final GroupValidationContext<?> context) {
+        if (validator == null) {
+            synchronized (this) {
+                if (validator == null) {
+                    try {
+                        validator = getConstraintValidator(annotation, validatorClasses,
owner, access);
+                        if (validator != null) {
+                            validator.initialize(annotation);
+                        }
+                    } catch (final RuntimeException re) {
+                        if (ValidationException.class.isInstance(re)) {
+                            throw re;
+                        }
+                        throw new ConstraintDefinitionException(re);
+                    }
+                }
+            }
         }
 
         context.setConstraintValidation(this);
@@ -201,7 +198,7 @@ public class ConstraintValidation<T exte
             try {
                 // stop validating when already failed and
                 // ReportAsSingleInvalidConstraint = true ?
-                for (Iterator<ConstraintValidation<?>> composed = getComposingValidations().iterator();
!failed && composed.hasNext();) {
+                for (Iterator<ConstraintValidation<?>> composed = getComposingValidations().iterator();
!failed && composed.hasNext(); ) {
                     composed.next().validate(context);
                     failed = listener.hasViolations();
                 }
@@ -236,6 +233,204 @@ public class ConstraintValidation<T exte
         }
     }
 
+    private <A extends Annotation, T> ConstraintValidator<A, ? super T> getConstraintValidator(A
annotation,
+                                                                                        
      Class<? extends ConstraintValidator<A, ?>>[] constraintClasses, Class<?>
owner, AccessStrategy access) {
+        if (constraintClasses != null && constraintClasses.length > 0) {
+            Type type = determineTargetedType(owner, access);
+
+            /**
+             * spec says in chapter 3.5.3.: The ConstraintValidator chosen to
+             * validate a declared type T is the one where the type supported by
+             * the ConstraintValidator is a supertype of T and where there is no
+             * other ConstraintValidator whose supported type is a supertype of
+             * T and not a supertype of the chosen ConstraintValidator supported
+             * type.
+             */
+            final Map<Type, Collection<Class<? extends ConstraintValidator<A,
?>>>> validatorTypes = getValidatorsTypes(constraintClasses);
+            reduceTarget(validatorTypes, access);
+
+            final List<Type> assignableTypes = new ArrayList<Type>(constraintClasses.length);
+            fillAssignableTypes(type, validatorTypes.keySet(), assignableTypes);
+            reduceAssignableTypes(assignableTypes);
+            checkOneType(assignableTypes, type, owner, annotation, access);
+
+            if ((type == Object.class || type == Object[].class) && validatorTypes.containsKey(Object.class)
&& validatorTypes.containsKey(Object[].class)) {
+                throw new ConstraintDefinitionException("Only a validator for Object or Object[]
should be provided for cross parameter validators");
+            }
+
+            final Collection<Class<? extends ConstraintValidator<A, ?>>>
key = validatorTypes.get(assignableTypes.get(0));
+            if (key.size() > 1) {
+                final String message = "Factory returned " + key.size() + " validators";
+                if (ParametersAccess.class.isInstance(access)) { // cross parameter
+                    throw new ConstraintDefinitionException(message);
+                }
+                throw new UnexpectedTypeException(message);
+            }
+
+            @SuppressWarnings("unchecked")
+            final ConstraintValidator<A, ? super T> validator = (ConstraintValidator<A,
? super T>) factory.getInstance(key.iterator().next());
+            if (validator == null) {
+                throw new ValidationException("Factory returned null validator for: " + key);
+
+            }
+            return validator;
+            // NOTE: validator initialization deferred until append phase
+        }
+        return null;
+    }
+
+    private <A extends Annotation> void reduceTarget(final Map<Type, Collection<Class<?
extends ConstraintValidator<A, ?>>>> validator, final AccessStrategy access)
{
+        for (final Map.Entry<Type, Collection<Class<? extends ConstraintValidator<A,
?>>>> entry : validator.entrySet()) {
+            final Collection<Class<? extends ConstraintValidator<A, ?>>>
validators = entry.getValue();
+            final Iterator<Class<? extends ConstraintValidator<A, ?>>>
it = validators.iterator();
+            while (it.hasNext()) {
+                final Type v = it.next();
+                if (!Class.class.isInstance(v)) {
+                    continue; // TODO: handle this case
+                }
+
+                final Class<?> clazz = Class.class.cast(v);
+                final SupportedValidationTarget target = clazz.getAnnotation(SupportedValidationTarget.class);
+                if (target != null) {
+                    final Collection<ValidationTarget> targets = Arrays.asList(target.value());
+                    final boolean isParameter = ParameterAccess.class.isInstance(access)
|| ParametersAccess.class.isInstance(access);
+                    if ((isParameter && !targets.contains(ValidationTarget.PARAMETERS))
+                        || (!isParameter && !targets.contains(ValidationTarget.ANNOTATED_ELEMENT)))
{
+                        it.remove();
+                    }
+                }
+            }
+            if (validators.isEmpty()) {
+                validator.remove(entry.getKey());
+            }
+        }
+    }
+
+    private static void checkOneType(List<Type> types, Type targetType, Class<?>
owner, Annotation anno,
+                                     AccessStrategy access) {
+
+        if (types.isEmpty()) {
+            final String message = "No validator could be found for type " + stringForType(targetType)
+                + ". See: @" + anno.annotationType().getSimpleName() + " at " + stringForLocation(owner,
access);
+            if (Object[].class.equals(targetType)) { // cross parameter
+                throw new ConstraintDefinitionException(message);
+            }
+            throw new UnexpectedTypeException(message);
+        } else if (types.size() > 1) {
+            StringBuilder buf = new StringBuilder();
+            buf.append("Ambiguous validators for type ");
+            buf.append(stringForType(targetType));
+            buf.append(". See: @").append(anno.annotationType().getSimpleName()).append("
at ").append(
+                stringForLocation(owner, access));
+            buf.append(". Validators are: ");
+            boolean comma = false;
+            for (Type each : types) {
+                if (comma)
+                    buf.append(", ");
+                comma = true;
+                buf.append(each);
+            }
+            throw new UnexpectedTypeException(buf.toString());
+        }
+    }
+
+    private static String stringForType(Type clazz) {
+        if (clazz instanceof Class<?>) {
+            if (((Class<?>) clazz).isArray()) {
+                return ((Class<?>) clazz).getComponentType().getName() + "[]";
+            } else {
+                return ((Class<?>) clazz).getName();
+            }
+        } else {
+            return clazz.toString();
+        }
+    }
+
+    private static String stringForLocation(Class<?> owner, AccessStrategy access)
{
+        if (access != null) {
+            return access.toString();
+        } else {
+            return owner.getName();
+        }
+    }
+
+    private static void fillAssignableTypes(Type type, Set<Type> validatorsTypes, List<Type>
suitableTypes) {
+        for (final Type validatorType : validatorsTypes) {
+            if (org.apache.commons.lang3.reflect.TypeUtils.isAssignable(type, validatorType)
+                && !suitableTypes.contains(validatorType)) {
+                suitableTypes.add(validatorType);
+            }
+        }
+    }
+
+    /**
+     * Tries to reduce all assignable classes down to a single class.
+     *
+     * @param assignableTypes The set of all classes which are assignable to the class of
+     *                        the value to be validated and which are handled by at least
+     *                        one of the validators for the specified constraint.
+     */
+    private static void reduceAssignableTypes(List<Type> assignableTypes) {
+        if (assignableTypes.size() <= 1) {
+            return; // no need to reduce
+        }
+        boolean removed;
+        do {
+            removed = false;
+            final Type type = assignableTypes.get(0);
+            for (int i = 1; i < assignableTypes.size(); i++) {
+                Type nextType = assignableTypes.get(i);
+                if (TypeUtils.isAssignable(nextType, type)) {
+                    assignableTypes.remove(0);
+                    i--;
+                    removed = true;
+                } else if (TypeUtils.isAssignable(type, nextType)) {
+                    assignableTypes.remove(i--);
+                    removed = true;
+                }
+            }
+        } while (removed && assignableTypes.size() > 1);
+
+    }
+
+    private static <A extends Annotation> Map<Type, Collection<Class<? extends
ConstraintValidator<A, ?>>>> getValidatorsTypes(
+        Class<? extends ConstraintValidator<A, ?>>[] constraintValidatorClasses)
{
+        final Map<Type, Collection<Class<? extends ConstraintValidator<A, ?>>>>
validatorsTypes = new HashMap<Type, Collection<Class<? extends ConstraintValidator<A,
?>>>>();
+        for (Class<? extends ConstraintValidator<A, ?>> validatorType : constraintValidatorClasses)
{
+            Type validatedType = TypeUtils.getTypeArguments(validatorType, ConstraintValidator.class).get(ConstraintValidator.class.getTypeParameters()[1]);
+            if (validatedType == null) {
+                throw new ValidationException(String.format("Could not detect validated type
for %s", validatorType));
+            }
+            if (validatedType instanceof GenericArrayType) {
+                Type componentType = TypeUtils.getArrayComponentType(validatedType);
+                if (componentType instanceof Class<?>) {
+                    validatedType = Array.newInstance((Class<?>) componentType, 0).getClass();
+                }
+            }
+            if (!validatorsTypes.containsKey(validatedType)) {
+                validatorsTypes.put(validatedType, new ArrayList<Class<? extends ConstraintValidator<A,
?>>>());
+            }
+            validatorsTypes.get(validatedType).add(validatorType);
+        }
+        return validatorsTypes;
+    }
+
+    /**
+     * implements spec chapter 3.5.3. ConstraintValidator resolution algorithm.
+     */
+    private static Type determineTargetedType(Class<?> owner, AccessStrategy access)
{
+        // if the constraint declaration is hosted on a class or an interface,
+        // the targeted type is the class or the interface.
+        if (access == null)
+            return owner;
+        Type type = access.getJavaType();
+        if (type == null)
+            return Object.class;
+        if (type instanceof Class<?>)
+            type = ClassUtils.primitiveToWrapper((Class<?>) type);
+        return type;
+    }
+
     /**
      * Initialize the validator (if not <code>null</code>) with the stored
      * annotation.
@@ -288,64 +483,33 @@ public class ConstraintValidation<T exte
 
     /**
      * Get the message template used by this constraint.
-     * 
+     *
      * @return String
      */
     public String getMessageTemplate() {
         return ConstraintAnnotationAttributes.MESSAGE.get(attributes);
     }
 
-    /**
-     * Get the {@link ConstraintValidator} invoked by this
-     * {@link ConstraintValidation}.
-     * 
-     * @return
-     */
     public ConstraintValidator<T, ?> getValidator() {
         return validator;
     }
 
-    /**
-     * Learn whether this {@link ConstraintValidation} belongs to the specified
-     * group.
-     * 
-     * @param reqGroup
-     * @return boolean
-     */
     protected boolean isMemberOf(Class<?> reqGroup) {
         return groups.contains(reqGroup);
     }
 
-    /**
-     * Get the owning class of this {@link ConstraintValidation}.
-     * 
-     * @return Class
-     */
     public Class<?> getOwner() {
         return owner;
     }
 
-    /**
-     * {@inheritDoc}
-     */
     public T getAnnotation() {
         return annotation;
     }
 
-    /**
-     * Get the {@link AccessStrategy} used by this {@link ConstraintValidation}.
-     * 
-     * @return {@link AccessStrategy}
-     */
     public AccessStrategy getAccess() {
         return access;
     }
 
-    /**
-     * Override the Annotation set at construction.
-     * 
-     * @param annotation
-     */
     public void setAnnotation(T annotation) {
         this.annotation = annotation;
     }
@@ -371,7 +535,7 @@ public class ConstraintValidation<T exte
      * Get the composing {@link ConstraintValidation} objects. This is
      * effectively an implementation-specific analogue to
      * {@link #getComposingConstraints()}.
-     * 
+     *
      * @return {@link Set} of {@link ConstraintValidation}
      */
     @SuppressWarnings("unchecked")



Mime
View raw message