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/04/18 21:37:43 UTC
bval git commit: XML config-related work for more TCK progress
Repository: bval
Updated Branches:
refs/heads/bv2 85ceb9fde -> cb0bb133f
XML config-related work for more TCK progress
Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/cb0bb133
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/cb0bb133
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/cb0bb133
Branch: refs/heads/bv2
Commit: cb0bb133f607c8e489aac6b15e99ff708284230c
Parents: 85ceb9f
Author: Matt Benson <mb...@apache.org>
Authored: Wed Apr 18 16:37:30 2018 -0500
Committer: Matt Benson <mb...@apache.org>
Committed: Wed Apr 18 16:37:30 2018 -0500
----------------------------------------------------------------------
.../apache/bval/jsr/ApacheValidatorFactory.java | 2 -
.../bval/jsr/descriptor/DescriptorManager.java | 3 +-
.../bval/jsr/descriptor/MetadataReader.java | 8 +-
.../apache/bval/jsr/descriptor/ParameterD.java | 32 ++-
.../bval/jsr/job/ValidateReturnValue.java | 17 +-
.../bval/jsr/metadata/CompositeBuilder.java | 12 +-
.../apache/bval/jsr/metadata/DualBuilder.java | 130 +++++++++-
.../java/org/apache/bval/jsr/metadata/Meta.java | 19 +-
.../org/apache/bval/jsr/metadata/Signature.java | 5 +
.../apache/bval/jsr/metadata/XmlBuilder.java | 97 ++++----
.../apache/bval/jsr/xml/MappingValidator.java | 244 +++++++++++++++++++
.../bval/jsr/xml/ValidationMappingParser.java | 20 +-
12 files changed, 512 insertions(+), 77 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/bval/blob/cb0bb133/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 2b67c30..481b501 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
@@ -369,7 +369,5 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
};
participantFactory.loadServices(MetadataSource.class)
.forEach(ms -> ms.process(configuration, getConstraintsCache()::add, addBuilder));
-
- getMetadataBuilders().getCustomizedTypes().forEach(getDescriptorManager()::getBeanDescriptor);
}
}
http://git-wip-us.apache.org/repos/asf/bval/blob/cb0bb133/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java
index 9495f7a..7c84f87 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java
@@ -90,7 +90,8 @@ public class DescriptorManager {
final MetadataBuilder.ForBean<T> customBuilder =
new HierarchyBuilder(validatorFactory, this::customBuilder).forBean(beanClass);
- return customBuilder.isEmpty() ? primaryBuilder : DualBuilder.forBean(primaryBuilder, customBuilder);
+ return customBuilder.isEmpty() ? primaryBuilder
+ : DualBuilder.forBean(beanClass, primaryBuilder, customBuilder, validatorFactory);
}
private <T> MetadataBuilder.ForBean<T> customBuilder(Class<T> beanClass) {
http://git-wip-us.apache.org/repos/asf/bval/blob/cb0bb133/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
index ec4fc66..0828933 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
@@ -19,7 +19,6 @@
package org.apache.bval.jsr.descriptor;
import java.lang.annotation.Annotation;
-import java.lang.annotation.ElementType;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
@@ -75,7 +74,10 @@ import org.apache.bval.util.ObjectUtils;
import org.apache.bval.util.Validate;
import org.apache.bval.util.reflection.Reflection;
import org.apache.bval.util.reflection.Reflection.Interfaces;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+@Privilizing(@CallTo(Reflection.class))
class MetadataReader {
class ForElement<E extends AnnotatedElement, B extends MetadataBuilder.ForElement<E>> {
@@ -294,10 +296,6 @@ class MetadataReader {
final Set<GroupConversion> groupConversions = builder.getGroupConversions(meta);
if (!groupConversions.isEmpty()) {
if (!isCascaded()) {
- // ignore group conversions without cascade on property getters:
- if (meta.getElementType() == ElementType.METHOD && Methods.isGetter((Method) meta.getHost())) {
- return Collections.emptySet();
- }
Exceptions.raise(ConstraintDeclarationException::new, "@%s declared without @%s on %s",
ConvertGroup.class.getSimpleName(), Valid.class.getSimpleName(), meta.describeHost());
}
http://git-wip-us.apache.org/repos/asf/bval/blob/cb0bb133/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ParameterD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ParameterD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ParameterD.java
index 52e3040..9bb4276 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ParameterD.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ParameterD.java
@@ -18,7 +18,11 @@
*/
package org.apache.bval.jsr.descriptor;
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Parameter;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import javax.validation.metadata.ParameterDescriptor;
@@ -42,7 +46,7 @@ public class ParameterD<P extends ExecutableD<?, ?, P>> extends CascadableContai
this.index = index;
name = reader.meta.getName();
- type = TypeUtils.getRawType(reader.meta.getType(), parent.getElementClass());
+ type = resolveType();
}
@Override
@@ -59,4 +63,30 @@ public class ParameterD<P extends ExecutableD<?, ?, P>> extends CascadableContai
public String getName() {
return name;
}
+
+ private Class<?> resolveType() {
+ final Class<?> declaringClass = getTarget().getDeclaringExecutable().getDeclaringClass();
+
+ Type t = getTarget().getParameterizedType();
+
+ int arrayDepth = 0;
+ while (t instanceof GenericArrayType) {
+ arrayDepth++;
+ t = ((GenericArrayType) t).getGenericComponentType();
+ }
+
+ Class<?> result = null;
+
+ while (result == null) {
+ result = TypeUtils.getRawType(t, declaringClass);
+ if (result != null) {
+ break;
+ }
+ if (t instanceof TypeVariable<?>) {
+ final TypeVariable<?> tv = (TypeVariable<?>) t;
+ t = tv.getBounds()[0];
+ }
+ }
+ return arrayDepth > 0 ? Array.newInstance(result, new int[arrayDepth]).getClass() : result;
+ }
}
http://git-wip-us.apache.org/repos/asf/bval/blob/cb0bb133/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateReturnValue.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateReturnValue.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateReturnValue.java
index 774566a..2cf92e3 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateReturnValue.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateReturnValue.java
@@ -21,11 +21,12 @@ import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
+import javax.validation.metadata.ExecutableDescriptor;
+
import org.apache.bval.jsr.ApacheFactoryContext;
import org.apache.bval.jsr.ConstraintViolationImpl;
import org.apache.bval.jsr.GraphContext;
import org.apache.bval.jsr.descriptor.ConstraintD;
-import org.apache.bval.jsr.descriptor.ExecutableD;
import org.apache.bval.jsr.descriptor.ReturnValueD;
import org.apache.bval.jsr.metadata.Meta;
import org.apache.bval.jsr.util.NodeImpl;
@@ -57,11 +58,9 @@ public abstract class ValidateReturnValue<E extends Executable, T> extends Valid
return (Class<T>) object.getClass();
}
- @SuppressWarnings("unchecked")
@Override
- protected ExecutableD<Method, ?, ?> describe() {
- return (ExecutableD<Method, ?, ?>) validatorContext.getDescriptorManager()
- .getBeanDescriptor(object.getClass())
+ protected ExecutableDescriptor describe() {
+ return validatorContext.getDescriptorManager().getBeanDescriptor(object.getClass())
.getConstraintsForMethod(executable.getName(), executable.getParameterTypes());
}
@@ -96,11 +95,9 @@ public abstract class ValidateReturnValue<E extends Executable, T> extends Valid
return (Class<T>) executable.getDeclaringClass();
}
- @SuppressWarnings("unchecked")
@Override
- protected ExecutableD<Constructor<T>, ?, ?> describe() {
- return (ExecutableD<Constructor<T>, ?, ?>) validatorContext.getDescriptorManager()
- .getBeanDescriptor(executable.getDeclaringClass())
+ protected ExecutableDescriptor describe() {
+ return validatorContext.getDescriptorManager().getBeanDescriptor(executable.getDeclaringClass())
.getConstraintsForConstructor(executable.getParameterTypes());
}
@@ -147,7 +144,7 @@ public abstract class ValidateReturnValue<E extends Executable, T> extends Valid
return describe() != null;
}
- protected abstract ExecutableD<?, ?, ?> describe();
+ protected abstract ExecutableDescriptor describe();
protected abstract T getRootBean();
http://git-wip-us.apache.org/repos/asf/bval/blob/cb0bb133/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
index 60419fb..b02f149 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
@@ -237,7 +237,7 @@ public class CompositeBuilder {
private final AnnotationBehaviorMergeStrategy annotationBehaviorStrategy;
protected final ApacheValidatorFactory validatorFactory;
- CompositeBuilder(ApacheValidatorFactory validatorFactory,
+ protected CompositeBuilder(ApacheValidatorFactory validatorFactory,
AnnotationBehaviorMergeStrategy annotationBehaviorMergeStrategy) {
super();
this.annotationBehaviorStrategy =
@@ -246,7 +246,15 @@ public class CompositeBuilder {
}
public <T> Collector<MetadataBuilder.ForBean<T>, ?, MetadataBuilder.ForBean<T>> compose() {
- return Collectors.collectingAndThen(Collectors.toList(), CompositeBuilder.ForBean::new);
+ return Collectors.collectingAndThen(Collectors.toList(),
+ delegates -> delegates.isEmpty() ? EmptyBuilder.instance().forBean()
+ : delegates.size() == 1 ? delegates.get(0) : new CompositeBuilder.ForBean<>(delegates));
+ }
+
+ public <E extends AnnotatedElement> Collector<MetadataBuilder.ForContainer<E>, ?, MetadataBuilder.ForContainer<E>> composeContainer() {
+ return Collectors.collectingAndThen(Collectors.toList(),
+ delegates -> delegates.isEmpty() ? EmptyBuilder.instance().forContainer()
+ : delegates.size() == 1 ? delegates.get(0) : new CompositeBuilder.ForContainer<>(delegates));
}
protected final <E extends Executable> List<Meta<Parameter>> getMetaParameters(Meta<E> meta,
http://git-wip-us.apache.org/repos/asf/bval/blob/cb0bb133/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualBuilder.java
index 3c6b07b..7ca5ebd 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualBuilder.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualBuilder.java
@@ -28,7 +28,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
+import java.util.TreeMap;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -36,7 +38,9 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
+import org.apache.bval.jsr.ApacheValidatorFactory;
import org.apache.bval.jsr.groups.GroupConversion;
+import org.apache.bval.jsr.util.Methods;
import org.apache.bval.jsr.util.ToUnmodifiable;
import org.apache.bval.util.Validate;
@@ -236,8 +240,128 @@ public class DualBuilder {
}
}
- public static <T> MetadataBuilder.ForBean<T> forBean(MetadataBuilder.ForBean<T> primaryDelegate,
- MetadataBuilder.ForBean<T> customDelegate) {
- return new DualBuilder.ForBean<>(primaryDelegate, customDelegate);
+ private static class CustomWrapper {
+ private static class ForBean<T> implements MetadataBuilder.ForBean<T> {
+
+ private final MetadataBuilder.ForBean<T> wrapped;
+ private final Map<String, MetadataBuilder.ForContainer<Method>> getters;
+ private final Map<Signature, MetadataBuilder.ForExecutable<Method>> methods;
+
+ ForBean(MetadataBuilder.ForBean<T> wrapped, Map<String, MetadataBuilder.ForContainer<Method>> getters,
+ Map<Signature, MetadataBuilder.ForExecutable<Method>> methods) {
+ super();
+ this.wrapped = Validate.notNull(wrapped, "wrapped");
+ this.getters = Validate.notNull(getters, "getters");
+ this.methods = Validate.notNull(methods, "methods");
+ }
+
+ @Override
+ public AnnotationBehavior getAnnotationBehavior() {
+ return wrapped.getAnnotationBehavior();
+ }
+
+ @Override
+ public MetadataBuilder.ForClass<T> getClass(Meta<Class<T>> meta) {
+ return wrapped.getClass(meta);
+ }
+
+ @Override
+ public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Meta<Class<T>> meta) {
+ return wrapped.getFields(meta);
+ }
+
+ @Override
+ public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Meta<Class<T>> meta) {
+ return getters;
+ }
+
+ @Override
+ public Map<Signature, MetadataBuilder.ForExecutable<Constructor<? extends T>>> getConstructors(
+ Meta<Class<T>> meta) {
+ return wrapped.getConstructors(meta);
+ }
+
+ @Override
+ public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Meta<Class<T>> meta) {
+ return methods;
+ }
+ }
+
+ private static class ForGetterMethod implements MetadataBuilder.ForExecutable<Method> {
+ private final MetadataBuilder.ForContainer<Method> returnValue;
+
+ private ForGetterMethod(MetadataBuilder.ForContainer<Method> returnValue) {
+ super();
+ this.returnValue = Validate.notNull(returnValue, "returnValue");
+ }
+
+ @Override
+ public AnnotationBehavior getAnnotationBehavior() {
+ return returnValue.getAnnotationBehavior();
+ }
+
+ @Override
+ public MetadataBuilder.ForContainer<Method> getReturnValue(Meta<Method> meta) {
+ return returnValue;
+ }
+
+ @Override
+ public MetadataBuilder.ForElement<Method> getCrossParameter(Meta<Method> meta) {
+ return EmptyBuilder.instance().forElement();
+ }
+
+ @Override
+ public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Meta<Method> meta) {
+ return Collections.emptyList();
+ }
+ }
+ }
+
+ public static <T> MetadataBuilder.ForBean<T> forBean(Class<T> beanClass, MetadataBuilder.ForBean<T> primaryDelegate,
+ MetadataBuilder.ForBean<T> customDelegate, ApacheValidatorFactory validatorFactory) {
+ return new DualBuilder.ForBean<>(primaryDelegate, wrapCustom(customDelegate, beanClass, validatorFactory));
+ }
+
+ private static <T> MetadataBuilder.ForBean<T> wrapCustom(MetadataBuilder.ForBean<T> customDelegate,
+ Class<T> beanClass, ApacheValidatorFactory validatorFactory) {
+ final Meta.ForClass<T> meta = new Meta.ForClass<>(beanClass);
+
+ final Map<String, MetadataBuilder.ForContainer<Method>> getters = customDelegate.getGetters(meta);
+ final Map<Signature, MetadataBuilder.ForExecutable<Method>> methods = customDelegate.getMethods(meta);
+
+ if (getters.isEmpty() && methods.keySet().stream().noneMatch(Signature::isGetter)) {
+ // nothing to merge
+ return customDelegate;
+ }
+ final CompositeBuilder composite =
+ CompositeBuilder.with(validatorFactory, AnnotationBehaviorMergeStrategy.consensus());
+
+ final Map<String, MetadataBuilder.ForContainer<Method>> mergedGetters = new TreeMap<>(getters);
+
+ methods.forEach((k, v) -> {
+ if (k.isGetter()) {
+ mergedGetters.compute(Methods.propertyName(k.getName()), (p, d) -> {
+ final Method getter = Methods.getter(beanClass, p);
+ return Stream.of(d, v.getReturnValue(new Meta.ForMethod(getter))).filter(Objects::nonNull)
+ .collect(composite.composeContainer());
+ });
+ }
+ });
+ final Map<Signature, MetadataBuilder.ForExecutable<Method>> mergedMethods = new TreeMap<>(methods);
+
+ getters.forEach((k, v) -> {
+ final Method getter = Methods.getter(beanClass, k);
+ final Signature signature = Signature.of(getter);
+
+ final MetadataBuilder.ForContainer<Method> rv;
+ if (methods.containsKey(signature)) {
+ rv = Stream.of(methods.get(signature).getReturnValue(new Meta.ForMethod(getter)), v)
+ .collect(composite.composeContainer());
+ } else {
+ rv = v;
+ }
+ mergedMethods.put(signature, new CustomWrapper.ForGetterMethod(rv));
+ });
+ return new CustomWrapper.ForBean<>(customDelegate, mergedGetters, mergedMethods);
}
}
http://git-wip-us.apache.org/repos/asf/bval/blob/cb0bb133/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Meta.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Meta.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Meta.java
index 48b6d96..dcdf0a6 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Meta.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Meta.java
@@ -27,12 +27,16 @@ import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
+import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Map;
import java.util.Objects;
import org.apache.bval.util.EmulatedAnnotatedType;
import org.apache.bval.util.Lazy;
import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.TypeUtils;
/**
* Validation class model.
@@ -214,7 +218,7 @@ public abstract class Meta<E extends AnnotatedElement> {
@Override
public Type getType() {
- return getHost().getType();
+ return getHost().getParameterizedType();
}
@Override
@@ -261,7 +265,18 @@ public abstract class Meta<E extends AnnotatedElement> {
@Override
public Type getType() {
- return getHost().getType();
+ Type result = getHost().getType();
+ if (result instanceof TypeVariable<?>) {
+ final Type parentType = parent.getType();
+ if (parentType instanceof ParameterizedType) {
+ final Map<TypeVariable<?>, Type> typeArguments =
+ TypeUtils.getTypeArguments((ParameterizedType) parentType);
+ if (typeArguments.containsKey(result)) {
+ return typeArguments.get(result);
+ }
+ }
+ }
+ return result;
}
@Override
http://git-wip-us.apache.org/repos/asf/bval/blob/cb0bb133/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Signature.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Signature.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Signature.java
index 2f28a3e..e8e534c 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Signature.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Signature.java
@@ -24,6 +24,7 @@ import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.apache.bval.jsr.util.Methods;
import org.apache.bval.util.Comparators;
import org.apache.bval.util.Lazy;
import org.apache.bval.util.LazyInt;
@@ -83,4 +84,8 @@ public final class Signature implements Comparable<Signature> {
public int compareTo(Signature sig) {
return COMPARATOR.compare(this, sig);
}
+
+ public boolean isGetter() {
+ return parameterTypes.length == 0 && Methods.isGetter(name);
+ }
}
http://git-wip-us.apache.org/repos/asf/bval/blob/cb0bb133/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java
index 5713402..b96f194 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java
@@ -27,16 +27,13 @@ import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
-import java.math.BigDecimal;
import java.util.Collections;
-import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
-import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -66,6 +63,7 @@ import org.apache.bval.jsr.xml.GetterType;
import org.apache.bval.jsr.xml.GroupConversionType;
import org.apache.bval.jsr.xml.GroupSequenceType;
import org.apache.bval.jsr.xml.GroupsType;
+import org.apache.bval.jsr.xml.MappingValidator;
import org.apache.bval.jsr.xml.MethodType;
import org.apache.bval.jsr.xml.ParameterType;
import org.apache.bval.jsr.xml.PayloadType;
@@ -73,6 +71,7 @@ import org.apache.bval.jsr.xml.ReturnValueType;
import org.apache.bval.util.Exceptions;
import org.apache.bval.util.Lazy;
import org.apache.bval.util.ObjectUtils;
+import org.apache.bval.util.StringUtils;
import org.apache.bval.util.Validate;
import org.apache.bval.util.reflection.Reflection;
import org.apache.commons.weaver.privilizer.Privilizing;
@@ -83,20 +82,33 @@ public class XmlBuilder {
//@formatter:off
public enum Version {
v10("1.0"), v11("1.1"), v20("2.0");
+ //@formatter:on
+
+ static Version of(ConstraintMappingsType constraintMappings) {
+ Validate.notNull(constraintMappings);
+ String version = constraintMappings.getVersion();
+ if (StringUtils.isBlank(version)) {
+ return v10;
+ }
+ version = version.trim();
+ for (Version candidate : values()) {
+ if (candidate.id.equals(version)) {
+ return candidate;
+ }
+ }
+ throw new ValidationException("Unknown schema version: " + version);
+ }
- final BigDecimal number;
private final String id;
private Version(String number) {
this.id = number;
- this.number = new BigDecimal(number);
}
public String getId() {
return id;
}
}
- //@formatter:on
private class ForBean<T> implements MetadataBuilder.ForBean<T> {
@@ -458,30 +470,20 @@ public class XmlBuilder {
}
}
- private static final Set<ConstraintAnnotationAttributes> RESERVED_PARAMS = Collections
- .unmodifiableSet(EnumSet.of(ConstraintAnnotationAttributes.GROUPS, ConstraintAnnotationAttributes.MESSAGE,
- ConstraintAnnotationAttributes.PAYLOAD, ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO));
-
- static final <T> T lazy(Lazy<T> lazy, String name) {
+ private static final <T> T lazy(Lazy<T> lazy, String name) {
Validate.validState(lazy != null, "%s not set", name);
return lazy.get();
}
private final ConstraintMappingsType constraintMappings;
- private final BigDecimal version;
+ private final Version version;
public XmlBuilder(ConstraintMappingsType constraintMappings) {
super();
this.constraintMappings = constraintMappings;
Validate.notNull(constraintMappings, "constraintMappings");
-
- BigDecimal v;
- try {
- v = new BigDecimal(constraintMappings.getVersion());
- } catch (NumberFormatException e) {
- v = Version.v10.number;
- }
- this.version = v;
+ this.version = Version.of(constraintMappings);
+ new MappingValidator(constraintMappings, this::resolveClass).validateMappings();
}
public Map<Class<?>, MetadataBuilder.ForBean<?>> forBeans() {
@@ -494,7 +496,7 @@ public class XmlBuilder {
}
boolean atLeast(Version v) {
- return version.compareTo(v.number) >= 0;
+ return version.compareTo(v) >= 0;
}
<T> Class<T> resolveClass(String className) {
@@ -555,8 +557,6 @@ public class XmlBuilder {
}
for (final ElementType elementType : constraint.getElement()) {
final String name = elementType.getName();
- checkValidName(name);
-
final Class<?> returnType = getAnnotationParameterType(annotationClass, name);
final Object elementValue = getElementValue(elementType, returnType);
annoBuilder.setValue(name, elementValue);
@@ -564,11 +564,6 @@ public class XmlBuilder {
return annoBuilder.createAnnotation();
}
- private void checkValidName(String name) {
- Exceptions.raiseIf(RESERVED_PARAMS.stream().map(ConstraintAnnotationAttributes::getAttributeName)
- .anyMatch(Predicate.isEqual(name)), ValidationException::new, "%s is a reserved parameter name.", name);
- }
-
private <A extends Annotation> Class<?> getAnnotationParameterType(final Class<A> annotationClass,
final String name) {
final Method m = Reflection.getPublicMethod(annotationClass, name);
@@ -646,27 +641,31 @@ public class XmlBuilder {
throw new ConstraintDeclarationException(e);
}
}
- if (Byte.class.equals(returnType) || byte.class.equals(returnType)) {
- // spec mandates it:
- return Byte.parseByte(value);
- }
- if (Short.class.equals(returnType) || short.class.equals(returnType)) {
- return Short.parseShort(value);
- }
- if (Integer.class.equals(returnType) || int.class.equals(returnType)) {
- return Integer.parseInt(value);
- }
- if (Long.class.equals(returnType) || long.class.equals(returnType)) {
- return Long.parseLong(value);
- }
- if (Float.class.equals(returnType) || float.class.equals(returnType)) {
- return Float.parseFloat(value);
- }
- if (Double.class.equals(returnType) || double.class.equals(returnType)) {
- return Double.parseDouble(value);
- }
- if (Boolean.class.equals(returnType) || boolean.class.equals(returnType)) {
- return Boolean.parseBoolean(value);
+ try {
+ if (Byte.class.equals(returnType) || byte.class.equals(returnType)) {
+ // spec mandates it:
+ return Byte.parseByte(value);
+ }
+ if (Short.class.equals(returnType) || short.class.equals(returnType)) {
+ return Short.parseShort(value);
+ }
+ if (Integer.class.equals(returnType) || int.class.equals(returnType)) {
+ return Integer.parseInt(value);
+ }
+ if (Long.class.equals(returnType) || long.class.equals(returnType)) {
+ return Long.parseLong(value);
+ }
+ if (Float.class.equals(returnType) || float.class.equals(returnType)) {
+ return Float.parseFloat(value);
+ }
+ if (Double.class.equals(returnType) || double.class.equals(returnType)) {
+ return Double.parseDouble(value);
+ }
+ if (Boolean.class.equals(returnType) || boolean.class.equals(returnType)) {
+ return Boolean.parseBoolean(value);
+ }
+ } catch (Exception e) {
+ Exceptions.raise(ValidationException::new, e, "Unable to coerce value '%s' to %s", value, returnType);
}
if (Character.class.equals(returnType) || char.class.equals(returnType)) {
Exceptions.raiseIf(value.length() > 1, ConstraintDeclarationException::new,
http://git-wip-us.apache.org/repos/asf/bval/blob/cb0bb133/bval-jsr/src/main/java/org/apache/bval/jsr/xml/MappingValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/MappingValidator.java b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/MappingValidator.java
new file mode 100644
index 0000000..f3b017e
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/MappingValidator.java
@@ -0,0 +1,244 @@
+/*
+ * 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.xml;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import javax.validation.ValidationException;
+
+import org.apache.bval.jsr.ConstraintAnnotationAttributes;
+import org.apache.bval.jsr.metadata.ContainerElementKey;
+import org.apache.bval.jsr.metadata.Meta;
+import org.apache.bval.jsr.metadata.Meta.ForConstructor;
+import org.apache.bval.jsr.metadata.Signature;
+import org.apache.bval.jsr.util.Methods;
+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.TypeUtils;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+
+@Privilizing(@CallTo(Reflection.class))
+public class MappingValidator {
+ private static final Set<ConstraintAnnotationAttributes> RESERVED_CONSTRAINT_ELEMENT_NAMES = Collections
+ .unmodifiableSet(EnumSet.of(ConstraintAnnotationAttributes.GROUPS, ConstraintAnnotationAttributes.MESSAGE,
+ ConstraintAnnotationAttributes.PAYLOAD, ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO));
+
+ private static <T> BinaryOperator<T> enforceUniqueness(String message, Function<? super T, ?> describe) {
+ return (t, u) -> {
+ throw Exceptions.create(ValidationException::new, message, describe.apply(t));
+ };
+ }
+
+ private final ConstraintMappingsType constraintMappings;
+ private final Function<String, Class<?>> resolveClass;
+
+ public MappingValidator(ConstraintMappingsType constraintMappings, Function<String, Class<?>> resolveClass) {
+ super();
+ this.constraintMappings = Validate.notNull(constraintMappings, "constraintMappings");
+ this.resolveClass = Validate.notNull(resolveClass, "resolveClass");
+ }
+
+ public void validateMappings() {
+ constraintMappings.getBean().stream().map(this::applyChecks).collect(Collectors.toMap(Function.identity(),
+ Function.identity(), enforceUniqueness("Duplicate XML constrained bean %s", Class::getName)));
+ }
+
+ private Class<?> applyChecks(BeanType bean) {
+ final Class<?> t = resolveClass.apply(bean.getClazz());
+
+ final ClassType classType = bean.getClassType();
+ if (classType != null) {
+ constraints(new Meta.ForClass<>(t), classType.getConstraint());
+ }
+ final Set<String> fieldProperties = fieldProperties(t, bean.getField());
+ final Set<String> getterProperties = getterProperties(t, bean.getGetter());
+ final Set<Signature> methods = methods(t, bean.getMethod());
+ @SuppressWarnings("unused")
+ final Set<Signature> constructors = constructors(t, bean.getConstructor());
+
+ final Set<String> propertyOverlap = new HashSet<>(fieldProperties);
+ propertyOverlap.retainAll(getterProperties);
+
+ if (!propertyOverlap.isEmpty()) {
+ Exceptions.raise(ValidationException::new,
+ "The following %s properties were specified via XML field and getter: %s", bean.getClazz(),
+ propertyOverlap);
+ }
+ final Set<String> getterMethodOverlap = methods.stream().filter(s -> s.getParameterTypes().length == 0)
+ .map(Signature::getName).filter(Methods::isGetter).map(Methods::propertyName)
+ .filter(getterProperties::contains).collect(Collectors.toSet());
+
+ if (!getterMethodOverlap.isEmpty()) {
+ Exceptions.raise(ValidationException::new,
+ "The following %s getters were specified via XML getter and method: %s", bean.getClazz(),
+ getterMethodOverlap);
+ }
+ return t;
+ }
+
+ private Set<String> fieldProperties(Class<?> t, List<FieldType> fields) {
+ return fields.stream().peek(f -> {
+ final Field fld = Reflection.find(t, c -> Reflection.getDeclaredField(c, f.getName()));
+ if (fld == null) {
+ Exceptions.raise(ValidationException::new, "Unknown XML constrained field %s of %s", f.getName(), t);
+ }
+ final Meta.ForField metaField = new Meta.ForField(fld);
+ constraints(metaField, f.getConstraint());
+ containerElements(metaField, f.getContainerElementType());
+ }).collect(Collectors.toMap(FieldType::getName, Function.identity(),
+ enforceUniqueness("Duplicate XML constrained field %s of " + t, FieldType::getName))).keySet();
+ }
+
+ private Set<String> getterProperties(Class<?> t, List<GetterType> getters) {
+ return getters.stream().peek(g -> {
+ final Method getter = Methods.getter(t, g.getName());
+ if (getter == null) {
+ Exceptions.raise(ValidationException::new, "Unknown XML constrained getter for property %s of %s",
+ g.getName(), t);
+ }
+ final Meta.ForMethod metaGetter = new Meta.ForMethod(getter);
+ constraints(metaGetter, g.getConstraint());
+ containerElements(metaGetter, g.getContainerElementType());
+ }).collect(Collectors.toMap(GetterType::getName, Function.identity(),
+ enforceUniqueness("Duplicate XML constrained getter %s of " + t, GetterType::getName))).keySet();
+ }
+
+ private Set<Signature> methods(Class<?> t, List<MethodType> methods) {
+ return methods.stream().map(mt -> {
+ final Class<?>[] parameterTypes = getParameterTypes(mt.getParameter());
+ final Signature result = new Signature(mt.getName(), parameterTypes);
+ final Method m = Reflection.find(t, c -> Reflection.getDeclaredMethod(c, mt.getName(), parameterTypes));
+ Exceptions.raiseIf(m == null, ValidationException::new, "Unknown method %s of %s", result, t);
+
+ Optional.of(mt).map(MethodType::getReturnValue).ifPresent(rv -> {
+ final Meta.ForMethod metaMethod = new Meta.ForMethod(m);
+ constraints(metaMethod, rv.getConstraint());
+ containerElements(metaMethod, rv.getContainerElementType());
+ });
+ final Parameter[] params = m.getParameters();
+
+ IntStream.range(0, parameterTypes.length).forEach(n -> {
+ final Meta.ForParameter metaParam = new Meta.ForParameter(params[n], params[n].getName());
+ final ParameterType parameterType = mt.getParameter().get(n);
+ constraints(metaParam, parameterType.getConstraint());
+ containerElements(metaParam, parameterType.getContainerElementType());
+ });
+
+ return result;
+ }).collect(Collectors.toSet());
+ }
+
+ private Set<Signature> constructors(Class<?> t, List<ConstructorType> ctors) {
+ return ctors.stream().map(ctor -> {
+ final Class<?>[] parameterTypes = getParameterTypes(ctor.getParameter());
+ final Signature result = new Signature(t.getSimpleName(), parameterTypes);
+ final Constructor<?> dc = Reflection.getDeclaredConstructor(t, parameterTypes);
+ Exceptions.raiseIf(dc == null, ValidationException::new, "Unknown %s constructor %s", t, result);
+
+ final ForConstructor<?> metaCtor = new Meta.ForConstructor<>(dc);
+ constraints(metaCtor, ctor.getReturnValue().getConstraint());
+ containerElements(metaCtor, ctor.getReturnValue().getContainerElementType());
+
+ final Parameter[] params = dc.getParameters();
+
+ IntStream.range(0, parameterTypes.length).forEach(n -> {
+ final Meta.ForParameter metaParam = new Meta.ForParameter(params[n], params[n].getName());
+ final ParameterType parameterType = ctor.getParameter().get(n);
+ constraints(metaParam, parameterType.getConstraint());
+ containerElements(metaParam, parameterType.getContainerElementType());
+ });
+ return result;
+ }).collect(Collectors.toSet());
+ }
+
+ private Class<?>[] getParameterTypes(List<ParameterType> paramElements) {
+ return paramElements.stream().map(ParameterType::getType).map(resolveClass).toArray(Class[]::new);
+ }
+
+ private Set<ContainerElementKey> containerElements(Meta<?> meta,
+ List<ContainerElementTypeType> containerElementTypes) {
+ if (containerElementTypes.isEmpty()) {
+ return Collections.emptySet();
+ }
+ final Class<?> containerType = TypeUtils.getRawType(meta.getType(), null);
+ final int typeParameterCount = containerType.getTypeParameters().length;
+ if (typeParameterCount == 0) {
+ Exceptions.raise(ValidationException::new, "Cannot specify container element types for %s",
+ meta.describeHost());
+ }
+ return containerElementTypes.stream().map(e -> {
+ Integer typeArgumentIndex = e.getTypeArgumentIndex();
+ if (typeArgumentIndex == null) {
+ if (typeParameterCount > 1) {
+ Exceptions.raise(ValidationException::new,
+ "Unable to resolve unspecified type argument index for %s", meta.describeHost());
+ }
+ typeArgumentIndex = Integer.valueOf(0);
+ }
+ final ContainerElementKey result = new ContainerElementKey(containerType, typeArgumentIndex);
+
+ final Meta.ForContainerElement elementMeta = new Meta.ForContainerElement(meta, result);
+
+ constraints(elementMeta, e.getConstraint());
+ containerElements(elementMeta, e.getContainerElementType());
+
+ return result;
+ }).collect(Collectors.toMap(Function.identity(), ContainerElementKey::getTypeArgumentIndex, enforceUniqueness(
+ "Duplicate XML constrained container element %d of " + meta.describeHost(), Function.identity()))).keySet();
+ }
+
+ private void constraints(Meta<?> meta, List<ConstraintType> constraints) {
+ constraints.forEach(constraint -> {
+ final Class<?> annotation = resolveClass.apply(constraint.getAnnotation());
+ Exceptions.raiseUnless(annotation.isAnnotation(), ValidationException::new, "%s is not an annotation",
+ annotation);
+
+ final Set<String> missingElements = Stream.of(Reflection.getDeclaredMethods(annotation))
+ .filter(m -> m.getParameterCount() == 0 && m.getDefaultValue() == null).map(Method::getName)
+ .collect(Collectors.toSet());
+
+ for (final ElementType elementType : constraint.getElement()) {
+ final String name = elementType.getName();
+ if (RESERVED_CONSTRAINT_ELEMENT_NAMES.stream().map(ConstraintAnnotationAttributes::getAttributeName)
+ .anyMatch(Predicate.isEqual(name))) {
+ Exceptions.raise(ValidationException::new, "Constraint of %s declares reserved parameter name %s.",
+ meta.describeHost(), name);
+ }
+ missingElements.remove(name);
+ }
+ Exceptions.raiseUnless(missingElements.isEmpty(), ValidationException::new,
+ "Missing required elements of %s: %s", annotation, missingElements);
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/cb0bb133/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java
index f03d4cd..651ff9f 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationMappingParser.java
@@ -19,15 +19,20 @@ package org.apache.bval.jsr.xml;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
import javax.validation.ValidationException;
import javax.validation.spi.ConfigurationState;
+import org.apache.bval.jsr.metadata.MetadataBuilder;
import org.apache.bval.jsr.metadata.MetadataBuilder.ForBean;
import org.apache.bval.jsr.metadata.MetadataSource;
import org.apache.bval.jsr.metadata.ValidatorMappingProvider;
@@ -59,10 +64,21 @@ public class ValidationMappingParser implements MetadataSource {
if (configurationState.isIgnoreXmlConfiguration()) {
return;
}
+ final Set<Class<?>> beanTypes = new HashSet<>();
for (final InputStream xmlStream : configurationState.getMappingStreams()) {
final ConstraintMappingsType mapping = parseXmlMappings(xmlStream);
+
Optional.of(mapping).map(this::toMappingProvider).ifPresent(addValidatorMappingProvider);
- new XmlBuilder(mapping).forBeans().forEach(addBuilder::accept);
+
+ final Map<Class<?>, MetadataBuilder.ForBean<?>> builders = new XmlBuilder(mapping).forBeans();
+ if (Collections.disjoint(beanTypes, builders.keySet())) {
+ builders.forEach(addBuilder::accept);
+ beanTypes.addAll(builders.keySet());
+ } else {
+ Exceptions.raise(ValidationException::new,
+ builders.keySet().stream().filter(beanTypes::contains).map(Class::getName).collect(Collectors
+ .joining("bean classes specified multiple times for XML validation mapping: [", "; ", "]")));
+ }
}
}
@@ -102,7 +118,7 @@ public class ValidationMappingParser implements MetadataSource {
final Class<? extends Annotation> annotationClass = clazz.asSubclass(Annotation.class);
Exceptions.raiseIf(validatorMappings.containsKey(annotationClass), ValidationException::new,
- "Constraint validator for %s already configured.", annotationClass);
+ "XML constraint validator(s) for %s already configured.", annotationClass);
validatorMappings.put(annotationClass, constraintDefinition.getValidatedBy());
}