defer CV class selection until validation time as is proper per specification; runtime type dependent
Project: http://git-wip-us.apache.org/repos/asf/bval/repo Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/7b915ca2 Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/7b915ca2 Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/7b915ca2 Branch: refs/heads/bv2 Commit: 7b915ca2af3ecb8f2e9021d2fc6374f649f1d599 Parents: b2b1b24 Author: Matt Benson <[email protected]> Authored: Mon Mar 26 18:53:51 2018 -0500 Committer: Matt Benson <[email protected]> Committed: Mon Mar 26 18:53:51 2018 -0500 ---------------------------------------------------------------------- .../apache/bval/jsr/ApacheFactoryContext.java | 4 + .../ComputeConstraintValidatorClass.java | 207 ------------------ .../apache/bval/jsr/descriptor/ConstraintD.java | 52 ----- .../job/ComputeConstraintValidatorClass.java | 213 +++++++++++++++++++ .../apache/bval/jsr/job/ValidateParameters.java | 6 + .../org/apache/bval/jsr/job/ValidationJob.java | 38 +++- .../jsr/valueextraction/ValueExtractors.java | 1 - 7 files changed, 259 insertions(+), 262 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/bval/blob/7b915ca2/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java index c470e36..7093ad9 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java @@ -179,4 +179,8 @@ public class ApacheFactoryContext implements ValidatorContext { public GroupsComputer getGroupsComputer() { return groupsComputer.get(); } + + public ConstraintCached getConstraintsCache() { + return factory.getConstraintsCache(); + } } http://git-wip-us.apache.org/repos/asf/bval/blob/7b915ca2/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComputeConstraintValidatorClass.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComputeConstraintValidatorClass.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComputeConstraintValidatorClass.java deleted file mode 100644 index 820a1d8..0000000 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComputeConstraintValidatorClass.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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.jsr.descriptor; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Array; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.WildcardType; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import javax.validation.ConstraintDefinitionException; -import javax.validation.ConstraintValidator; -import javax.validation.UnexpectedTypeException; -import javax.validation.constraintvalidation.ValidationTarget; - -import org.apache.bval.jsr.ApacheValidatorFactory; -import org.apache.bval.jsr.ConstraintCached.ConstraintValidatorInfo; -import org.apache.bval.util.Exceptions; -import org.apache.bval.util.Validate; -import org.apache.bval.util.reflection.Reflection; -import org.apache.bval.util.reflection.Reflection.Interfaces; -import org.apache.bval.util.reflection.TypeUtils; -import org.apache.commons.weaver.privilizer.Privilizing; -import org.apache.commons.weaver.privilizer.Privilizing.CallTo; - -@Privilizing(@CallTo(Reflection.class)) -class ComputeConstraintValidatorClass<A extends Annotation> - implements Supplier<Class<? extends ConstraintValidator<A, ?>>> { - - private static class TypeWrapper { - final Class<?> componentType; - final int arrayDepth; - - TypeWrapper(Class<?> type) { - Class<?> c = type; - int d = 0; - while (Object[].class.isAssignableFrom(c)) { - d++; - c = c.getComponentType(); - } - this.componentType = c; - this.arrayDepth = d; - } - - Class<?> unwrapArrayComponentType(Class<?> t) { - Exceptions.raiseUnless(t.isAssignableFrom(componentType), IllegalArgumentException::new, - "%s not assignable from %s", t, componentType); - if (arrayDepth == 0) { - return t; - } - return Array.newInstance(t, new int[arrayDepth]).getClass(); - } - } - - private static final String CV = ConstraintValidator.class.getSimpleName(); - private static final WildcardType UNBOUNDED = TypeUtils.wildcardType().build(); - - private static Class<?> getValidatedType(Class<? extends ConstraintValidator<?, ?>> validatorType) { - final Type result = TypeUtils.getTypeArguments(validatorType, ConstraintValidator.class) - .get(ConstraintValidator.class.getTypeParameters()[1]); - if (!isSupported(result)) { - Exceptions.raise(ConstraintDefinitionException::new, "Validated type %s declared by %s %s is unsupported", - result, CV, validatorType.getName()); - } - return TypeUtils.getRawType(result, null); - } - - private static boolean isSupported(Type validatedType) { - if (validatedType instanceof Class<?>) { - return true; - } - if (validatedType instanceof ParameterizedType) { - return Stream.of(((ParameterizedType) validatedType).getActualTypeArguments()) - .allMatch(arg -> TypeUtils.equals(arg, UNBOUNDED)); - } - return false; - } - - private final ApacheValidatorFactory validatorFactory; - private final Class<?> validatedType; - private final ValidationTarget validationTarget; - private final A constraint; - private final boolean composed; - - ComputeConstraintValidatorClass(ApacheValidatorFactory validatorFactory, ValidationTarget validationTarget, - A constraint, Class<?> validatedType) { - super(); - this.validatorFactory = Validate.notNull(validatorFactory, "validatorFactory"); - this.validationTarget = Validate.notNull(validationTarget, "validationTarget"); - this.constraint = Validate.notNull(constraint, "constraint"); - this.validatedType = Validate.notNull(validatedType, "validatedType"); - this.composed = validatorFactory.getAnnotationsManager().isComposed(constraint); - } - - @Override - public Class<? extends ConstraintValidator<A, ?>> get() { - @SuppressWarnings("unchecked") - final Class<A> constraintType = (Class<A>) constraint.annotationType(); - return findValidator(validatorFactory.getConstraintsCache().getConstraintValidatorInfo(constraintType)); - } - - private Class<? extends ConstraintValidator<A, ?>> findValidator(Set<ConstraintValidatorInfo<A>> infos) { - switch (validationTarget) { - case PARAMETERS: - return findCrossParameterValidator(infos); - case ANNOTATED_ELEMENT: - return findAnnotatedElementValidator(infos); - default: - return null; - } - } - - private Class<? extends ConstraintValidator<A, ?>> findCrossParameterValidator( - Set<ConstraintValidatorInfo<A>> infos) { - - final Set<ConstraintValidatorInfo<A>> set = - infos.stream().filter(info -> info.getSupportedTargets().contains(ValidationTarget.PARAMETERS)) - .collect(Collectors.toSet()); - - @SuppressWarnings("unchecked") - final Class<A> constraintType = (Class<A>) constraint.annotationType(); - - final int size = set.size(); - Exceptions.raiseIf(size > 1 || !composed && set.isEmpty(), ConstraintDefinitionException::new, - "%d cross-parameter %ss found for constraint type %s", size, CV, constraintType); - - final Class<? extends ConstraintValidator<A, ?>> result = set.iterator().next().getType(); - if (!TypeUtils.isAssignable(Object[].class, getValidatedType(result))) { - Exceptions.raise(ConstraintDefinitionException::new, - "Cross-parameter %s %s does not support the validation of an object array", CV, result.getName()); - } - return result; - } - - private Class<? extends ConstraintValidator<A, ?>> findAnnotatedElementValidator( - Set<ConstraintValidatorInfo<A>> infos) { - - final Map<Class<?>, Class<? extends ConstraintValidator<?, ?>>> validators = infos.stream() - .filter(info -> info.getSupportedTargets().contains(ValidationTarget.ANNOTATED_ELEMENT)) - .map(ConstraintValidatorInfo::getType).collect( - Collectors.toMap(ComputeConstraintValidatorClass::getValidatedType, Function.identity(), (v1, v2) -> { - Exceptions.raiseUnless(Objects.equals(v1, v2), UnexpectedTypeException::new, - "Detected collision of constraint and target type between %s and %s", v1, v2); - return v1; - })); - - final Map<Type, Class<? extends ConstraintValidator<?, ?>>> candidates = new HashMap<>(); - - walkHierarchy().filter(validators::containsKey).forEach(type -> { - // if we haven't already found a candidate whose validated type - // is a subtype of the current evaluated type, save: - if (!candidates.keySet().stream().anyMatch(k -> TypeUtils.isAssignable(k, type))) { - candidates.put(type, validators.get(type)); - } - }); - final String cond; - switch (candidates.size()) { - case 1: - @SuppressWarnings("unchecked") - final Class<? extends ConstraintValidator<A, ?>> result = - (Class<? extends ConstraintValidator<A, ?>>) candidates.values().iterator().next(); - return result; - case 0: - if (composed) { - return null; - } - cond = "No compliant"; - break; - default: - cond = "> 1 maximally specific"; - break; - } - throw Exceptions.create(UnexpectedTypeException::new, "%s %s %s found for annotated element of type %s", cond, - constraint.annotationType().getName(), CV, TypeUtils.toString(validatedType)); - } - - // account for validated array types by unwrapping and rewrapping component - // type hierarchy: - private Stream<Class<?>> walkHierarchy() { - final TypeWrapper w = new TypeWrapper(Reflection.primitiveToWrapper(validatedType)); - Stream.Builder<Class<?>> hierarchy = Stream.builder(); - Reflection.hierarchy(w.componentType, Interfaces.INCLUDE).forEach(hierarchy); - return hierarchy.build().map(w::unwrapArrayComponentType); - } -} http://git-wip-us.apache.org/repos/asf/bval/blob/7b915ca2/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java index 16007c5..bd8c1f8 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java @@ -35,29 +35,23 @@ import javax.validation.ConstraintTarget; import javax.validation.ConstraintValidator; import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; -import javax.validation.UnexpectedTypeException; import javax.validation.ValidationException; import javax.validation.groups.Default; import javax.validation.metadata.ConstraintDescriptor; import javax.validation.metadata.Scope; import javax.validation.metadata.ValidateUnwrappedValue; -import javax.validation.valueextraction.UnwrapByDefault; import javax.validation.valueextraction.Unwrapping; import javax.validation.valueextraction.Unwrapping.Skip; import javax.validation.valueextraction.Unwrapping.Unwrap; -import javax.validation.valueextraction.ValueExtractor; import org.apache.bval.jsr.ApacheValidatorFactory; import org.apache.bval.jsr.ConstraintAnnotationAttributes; import org.apache.bval.jsr.ConstraintAnnotationAttributes.Worker; -import org.apache.bval.jsr.metadata.ContainerElementKey; import org.apache.bval.jsr.metadata.Meta; import org.apache.bval.jsr.util.AnnotationsManager; import org.apache.bval.jsr.util.ToUnmodifiable; -import org.apache.bval.jsr.valueextraction.ValueExtractors; import org.apache.bval.util.Exceptions; import org.apache.bval.util.Validate; -import org.apache.bval.util.reflection.TypeUtils; public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A> { private enum Optionality { @@ -77,8 +71,6 @@ public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A private final Meta<?> meta; private final Set<Class<? extends Payload>> payload; - private final Class<?> validatedType; - private final Set<Class<?>> groups; private final boolean reportAsSingle; private final ValidateUnwrappedValue valueUnwrapping; @@ -86,7 +78,6 @@ public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A private final Set<ConstraintDescriptor<?>> composingConstraints; private final List<Class<? extends ConstraintValidator<A, ?>>> constraintValidatorClasses; - private final Class<? extends ConstraintValidator<A, ?>> constraintValidatorClass; public ConstraintD(A annotation, Scope scope, Meta<?> meta, ApacheValidatorFactory validatorFactory) { this.annotation = Validate.notNull(annotation, "annotation"); @@ -94,8 +85,6 @@ public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A this.meta = Validate.notNull(meta, "meta"); payload = computePayload(); - validatedType = computeValidatedType(validatorFactory); - groups = computeGroups(); reportAsSingle = annotation.annotationType().isAnnotationPresent(ReportAsSingleViolation.class); valueUnwrapping = computeValidateUnwrappedValue(); @@ -104,8 +93,6 @@ public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A Validate.notNull(validatorFactory, "validatorFactory"); composingConstraints = computeComposingConstraints(validatorFactory); constraintValidatorClasses = computeConstraintValidatorClasses(validatorFactory); - constraintValidatorClass = new ComputeConstraintValidatorClass<>(validatorFactory, meta.getValidationTarget(), - annotation, validatedType).get(); } @Override @@ -179,14 +166,6 @@ public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A return meta.getElementType(); } - public Class<?> getValidatedType() { - return validatedType; - } - - public Class<? extends ConstraintValidator<A, ?>> getConstraintValidatorClass() { - return constraintValidatorClass; - } - private <T> T read(ConstraintAnnotationAttributes attr) { return read(attr, Optionality.OPTIONAL); } @@ -245,35 +224,4 @@ public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A } return result; } - - private Class<?> computeValidatedType(ApacheValidatorFactory validatorFactory) { - final Class<?> rawType = TypeUtils.getRawType(meta.getType(), null); - - if (rawType == null) { - Exceptions.raise(UnexpectedTypeException::new, "Could not calculate validated type from %s", - meta.getType()); - } - if (payload.contains(Unwrapping.Skip.class)) { - return rawType; - } - final ValueExtractor<?> valueExtractor = - validatorFactory.getValueExtractors().find(new ContainerElementKey(meta.getAnnotatedType(), null)); - - final boolean unwrap = payload.contains(Unwrapping.Unwrap.class); - - if (valueExtractor == null) { - if (unwrap) { - Exceptions.raise(ConstraintDeclarationException::new, "No compatible %s found for %s", - ValueExtractor.class.getSimpleName(), meta.getType()); - } - } else { - @SuppressWarnings("unchecked") - final Class<? extends ValueExtractor<?>> extractorClass = - (Class<? extends ValueExtractor<?>>) valueExtractor.getClass(); - if (unwrap || extractorClass.isAnnotationPresent(UnwrapByDefault.class)) { - return ValueExtractors.getExtractedType(valueExtractor, meta.getType()); - } - } - return rawType; - } } http://git-wip-us.apache.org/repos/asf/bval/blob/7b915ca2/bval-jsr/src/main/java/org/apache/bval/jsr/job/ComputeConstraintValidatorClass.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ComputeConstraintValidatorClass.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ComputeConstraintValidatorClass.java new file mode 100644 index 0000000..d9c0b56 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ComputeConstraintValidatorClass.java @@ -0,0 +1,213 @@ +/* + * 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.jsr.job; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Array; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.validation.ConstraintDefinitionException; +import javax.validation.ConstraintValidator; +import javax.validation.UnexpectedTypeException; +import javax.validation.constraintvalidation.ValidationTarget; + +import org.apache.bval.jsr.ConstraintCached; +import org.apache.bval.jsr.ConstraintCached.ConstraintValidatorInfo; +import org.apache.bval.jsr.descriptor.ConstraintD; +import org.apache.bval.util.Exceptions; +import org.apache.bval.util.Validate; +import org.apache.bval.util.reflection.Reflection; +import org.apache.bval.util.reflection.Reflection.Interfaces; +import org.apache.bval.util.reflection.TypeUtils; +import org.apache.commons.weaver.privilizer.Privilizing; +import org.apache.commons.weaver.privilizer.Privilizing.CallTo; + +@Privilizing(@CallTo(Reflection.class)) +class ComputeConstraintValidatorClass<A extends Annotation> + implements Supplier<Class<? extends ConstraintValidator<A, ?>>> { + + private static class TypeWrapper { + final Class<?> componentType; + final int arrayDepth; + + TypeWrapper(Class<?> type) { + Class<?> c = type; + int d = 0; + while (Object[].class.isAssignableFrom(c)) { + d++; + c = c.getComponentType(); + } + this.componentType = c; + this.arrayDepth = d; + } + + Class<?> unwrapArrayComponentType(Class<?> t) { + Exceptions.raiseUnless(t.isAssignableFrom(componentType), IllegalArgumentException::new, + "%s not assignable from %s", t, componentType); + if (arrayDepth == 0) { + return t; + } + return Array.newInstance(t, new int[arrayDepth]).getClass(); + } + } + + private static final String CV = ConstraintValidator.class.getSimpleName(); + private static final WildcardType UNBOUNDED = TypeUtils.wildcardType().build(); + + private static Class<?> getValidatedType(Class<? extends ConstraintValidator<?, ?>> validatorType) { + final Type result = TypeUtils.getTypeArguments(validatorType, ConstraintValidator.class) + .get(ConstraintValidator.class.getTypeParameters()[1]); + if (!isSupported(result)) { + Exceptions.raise(ConstraintDefinitionException::new, "Validated type %s declared by %s %s is unsupported", + result, CV, validatorType.getName()); + } + return TypeUtils.getRawType(result, null); + } + + private static boolean isSupported(Type validatedType) { + if (validatedType instanceof Class<?>) { + return true; + } + if (validatedType instanceof ParameterizedType) { + return Stream.of(((ParameterizedType) validatedType).getActualTypeArguments()) + .allMatch(arg -> TypeUtils.equals(arg, UNBOUNDED)); + } + return false; + } + + private final ConstraintCached constraintsCache; + private final ConstraintD<?> descriptor; + private final ValidationTarget validationTarget; + private final Class<?> validatedType; + + ComputeConstraintValidatorClass(ConstraintCached constraintsCache, ConstraintD<A> descriptor, + ValidationTarget validationTarget, Class<?> validatedType) { + super(); + this.constraintsCache = Validate.notNull(constraintsCache, "constraintsCache"); + this.descriptor = Validate.notNull(descriptor, "descriptor"); + this.validationTarget = Validate.notNull(validationTarget, "validationTarget"); + this.validatedType = Validate.notNull(validatedType, "validatedType"); + } + + @Override + public Class<? extends ConstraintValidator<A, ?>> get() { + @SuppressWarnings("unchecked") + final Class<A> constraintType = (Class<A>) descriptor.getAnnotation().annotationType(); + return findValidator(constraintsCache.getConstraintValidatorInfo(constraintType)); + } + + private Class<? extends ConstraintValidator<A, ?>> findValidator(Set<ConstraintValidatorInfo<A>> infos) { + switch (validationTarget) { + case PARAMETERS: + return findCrossParameterValidator(infos); + case ANNOTATED_ELEMENT: + return findAnnotatedElementValidator(infos); + default: + return null; + } + } + + private Class<? extends ConstraintValidator<A, ?>> findCrossParameterValidator( + Set<ConstraintValidatorInfo<A>> infos) { + + final Set<ConstraintValidatorInfo<A>> set = + infos.stream().filter(info -> info.getSupportedTargets().contains(ValidationTarget.PARAMETERS)) + .collect(Collectors.toSet()); + + @SuppressWarnings("unchecked") + final Class<A> constraintType = (Class<A>) descriptor.getAnnotation().annotationType(); + + final int size = set.size(); + Exceptions.raiseIf(size > 1 || !isComposed() && set.isEmpty(), ConstraintDefinitionException::new, + "%d cross-parameter %ss found for constraint type %s", size, CV, constraintType); + + final Class<? extends ConstraintValidator<A, ?>> result = set.iterator().next().getType(); + if (!TypeUtils.isAssignable(Object[].class, getValidatedType(result))) { + Exceptions.raise(ConstraintDefinitionException::new, + "Cross-parameter %s %s does not support the validation of an object array", CV, result.getName()); + } + return result; + } + + private Class<? extends ConstraintValidator<A, ?>> findAnnotatedElementValidator( + Set<ConstraintValidatorInfo<A>> infos) { + + final Map<Class<?>, Class<? extends ConstraintValidator<?, ?>>> validators = infos.stream() + .filter(info -> info.getSupportedTargets().contains(ValidationTarget.ANNOTATED_ELEMENT)) + .map(ConstraintValidatorInfo::getType).collect( + Collectors.toMap(ComputeConstraintValidatorClass::getValidatedType, Function.identity(), (v1, v2) -> { + Exceptions.raiseUnless(Objects.equals(v1, v2), UnexpectedTypeException::new, + "Detected collision of constraint and target type between %s and %s", v1, v2); + return v1; + })); + + final Map<Type, Class<? extends ConstraintValidator<?, ?>>> candidates = new HashMap<>(); + + walkHierarchy().filter(validators::containsKey).forEach(type -> { + // if we haven't already found a candidate whose validated type + // is a subtype of the current evaluated type, save: + if (!candidates.keySet().stream().anyMatch(k -> TypeUtils.isAssignable(k, type))) { + candidates.put(type, validators.get(type)); + } + }); + final String cond; + switch (candidates.size()) { + case 1: + @SuppressWarnings("unchecked") + final Class<? extends ConstraintValidator<A, ?>> result = + (Class<? extends ConstraintValidator<A, ?>>) candidates.values().iterator().next(); + return result; + case 0: + if (isComposed()) { + return null; + } + cond = "No compliant"; + break; + default: + cond = "> 1 maximally specific"; + break; + } + throw Exceptions.create(UnexpectedTypeException::new, "%s %s %s found for annotated element of type %s", cond, + descriptor.getAnnotation().annotationType().getName(), CV, TypeUtils.toString(validatedType)); + } + + // account for validated array types by unwrapping and rewrapping component + // type hierarchy: + private Stream<Class<?>> walkHierarchy() { + final TypeWrapper w = new TypeWrapper(Reflection.primitiveToWrapper(validatedType)); + Stream.Builder<Class<?>> hierarchy = Stream.builder(); + Reflection.hierarchy(w.componentType, Interfaces.INCLUDE).forEach(hierarchy); + if (validatedType.isInterface()) { + hierarchy.accept(Object.class); + } + return hierarchy.build().map(w::unwrapArrayComponentType); + } + + private boolean isComposed() { + return !descriptor.getComposingConstraints().isEmpty(); + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/7b915ca2/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java index d4f9668..0e2b30c 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java @@ -29,6 +29,7 @@ import java.util.stream.IntStream; import javax.validation.ConstraintViolation; import javax.validation.ParameterNameProvider; import javax.validation.Path; +import javax.validation.constraintvalidation.ValidationTarget; import javax.validation.metadata.ExecutableDescriptor; import org.apache.bval.jsr.ApacheFactoryContext; @@ -120,6 +121,11 @@ public abstract class ValidateParameters<E extends Executable, T> extends Valida } @Override + protected ValidationTarget getValidationTarget() { + return ValidationTarget.PARAMETERS; + } + + @Override void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink) { executableDescriptor.getParameterDescriptors().stream() .map(pd -> new SproutFrame<ParameterD<?>>(this, (ParameterD<?>) pd, parameter(pd.getIndex()))) http://git-wip-us.apache.org/repos/asf/bval/blob/7b915ca2/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java index 0501a8a..d2be23a 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java @@ -48,12 +48,14 @@ import javax.validation.Path; import javax.validation.TraversableResolver; import javax.validation.UnexpectedTypeException; import javax.validation.ValidationException; +import javax.validation.constraintvalidation.ValidationTarget; import javax.validation.groups.Default; import javax.validation.metadata.CascadableDescriptor; import javax.validation.metadata.ContainerDescriptor; import javax.validation.metadata.ElementDescriptor.ConstraintFinder; import javax.validation.metadata.PropertyDescriptor; import javax.validation.metadata.ValidateUnwrappedValue; +import javax.validation.valueextraction.UnwrapByDefault; import javax.validation.valueextraction.ValueExtractor; import org.apache.bval.jsr.ApacheFactoryContext; @@ -93,6 +95,10 @@ public abstract class ValidationJob<T> { this.context = Validate.notNull(context, "context"); } + protected ValidationTarget getValidationTarget() { + return ValidationTarget.ANNOTATED_ELEMENT; + } + final ValidationJob<T> getJob() { return ValidationJob.this; } @@ -230,7 +236,8 @@ public abstract class ValidationJob<T> { @SuppressWarnings({ "rawtypes" }) private ConstraintValidator getConstraintValidator(ConstraintD<?> constraint) { final Class<? extends ConstraintValidator> constraintValidatorClass = - constraint.getConstraintValidatorClass(); + new ComputeConstraintValidatorClass<>(validatorContext.getConstraintsCache(), constraint, + getValidationTarget(), computeValidatedType(constraint)).get(); if (constraintValidatorClass == null) { if (constraint.getComposingConstraints().isEmpty()) { @@ -254,7 +261,34 @@ public abstract class ValidationJob<T> { return constraintValidator; } - protected Stream<Class<?>> expand(Class<?> group) { + private Class<?> computeValidatedType(ConstraintD<?> constraint) { + final Class<?> elementClass = descriptor.getElementClass(); + + if (constraint.getValueUnwrapping() == ValidateUnwrappedValue.SKIP) { + return elementClass; + } + final ValueExtractor<?> valueExtractor = + validatorContext.getValueExtractors().find(new ContainerElementKey(elementClass, null)); + + final boolean unwrap = constraint.getValueUnwrapping() == ValidateUnwrappedValue.UNWRAP; + + if (valueExtractor == null) { + if (unwrap) { + Exceptions.raise(ConstraintDeclarationException::new, "No compatible %s found for %s", + ValueExtractor.class.getSimpleName(), elementClass); + } + } else { + @SuppressWarnings("unchecked") + final Class<? extends ValueExtractor<?>> extractorClass = + (Class<? extends ValueExtractor<?>>) valueExtractor.getClass(); + if (unwrap || extractorClass.isAnnotationPresent(UnwrapByDefault.class)) { + return ValueExtractors.getExtractedType(valueExtractor, elementClass); + } + } + return elementClass; + } + + private Stream<Class<?>> expand(Class<?> group) { if (Default.class.equals(group)) { final List<Class<?>> groupSequence = descriptor.getGroupSequence(); if (groupSequence != null) { http://git-wip-us.apache.org/repos/asf/bval/blob/7b915ca2/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java index a99cd3d..ca45701 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java @@ -90,7 +90,6 @@ public class ValueExtractors { } public static Class<?> getExtractedType(ValueExtractor<?> extractor, Type target) { - final ContainerElementKey key = ContainerElementKey.forValueExtractor(extractor); Type result = key.getAnnotatedType().getType(); if (result instanceof WildcardType && key.getTypeArgumentIndex() != null) {
