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());
         }