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 [2/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/AnnotationConstraintBuilder.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationConstraintBuilder.java?rev=1498347&r1=1498346&r2=1498347&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 Mon Jul 1 10:06:18 2013
@@ -18,6 +18,23 @@
*/
package org.apache.bval.jsr303;
+import org.apache.bval.jsr303.groups.GroupsComputer;
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.jsr303.xml.AnnotationProxyBuilder;
+import org.apache.bval.util.AccessStrategy;
+
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.validation.Constraint;
+import javax.validation.ConstraintDeclarationException;
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.ConstraintTarget;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.OverridesAttribute;
+import javax.validation.Payload;
+import javax.validation.ReportAsSingleViolation;
+import javax.validation.constraintvalidation.SupportedValidationTarget;
+import javax.validation.constraintvalidation.ValidationTarget;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -34,16 +51,6 @@ import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.validation.ConstraintDeclarationException;
-import javax.validation.ConstraintValidator;
-import javax.validation.OverridesAttribute;
-import javax.validation.Payload;
-import javax.validation.ReportAsSingleViolation;
-
-import org.apache.bval.jsr303.groups.GroupsComputer;
-import org.apache.bval.jsr303.xml.AnnotationProxyBuilder;
-import org.apache.bval.util.AccessStrategy;
-
/**
* Description: helper class that builds a {@link ConstraintValidation} or its
* composite constraint validations by parsing the jsr303-annotations and
@@ -65,12 +72,13 @@ final class AnnotationConstraintBuilder<
* @param access
*/
public AnnotationConstraintBuilder(Class<? extends ConstraintValidator<A, ?>>[] validatorClasses,
- ConstraintValidator<A, ?> constraintValidator, A annotation, Class<?> owner, AccessStrategy access) {
+ ConstraintValidator<A, ?> constraintValidator, A annotation, Class<?> owner, AccessStrategy access,
+ ConstraintTarget target, RuntimeException missingValidatorException) {
boolean reportFromComposite =
annotation != null && annotation.annotationType().isAnnotationPresent(ReportAsSingleViolation.class);
constraintValidation =
new ConstraintValidation<A>(validatorClasses, constraintValidator, annotation, owner, access,
- reportFromComposite);
+ reportFromComposite, target, missingValidatorException);
buildFromAnnotation();
}
@@ -84,26 +92,91 @@ final class AnnotationConstraintBuilder<
// checked by TCK-Tests)
if (method.getParameterTypes().length == 0) {
try {
- if (ConstraintAnnotationAttributes.PAYLOAD.getAttributeName().equals(method.getName())) {
+ final String name = method.getName();
+ if (ConstraintAnnotationAttributes.PAYLOAD.getAttributeName().equals(name)) {
buildPayload(method);
- } else if (ConstraintAnnotationAttributes.GROUPS.getAttributeName().equals(
- method.getName())) {
+ } else if (ConstraintAnnotationAttributes.GROUPS.getAttributeName().equals(name)) {
buildGroups(method);
+ } else if (ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO.getAttributeName().equals(name)) {
+ buildValidationAppliesTo(method);
+ } else if (name.startsWith("valid")) {
+ throw new ConstraintDefinitionException("constraints parameters can't start with valid: " + name);
} else {
- constraintValidation.getAttributes().put(method.getName(),
- method.invoke(constraintValidation.getAnnotation()));
+ constraintValidation.getAttributes().put(name, method.invoke(constraintValidation.getAnnotation()));
}
- } catch (Exception e) { // do nothing
+ } catch (final ConstraintDefinitionException cde) {
+ throw cde;
+ } catch (final Exception e) { // do nothing
log.log(Level.WARNING, String.format("Error processing annotation: %s ", constraintValidation.getAnnotation()), e);
}
}
}
+
+ // valid validationAppliesTo
+ final Constraint annotation = constraintValidation.getAnnotation().annotationType().getAnnotation(Constraint.class);
+ if (annotation == null) {
+ return null;
+ }
+
+ final Pair validationTarget = computeValidationTarget(annotation.validatedBy());
+ for (final Annotation a : constraintValidation.getAnnotation().annotationType().getAnnotations()) {
+ final Constraint inheritedConstraint = a.annotationType().getAnnotation(Constraint.class);
+ if (inheritedConstraint != null && !a.annotationType().getName().startsWith("javax.validation.constraints.")) {
+ final Pair validationTargetInherited = computeValidationTarget(inheritedConstraint.validatedBy());
+ if ((validationTarget.a > 0 && validationTargetInherited.b > 0 && validationTarget.b == 0)
+ || (validationTarget.b > 0 && validationTargetInherited.a > 0 && validationTarget.a == 0)) {
+ throw new ConstraintDefinitionException("Parent and child constraint have different targets");
+ }
+ }
+ }
+
return null;
}
});
}
}
+ private Pair computeValidationTarget(final Class<?>[] validators) {
+ int param = 0;
+ int annotatedElt = 0;
+
+ for (final Class<?> validator : validators) {
+ final SupportedValidationTarget supportedAnnotationTypes = validator.getAnnotation(SupportedValidationTarget.class);
+ if (supportedAnnotationTypes != null) {
+ final List<ValidationTarget> values = Arrays.asList(supportedAnnotationTypes.value());
+ if (values.contains(ValidationTarget.PARAMETERS)) {
+ param++;
+ }
+ if (values.contains(ValidationTarget.ANNOTATED_ELEMENT)) {
+ annotatedElt++;
+ }
+ } else {
+ annotatedElt++;
+ }
+ }
+
+ if (annotatedElt == 0 && param >= 1 && constraintValidation.getValidationAppliesTo() != null) { // pure cross param
+ throw new ConstraintDefinitionException("pure cross parameter constraints shouldn't get validationAppliesTo attribute");
+ } else {
+ if (param >= 1 && annotatedElt >= 1 && constraintValidation.getValidationAppliesTo() == null) { // generic and cross param
+ throw new ConstraintDefinitionException("cross parameter AND generic constraints should get validationAppliesTo attribute");
+ } else if (param == 0 && constraintValidation.getValidationAppliesTo() != null) { // pure generic
+ throw new ConstraintDefinitionException("pure generic constraints shouldn't get validationAppliesTo attribute");
+ }
+ }
+
+ return new Pair(annotatedElt, param);
+ }
+
+ private void buildValidationAppliesTo(final Method method) throws InvocationTargetException, IllegalAccessException {
+ final Object validationAppliesTo = method.invoke(constraintValidation.getAnnotation());
+ if (ConstraintTarget.class.isInstance(validationAppliesTo)) {
+ constraintValidation.setValidationAppliesTo(ConstraintTarget.class.cast(validationAppliesTo));
+ } else {
+ throw new ConstraintDefinitionException("validationAppliesTo type is " + ConstraintTarget.class.getName());
+ }
+ }
+
private void buildGroups(Method method) throws IllegalAccessException, InvocationTargetException {
Object raw = method.invoke(constraintValidation.getAnnotation());
Class<?>[] garr;
@@ -150,6 +223,11 @@ final class AnnotationConstraintBuilder<
*/
public void addComposed(ConstraintValidation<?> composite) {
applyOverridesAttributes(composite);
+
+ if (constraintValidation.getValidationAppliesTo() != null) {
+ composite.setValidationAppliesTo(constraintValidation.getValidationAppliesTo());
+ }
+
constraintValidation.addComposed(composite); // add AFTER apply()
}
@@ -273,4 +351,14 @@ final class AnnotationConstraintBuilder<
return action.run();
}
}
+
+ private static class Pair {
+ private int a;
+ private int b;
+
+ private Pair(int a, int b) {
+ this.a = a;
+ this.b = b;
+ }
+ }
}
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=1498347&r1=1498346&r2=1498347&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 Mon Jul 1 10:06:18 2013
@@ -18,6 +18,29 @@
*/
package org.apache.bval.jsr303;
+import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.groups.GroupConversionDescriptorImpl;
+import org.apache.bval.jsr303.util.ConstraintDefinitionValidator;
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.model.Features;
+import org.apache.bval.model.Meta;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.PropertyAccess;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.ConstraintValidator;
+import javax.validation.UnexpectedTypeException;
+import javax.validation.Valid;
+import javax.validation.ValidationException;
+import javax.validation.constraintvalidation.SupportedValidationTarget;
+import javax.validation.constraintvalidation.ValidationTarget;
+import javax.validation.groups.ConvertGroup;
+import javax.validation.groups.Default;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
@@ -25,28 +48,14 @@ import java.lang.reflect.GenericArrayTyp
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import javax.validation.Constraint;
-import javax.validation.ConstraintValidator;
-import javax.validation.UnexpectedTypeException;
-import javax.validation.Valid;
-import javax.validation.ValidationException;
-import javax.validation.groups.Default;
-
-import org.apache.bval.jsr303.util.ConstraintDefinitionValidator;
-import org.apache.bval.jsr303.util.SecureActions;
-import org.apache.bval.model.Features;
-import org.apache.bval.model.MetaBean;
-import org.apache.bval.model.MetaProperty;
-import org.apache.bval.util.AccessStrategy;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.ClassUtils;
-import org.apache.commons.lang3.reflect.TypeUtils;
-
/**
* Description: implements uniform handling of JSR303 {@link Constraint}
* annotations, including composed constraints and the resolution of
@@ -82,12 +91,12 @@ public final class AnnotationProcessor {
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
- public boolean processAnnotations(MetaProperty prop, Class<?> owner, AnnotatedElement element,
+ public boolean processAnnotations(Meta prop, Class<?> owner, AnnotatedElement element,
AccessStrategy access, AppendValidation appender) throws IllegalAccessException, InvocationTargetException {
boolean changed = false;
for (Annotation annotation : element.getDeclaredAnnotations()) {
- changed |= processAnnotation(annotation, prop, owner, access, appender);
+ changed |= processAnnotation(annotation, prop, owner, access, appender, true);
}
return changed;
}
@@ -109,7 +118,7 @@ public final class AnnotationProcessor {
*/
public final <A extends Annotation> boolean processAnnotation(A annotation, Class<?> owner, AppendValidation appender)
throws IllegalAccessException, InvocationTargetException {
- return processAnnotation(annotation, null, owner, null, appender);
+ return processAnnotation(annotation, null, owner, null, appender, true);
}
/**
@@ -131,11 +140,24 @@ public final class AnnotationProcessor {
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
- public <A extends Annotation> boolean processAnnotation(A annotation, MetaProperty prop, Class<?> owner,
- AccessStrategy access, AppendValidation appender) throws IllegalAccessException, InvocationTargetException {
+ public <A extends Annotation> boolean processAnnotation(A annotation, Meta prop, Class<?> owner,
+ AccessStrategy access, AppendValidation appender, boolean reflection) throws IllegalAccessException, InvocationTargetException {
if (annotation instanceof Valid) {
return addAccessStrategy(prop, access);
}
+
+ if (ConvertGroup.class.isInstance(annotation) || ConvertGroup.List.class.isInstance(annotation)) {
+ if (!reflection) {
+ Collection<Annotation> annotations = prop.getFeature(Jsr303Features.Property.ANNOTATIONS_TO_PROCESS);
+ if (annotations == null) {
+ annotations = new ArrayList<Annotation>();
+ prop.putFeature(Jsr303Features.Property.ANNOTATIONS_TO_PROCESS, annotations);
+ }
+ annotations.add(annotation);
+ }
+ return true;
+ }
+
/**
* An annotation is considered a constraint definition if its retention
* policy contains RUNTIME and if the annotation itself is annotated
@@ -154,12 +176,11 @@ public final class AnnotationProcessor {
* annotated by @Constraint) whose value element has a return type of an
* array of constraint annotations in a special way.
*/
- Object result =
- SecureActions.getAnnotationValue(annotation, ConstraintAnnotationAttributes.VALUE.getAttributeName());
+ final Object result = SecureActions.getAnnotationValue(annotation, ConstraintAnnotationAttributes.VALUE.getAttributeName());
if (result instanceof Annotation[]) {
boolean changed = false;
- for (Annotation each : (Annotation[]) result) {
- changed |= processAnnotation(each, prop, owner, access, appender);
+ for (final Annotation each : (Annotation[]) result) {
+ changed |= processAnnotation(each, prop, owner, access, appender, reflection);
}
return changed;
}
@@ -174,7 +195,7 @@ public final class AnnotationProcessor {
* @param access
* @return whether anything took place.
*/
- public boolean addAccessStrategy(MetaProperty prop, AccessStrategy access) {
+ public boolean addAccessStrategy(Meta prop, AccessStrategy access) {
if (prop == null) {
return false;
}
@@ -223,7 +244,7 @@ public final class AnnotationProcessor {
*
* @param annotation
* constraint annotation
- * @param constraintClasses
+ * @param rawConstraintClasses
* known {@link ConstraintValidator} implementation classes for
* <code>annotation</code>
* @param prop
@@ -238,13 +259,23 @@ public final class AnnotationProcessor {
* @throws InvocationTargetException
*/
private <A extends Annotation> boolean applyConstraint(A annotation,
- Class<? extends ConstraintValidator<A, ?>>[] constraintClasses, MetaProperty prop, Class<?> owner,
+ Class<? extends ConstraintValidator<A, ?>>[] rawConstraintClasses, Meta prop, Class<?> owner,
AccessStrategy access, AppendValidation appender) throws IllegalAccessException, InvocationTargetException {
- final ConstraintValidator<A, ?> validator =
- getConstraintValidator(annotation, constraintClasses, owner, access);
+ final Class<? extends ConstraintValidator<A, ?>>[] constraintClasses = select(rawConstraintClasses, access);
+ if (constraintClasses != null && constraintClasses.length == 0 && rawConstraintClasses.length > 0) {
+ 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);
+ new AnnotationConstraintBuilder<A>(constraintClasses, validator, annotation, owner, access, null, missingValidatorException);
// JSR-303 3.4.4: Add implicit groups
if (prop != null && prop.getParentMetaBean() != null) {
@@ -284,10 +315,36 @@ public final class AnnotationProcessor {
return true;
}
+ private static <A extends Annotation> Class<? extends ConstraintValidator<A, ?>>[] select(
+ final Class<? extends ConstraintValidator<A, ?>>[] rawConstraintClasses, final AccessStrategy access) {
+ final boolean isReturn = ReturnAccess.class.isInstance(access);
+ final boolean isParam = ParametersAccess.class.isInstance(access);
+ if (rawConstraintClasses != null && (isReturn || isParam)) {
+ final Collection<Class<? extends ConstraintValidator<A, ?>>> selected = new ArrayList<Class<? extends ConstraintValidator<A, ?>>>();
+ for (final Class<? extends ConstraintValidator<A, ?>> constraint : rawConstraintClasses) {
+ final SupportedValidationTarget target = constraint.getAnnotation(SupportedValidationTarget.class);
+ if (target == null && isReturn) {
+ selected.add(constraint);
+ } else if (target != null) {
+ for (final ValidationTarget validationTarget : target.value()) {
+ if (isReturn && ValidationTarget.ANNOTATED_ELEMENT.equals(validationTarget)) {
+ selected.add(constraint);
+ } else if (isParam && ValidationTarget.PARAMETERS.equals(validationTarget)) {
+ selected.add(constraint);
+ }
+ }
+ }
+ }
+ return selected.toArray(new Class[selected.size()]);
+ }
+ 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
@@ -296,20 +353,31 @@ public final class AnnotationProcessor {
* T and not a supertype of the chosen ConstraintValidator supported
* type.
*/
- Map<Type, Class<? extends ConstraintValidator<A, ?>>> validatorTypes =
- getValidatorsTypes(constraintClasses);
+ 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(validatorTypes.get(assignableTypes.get(0)));
+ 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: "
- + validatorTypes.get(assignableTypes.get(0)));
+ throw new ValidationException("Factory returned null validator for: " + key);
}
return validator;
@@ -318,15 +386,43 @@ public final class AnnotationProcessor {
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()) {
- StringBuilder buf =
- new StringBuilder().append("No validator could be found for type ").append(stringForType(targetType))
- .append(". See: @").append(anno.annotationType().getSimpleName()).append(" at ").append(
- stringForLocation(owner, access));
- throw new UnexpectedTypeException(buf.toString());
+ 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 ");
@@ -380,7 +476,7 @@ public final class AnnotationProcessor {
}
private static void fillAssignableTypes(Type type, Set<Type> validatorsTypes, List<Type> suitableTypes) {
- for (Type validatorType : validatorsTypes) {
+ for (final Type validatorType : validatorsTypes) {
if (org.apache.commons.lang3.reflect.TypeUtils.isAssignable(type, validatorType)
&& !suitableTypes.contains(validatorType)) {
suitableTypes.add(validatorType);
@@ -426,14 +522,11 @@ public final class AnnotationProcessor {
* @param constraintValidatorClasses
* @return {@link Map} of {@link Type} : {@link ConstraintValidator} subtype
*/
- private static <A extends Annotation> Map<Type, Class<? extends ConstraintValidator<A, ?>>> getValidatorsTypes(
+ private static <A extends Annotation> Map<Type, Collection<Class<? extends ConstraintValidator<A, ?>>>> getValidatorsTypes(
Class<? extends ConstraintValidator<A, ?>>[] constraintValidatorClasses) {
- Map<Type, Class<? extends ConstraintValidator<A, ?>>> validatorsTypes =
- new HashMap<Type, Class<? extends ConstraintValidator<A, ?>>>();
+ 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]);
+ 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));
}
@@ -443,7 +536,10 @@ public final class AnnotationProcessor {
validatedType = Array.newInstance((Class<?>) componentType, 0).getClass();
}
}
- validatorsTypes.put(validatedType, validatorType);
+ 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/ApacheFactoryContext.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheFactoryContext.java?rev=1498347&r1=1498346&r2=1498347&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheFactoryContext.java (original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheFactoryContext.java Mon Jul 1 10:06:18 2013
@@ -26,6 +26,7 @@ import java.util.List;
import javax.validation.ConstraintValidatorFactory;
import javax.validation.MessageInterpolator;
+import javax.validation.ParameterNameProvider;
import javax.validation.TraversableResolver;
import javax.validation.ValidationException;
import javax.validation.Validator;
@@ -53,6 +54,7 @@ public class ApacheFactoryContext implem
private MessageInterpolator messageInterpolator;
private TraversableResolver traversableResolver;
+ private ParameterNameProvider parameterNameProvider;
private ConstraintValidatorFactory constraintValidatorFactory;
/**
@@ -119,6 +121,11 @@ public class ApacheFactoryContext implem
return this;
}
+ public ValidatorContext parameterNameProvider(ParameterNameProvider parameterNameProvider) {
+ this.parameterNameProvider = parameterNameProvider;
+ return this;
+ }
+
/**
* Get the {@link ConstraintValidatorFactory}.
*
@@ -159,6 +166,10 @@ public class ApacheFactoryContext implem
return traversableResolver == null ? factory.getTraversableResolver() : traversableResolver;
}
+ public ParameterNameProvider getParameterNameProvider() {
+ return parameterNameProvider == null ? factory.getParameterNameProvider() : parameterNameProvider;
+ }
+
/**
* Create MetaBeanManager that uses factories:
* <ol>
Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorConfiguration.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorConfiguration.java?rev=1498347&r1=1498346&r2=1498347&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorConfiguration.java (original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorConfiguration.java Mon Jul 1 10:06:18 2013
@@ -87,5 +87,7 @@ public interface ApacheValidatorConfigur
* </ol>
*/
String METABEAN_FACTORY_CLASSNAMES = "apache.bval.metabean-factory-classnames";
+
+ String EXECUTABLE_VALIDATION_TYPES = "apache.bval.executable-validation.types";
}
}
Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorFactory.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorFactory.java?rev=1498347&r1=1498346&r2=1498347&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorFactory.java (original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorFactory.java Mon Jul 1 10:06:18 2013
@@ -18,31 +18,35 @@
*/
package org.apache.bval.jsr303;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Modifier;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import org.apache.bval.jsr303.xml.AnnotationIgnores;
+import org.apache.bval.jsr303.xml.MetaConstraint;
+import org.apache.bval.jsr303.xml.ValidationMappingParser;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.ClassUtils;
import javax.validation.ConstraintValidatorFactory;
import javax.validation.MessageInterpolator;
+import javax.validation.ParameterNameProvider;
import javax.validation.TraversableResolver;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
+import javax.validation.executable.ExecutableType;
import javax.validation.spi.ConfigurationState;
-
-import org.apache.bval.jsr303.xml.AnnotationIgnores;
-import org.apache.bval.jsr303.xml.MetaConstraint;
-import org.apache.bval.jsr303.xml.ValidationMappingParser;
-import org.apache.bval.util.AccessStrategy;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.ClassUtils;
+import java.io.Closeable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* Description: a factory is a complete configurated object that can create
@@ -51,12 +55,12 @@ import org.apache.commons.lang3.ClassUti
*/
public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
private static volatile ApacheValidatorFactory DEFAULT_FACTORY;
- private static final ConstraintDefaults defaultConstraints =
- new ConstraintDefaults();
+ private static final ConstraintDefaults defaultConstraints = new ConstraintDefaults();
private MessageInterpolator messageResolver;
private TraversableResolver traversableResolver;
private ConstraintValidatorFactory constraintValidatorFactory;
+ private ParameterNameProvider parameterNameProvider;
private final Map<String, String> properties;
/**
@@ -72,6 +76,9 @@ public class ApacheValidatorFactory impl
private final Map<Class<?>, List<AccessStrategy>> validAccesses;
private final Map<Class<?>, List<MetaConstraint<?, ? extends Annotation>>> constraintMap;
+ private final Collection<ExecutableType> executableTypes = new CopyOnWriteArrayList<ExecutableType>();
+ private final Collection<Closeable> toClose = new ArrayList<Closeable>();
+
/**
* Convenience method to retrieve a default global ApacheValidatorFactory
*
@@ -79,9 +86,8 @@ public class ApacheValidatorFactory impl
*/
public static synchronized ApacheValidatorFactory getDefault() {
if (DEFAULT_FACTORY == null) {
- DEFAULT_FACTORY =
- Validation.byProvider(ApacheValidationProvider.class).configure().buildValidatorFactory()
- .unwrap(ApacheValidatorFactory.class);
+ DEFAULT_FACTORY = Validation.byProvider(ApacheValidationProvider.class).configure()
+ .buildValidatorFactory().unwrap(ApacheValidatorFactory.class);
}
return DEFAULT_FACTORY;
}
@@ -102,8 +108,7 @@ public class ApacheValidatorFactory impl
properties = new HashMap<String, String>();
defaultSequences = new HashMap<Class<?>, Class<?>[]>();
validAccesses = new HashMap<Class<?>, List<AccessStrategy>>();
- constraintMap =
- new HashMap<Class<?>, List<MetaConstraint<?, ? extends Annotation>>>();
+ constraintMap = new HashMap<Class<?>, List<MetaConstraint<?, ? extends Annotation>>>();
configure(configurationState);
}
@@ -115,12 +120,25 @@ public class ApacheValidatorFactory impl
*/
protected void configure(ConfigurationState configuration) {
getProperties().putAll(configuration.getProperties());
+ setParameterNameProvider(configuration.getParameterNameProvider());
setMessageInterpolator(configuration.getMessageInterpolator());
setTraversableResolver(configuration.getTraversableResolver());
- setConstraintValidatorFactory(configuration
- .getConstraintValidatorFactory());
- ValidationMappingParser parser = new ValidationMappingParser(this);
- parser.processMappingConfig(configuration.getMappingStreams());
+ setConstraintValidatorFactory(configuration.getConstraintValidatorFactory());
+
+ if (ConfigurationImpl.class.isInstance(configuration)) {
+ final ConfigurationImpl impl = ConfigurationImpl.class.cast(configuration);
+ executableTypes.addAll(impl.getExecutableValidation());
+ toClose.add(impl.getClosable());
+ } else {
+ final String executableTypesStr = getProperties().get(ApacheValidatorConfiguration.Properties.EXECUTABLE_VALIDATION_TYPES);
+ if (executableTypesStr != null && !executableTypesStr.isEmpty()) {
+ for (final String s : executableTypesStr.split(",")) {
+ executableTypes.add(ExecutableType.valueOf(s.trim()));
+ }
+ }
+ }
+
+ new ValidationMappingParser(this).processMappingConfig(configuration.getMappingStreams());
}
/**
@@ -169,7 +187,9 @@ public class ApacheValidatorFactory impl
* @param messageResolver
*/
public final void setMessageInterpolator(MessageInterpolator messageResolver) {
- this.messageResolver = messageResolver;
+ if (messageResolver != null) {
+ this.messageResolver = messageResolver;
+ }
}
/**
@@ -186,7 +206,15 @@ public class ApacheValidatorFactory impl
*/
public final void setTraversableResolver(
TraversableResolver traversableResolver) {
- this.traversableResolver = traversableResolver;
+ if (traversableResolver != null) {
+ this.traversableResolver = traversableResolver;
+ }
+ }
+
+ public void setParameterNameProvider(final ParameterNameProvider parameterNameProvider) {
+ if (parameterNameProvider != null) {
+ this.parameterNameProvider = parameterNameProvider;
+ }
}
/**
@@ -203,7 +231,12 @@ public class ApacheValidatorFactory impl
*/
public final void setConstraintValidatorFactory(
ConstraintValidatorFactory constraintValidatorFactory) {
- this.constraintValidatorFactory = constraintValidatorFactory;
+ if (constraintValidatorFactory != null) {
+ this.constraintValidatorFactory = constraintValidatorFactory;
+ if (DefaultConstraintValidatorFactory.class.isInstance(constraintValidatorFactory)) {
+ toClose.add(Closeable.class.cast(constraintValidatorFactory));
+ }
+ }
}
/**
@@ -213,6 +246,21 @@ public class ApacheValidatorFactory impl
return constraintValidatorFactory;
}
+ public ParameterNameProvider getParameterNameProvider() {
+ return parameterNameProvider;
+ }
+
+ public void close() {
+ try {
+ for (final Closeable c : toClose) {
+ c.close();
+ }
+ toClose.clear();
+ } catch (final Exception e) {
+ // no-op
+ }
+ }
+
/**
* Return an object of the specified type to allow access to the
* provider-specific API. If the Bean Validation provider implementation
@@ -224,9 +272,7 @@ public class ApacheValidatorFactory impl
*/
public <T> T unwrap(final Class<T> type) {
if (type.isInstance(this)) {
- @SuppressWarnings("unchecked")
- T result = (T) this;
- return result;
+ return (T) this;
}
// FIXME 2011-03-27 jw:
@@ -297,8 +343,8 @@ public class ApacheValidatorFactory impl
* @param beanClass
* @param metaConstraint
*/
- public void addMetaConstraint(Class<?> beanClass,
- MetaConstraint<?, ?> metaConstraint) {
+ public void addMetaConstraint(final Class<?> beanClass,
+ final MetaConstraint<?, ?> metaConstraint) {
List<MetaConstraint<?, ? extends Annotation>> slot;
synchronized (constraintMap) {
slot = constraintMap.get(beanClass);
@@ -339,6 +385,10 @@ public class ApacheValidatorFactory impl
defaultSequences.put(beanClass, safeArray(groupSequence));
}
+ public Collection<ExecutableType> getExecutableTypes() {
+ return executableTypes;
+ }
+
/**
* Retrieve the runtime constraint configuration for a given class.
*
Added: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToList.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToList.java?rev=1498347&view=auto
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToList.java (added)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToList.java Mon Jul 1 10:06:18 2013
@@ -0,0 +1,51 @@
+/*
+ * 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 java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Description: {@link org.apache.bval.jsr303.AppendValidation} implementation that acts as an intermediate
+ * cache of validations for further processing.<br/>
+ */
+public class AppendValidationToList extends BaseAppendValidation {
+ private final List<ConstraintValidation<?>> validations = new ArrayList<ConstraintValidation<?>>();
+
+ /**
+ * Create a new AppendValidationToList instance.
+ */
+ public AppendValidationToList() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T extends Annotation> void performAppend(ConstraintValidation<T> validation) {
+ validations.add(validation);
+ }
+
+ /**
+ * Get the list of cached validations.
+ * @return {@link java.util.List} of {@link org.apache.bval.jsr303.ConstraintValidation}
+ */
+ public List<ConstraintValidation<?>> getValidations() {
+ return validations;
+ }
+}
Modified: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/BeanDescriptorImpl.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/BeanDescriptorImpl.java?rev=1498347&r1=1498346&r2=1498347&view=diff
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/BeanDescriptorImpl.java (original)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/BeanDescriptorImpl.java Mon Jul 1 10:06:18 2013
@@ -18,35 +18,149 @@
*/
package org.apache.bval.jsr303;
+import org.apache.bval.Validate;
+import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.groups.GroupConversionDescriptorImpl;
+import org.apache.bval.jsr303.util.ClassHelper;
+import org.apache.bval.jsr303.util.SecureActions;
import org.apache.bval.model.Features;
import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaConstructor;
+import org.apache.bval.model.MetaMethod;
+import org.apache.bval.model.MetaParameter;
import org.apache.bval.model.MetaProperty;
+import org.apache.bval.model.Validation;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.commons.lang3.ClassUtils;
+import javax.validation.Constraint;
+import javax.validation.ConstraintDeclarationException;
+import javax.validation.ConstraintTarget;
+import javax.validation.Valid;
+import javax.validation.groups.ConvertGroup;
import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.ConstructorDescriptor;
+import javax.validation.metadata.ExecutableDescriptor;
+import javax.validation.metadata.GroupConversionDescriptor;
+import javax.validation.metadata.MethodDescriptor;
+import javax.validation.metadata.MethodType;
+import javax.validation.metadata.ParameterDescriptor;
import javax.validation.metadata.PropertyDescriptor;
+import javax.validation.metadata.ReturnValueDescriptor;
+import java.beans.Introspector;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
/**
* Description: Implements {@link BeanDescriptor}.<br/>
*/
public class BeanDescriptorImpl extends ElementDescriptorImpl implements BeanDescriptor {
+ private static final CopyOnWriteArraySet<ConstraintValidation<?>> NO_CONSTRAINTS = new CopyOnWriteArraySet<ConstraintValidation<?>>();
/**
* The {@link ApacheFactoryContext} (not) used by this
* {@link BeanDescriptorImpl}
*/
protected final ApacheFactoryContext factoryContext;
+ private final AnnotationProcessor annotationProcessor;
+ private Set<ConstructorDescriptor> constrainedConstructors = new CopyOnWriteArraySet<ConstructorDescriptor>();
+ private Map<Method, MethodDescriptor> methodConstraints = new HashMap<Method, MethodDescriptor>();
+ private Set<MethodDescriptor> containedMethods = new CopyOnWriteArraySet<MethodDescriptor>();
+ private Map<Constructor<?>, ConstructorDescriptor> contructorConstraints = new HashMap<Constructor<?>, ConstructorDescriptor>();
- /**
- * Create a new BeanDescriptorImpl instance.
- *
- * @param factoryContext
- * @param metaBean
- */
protected BeanDescriptorImpl(ApacheFactoryContext factoryContext, MetaBean metaBean) {
super(metaBean, metaBean.getBeanClass(), metaBean.getValidations());
this.factoryContext = factoryContext;
+ this.annotationProcessor = new AnnotationProcessor(factoryContext);
+
+ buildExecutableDescriptors();
+ }
+
+ private static void addGroupConvertion(final MetaProperty prop, final PropertyDescriptorImpl edesc) {
+ boolean fieldFound = false;
+ boolean methodFound = false;
+ Class<?> current = prop.getParentMetaBean().getBeanClass();
+ while (current != null && current != Object.class && (!methodFound || !fieldFound)) {
+ if (!fieldFound) {
+ final Field field = SecureActions.getDeclaredField(current, prop.getName()).run();
+ if (field != null) {
+ final ConvertGroup.List convertGroupList = field.getAnnotation(ConvertGroup.List.class);
+ if (convertGroupList != null) {
+ for (final ConvertGroup convertGroup : convertGroupList.value()) {
+ edesc.addGroupConversion(new GroupConversionDescriptorImpl(new Group(convertGroup.from()), new Group(convertGroup.to())));
+ }
+ }
+
+ final ConvertGroup convertGroup = field.getAnnotation(ConvertGroup.class);
+ if (convertGroup != null) {
+ edesc.addGroupConversion(new GroupConversionDescriptorImpl(new Group(convertGroup.from()), new Group(convertGroup.to())));
+ }
+ fieldFound = true;
+ }
+ }
+
+ if (!methodFound) {
+ final String name = Character.toUpperCase(prop.getName().charAt(0)) + prop.getName().substring(1);
+ for (final Method method : Arrays.asList(
+ SecureActions.getDeclaredMethod(current, "is" + name).run(),
+ SecureActions.getDeclaredMethod(current, "get" + name).run())) {
+
+ if (method != null) {
+ final ConvertGroup.List convertGroupList = method.getAnnotation(ConvertGroup.List.class);
+ if (convertGroupList != null) {
+ for (final ConvertGroup convertGroup : convertGroupList.value()) {
+ edesc.addGroupConversion(new GroupConversionDescriptorImpl(new Group(convertGroup.from()), new Group(convertGroup.to())));
+ }
+ }
+
+ final ConvertGroup convertGroup = method.getAnnotation(ConvertGroup.class);
+ if (convertGroup != null) {
+ edesc.addGroupConversion(new GroupConversionDescriptorImpl(new Group(convertGroup.from()), new Group(convertGroup.to())));
+ }
+
+ methodFound = true;
+ break;
+ }
+ }
+ }
+
+ current = current.getSuperclass();
+ }
+
+ final Collection<Annotation> annotations = prop.getFeature(Jsr303Features.Property.ANNOTATIONS_TO_PROCESS);
+ if (annotations != null) {
+ for (final Annotation a : annotations) {
+ if (ConvertGroup.List.class.isInstance(a)) {
+ for (final ConvertGroup convertGroup : ConvertGroup.List.class.cast(a).value()) {
+ edesc.addGroupConversion(new GroupConversionDescriptorImpl(new Group(convertGroup.from()), new Group(convertGroup.to())));
+ }
+ }
+
+ if (ConvertGroup.class.isInstance(a)) {
+ final ConvertGroup convertGroup = ConvertGroup.class.cast(a);
+ edesc.addGroupConversion(new GroupConversionDescriptorImpl(new Group(convertGroup.from()), new Group(convertGroup.to())));
+ }
+ }
+ annotations.clear();
+ }
+
+ if (!edesc.getGroupConversions().isEmpty() && !edesc.isCascaded()) {
+ throw new ConstraintDeclarationException("@Valid is needed for group conversion");
+ }
}
/**
@@ -56,7 +170,7 @@ public class BeanDescriptorImpl extends
* <li>a constraint is hosted on one of the bean properties, OR</li>
* <li>a bean property is marked for cascade (<code>@Valid</code>)</li>
* </ul>
- *
+ *
* @return true if the bean involves validation
*/
public boolean isBeanConstrained() {
@@ -84,9 +198,8 @@ public class BeanDescriptorImpl extends
* either the property does not exist or has no constraint. The returned
* object (and associated objects including ConstraintDescriptors) are
* immutable.
- *
- * @param propertyName
- * property evaluated
+ *
+ * @param propertyName property evaluated
*/
public PropertyDescriptor getConstraintsForProperty(String propertyName) {
if (propertyName == null || propertyName.trim().length() == 0) {
@@ -106,6 +219,7 @@ public class BeanDescriptorImpl extends
PropertyDescriptorImpl edesc = prop.getFeature(Jsr303Features.Property.PropertyDescriptor);
if (edesc == null) {
edesc = new PropertyDescriptorImpl(prop);
+ addGroupConvertion(prop, edesc);
prop.putFeature(Jsr303Features.Property.PropertyDescriptor, edesc);
}
return edesc;
@@ -113,24 +227,591 @@ public class BeanDescriptorImpl extends
/**
* {@inheritDoc}
- *
+ *
* @return the property descriptors having at least a constraint defined
*/
public Set<PropertyDescriptor> getConstrainedProperties() {
Set<PropertyDescriptor> validatedProperties = new HashSet<PropertyDescriptor>();
for (MetaProperty prop : metaBean.getProperties()) {
if (prop.getValidations().length > 0
- || (prop.getMetaBean() != null || prop.getFeature(Features.Property.REF_CASCADE) != null)) {
+ || (prop.getMetaBean() != null || prop.getFeature(Features.Property.REF_CASCADE) != null)) {
validatedProperties.add(getPropertyDescriptor(prop));
}
}
return Collections.unmodifiableSet(validatedProperties);
}
+ public MethodDescriptor getConstraintsForMethod(String methodName, Class<?>... parameterTypes) {
+ if (methodName == null) {
+ throw new IllegalArgumentException("Method name can't be null");
+ }
+
+ Class<?> beanClass = metaBean.getBeanClass();
+ Method method = null;
+ do {
+ try {
+ method = beanClass.getDeclaredMethod(methodName, parameterTypes);
+ break;
+ } catch (final NoSuchMethodException e) {
+ // no-op
+ }
+ beanClass = beanClass.getSuperclass();
+ } while (beanClass != Object.class && beanClass != null);
+ if (method == null) {
+ return null;
+ }
+
+ final MethodDescriptor descriptor = methodConstraints.get(method);
+ if (descriptor != null) {
+ final boolean hasConstraint = descriptor.hasConstrainedParameters() || descriptor.hasConstrainedReturnValue();
+ if (!hasConstraint) {
+ return null;
+ }
+ return descriptor;
+ }
+
+ // TODO: surely remove it
+ for (final MetaMethod metaMethod : metaBean.getMethods()) {
+ if (metaMethod.getMethod().equals(method)) {
+ final MethodDescriptorImpl methodDescriptor = createMethodDescriptor(metaMethod);
+ ensureNotNullDescriptors(metaMethod.getMethod().getReturnType(), methodDescriptor);
+ methodConstraints.put(method, methodDescriptor);
+ containedMethods.add(methodDescriptor);
+ return methodDescriptor;
+ }
+ }
+
+ return null;
+ }
+
+ private MethodDescriptorImpl createMethodDescriptor(final MetaMethod metaMethod) {
+ MethodDescriptorImpl edesc = metaMethod.getFeature(Jsr303Features.Method.MethodDescriptor);
+ if (edesc == null) {
+ edesc = new MethodDescriptorImpl(metaBean, metaMethod);
+ metaMethod.putFeature(Jsr303Features.Method.MethodDescriptor, edesc);
+ }
+ return edesc;
+ }
+
+ public Set<MethodDescriptor> getConstrainedMethods(MethodType methodType, MethodType... methodTypes) {
+ final Set<MethodDescriptor> desc = new HashSet<MethodDescriptor>();
+ desc.addAll(filter(containedMethods, methodType));
+ if (methodTypes != null) {
+ for (final MethodType type : methodTypes) {
+ desc.addAll(filter(containedMethods, type));
+ }
+ }
+ return desc;
+ }
+
+ private static Collection<MethodDescriptor> filter(final Set<MethodDescriptor> containedMethods, final MethodType type) {
+ final Collection<MethodDescriptor> list = new ArrayList<MethodDescriptor>();
+ for (final MethodDescriptor d : containedMethods) {
+ final boolean getter = d.getName().startsWith("get") && d.getParameterDescriptors().isEmpty();
+
+ switch (type) {
+ case GETTER:
+ if (getter) {
+ list.add(d);
+ }
+ break;
+
+ case NON_GETTER:
+ if (!getter) {
+ list.add(d);
+ }
+ }
+ }
+ return list;
+ }
+
+ public ConstructorDescriptor getConstraintsForConstructor(Class<?>... parameterTypes) {
+ final Constructor<?> declaredConstructor;
+ try {
+ declaredConstructor = metaBean.getBeanClass().getDeclaredConstructor(parameterTypes);
+ } catch (final NoSuchMethodException e) {
+ return null;
+ }
+
+ final ConstructorDescriptor descriptor = contructorConstraints.get(declaredConstructor);
+ if (descriptor != null && (descriptor.hasConstrainedParameters() || descriptor.hasConstrainedReturnValue())) {
+ return descriptor;
+ }
+
+ return null;
+ }
+
+ public Set<ConstructorDescriptor> getConstrainedConstructors() {
+ return constrainedConstructors;
+ }
+
/**
* {@inheritDoc}
*/
public String toString() {
return "BeanDescriptorImpl{" + "returnType=" + elementClass + '}';
}
+
+ public void buildExecutableDescriptors() {
+ try {
+ buildMethodConstraints();
+ setConstrained(containedMethods, methodConstraints.values());
+
+ buildConstructorConstraints();
+ setConstrained(constrainedConstructors, contructorConstraints.values());
+ } catch (final Exception ex) {
+ if (RuntimeException.class.isInstance(ex)) {
+ throw RuntimeException.class.cast(ex);
+ }
+
+ throw new IllegalArgumentException(ex.getMessage(), ex);
+ }
+ }
+
+ private <A extends ExecutableDescriptor> void setConstrained(final Set<A> dest, final Collection<A> src) {
+ for (final A d : src) {
+ if (d.hasConstrainedParameters() || d.hasConstrainedReturnValue()) {
+ dest.add(d);
+ }
+ }
+ }
+
+ private void buildConstructorConstraints() throws InvocationTargetException, IllegalAccessException {
+ for (final Constructor<?> cons : SecureActions.getDeclaredConstructors(getMetaBean().getBeanClass()).run()) {
+ final ConstructorDescriptorImpl consDesc = new ConstructorDescriptorImpl(getMetaBean(), new Validation[0]);
+ contructorConstraints.put(cons, consDesc);
+
+ final List<String> names = factoryContext.getParameterNameProvider().getParameterNames(cons);
+ final boolean isInnerClass = cons.getDeclaringClass().getEnclosingClass() != null && !Modifier.isStatic(cons.getDeclaringClass().getModifiers());
+
+ {
+ final Annotation[][] paramsAnnos = cons.getParameterAnnotations();
+
+ int idx = 0;
+ if (isInnerClass) { // paramsAnnos.length = parameterTypes.length - 1 in this case
+ final ParameterDescriptorImpl paramDesc = new ParameterDescriptorImpl(getMetaBean(), new Validation[0], names.get(idx));
+ consDesc.getParameterDescriptors().add(paramDesc);
+ idx++;
+ }
+
+ for (final Annotation[] paramAnnos : paramsAnnos) {
+ if (factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotationOnParameter(cons, idx)) {
+ consDesc.getParameterDescriptors().add(new ParameterDescriptorImpl(metaBean, new Validation[0], names.get(idx)));
+ } else if (cons.getParameterTypes().length > idx) {
+ ParameterAccess access = new ParameterAccess(cons.getParameterTypes()[idx], idx);
+ consDesc.addValidations(processAnnotations(consDesc, paramAnnos, access, idx, names.get(idx)).getValidations());
+ } // else anonymous class so that's fine
+ idx++;
+ }
+
+ if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(cons)) {
+ for (final Annotation anno : cons.getAnnotations()) {
+ if (!Valid.class.isInstance(anno)) {
+ processAnnotations(null, consDesc, cons.getDeclaringClass(), anno);
+ } else {
+ consDesc.setCascaded(true);
+ }
+ }
+ }
+ }
+
+ if (factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotationOnCrossParameter(cons) && consDesc.getCrossParameterDescriptor() != null) {
+ consDesc.setCrossParameterDescriptor(null);
+ }
+ if (factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotationOnReturn(cons) && consDesc.getReturnValueDescriptor() != null) {
+ consDesc.setReturnValueDescriptor(null);
+ }
+
+ final MetaConstructor metaConstructor = metaBean.getConstructor(cons);
+ if (metaConstructor != null) {
+ for (final Annotation anno : metaConstructor.getAnnotations()) {
+ if (!Valid.class.isInstance(anno)) {
+ processAnnotations(null, consDesc, cons.getDeclaringClass(), anno);
+ } else {
+ consDesc.setCascaded(true);
+ }
+ }
+
+ // parameter validations
+ final Collection<MetaParameter> paramsAnnos = metaConstructor.getParameters();
+ for (final MetaParameter paramAnnos : paramsAnnos) {
+ final int idx = paramAnnos.getIndex();
+ final ParameterAccess access = new ParameterAccess(cons.getParameterTypes()[idx], idx);
+ processAnnotations(consDesc, paramAnnos.getAnnotations(), access, idx, names.get(idx));
+ }
+ }
+
+ if (!consDesc.getGroupConversions().isEmpty() && !consDesc.isCascaded()) {
+ throw new ConstraintDeclarationException("@Valid is needed to define a group conversion");
+ }
+
+ ensureNotNullDescriptors(cons.getDeclaringClass(), consDesc);
+ }
+ }
+
+ private void ensureNotNullDescriptors(final Class<?> returnType, final InvocableElementDescriptor consDesc) {
+ // can't be null
+ if (consDesc.getCrossParameterDescriptor() == null) {
+ consDesc.setCrossParameterDescriptor(new CrossParameterDescriptorImpl(getMetaBean(), NO_CONSTRAINTS));
+ }
+ if (consDesc.getReturnValueDescriptor() == null) {
+ consDesc.setReturnValueDescriptor(new ReturnValueDescriptorImpl(getMetaBean(), returnType, NO_CONSTRAINTS, consDesc.isCascaded()));
+ }
+ // enforce it since ReturnValueDescriptor can be created before cascaded is set to true
+ final ReturnValueDescriptorImpl returnValueDescriptor = ReturnValueDescriptorImpl.class.cast(consDesc.getReturnValueDescriptor());
+ returnValueDescriptor.setCascaded(consDesc.isCascaded());
+ if (returnValueDescriptor.getGroupConversions().isEmpty()) {
+ // loop to not forget to map calling addGroupConversion()
+ for (final GroupConversionDescriptor c : consDesc.getGroupConversions()) {
+ returnValueDescriptor.addGroupConversion(c);
+ }
+ }
+
+ }
+
+ private void processAnnotations(final Method mtd, final InvocableElementDescriptor consDesc, final Class<?> clazz, final Annotation anno) throws InvocationTargetException, IllegalAccessException {
+ if (mtd == null || !factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotationOnReturn(mtd)) {
+ final ReturnAccess returnAccess = new ReturnAccess(clazz);
+ final AppendValidationToList validations = new AppendValidationToList();
+ processAnnotation(anno, consDesc, returnAccess, validations);
+ final List<ConstraintValidation<?>> list = removeFromListValidationAppliesTo(validations.getValidations(), ConstraintTarget.PARAMETERS);
+ consDesc.addValidations(list);
+
+ ReturnValueDescriptorImpl returnValueDescriptor = ReturnValueDescriptorImpl.class.cast(consDesc.getReturnValueDescriptor());
+ if (consDesc.getReturnValueDescriptor() != null) {
+ returnValueDescriptor.getMutableConstraintDescriptors().addAll(list);
+ } else {
+ returnValueDescriptor = new ReturnValueDescriptorImpl(getMetaBean(), clazz, list, consDesc.isCascaded());
+ consDesc.setReturnValueDescriptor(returnValueDescriptor);
+ }
+ }
+
+ if (mtd == null || !factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotationOnCrossParameter(mtd)) {
+ final ParametersAccess parametersAccess = new ParametersAccess();
+ final AppendValidationToList validations = new AppendValidationToList();
+ processAnnotation(anno, consDesc, parametersAccess, validations);
+ final List<ConstraintValidation<?>> list = removeFromListValidationAppliesTo(validations.getValidations(), ConstraintTarget.RETURN_VALUE);
+ consDesc.addValidations(list);
+ if (consDesc.getCrossParameterDescriptor() != null) {
+ CrossParameterDescriptorImpl.class.cast(consDesc.getCrossParameterDescriptor()).getMutableConstraintDescriptors().addAll(list);
+ } else {
+ consDesc.setCrossParameterDescriptor(new CrossParameterDescriptorImpl(getMetaBean(), list));
+ }
+ }
+ }
+
+ private static List<ConstraintValidation<?>> removeFromListValidationAppliesTo(final List<ConstraintValidation<?>> validations, final ConstraintTarget constraint) {
+ final Iterator<ConstraintValidation<?>> i = validations.iterator();
+ while (i.hasNext()) {
+ if (constraint.equals(i.next().getValidationAppliesTo())) {
+ i.remove();
+ }
+ }
+ return validations;
+ }
+
+ private void buildMethodConstraints() throws InvocationTargetException, IllegalAccessException {
+
+ Class<?> current = getMetaBean().getBeanClass();
+ do {
+ for (final Method method : current.getDeclaredMethods()) {
+ final boolean getter = method.getName().startsWith("get") && method.getParameterTypes().length == 0;
+
+ final MethodDescriptorImpl methodDesc = new MethodDescriptorImpl(getMetaBean(), new Validation[0], method);
+ methodConstraints.put(method, methodDesc);
+
+ final List<Class<?>> classHierarchy = new ArrayList<Class<?>>();
+ ClassHelper.fillFullClassHierarchyAsList(classHierarchy, current);
+ classHierarchy.remove(current);
+
+ final Collection<Method> parents = new ArrayList<Method>();
+ for (final Class<?> clazz : classHierarchy) {
+ final Method overriden = SecureActions.getDeclaredMethod(clazz, method.getName(), method.getParameterTypes()).run();
+ if (overriden != null) {
+ processMethod(overriden, methodDesc);
+ parents.add(overriden);
+ }
+ }
+
+ processMethod(method, methodDesc);
+
+ ensureNotNullDescriptors(method.getReturnType(), methodDesc);
+
+ // validations, TODO: if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(method)) {
+ if (parents != null) {
+ if (parents.size() > 1) {
+ for (final Method parent : parents) {
+ final InvocableElementDescriptor elementDescriptor = InvocableElementDescriptor.class.cast(factoryContext.getValidator().getConstraintsForClass(parent.getDeclaringClass()).getConstraintsForMethod(parent.getName(), parent.getParameterTypes()));
+ if (elementDescriptor != null) {
+ ensureNoParameterConstraint(elementDescriptor, "Parameter constraints can't be defined for parallel interfaces/parents");
+ } else {
+ ensureMethodDoesntDefineParameterConstraint(methodDesc);
+ }
+ ensureNoReturnValueAddedInChild(methodDesc.getReturnValueDescriptor(), parent, "Return value constraints should be the same for parent and children");
+ }
+ } else if (!parents.isEmpty()) {
+ final Method parent = parents.iterator().next();
+ ensureNoReturnValueAddedInChild(methodDesc.getReturnValueDescriptor(), parent, "Return value constraints should be at least the same for parent and children");
+
+ final MethodDescriptor parentDesc = factoryContext.getValidator().getConstraintsForClass(parent.getDeclaringClass()).getConstraintsForMethod(parent.getName(), parent.getParameterTypes());
+ if (parentDesc != null) {
+ final Iterator<ParameterDescriptor> parentPd = parentDesc.getParameterDescriptors().iterator();
+ for (final ParameterDescriptor pd : methodDesc.getParameterDescriptors()) {
+ final ParameterDescriptor next = parentPd.next();
+ if (pd.getConstraintDescriptors().size() != next.getConstraintDescriptors().size()) {
+ throw new ConstraintDeclarationException("child shouldn't get more constraint than parent");
+ }
+ if (pd.isCascaded() != next.isCascaded()) { // @Valid
+ throw new ConstraintDeclarationException("child shouldn't get more constraint than parent");
+ }
+ }
+ } else {
+ ensureMethodDoesntDefineParameterConstraint(methodDesc);
+ }
+ }
+
+ final Class<?>[] interfaces = method.getDeclaringClass().getInterfaces();
+ final Collection<Method> itfWithThisMethod = new ArrayList<Method>();
+ for (final Class<?> i : interfaces) {
+ final Method m = SecureActions.getDeclaredMethod(i, method.getName(), method.getParameterTypes()).run();
+ if (m != null) {
+ itfWithThisMethod.add(m);
+ }
+ }
+ if (itfWithThisMethod.size() > 1) {
+ for (final Method m : itfWithThisMethod) {
+ ensureNoConvertGroup(m, "ConvertGroup can't be used in parallel interfaces");
+ }
+ } else if (itfWithThisMethod.size() == 1) {
+ ensureNoConvertGroup(itfWithThisMethod.iterator().next(), "ConvertGroup can't be used in interface AND parent class");
+ }
+
+ int returnValid = 0;
+ if (method.getAnnotation(Valid.class) != null) {
+ returnValid++;
+ }
+ for (final Class<?> clazz : classHierarchy) {
+ final Method overriden = SecureActions.getDeclaredMethod(clazz, method.getName(), method.getParameterTypes()).run();
+ if (overriden != null) {
+ if (overriden.getAnnotation(Valid.class) != null) {
+ returnValid++;
+ }
+ }
+ }
+ if (returnValid > 1 && !(interfaces.length == returnValid && method.getAnnotation(Valid.class) == null)) {
+ throw new ConstraintDeclarationException("@Valid on returned value can't be set more than once");
+ }
+ }
+
+ if (getter) {
+ final MetaProperty prop = metaBean.getProperty(Introspector.decapitalize(method.getName().substring(3)));
+ if (prop != null && prop.getFeature(Features.Property.REF_CASCADE) != null) {
+ methodDesc.setCascaded(true);
+ }
+ }
+
+ if (!methodDesc.getGroupConversions().isEmpty() && !methodDesc.isCascaded()) {
+ throw new ConstraintDeclarationException("@Valid is needed to define a group conversion");
+ }
+ }
+ current = current.getSuperclass();
+ } while (current != null && current != Object.class);
+ }
+
+ private void ensureMethodDoesntDefineParameterConstraint(MethodDescriptorImpl methodDesc) {
+ for (final ParameterDescriptor pd : methodDesc.getParameterDescriptors()) {
+ if (!pd.getConstraintDescriptors().isEmpty()) {
+ throw new ConstraintDeclarationException("child shouldn't get more constraint than parent");
+ }
+ if (pd.isCascaded()) { // @Valid
+ throw new ConstraintDeclarationException("child shouldn't get more constraint than parent");
+ }
+ }
+ }
+
+ private void ensureNoReturnValueAddedInChild(final ReturnValueDescriptor returnValueDescriptor, final Method parent, final String msg) {
+ final MethodDescriptor parentDesc = factoryContext.getValidator().getConstraintsForClass(parent.getDeclaringClass()).getConstraintsForMethod(parent.getName(), parent.getParameterTypes());
+ if (parentDesc == null) {
+ return;
+ }
+
+ final ReturnValueDescriptor parentReturnDesc = parentDesc.getReturnValueDescriptor();
+ if (parentReturnDesc.isCascaded() && !returnValueDescriptor.isCascaded() || parentReturnDesc.getConstraintDescriptors().size() > returnValueDescriptor.getConstraintDescriptors().size()) {
+ throw new ConstraintDeclarationException(msg);
+ }
+ }
+
+ private static void ensureNoParameterConstraint(final InvocableElementDescriptor constraintsForMethod, final String msg) {
+ for (final ParameterDescriptor parameterDescriptor : constraintsForMethod.getParameterDescriptors()) {
+ if (!parameterDescriptor.getConstraintDescriptors().isEmpty() || parameterDescriptor.isCascaded()) {
+ throw new ConstraintDeclarationException(msg);
+ }
+ }
+ }
+
+ private static void ensureNoConvertGroup(final Method method, final String msg) {
+ for (final Annotation[] annotations : method.getParameterAnnotations()) {
+ for (final Annotation a : annotations) {
+ if (ConvertGroup.class.isInstance(a)) {
+ throw new ConstraintDeclarationException(msg);
+ }
+ }
+ }
+ if (method.getAnnotation(ConvertGroup.class) != null) {
+ throw new ConstraintDeclarationException(msg);
+ }
+ }
+
+ private void processMethod(Method method, MethodDescriptorImpl methodDesc) throws InvocationTargetException, IllegalAccessException {
+ { // reflection
+ if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(method)) {
+ // return value validations and/or cross-parameter validation
+ for (Annotation anno : method.getAnnotations()) {
+ if (anno instanceof Valid || anno instanceof Validate) {
+ methodDesc.setCascaded(true);
+ } else {
+ processAnnotations(method, methodDesc, method.getReturnType(), anno);
+ }
+ }
+ }
+
+ // parameter validations
+ final Annotation[][] paramsAnnos = method.getParameterAnnotations();
+ int idx = 0;
+ final List<String> names = factoryContext.getParameterNameProvider().getParameterNames(method);
+ for (final Annotation[] paramAnnos : paramsAnnos) {
+ if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotationOnParameter(method, idx)) {
+ final ParameterAccess access = new ParameterAccess(method.getParameterTypes()[idx], idx);
+ processAnnotations(methodDesc, paramAnnos, access, idx, names.get(idx));
+ } else {
+ final ParameterDescriptorImpl parameterDescriptor = new ParameterDescriptorImpl(metaBean, new Validation[0], names.get(idx));
+ parameterDescriptor.setIndex(idx);
+ methodDesc.getParameterDescriptors().add(parameterDescriptor);
+ }
+ idx++;
+ }
+ }
+
+ if (factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotationOnCrossParameter(method) && methodDesc.getCrossParameterDescriptor() != null) {
+ methodDesc.setCrossParameterDescriptor(null);
+ }
+ if (factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotationOnReturn(method) && methodDesc.getReturnValueDescriptor() != null) {
+ methodDesc.setReturnValueDescriptor(null);
+ }
+
+ final MetaMethod metaMethod = metaBean.getMethod(method);
+ if (metaMethod != null) {
+ for (final Annotation anno : metaMethod.getAnnotations()) {
+ if (anno instanceof Valid) {
+ methodDesc.setCascaded(true);
+ } else {
+ // set first param as null to force it to be read
+ processAnnotations(null, methodDesc, method.getReturnType(), anno);
+ }
+ }
+
+ // parameter validations
+ final Collection<MetaParameter> paramsAnnos = metaMethod.getParameters();
+ final List<String> names = factoryContext.getParameterNameProvider().getParameterNames(method);
+ for (final MetaParameter paramAnnos : paramsAnnos) {
+ final int idx = paramAnnos.getIndex();
+ final ParameterAccess access = new ParameterAccess(method.getParameterTypes()[idx], idx);
+ processAnnotations(methodDesc, paramAnnos.getAnnotations(), access, idx, names.get(idx));
+ }
+
+ }
+
+ }
+
+ private AppendValidationToList processAnnotations(InvocableElementDescriptor methodDesc, Annotation[] paramAnnos, AccessStrategy access, int idx, String name)
+ throws InvocationTargetException, IllegalAccessException {
+ final AppendValidationToList validations = new AppendValidationToList();
+ boolean cascaded = false;
+
+ Group[] from = null;
+ Group[] to = null;
+
+ for (final Annotation anno : paramAnnos) {
+ if (anno instanceof Valid || anno instanceof Validate) {
+ cascaded = true;
+ } else if (ConvertGroup.class.isInstance(anno)) {
+ final ConvertGroup cg = ConvertGroup.class.cast(anno);
+ from = new Group[]{new Group(cg.from())};
+ to = new Group[]{new Group(cg.to())};
+ } else if (ConvertGroup.List.class.isInstance(anno)) {
+ final ConvertGroup.List cgl = ConvertGroup.List.class.cast(anno);
+ final ConvertGroup[] groups = cgl.value();
+ from = new Group[groups.length];
+ to = new Group[groups.length];
+ for (int i = 0; i < to.length; i++) {
+ from[i] = new Group(groups[i].from());
+ to[i] = new Group(groups[i].to());
+ }
+ } else {
+ processAnnotation(anno, methodDesc, access, validations);
+ }
+ }
+
+ ParameterDescriptorImpl paramDesc = null;
+ for (final ParameterDescriptor pd : methodDesc.getParameterDescriptors()) {
+ if (pd.getIndex() == idx) {
+ paramDesc = ParameterDescriptorImpl.class.cast(pd);
+ }
+ }
+
+ if (paramDesc == null) {
+ paramDesc = new ParameterDescriptorImpl(Class.class.cast(access.getJavaType()), // set from getParameterTypes() so that's a Class<?>
+ validations.getValidations().toArray(new Validation[validations.getValidations().size()]), name);
+ paramDesc.setIndex(idx);
+ methodDesc.getParameterDescriptors().add(paramDesc);
+ paramDesc.setCascaded(cascaded);
+ } else {
+ paramDesc.getMutableConstraintDescriptors().addAll(validations.getValidations());
+ if (cascaded) {
+ paramDesc.setCascaded(true);
+ } // else keep previous config
+ }
+ if (paramDesc.isCascaded() && from != null) {
+ for (int i = 0; i < from.length; i++) {
+ paramDesc.addGroupConversion(new GroupConversionDescriptorImpl(from[i], to[i]));
+ }
+ } else if (from != null) {
+ throw new ConstraintDeclarationException("Group conversion is only relevant for @Valid cases");
+ }
+
+ return validations;
+ }
+
+ private <A extends Annotation> void processAnnotation(A annotation, InvocableElementDescriptor desc,
+ AccessStrategy access, AppendValidation validations) throws InvocationTargetException, IllegalAccessException {
+
+ if (annotation instanceof Valid || annotation instanceof Validate) {
+ desc.setCascaded(true);
+ } else if (ConvertGroup.class.isInstance(annotation) && ReturnAccess.class.isInstance(access)) { // access is just tested to ensure to not read it twice with cross parameter
+ final ConvertGroup cg = ConvertGroup.class.cast(annotation);
+ desc.addGroupConversion(new GroupConversionDescriptorImpl(new Group(cg.from()), new Group(cg.to())));
+ } else if (ConvertGroup.List.class.isInstance(annotation) && ReturnAccess.class.isInstance(access)) {
+ final ConvertGroup.List cgl = ConvertGroup.List.class.cast(annotation);
+ for (final ConvertGroup cg : cgl.value()) {
+ desc.addGroupConversion(new GroupConversionDescriptorImpl(new Group(cg.from()), new Group(cg.to())));
+ }
+ } else {
+ Constraint vcAnno = annotation.annotationType().getAnnotation(Constraint.class);
+ if (vcAnno != null) {
+ annotationProcessor.processAnnotation(annotation, null, ClassUtils.primitiveToWrapper((Class<?>) access.getJavaType()), access, validations, true);
+ } else {
+ /**
+ * Multi-valued constraints
+ */
+ if (ConstraintAnnotationAttributes.VALUE.isDeclaredOn(annotation.annotationType())) {
+ Annotation[] children = ConstraintAnnotationAttributes.VALUE.getValue(annotation);
+ if (children != null) {
+ for (Annotation child : children) {
+ processAnnotation(child, desc, access, validations); // recursion
+ }
+ }
+ }
+ }
+ }
+ }
}
Added: bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/BootstrapConfigurationImpl.java
URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/BootstrapConfigurationImpl.java?rev=1498347&view=auto
==============================================================================
--- bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/BootstrapConfigurationImpl.java (added)
+++ bval/branches/bval-11/bval-jsr303/src/main/java/org/apache/bval/jsr303/BootstrapConfigurationImpl.java Mon Jul 1 10:06:18 2013
@@ -0,0 +1,93 @@
+/*
+ * 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 javax.validation.BootstrapConfiguration;
+import javax.validation.executable.ExecutableType;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+public class BootstrapConfigurationImpl implements BootstrapConfiguration {
+ private Map<String, String> properties;
+ private Set<ExecutableType> defaultValidatedExecutableTypes;
+ private boolean executableValidationEnabled;
+ private Set<String> constraintMappingResourcePaths;
+ private String parameterNameProviderClassName;
+ private String traversableResolverClassName;
+ private String messageInterpolatorClassName;
+ private String constraintValidatorFactoryClassName;
+ private String defaultProviderClassName;
+
+ public BootstrapConfigurationImpl(final String defaultProviderClassName,
+ final String constraintValidatorFactoryClassName,
+ final String messageInterpolatorClassName,
+ final String traversableResolverClassName,
+ final String parameterNameProviderClassName,
+ final Set<String> constraintMappingResourcePaths,
+ final boolean executableValidationEnabled,
+ final Set<ExecutableType> defaultValidatedExecutableTypes,
+ final Map<String, String> properties) {
+ this.properties = Collections.unmodifiableMap(properties);
+ this.defaultValidatedExecutableTypes = Collections.unmodifiableSet(defaultValidatedExecutableTypes);
+ this.executableValidationEnabled = executableValidationEnabled;
+ this.constraintMappingResourcePaths = Collections.unmodifiableSet(constraintMappingResourcePaths);
+ this.parameterNameProviderClassName = parameterNameProviderClassName;
+ this.traversableResolverClassName = traversableResolverClassName;
+ this.messageInterpolatorClassName = messageInterpolatorClassName;
+ this.constraintValidatorFactoryClassName = constraintValidatorFactoryClassName;
+ this.defaultProviderClassName = defaultProviderClassName;
+ }
+
+ public String getDefaultProviderClassName() {
+ return defaultProviderClassName;
+ }
+
+ public String getConstraintValidatorFactoryClassName() {
+ return constraintValidatorFactoryClassName;
+ }
+
+ public String getMessageInterpolatorClassName() {
+ return messageInterpolatorClassName;
+ }
+
+ public String getTraversableResolverClassName() {
+ return traversableResolverClassName;
+ }
+
+ public String getParameterNameProviderClassName() {
+ return parameterNameProviderClassName;
+ }
+
+ public Set<String> getConstraintMappingResourcePaths() {
+ return constraintMappingResourcePaths;
+ }
+
+ public boolean isExecutableValidationEnabled() {
+ return executableValidationEnabled;
+ }
+
+ public Set<ExecutableType> getDefaultValidatedExecutableTypes() {
+ return defaultValidatedExecutableTypes;
+ }
+
+ public Map<String, String> getProperties() {
+ return properties;
+ }
+}