You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bval.apache.org by mb...@apache.org on 2018/03/26 23:59:30 UTC
[1/2] bval git commit: ValueExtractors work for TCK and spec
compliance
Repository: bval
Updated Branches:
refs/heads/bv2 313512196 -> 7b915ca2a
ValueExtractors work for TCK and spec compliance
Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/b2b1b246
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/b2b1b246
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/b2b1b246
Branch: refs/heads/bv2
Commit: b2b1b246133b8cb6636fa273575794e42541b802
Parents: 3135121
Author: Matt Benson <mb...@apache.org>
Authored: Mon Mar 26 17:44:09 2018 -0500
Committer: Matt Benson <mb...@apache.org>
Committed: Mon Mar 26 17:44:09 2018 -0500
----------------------------------------------------------------------
.../apache/bval/jsr/ApacheValidatorFactory.java | 3 +-
.../org/apache/bval/jsr/ConfigurationImpl.java | 4 +-
.../jsr/valueextraction/ValueExtractors.java | 109 ++++++++++++-------
.../main/java/org/apache/bval/util/Lazy.java | 6 +
4 files changed, 78 insertions(+), 44 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/bval/blob/b2b1b246/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java
index 40849ac..5e5118f 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java
@@ -45,6 +45,7 @@ import org.apache.bval.jsr.metadata.MetadataBuilders;
import org.apache.bval.jsr.metadata.MetadataSource;
import org.apache.bval.jsr.util.AnnotationsManager;
import org.apache.bval.jsr.valueextraction.ValueExtractors;
+import org.apache.bval.jsr.valueextraction.ValueExtractors.OnDuplicateContainerElementKey;
import org.apache.bval.util.CloseableAble;
import org.apache.bval.util.reflection.Reflection;
import org.apache.commons.weaver.privilizer.Privilizing;
@@ -87,7 +88,7 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
}
private static ValueExtractors createBaseValueExtractors(ParticipantFactory participantFactory) {
- final ValueExtractors result = new ValueExtractors();
+ final ValueExtractors result = new ValueExtractors(OnDuplicateContainerElementKey.OVERWRITE);
participantFactory.loadServices(ValueExtractor.class).forEach(result::add);
return result;
}
http://git-wip-us.apache.org/repos/asf/bval/blob/b2b1b246/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java
index d726d4c..20ac2f8 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java
@@ -135,7 +135,7 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
private final LazyParticipant<ClockProvider> clockProvider = new LazyParticipant<>(this::getDefaultClockProvider);
- private final ValueExtractors bootstrapValueExtractors = new ValueExtractors();
+ private final ValueExtractors bootstrapValueExtractors = ValueExtractors.EMPTY.createChild();
private final ValueExtractors valueExtractors = bootstrapValueExtractors.createChild();
private final Lazy<BootstrapConfiguration> bootstrapConfiguration = new Lazy<>(this::createBootstrapConfiguration);
@@ -371,7 +371,7 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
@Override
public Set<ValueExtractor<?>> getValueExtractors() {
- return Collections.unmodifiableSet(new LinkedHashSet<>(valueExtractors.getLocalValueExtractors().values()));
+ return Collections.unmodifiableSet(new LinkedHashSet<>(valueExtractors.getValueExtractors().values()));
}
public void deferBootstrapOverrides() {
http://git-wip-us.apache.org/repos/asf/bval/blob/b2b1b246/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 00577e3..a99cd3d 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
@@ -26,7 +26,9 @@ import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
+import java.util.TreeMap;
import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -52,38 +54,39 @@ import org.apache.bval.util.reflection.TypeUtils;
* {@link ValueExtractor} collection of some level of a bean validation hierarchy.
*/
public class ValueExtractors {
- public static final ValueExtractors DEFAULT;
+ public enum OnDuplicateContainerElementKey {
+ EXCEPTION, OVERWRITE;
+ }
+
+ public static final ValueExtractors EMPTY =
+ new ValueExtractors(null, OnDuplicateContainerElementKey.EXCEPTION, Collections.emptyMap());
+ public static final ValueExtractors DEFAULT;
static {
- DEFAULT = new ValueExtractors(null) {
- {
- final Properties defaultExtractors = new Properties();
- try {
- defaultExtractors.load(ValueExtractors.class.getResourceAsStream("DefaultExtractors.properties"));
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- split(defaultExtractors.getProperty(ValueExtractor.class.getName())).map(cn -> {
- try {
- @SuppressWarnings("unchecked")
- final Class<? extends ValueExtractor<?>> result =
- (Class<? extends ValueExtractor<?>>) Reflection.toClass(cn)
- .asSubclass(ValueExtractor.class);
- return result;
- } catch (Exception e) {
- throw new IllegalStateException(e);
- }
- }).map(ValueExtractors::newInstance).forEach(super::add);
-
- split(defaultExtractors.getProperty(ValueExtractor.class.getName() + ".container"))
- .flatMap(ValueExtractors::loadValueExtractors).forEach(super::add);
+ final Properties defaultExtractors = new Properties();
+ try {
+ defaultExtractors.load(ValueExtractors.class.getResourceAsStream("DefaultExtractors.properties"));
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ final Map<ContainerElementKey, ValueExtractor<?>> m = new TreeMap<>();
+ final Consumer<ValueExtractor<?>> put = ve -> m.put(ContainerElementKey.forValueExtractor(ve), ve);
+
+ split(defaultExtractors.getProperty(ValueExtractor.class.getName())).map(cn -> {
+ try {
+ @SuppressWarnings("unchecked")
+ final Class<? extends ValueExtractor<?>> result =
+ (Class<? extends ValueExtractor<?>>) Reflection.toClass(cn).asSubclass(ValueExtractor.class);
+ return result;
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
}
+ }).map(ValueExtractors::newInstance).forEach(put);
- @Override
- public void add(ValueExtractor<?> extractor) {
- throw new UnsupportedOperationException();
- }
- };
+ split(defaultExtractors.getProperty(ValueExtractor.class.getName() + ".container"))
+ .flatMap(ValueExtractors::loadValueExtractors).forEach(put);
+
+ DEFAULT = new ValueExtractors(null, OnDuplicateContainerElementKey.EXCEPTION, Collections.unmodifiableMap(m));
}
public static Class<?> getExtractedType(ValueExtractor<?> extractor, Type target) {
@@ -152,32 +155,56 @@ public class ValueExtractors {
return c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1);
}
- private final Lazy<Map<ContainerElementKey, ValueExtractor<?>>> valueExtractors = new Lazy<>(HashMap::new);
private final ValueExtractors parent;
+ private final Lazy<Map<ContainerElementKey, ValueExtractor<?>>> valueExtractors = new Lazy<>(TreeMap::new);
+ private final OnDuplicateContainerElementKey onDuplicateContainerElementKey;
public ValueExtractors() {
- this(DEFAULT);
+ this(OnDuplicateContainerElementKey.EXCEPTION);
+ }
+
+ public ValueExtractors(OnDuplicateContainerElementKey onDuplicateContainerElementKey) {
+ this(DEFAULT, Validate.notNull(onDuplicateContainerElementKey));
}
- private ValueExtractors(ValueExtractors parent) {
+ private ValueExtractors(ValueExtractors parent, OnDuplicateContainerElementKey onDuplicateContainerElementKey) {
this.parent = parent;
+ this.onDuplicateContainerElementKey = onDuplicateContainerElementKey;
+ }
+
+ private ValueExtractors(ValueExtractors parent, OnDuplicateContainerElementKey onDuplicateContainerElementKey,
+ Map<ContainerElementKey, ValueExtractor<?>> backingMap) {
+ this(parent, onDuplicateContainerElementKey);
+ this.valueExtractors.reset(backingMap);
}
public ValueExtractors createChild() {
- return new ValueExtractors(this);
+ return createChild(OnDuplicateContainerElementKey.EXCEPTION);
}
- public void add(ValueExtractor<?> extractor) {
- Validate.notNull(extractor);
- valueExtractors.get().compute(ContainerElementKey.forValueExtractor(extractor), (k, v) -> {
- Exceptions.raiseIf(v != null, ValueExtractorDeclarationException::new,
- "Multiple context-level %ss specified for %s", f -> f.args(ValueExtractor.class.getSimpleName(), k));
- return extractor;
- });
+ public ValueExtractors createChild(OnDuplicateContainerElementKey onDuplicateContainerElementKey) {
+ return new ValueExtractors(this, onDuplicateContainerElementKey);
}
- public Map<ContainerElementKey, ValueExtractor<?>> getLocalValueExtractors() {
- return valueExtractors.optional().map(Collections::unmodifiableMap).orElseGet(Collections::emptyMap);
+ public void add(ValueExtractor<?> extractor) {
+ Validate.notNull(extractor);
+ final ContainerElementKey key = ContainerElementKey.forValueExtractor(extractor);
+ if (key == null) {
+ Exceptions.raise(IllegalStateException::new, "Computed null %s for %s",
+ ContainerElementKey.class.getSimpleName(), extractor);
+ }
+ final Map<ContainerElementKey, ValueExtractor<?>> m = valueExtractors.get();
+ if (onDuplicateContainerElementKey == OnDuplicateContainerElementKey.EXCEPTION) {
+ synchronized (this) {
+ if (m.containsKey(key)) {
+ Exceptions.raise(ValueExtractorDeclarationException::new,
+ "Multiple context-level %ss specified for %s", ValueExtractor.class.getSimpleName(), key);
+ }
+ m.put(key, extractor);
+ }
+ } else {
+ m.put(key, extractor);
+ }
}
public Map<ContainerElementKey, ValueExtractor<?>> getValueExtractors() {
http://git-wip-us.apache.org/repos/asf/bval/blob/b2b1b246/bval-jsr/src/main/java/org/apache/bval/util/Lazy.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/util/Lazy.java b/bval-jsr/src/main/java/org/apache/bval/util/Lazy.java
index 4796de3..fbc885b 100644
--- a/bval-jsr/src/main/java/org/apache/bval/util/Lazy.java
+++ b/bval-jsr/src/main/java/org/apache/bval/util/Lazy.java
@@ -39,6 +39,12 @@ public class Lazy<T> implements Supplier<T> {
return this;
}
+ public synchronized Lazy<T> reset(T value) {
+ this.value = value;
+ this.init = null;
+ return this;
+ }
+
@Override
public T get() {
if (init != null) {
[2/2] bval git commit: defer CV class selection until validation time
as is proper per specification; runtime type dependent
Posted by mb...@apache.org.
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 <mb...@apache.org>
Authored: Mon Mar 26 18:53:51 2018 -0500
Committer: Matt Benson <mb...@apache.org>
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) {