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")