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/02/21 21:01:51 UTC

[01/17] bval git commit: BV2: pom updates [Forced Update!]

Repository: bval
Updated Branches:
  refs/heads/bv2 3f287a7af -> 1eb57573d (forced update)


BV2: pom updates


Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/af6ee90d
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/af6ee90d
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/af6ee90d

Branch: refs/heads/bv2
Commit: af6ee90d3c31e6503a2483a66c0b7a06a5c5129b
Parents: cd050cf
Author: Matt Benson <mb...@apache.org>
Authored: Wed Feb 21 14:27:41 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Wed Feb 21 14:27:41 2018 -0600

----------------------------------------------------------------------
 bundle/pom.xml       |  2 +-
 bval-core/pom.xml    |  2 +-
 bval-extras/pom.xml  |  4 ++--
 bval-json/pom.xml    |  2 +-
 bval-jsr/pom.xml     | 19 +++++++++++++------
 bval-tck11/pom.xml   |  8 ++++----
 bval-xstream/pom.xml |  2 +-
 pom.xml              | 39 ++++++++++++++++++++++++---------------
 8 files changed, 47 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/af6ee90d/bundle/pom.xml
----------------------------------------------------------------------
diff --git a/bundle/pom.xml b/bundle/pom.xml
index 80cd95d..2f57116 100644
--- a/bundle/pom.xml
+++ b/bundle/pom.xml
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.apache.bval</groupId>
         <artifactId>bval-parent</artifactId>
-        <version>1.1.3-SNAPSHOT</version>
+        <version>2.0.0-SNAPSHOT</version>
     </parent>
 
     <!-- use fully qualified naming for OSGi bundles -->

http://git-wip-us.apache.org/repos/asf/bval/blob/af6ee90d/bval-core/pom.xml
----------------------------------------------------------------------
diff --git a/bval-core/pom.xml b/bval-core/pom.xml
index 48026d4..b7a7f8b 100644
--- a/bval-core/pom.xml
+++ b/bval-core/pom.xml
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.apache.bval</groupId>
         <artifactId>bval-parent</artifactId>
-        <version>1.1.3-SNAPSHOT</version>
+        <version>2.0.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>bval-core</artifactId>

http://git-wip-us.apache.org/repos/asf/bval/blob/af6ee90d/bval-extras/pom.xml
----------------------------------------------------------------------
diff --git a/bval-extras/pom.xml b/bval-extras/pom.xml
index 7a3edff..3dc7982 100644
--- a/bval-extras/pom.xml
+++ b/bval-extras/pom.xml
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.apache.bval</groupId>
         <artifactId>bval-parent</artifactId>
-        <version>1.1.3-SNAPSHOT</version>
+        <version>2.0.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>bval-extras</artifactId>
@@ -69,7 +69,7 @@
             <dependencies>
                 <dependency>
                     <groupId>org.apache.geronimo.specs</groupId>
-                    <artifactId>geronimo-validation_1.1_spec</artifactId>
+                    <artifactId>geronimo-validation_2.0_spec</artifactId>
                     <scope>provided</scope>
                 </dependency>
             </dependencies>

http://git-wip-us.apache.org/repos/asf/bval/blob/af6ee90d/bval-json/pom.xml
----------------------------------------------------------------------
diff --git a/bval-json/pom.xml b/bval-json/pom.xml
index a489071..59dcb17 100644
--- a/bval-json/pom.xml
+++ b/bval-json/pom.xml
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.apache.bval</groupId>
         <artifactId>bval-parent</artifactId>
-        <version>1.1.3-SNAPSHOT</version>
+        <version>2.0.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>bval-json</artifactId>

http://git-wip-us.apache.org/repos/asf/bval/blob/af6ee90d/bval-jsr/pom.xml
----------------------------------------------------------------------
diff --git a/bval-jsr/pom.xml b/bval-jsr/pom.xml
index e716645..561d2d4 100644
--- a/bval-jsr/pom.xml
+++ b/bval-jsr/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.bval</groupId>
         <artifactId>bval-parent</artifactId>
-        <version>1.1.3-SNAPSHOT</version>
+        <version>2.0.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>bval-jsr</artifactId>
@@ -38,7 +38,7 @@
     </properties>
     <profiles>
         <!--
-            default profile using geronimo-validation_1.0_spec.jar active when
+            default profile using geronimo-validation_2.0_spec.jar active when
             property "ri" is not present.
         -->
         <profile>
@@ -51,7 +51,7 @@
             <dependencies>
                 <dependency>
                     <groupId>org.apache.geronimo.specs</groupId>
-                    <artifactId>geronimo-validation_1.1_spec</artifactId>
+                    <artifactId>geronimo-validation_2.0_spec</artifactId>
                     <scope>provided</scope>
                 </dependency>
             </dependencies>
@@ -215,6 +215,10 @@
             <version>1.0</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+	        <groupId>com.oracle</groupId>
+	        <artifactId>javafx</artifactId>
+        </dependency>
 
         <!-- Testing dependencies -->
         <dependency>
@@ -244,10 +248,10 @@
         </resources>
 
         <plugins>
-            <plugin> <!-- http://jira.codehaus.org/browse/MJAXB-37 -->
+            <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>jaxb2-maven-plugin</artifactId>
-                <version>1.4</version>
+                <version>2.3.1</version>
                 <executions>
                     <execution>
                         <goals>
@@ -258,8 +262,11 @@
                 <configuration>
                     <packageName>org.apache.bval.jsr.xml</packageName>
                     <extension>true</extension>
-                    <schemaFiles>validation-configuration-1.1.xsd,validation-mapping-1.1.xsd</schemaFiles>
                     <enableIntrospection>true</enableIntrospection>
+                    <sources>
+	                    <source>${project.basedir}/src/main/xsd/validation-configuration-2.0.xsd</source>
+	                    <source>${project.basedir}/src/main/xsd/validation-mapping-2.0.xsd</source>
+                    </sources>
                 </configuration>
             </plugin>
 

http://git-wip-us.apache.org/repos/asf/bval/blob/af6ee90d/bval-tck11/pom.xml
----------------------------------------------------------------------
diff --git a/bval-tck11/pom.xml b/bval-tck11/pom.xml
index fb2ae65..63611b9 100644
--- a/bval-tck11/pom.xml
+++ b/bval-tck11/pom.xml
@@ -23,7 +23,7 @@ under the License.
     <parent>
         <artifactId>bval-parent</artifactId>
         <groupId>org.apache.bval</groupId>
-        <version>1.1.3-SNAPSHOT</version>
+        <version>2.0.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>bval-tck11-runner</artifactId>
@@ -32,15 +32,15 @@ under the License.
 
     <properties>
         <tck.version>1.1.4.Final</tck.version>
-        <owb.version>1.7.2</owb.version>
-        <arquillian.version>1.1.13.Final</arquillian.version>
+        <owb.version>1.6.0</owb.version>
+        <arquillian.version>1.1.8.Final</arquillian.version>
         <validation.provider>org.apache.bval.jsr.ApacheValidationProvider</validation.provider>
     </properties>
 
     <dependencies>
         <dependency>
           <groupId>org.apache.geronimo.specs</groupId>
-            <artifactId>geronimo-validation_1.1_spec</artifactId>
+            <artifactId>geronimo-validation_2.0_spec</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>

http://git-wip-us.apache.org/repos/asf/bval/blob/af6ee90d/bval-xstream/pom.xml
----------------------------------------------------------------------
diff --git a/bval-xstream/pom.xml b/bval-xstream/pom.xml
index 67ac48d..bf90180 100644
--- a/bval-xstream/pom.xml
+++ b/bval-xstream/pom.xml
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.apache.bval</groupId>
         <artifactId>bval-parent</artifactId>
-        <version>1.1.3-SNAPSHOT</version>
+        <version>2.0.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>bval-xstream</artifactId>

http://git-wip-us.apache.org/repos/asf/bval/blob/af6ee90d/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 470ddd0..42e8d80 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,14 +35,16 @@
     <artifactId>bval-parent</artifactId>
     <name>Apache BVal :: bval-parent (Parent POM)</name>
     <packaging>pom</packaging>
-    <version>1.1.3-SNAPSHOT</version>
+    <version>2.0.0-SNAPSHOT</version>
 
     <description>Apache BVal parent pom</description>
 
     <properties>
         <site.deploy.url>sftp://people.apache.org/home/${user.name}/public_html/bval/${project.version}/staging-site</site.deploy.url>
         <commons.weaver.version>1.3</commons.weaver.version>
-        <privilizer.policy>DYNAMIC</privilizer.policy>
+<!-- temporarily disable privilizer due to Java 8 incompatibility -->
+<!--         <privilizer.policy>DYNAMIC</privilizer.policy> -->
+        <privilizer.policy>NEVER</privilizer.policy>
     </properties>
 
     <inceptionYear>2010</inceptionYear>
@@ -257,10 +259,10 @@
                 <configuration>
                     <show>package</show>
                     <quiet>true</quiet>
-                    <source>1.6</source>
+                    <source>1.8</source>
                     <links>
-                        <link>http://docs.oracle.com/javase/6/docs/api</link>
-                        <link>http://docs.oracle.com/javaee/7/api</link>
+                        <link>http://docs.oracle.com/javase/8/docs/api</link>
+<!--                         <link>http://docs.oracle.com/javaee/7/api</link> -->
                     </links>
                 </configuration>
                 <reportSets>
@@ -292,8 +294,8 @@
         <dependencies>
             <dependency>
                 <groupId>org.apache.geronimo.specs</groupId>
-                <artifactId>geronimo-validation_1.1_spec</artifactId>
-                <version>1.0</version>
+                <artifactId>geronimo-validation_2.0_spec</artifactId>
+                <version>1.0-SNAPSHOT</version>
             </dependency>
             <!-- Optional profile to use Spec RI API -->
             <dependency>
@@ -367,6 +369,13 @@
                 <version>7.0.72</version>
                 <scope>provided</scope>
             </dependency>
+            <dependency>
+	            <groupId>com.oracle</groupId>
+	            <artifactId>javafx</artifactId>
+	            <version>8.0</version>
+	            <systemPath>${java.home}/lib/ext/jfxrt.jar</systemPath>
+	            <scope>system</scope>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
@@ -391,8 +400,8 @@
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-compiler-plugin</artifactId>
                     <configuration>
-                        <source>1.6</source>
-                        <target>1.6</target>
+                        <source>1.8</source>
+                        <target>1.8</target>
                         <encoding>${project.build.sourceEncoding}</encoding>
                     </configuration>
                 </plugin>
@@ -406,9 +415,9 @@
                             </manifest>
                             <manifestEntries>
                                 <Implementation-Build>${buildNumber}</Implementation-Build>
-                                <Specification-Title>JSR-349 Bean Validation</Specification-Title>
+                                <Specification-Title>JSR-380 Bean Validation</Specification-Title>
                                 <Specification-Vendor>Sun Microsystems, Inc.</Specification-Vendor>
-                                <Specification-Version>1.1</Specification-Version>
+                                <Specification-Version>2.0</Specification-Version>
                             </manifestEntries>
                         </archive>
                     </configuration>
@@ -424,8 +433,8 @@
                         <verbose>false</verbose>
                         <maxmemory>512m</maxmemory>
                         <links>
-                            <link>http://docs.oracle.com/javase/6/docs/api</link>
-                            <link>http://docs.oracle.com/javaee/7/api</link>
+                            <link>http://docs.oracle.com/javase/8/docs/api</link>
+<!--                             <link>http://docs.oracle.com/javaee/7/api</link> -->
                         </links>
                     </configuration>
                 </plugin>
@@ -459,7 +468,7 @@
                 <plugin>
                     <groupId>org.apache.felix</groupId>
                     <artifactId>maven-bundle-plugin</artifactId>
-                    <version>2.0.1</version>
+                    <version>3.3.0</version>
                     <extensions>true</extensions>
                     <configuration>
                         <instructions>
@@ -626,7 +635,7 @@
                                     <version>[2.2.1,)</version>
                                 </requireMavenVersion>
                                 <requireJavaVersion>
-                                    <version>[1.6,)</version>
+                                    <version>[1.8,)</version>
                                 </requireJavaVersion>
                             </rules>
                         </configuration>


[06/17] bval git commit: BV2: value extraction

Posted by mb...@apache.org.
BV2: value extraction


Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/a602e414
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/a602e414
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/a602e414

Branch: refs/heads/bv2
Commit: a602e414fbf7641395afe80dd744f68e29ef13c4
Parents: fad43eb
Author: Matt Benson <mb...@apache.org>
Authored: Wed Feb 21 14:35:25 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Wed Feb 21 14:44:28 2018 -0600

----------------------------------------------------------------------
 .../bval/jsr/valueextraction/FxExtractor.java   |  96 ++++++++++
 .../IterableElementExtractor.java               |  30 +++
 .../valueextraction/ListElementExtractor.java   |  34 ++++
 .../bval/jsr/valueextraction/MapExtractor.java  |  42 +++++
 .../jsr/valueextraction/OptionalExtractor.java  |  65 +++++++
 .../jsr/valueextraction/ValueExtractors.java    | 181 +++++++++++++++++++
 .../DefaultExtractors.properties                |  25 +++
 7 files changed, 473 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/a602e414/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/FxExtractor.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/FxExtractor.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/FxExtractor.java
new file mode 100644
index 0000000..15601b2
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/FxExtractor.java
@@ -0,0 +1,96 @@
+/*
+ * 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.valueextraction;
+
+import java.util.Optional;
+import java.util.function.BooleanSupplier;
+
+import javax.validation.valueextraction.ExtractedValue;
+import javax.validation.valueextraction.UnwrapByDefault;
+import javax.validation.valueextraction.ValueExtractor;
+
+import org.apache.bval.util.reflection.Reflection;
+
+import javafx.beans.property.ReadOnlyListProperty;
+import javafx.beans.property.ReadOnlyMapProperty;
+import javafx.beans.property.ReadOnlySetProperty;
+import javafx.beans.value.ObservableValue;
+
+@SuppressWarnings("restriction")
+public abstract class FxExtractor {
+    public static class Activation implements BooleanSupplier {
+
+        @Override
+        public boolean getAsBoolean() {
+            try {
+                return Reflection.toClass("javafx.beans.Observable") != null;
+            } catch (ClassNotFoundException e) {
+                return false;
+            }
+        }
+    }
+
+    @UnwrapByDefault
+    public static class ForObservableValue implements ValueExtractor<ObservableValue<@ExtractedValue ?>> {
+
+        @Override
+        public void extractValues(ObservableValue<?> originalValue, ValueExtractor.ValueReceiver receiver) {
+            receiver.value(null, originalValue.getValue());
+        }
+    }
+
+    public static class ForListProperty implements ValueExtractor<ReadOnlyListProperty<@ExtractedValue ?>> {
+
+        @Override
+        public void extractValues(ReadOnlyListProperty<?> originalValue, ValueExtractor.ValueReceiver receiver) {
+            Optional.ofNullable(originalValue.getValue()).ifPresent(l -> {
+                for (int i = 0, sz = l.size(); i < sz; i++) {
+                    receiver.indexedValue("<list element>", i, l.get(i));
+                }
+            });
+        }
+    }
+
+    public static class ForSetProperty implements ValueExtractor<ReadOnlySetProperty<@ExtractedValue ?>> {
+
+        @Override
+        public void extractValues(ReadOnlySetProperty<?> originalValue, ValueExtractor.ValueReceiver receiver) {
+            Optional.ofNullable(originalValue.getValue())
+                .ifPresent(s -> s.forEach(e -> receiver.iterableValue("<iterable element>", e)));
+        }
+    }
+
+    public static class ForMapPropertyKey implements ValueExtractor<ReadOnlyMapProperty<@ExtractedValue ?, ?>> {
+
+        @Override
+        public void extractValues(ReadOnlyMapProperty<?, ?> originalValue, ValueExtractor.ValueReceiver receiver) {
+            Optional.ofNullable(originalValue.getValue())
+                .ifPresent(m -> m.keySet().forEach(k -> receiver.keyedValue("<map key>", k, k)));
+        }
+    }
+
+    public static class ForMapPropertyValue implements ValueExtractor<ReadOnlyMapProperty<?, @ExtractedValue ?>> {
+
+        @Override
+        public void extractValues(ReadOnlyMapProperty<?, ?> originalValue, ValueExtractor.ValueReceiver receiver) {
+            Optional.ofNullable(originalValue.getValue()).ifPresent(
+                m -> m.entrySet().forEach(e -> receiver.keyedValue("<map value>", e.getKey(), e.getValue())));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a602e414/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/IterableElementExtractor.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/IterableElementExtractor.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/IterableElementExtractor.java
new file mode 100644
index 0000000..8fd2fc0
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/IterableElementExtractor.java
@@ -0,0 +1,30 @@
+/*
+ * 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.valueextraction;
+
+import javax.validation.valueextraction.ExtractedValue;
+import javax.validation.valueextraction.ValueExtractor;
+
+public class IterableElementExtractor implements ValueExtractor<Iterable<@ExtractedValue ?>> {
+
+    @Override
+    public void extractValues(Iterable<?> originalValue, ValueExtractor.ValueReceiver receiver) {
+        originalValue.forEach(v -> receiver.iterableValue("<iterable element>", v));
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a602e414/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ListElementExtractor.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ListElementExtractor.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ListElementExtractor.java
new file mode 100644
index 0000000..bd84726
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ListElementExtractor.java
@@ -0,0 +1,34 @@
+/*
+ * 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.valueextraction;
+
+import java.util.List;
+
+import javax.validation.valueextraction.ExtractedValue;
+import javax.validation.valueextraction.ValueExtractor;
+
+public class ListElementExtractor implements ValueExtractor<List<@ExtractedValue ?>> {
+
+    @Override
+    public void extractValues(List<?> originalValue, ValueExtractor.ValueReceiver receiver) {
+        for (int i = 0, sz = originalValue.size(); i < sz; i++) {
+            receiver.indexedValue("<list element>", i, originalValue.get(i));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a602e414/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/MapExtractor.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/MapExtractor.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/MapExtractor.java
new file mode 100644
index 0000000..a6848b8
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/MapExtractor.java
@@ -0,0 +1,42 @@
+/*
+ * 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.valueextraction;
+
+import java.util.Map;
+
+import javax.validation.valueextraction.ExtractedValue;
+import javax.validation.valueextraction.ValueExtractor;
+
+public abstract class MapExtractor {
+    public static class ForKey implements ValueExtractor<Map<@ExtractedValue ?, ?>> {
+
+        @Override
+        public void extractValues(Map<?, ?> originalValue, ValueExtractor.ValueReceiver receiver) {
+            originalValue.keySet().forEach(k -> receiver.keyedValue("<map key>", k, k));
+        }
+    }
+
+    public static class ForValue implements ValueExtractor<Map<?, @ExtractedValue ?>> {
+
+        @Override
+        public void extractValues(Map<?, ?> originalValue, ValueExtractor.ValueReceiver receiver) {
+            originalValue.entrySet().forEach(e -> receiver.keyedValue("<map value>", e.getKey(), e.getValue()));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a602e414/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/OptionalExtractor.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/OptionalExtractor.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/OptionalExtractor.java
new file mode 100644
index 0000000..5f073cc
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/OptionalExtractor.java
@@ -0,0 +1,65 @@
+/*
+ * 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.valueextraction;
+
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+
+import javax.validation.valueextraction.ExtractedValue;
+import javax.validation.valueextraction.UnwrapByDefault;
+import javax.validation.valueextraction.ValueExtractor;
+
+public abstract class OptionalExtractor {
+    public static class ForObject implements ValueExtractor<Optional<@ExtractedValue ?>> {
+
+        @Override
+        public void extractValues(Optional<?> originalValue, ValueExtractor.ValueReceiver receiver) {
+            receiver.value(null, originalValue.orElse(null));
+        }
+    }
+
+    @UnwrapByDefault
+    public static class ForInt implements ValueExtractor<@ExtractedValue(type = Integer.class) OptionalInt> {
+
+        @Override
+        public void extractValues(OptionalInt originalValue, ValueExtractor.ValueReceiver receiver) {
+            receiver.value(null, originalValue.isPresent() ? Integer.valueOf(originalValue.getAsInt()) : null);
+        }
+    }
+
+    @UnwrapByDefault
+    public static class ForLong implements ValueExtractor<@ExtractedValue(type = Long.class) OptionalLong> {
+
+        @Override
+        public void extractValues(OptionalLong originalValue, ValueExtractor.ValueReceiver receiver) {
+            receiver.value(null, originalValue.isPresent() ? Long.valueOf(originalValue.getAsLong()) : null);
+        }
+    }
+
+    @UnwrapByDefault
+    public static class ForDouble implements ValueExtractor<@ExtractedValue(type = Double.class) OptionalDouble> {
+
+        @Override
+        public void extractValues(OptionalDouble originalValue, ValueExtractor.ValueReceiver receiver) {
+            receiver.value(null, originalValue.isPresent() ? Double.valueOf(originalValue.getAsDouble()) : null);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a602e414/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
new file mode 100644
index 0000000..50feb6c
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java
@@ -0,0 +1,181 @@
+/*
+ *  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.valueextraction;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
+import java.util.function.BooleanSupplier;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.valueextraction.ValueExtractor;
+import javax.validation.valueextraction.ValueExtractorDeclarationException;
+import javax.validation.valueextraction.ValueExtractorDefinitionException;
+
+import org.apache.bval.jsr.metadata.ContainerElementKey;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.StringUtils;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.bval.util.reflection.TypeUtils;
+
+public class ValueExtractors {
+    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);
+            }
+
+            @Override
+            public void add(ValueExtractor<?> extractor) {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    public static Class<?> getExtractedType(ValueExtractor<?> extractor, Type target) {
+        final ContainerElementKey key = ContainerElementKey.forValueExtractor(extractor);
+        Type result = key.getAnnotatedType().getType();
+        if (result instanceof TypeVariable<?>) {
+            result = TypeUtils.getTypeArguments(target, key.getContainerClass()).get(result);
+        }
+        Exceptions.raiseUnless(result instanceof Class<?>, ValueExtractorDefinitionException::new,
+            "%s did not resolve to a %s relative to %s", key, Class.class.getName(), target);
+        return (Class<?>) result;
+    }
+
+    private static Stream<String> split(String s) {
+        return Stream.of(StringUtils.split(s, ','));
+    }
+
+    private static <T> T newInstance(Class<T> t) {
+        try {
+            return t.newInstance();
+        } catch (InstantiationException | IllegalAccessException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private static Stream<ValueExtractor<?>> loadValueExtractors(String containerClassName) {
+        try {
+            final Class<? extends BooleanSupplier> activation =
+                Reflection.toClass(containerClassName + "$Activation").asSubclass(BooleanSupplier.class);
+            if (!newInstance(activation).getAsBoolean()) {
+                return Stream.empty();
+            }
+        } catch (ClassNotFoundException e) {
+            // always active
+        }
+        final Class<?> containerClass;
+        try {
+            containerClass = Reflection.toClass(containerClassName);
+        } catch (ClassNotFoundException e) {
+            throw new IllegalStateException(e);
+        }
+        return Stream.of(containerClass.getClasses()).filter(ValueExtractor.class::isAssignableFrom).map(c -> {
+            @SuppressWarnings("unchecked")
+            final Class<? extends ValueExtractor<?>> result =
+                (Class<? extends ValueExtractor<?>>) c.asSubclass(ValueExtractor.class);
+            return result;
+        }).map(ValueExtractors::newInstance);
+    }
+
+    private final Lazy<Map<ContainerElementKey, ValueExtractor<?>>> valueExtractors = new Lazy<>(HashMap::new);
+    private final ValueExtractors parent;
+
+    public ValueExtractors() {
+        this(DEFAULT);
+    }
+
+    private ValueExtractors(ValueExtractors parent) {
+        this.parent = parent;
+    }
+
+    public ValueExtractors createChild() {
+        return new ValueExtractors(this);
+    }
+
+    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", ValueExtractor.class.getSimpleName(), k);
+            return extractor;
+        });
+    }
+
+    public Map<ContainerElementKey, ValueExtractor<?>> getValueExtractors() {
+        final Lazy<Map<ContainerElementKey, ValueExtractor<?>>> result = new Lazy<>(HashMap::new);
+        populate(result);
+        return result.optional().orElseGet(Collections::emptyMap);
+    }
+
+    public ValueExtractor<?> find(ContainerElementKey key) {
+        final Map<ContainerElementKey, ValueExtractor<?>> allValueExtractors = getValueExtractors();
+        if (allValueExtractors.containsKey(key)) {
+            return allValueExtractors.get(key);
+        }
+        // search for assignable ContainerElementKey:
+        Set<ContainerElementKey> assignableKeys = key.getAssignableKeys();
+        while (!assignableKeys.isEmpty()) {
+            final Optional<ValueExtractor<?>> found = assignableKeys.stream().filter(allValueExtractors::containsKey)
+                .<ValueExtractor<?>> map(allValueExtractors::get).findFirst();
+            if (found.isPresent()) {
+                return found.get();
+            }
+            assignableKeys = assignableKeys.stream().map(ContainerElementKey::getAssignableKeys)
+                .flatMap(Collection::stream).collect(Collectors.toSet());
+        }
+        return null;
+    }
+
+    protected void populate(Supplier<Map<ContainerElementKey, ValueExtractor<?>>> target) {
+        Optional.ofNullable(parent).ifPresent(p -> p.populate(target));
+        valueExtractors.optional().ifPresent(m -> target.get().putAll(m));
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a602e414/bval-jsr/src/main/resources/org/apache/bval/jsr/valueextraction/DefaultExtractors.properties
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/resources/org/apache/bval/jsr/valueextraction/DefaultExtractors.properties b/bval-jsr/src/main/resources/org/apache/bval/jsr/valueextraction/DefaultExtractors.properties
new file mode 100644
index 0000000..9d9b768
--- /dev/null
+++ b/bval-jsr/src/main/resources/org/apache/bval/jsr/valueextraction/DefaultExtractors.properties
@@ -0,0 +1,25 @@
+# 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.
+
+javax.validation.valueextraction.ValueExtractor=\
+  org.apache.bval.jsr.valueextraction.IterableElementExtractor,\
+  org.apache.bval.jsr.valueextraction.ListElementExtractor
+
+javax.validation.valueextraction.ValueExtractor.container=\
+  org.apache.bval.jsr.valueextraction.MapExtractor,\
+  org.apache.bval.jsr.valueextraction.OptionalExtractor,\
+  org.apache.bval.jsr.valueextraction.FxExtractor


[16/17] bval git commit: code updates

Posted by mb...@apache.org.
code updates


Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/f87be824
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/f87be824
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/f87be824

Branch: refs/heads/bv2
Commit: f87be824d77a26424e6856183391c8d3340b5241
Parents: b808381
Author: Matt Benson <mb...@apache.org>
Authored: Wed Feb 21 14:58:35 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Wed Feb 21 14:59:59 2018 -0600

----------------------------------------------------------------------
 .../bval/routines/EMailValidationUtils.java     |   7 +-
 .../main/java/org/apache/bval/el/ELFacade.java  |  16 +--
 .../bval/jsr/AnnotationConstraintBuilder.java   |  38 ++---
 .../apache/bval/jsr/AnnotationProcessor.java    |  24 ++--
 .../jsr/ConstraintAnnotationAttributes.java     |  46 +++---
 .../org/apache/bval/jsr/ConstraintCached.java   | 143 +++++++++++++++++--
 .../org/apache/bval/jsr/ConstraintDefaults.java | 103 +++++--------
 .../bval/jsr/ConstraintDescriptorImpl.java      |  13 ++
 .../apache/bval/jsr/ConstraintValidation.java   |  33 ++++-
 .../jsr/ConstraintValidatorContextImpl.java     |  20 ++-
 .../bval/jsr/ConstraintValidatorIdentity.java   |   9 +-
 .../bval/jsr/ConstraintViolationImpl.java       |  94 +++++-------
 .../jsr/DefaultConstraintValidatorFactory.java  |  12 +-
 .../bval/jsr/DefaultMessageInterpolator.java    |  35 ++---
 .../jsr/DefaultValidationProviderResolver.java  |  50 +++----
 .../org/apache/bval/jsr/GraphBeanIdentity.java  |  28 +---
 .../bval/jsr/ParameterDescriptorImpl.java       |   7 +
 .../apache/bval/jsr/PropertyDescriptorImpl.java |   9 ++
 .../bval/jsr/ReturnValueDescriptorImpl.java     |   8 ++
 .../java/org/apache/bval/jsr/groups/Group.java  |  13 +-
 .../groups/GroupConversionDescriptorImpl.java   |   3 +-
 .../java/org/apache/bval/jsr/groups/Groups.java |  67 +++++----
 .../apache/bval/jsr/groups/GroupsComputer.java  | 111 +++++++-------
 .../parameter/DefaultParameterNameProvider.java |  25 ++--
 .../resolver/CachingTraversableResolver.java    |  26 ++--
 25 files changed, 506 insertions(+), 434 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-core/src/main/java/org/apache/bval/routines/EMailValidationUtils.java
----------------------------------------------------------------------
diff --git a/bval-core/src/main/java/org/apache/bval/routines/EMailValidationUtils.java b/bval-core/src/main/java/org/apache/bval/routines/EMailValidationUtils.java
index 1158c7f..0835bae 100644
--- a/bval-core/src/main/java/org/apache/bval/routines/EMailValidationUtils.java
+++ b/bval-core/src/main/java/org/apache/bval/routines/EMailValidationUtils.java
@@ -29,12 +29,11 @@ public class EMailValidationUtils {
     private static String ATOM = "[^\\x00-\\x1F\\(\\)\\<\\>\\@\\,\\;\\:\\\\\\\"\\.\\[\\]\\s]";
     private static String DOMAIN = "(" + ATOM + "+(\\." + ATOM + "+)*";
     private static String IP_DOMAIN = "\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\]";
-    public static final java.util.regex.Pattern DEFAULT_EMAIL_PATTERN;
+    public static final Pattern DEFAULT_EMAIL_PATTERN;
 
     static {
-        DEFAULT_EMAIL_PATTERN =
-            java.util.regex.Pattern.compile("^" + ATOM + "+(\\." + ATOM + "+)*@" + DOMAIN + "|" + IP_DOMAIN + ")$",
-                java.util.regex.Pattern.CASE_INSENSITIVE);
+        DEFAULT_EMAIL_PATTERN = Pattern.compile("^" + ATOM + "+(\\." + ATOM + "+)*@" + DOMAIN + "|" + IP_DOMAIN + ")$",
+            Pattern.CASE_INSENSITIVE);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/el/ELFacade.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/el/ELFacade.java b/bval-jsr/src/main/java/org/apache/bval/el/ELFacade.java
index 49f236b..9798455 100644
--- a/bval-jsr/src/main/java/org/apache/bval/el/ELFacade.java
+++ b/bval-jsr/src/main/java/org/apache/bval/el/ELFacade.java
@@ -54,10 +54,9 @@ public final class ELFacade implements MessageEvaluator {
             if (EXPRESSION_FACTORY != null) {
                 final BValELContext context = new BValELContext();
                 final VariableMapper variables = context.getVariableMapper();
-                for (final Map.Entry<String, Object> var : annotationParameters.entrySet()) {
-                    variables.setVariable(var.getKey(),
-                        EXPRESSION_FACTORY.createValueExpression(var.getValue(), Object.class));
-                }
+                annotationParameters.forEach(
+                    (k, v) -> variables.setVariable(k, EXPRESSION_FACTORY.createValueExpression(v, Object.class)));
+
                 variables.setVariable("validatedValue",
                     EXPRESSION_FACTORY.createValueExpression(validatedValue, Object.class));
 
@@ -83,13 +82,8 @@ public final class ELFacade implements MessageEvaluator {
     }
 
     private static class BValELContext extends ELContext {
-        private final FunctionMapper functions;
-        private final VariableMapper variables;
-
-        public BValELContext() {
-            this.variables = new BValVariableMapper();
-            this.functions = new BValFunctionMapper();
-        }
+        private final FunctionMapper functions = new BValFunctionMapper();
+        private final VariableMapper variables = new BValVariableMapper();
 
         @Override
         public ELResolver getELResolver() {

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationConstraintBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationConstraintBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationConstraintBuilder.java
index 017cabb..4b15ba7 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationConstraintBuilder.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationConstraintBuilder.java
@@ -72,7 +72,7 @@ final class AnnotationConstraintBuilder<A extends Annotation> {
         final boolean reportFromComposite =
             annotation != null && annotation.annotationType().isAnnotationPresent(ReportAsSingleViolation.class);
         constraintValidation =
-            new ConstraintValidation<A>(validatorClasses, annotation, owner, access, reportFromComposite, target);
+            new ConstraintValidation<>(validatorClasses, annotation, owner, access, reportFromComposite, target);
         buildFromAnnotation();
     }
 
@@ -139,7 +139,7 @@ final class AnnotationConstraintBuilder<A extends Annotation> {
         if (!foundGroups) {
             throw new ConstraintDefinitionException("Annotation " + annotationType.getName() + " has no groups method");
         }
-        if (validationAppliesTo != null && !ConstraintTarget.IMPLICIT.equals(validationAppliesTo.getDefaultValue())) {
+        if (validationAppliesTo != null && ConstraintTarget.IMPLICIT != validationAppliesTo.getDefaultValue()) {
             throw new ConstraintDefinitionException("validationAppliesTo default value should be IMPLICIT");
         }
 
@@ -257,9 +257,9 @@ final class AnnotationConstraintBuilder<A extends Annotation> {
 
         final Set<Class<? extends Payload>> payloadSet;
         if (payload_raw == null) {
-            payloadSet = Collections.<Class<? extends Payload>> emptySet();
+            payloadSet = Collections.emptySet();
         } else {
-            payloadSet = new HashSet<Class<? extends Payload>>(payload_raw.length);
+            payloadSet = new HashSet<>(payload_raw.length);
             Collections.addAll(payloadSet, payload_raw);
         }
         constraintValidation.setPayload(payloadSet);
@@ -323,18 +323,13 @@ final class AnnotationConstraintBuilder<A extends Annotation> {
      * @return An integer index always >= 0
      */
     private int computeIndex(ConstraintValidation<?> composite) {
-        int idx = 0;
-        for (ConstraintValidation<?> each : constraintValidation.getComposingValidations()) {
-            if (each.getAnnotation().annotationType() == composite.getAnnotation().annotationType()) {
-                idx++;
-            }
-        }
-        return idx;
+        return (int) constraintValidation.getComposingValidations().stream()
+            .filter(v -> v.getAnnotation().annotationType().equals(composite.getAnnotation().annotationType())).count();
     }
 
     /** read overridesAttributes from constraintValidation.annotation */
     private void buildOverridesAttributes() {
-        overrides = new LinkedList<ConstraintOverrides>();
+        overrides = new LinkedList<>();
         for (Method method : constraintValidation.getAnnotation().annotationType().getDeclaredMethods()) {
             final OverridesAttribute.List overridesAttributeList = method.getAnnotation(OverridesAttribute.List.class);
             if (overridesAttributeList != null) {
@@ -359,12 +354,9 @@ final class AnnotationConstraintBuilder<A extends Annotation> {
     }
 
     private ConstraintOverrides findOverride(Class<? extends Annotation> constraint, int constraintIndex) {
-        for (ConstraintOverrides each : overrides) {
-            if (each.constraintType == constraint && each.constraintIndex == constraintIndex) {
-                return each;
-            }
-        }
-        return null;
+        return overrides.stream()
+            .filter(ov -> ov.constraintType.equals(constraint) && ov.constraintIndex == constraintIndex).findFirst()
+            .orElse(null);
     }
 
     /**
@@ -381,7 +373,7 @@ final class AnnotationConstraintBuilder<A extends Annotation> {
         private ConstraintOverrides(Class<? extends Annotation> constraintType, int constraintIndex) {
             this.constraintType = constraintType;
             this.constraintIndex = constraintIndex;
-            values = new HashMap<String, Object>();
+            values = new HashMap<>();
         }
 
         @SuppressWarnings("unchecked")
@@ -392,11 +384,9 @@ final class AnnotationConstraintBuilder<A extends Annotation> {
             // And the annotation
             final Annotation originalAnnot = composite.getAnnotation();
             final AnnotationProxyBuilder<Annotation> apb = new AnnotationProxyBuilder<Annotation>(originalAnnot);
-            for (String key : values.keySet()) {
-                apb.putValue(key, values.get(key));
-            }
-            final Annotation newAnnot = apb.createAnnotation();
-            ((ConstraintValidation<Annotation>) composite).setAnnotation(newAnnot);
+            values.forEach(apb::putValue);
+
+            ((ConstraintValidation<Annotation>) composite).setAnnotation(apb.createAnnotation());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationProcessor.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationProcessor.java b/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationProcessor.java
index 4bdb331..3c4b046 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationProcessor.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/AnnotationProcessor.java
@@ -38,7 +38,9 @@ import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -121,7 +123,7 @@ public final class AnnotationProcessor {
             if (!reflection) {
                 Collection<Annotation> annotations = prop.getFeature(JsrFeatures.Property.ANNOTATIONS_TO_PROCESS);
                 if (annotations == null) {
-                    annotations = new ArrayList<Annotation>();
+                    annotations = new ArrayList<>();
                     prop.putFeature(JsrFeatures.Property.ANNOTATIONS_TO_PROCESS, annotations);
                 }
                 annotations.add(annotation);
@@ -129,18 +131,19 @@ public final class AnnotationProcessor {
             return true;
         }
 
-        /**
+        /*
          * An annotation is considered a constraint definition if its retention
          * policy contains RUNTIME and if the annotation itself is annotated
          * with javax.validation.Constraint.
          */
         final Constraint vcAnno = annotation.annotationType().getAnnotation(Constraint.class);
         if (vcAnno != null) {
-            Class<? extends ConstraintValidator<A, ?>>[] validatorClasses;
-            validatorClasses = findConstraintValidatorClasses(annotation, vcAnno);
+            Class<? extends ConstraintValidator<A, ?>>[] validatorClasses =
+                findConstraintValidatorClasses(annotation, vcAnno);
             return applyConstraint(annotation, validatorClasses, prop, owner, access, appender);
         }
-        /**
+
+        /*
          * Multi-valued constraints: To support this requirement, the bean
          * validation provider treats regular annotations (annotations not
          * annotated by @Constraint) whose value element has a return type of an
@@ -202,15 +205,14 @@ public final class AnnotationProcessor {
             vcAnno = annotation.annotationType().getAnnotation(Constraint.class);
         }
         final Class<A> annotationType = (Class<A>) annotation.annotationType();
-        Class<? extends ConstraintValidator<A, ?>>[] validatorClasses =
-            factory.getConstraintsCache().getConstraintValidators(annotationType);
+        List<Class<? extends ConstraintValidator<A, ?>>> validatorClasses =
+            factory.getConstraintsCache().getConstraintValidatorClasses(annotationType);
         if (validatorClasses == null) {
-            validatorClasses = (Class<? extends ConstraintValidator<A, ?>>[]) vcAnno.validatedBy();
-            if (validatorClasses.length == 0) {
-                validatorClasses = factory.getDefaultConstraints().getValidatorClasses(annotationType);
+            validatorClasses = Arrays.asList((Class<? extends ConstraintValidator<A, ?>>[]) vcAnno.validatedBy());
+            if (validatorClasses.isEmpty()) {
             }
         }
-        return validatorClasses;
+        return validatorClasses.toArray(new Class[validatorClasses.size()]);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintAnnotationAttributes.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintAnnotationAttributes.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintAnnotationAttributes.java
index 24b38ea..df99bf9 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintAnnotationAttributes.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintAnnotationAttributes.java
@@ -16,6 +16,7 @@
  */
 package org.apache.bval.jsr;
 
+import org.apache.bval.util.Exceptions;
 import org.apache.bval.util.reflection.Reflection;
 import org.apache.bval.util.reflection.TypeUtils;
 import org.apache.commons.weaver.privilizer.Privilizing;
@@ -125,29 +126,24 @@ public enum ConstraintAnnotationAttributes {
     public <V> V get(Map<? super String, ? super V> map) {
         @SuppressWarnings("unchecked")
         final V result = (V) map.get(getAttributeName());
-        if (TypeUtils.isInstance(result, getType())) {
-            return result;
-        }
-        throw new IllegalStateException(String.format("Invalid '%s' value: %s", getAttributeName(), result));
+        Exceptions.raiseUnless(TypeUtils.isInstance(result, getType()), IllegalStateException::new,
+            "Invalid '%s' value: %s", getAttributeName(), result);
+        return result;
     }
 
     public <C extends Annotation> Worker<C> analyze(final Class<C> clazz) {
         if (clazz.getName().startsWith("javax.validation.constraint.")) { // cache only APIs classes to avoid memory leaks
             @SuppressWarnings("unchecked")
-            Worker<C> w = Worker.class.cast(WORKER_CACHE.get(clazz));
-            if (w == null) {
-                w = new Worker<C>(clazz);
-                WORKER_CACHE.putIfAbsent(clazz, w);
-                return w;
-            }
+            final Worker<C> w = (Worker<C>) WORKER_CACHE.computeIfAbsent(clazz, Worker::new);
+            return w;
         }
         return new Worker<C>(clazz);
     }
 
     // this is static but related to Worker
-    private static final ConcurrentMap<Class<?>, Worker<?>> WORKER_CACHE = new ConcurrentHashMap<Class<?>, Worker<?>>();
+    private static final ConcurrentMap<Class<?>, Worker<?>> WORKER_CACHE = new ConcurrentHashMap<>();
     private static final ConcurrentMap<Class<?>, ConcurrentMap<String, Method>> METHOD_BY_NAME_AND_CLASS =
-        new ConcurrentHashMap<Class<?>, ConcurrentMap<String, Method>>();
+        new ConcurrentHashMap<>();
     private static final Method NULL_METHOD;
     static {
         try {
@@ -171,14 +167,8 @@ public enum ConstraintAnnotationAttributes {
         }
 
         private Method findMethod(final Class<C> constraintType, final String attributeName) {
-            ConcurrentMap<String, Method> cache = METHOD_BY_NAME_AND_CLASS.get(constraintType);
-            if (cache == null) {
-                cache = new ConcurrentHashMap<String, Method>();
-                final ConcurrentMap<String, Method> old = METHOD_BY_NAME_AND_CLASS.putIfAbsent(constraintType, cache);
-                if (old != null) {
-                    cache = old;
-                }
-            }
+            ConcurrentMap<String, Method> cache =
+                METHOD_BY_NAME_AND_CLASS.computeIfAbsent(constraintType, t -> new ConcurrentHashMap<>());
 
             final Method found = cache.get(attributeName);
             if (found != null) {
@@ -189,15 +179,19 @@ public enum ConstraintAnnotationAttributes {
                 cache.putIfAbsent(attributeName, NULL_METHOD);
                 return null;
             }
-            final Method oldMtd = cache.putIfAbsent(attributeName, m);
-            if (oldMtd != null) {
-                return oldMtd;
-            }
-            return m;
+            return cache.computeIfAbsent(attributeName, s -> m);
         }
 
         public boolean isValid() {
-            return method != null && method != NULL_METHOD;
+            return method != null && method != NULL_METHOD && TypeUtils.isAssignable(method.getReturnType(), type);
+        }
+
+        /**
+         * @since 2.0
+         * @return {@link Type}
+         */
+        public Type getSpecificType() {
+            return isValid() ? method.getGenericReturnType() : type;
         }
 
         public <T> T read(final Annotation constraint) {

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java
index 1bb012f..a1f3924 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java
@@ -18,18 +18,92 @@
  */
 package org.apache.bval.jsr;
 
-import javax.validation.ConstraintValidator;
 import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.constraintvalidation.SupportedValidationTarget;
+import javax.validation.constraintvalidation.ValidationTarget;
+
+import org.apache.bval.jsr.metadata.AnnotationDeclaredValidatorMappingProvider;
+import org.apache.bval.jsr.metadata.CompositeValidatorMappingProvider;
+import org.apache.bval.jsr.metadata.DualValidationMappingProvider;
+import org.apache.bval.jsr.metadata.ValidatorMappingProvider;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.ObjectUtils;
+import org.apache.bval.util.Validate;
 
 /**
- * Description: hold the relationship annotation->validatedBy[] ConstraintValidator classes that are already parsed in a
- * cache.<br/>
+ * Description: hold the relationship annotation->validatedBy[]
+ * ConstraintValidator classes that are already parsed in a cache.<br/>
  */
 public class ConstraintCached {
-    private final Map<Class<? extends Annotation>, Class<? extends ConstraintValidator<?, ?>>[]> classes =
-        new HashMap<Class<? extends Annotation>, Class<? extends ConstraintValidator<?, ?>>[]>();
+
+    /**
+     * Describes a {@link ConstraintValidator} implementation type.
+     * 
+     * @since 2.0
+     */
+    public static final class ConstraintValidatorInfo<T extends Annotation> {
+        private static final Set<ValidationTarget> DEFAULT_VALIDATION_TARGETS =
+            Collections.singleton(ValidationTarget.ANNOTATED_ELEMENT);
+
+        private final Class<? extends ConstraintValidator<T, ?>> type;
+        private Set<ValidationTarget> supportedTargets;
+
+        ConstraintValidatorInfo(Class<? extends ConstraintValidator<T, ?>> type) {
+            super();
+            this.type = Validate.notNull(type);
+            final SupportedValidationTarget svt = type.getAnnotation(SupportedValidationTarget.class);
+
+            supportedTargets = svt == null ? DEFAULT_VALIDATION_TARGETS
+                : Collections.unmodifiableSet(EnumSet.copyOf(Arrays.asList(svt.value())));
+        }
+
+        public Class<? extends ConstraintValidator<T, ?>> getType() {
+            return type;
+        }
+
+        public Set<ValidationTarget> getSupportedTargets() {
+            return supportedTargets;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return obj == this
+                || obj instanceof ConstraintValidatorInfo<?> && ((ConstraintValidatorInfo<?>) obj).type.equals(type);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type);
+        }
+    }
+
+    private final Map<Class<? extends Annotation>, Set<ConstraintValidatorInfo<?>>> constraintValidatorInfo =
+        new HashMap<>();
+
+    private final List<ValidatorMappingProvider> customValidatorMappingProviders = new ArrayList<>();
+    private final Lazy<ValidatorMappingProvider> validatorMappingProvider =
+        new Lazy<>(this::createValidatorMappingProvider);
+
+    public void add(ValidatorMappingProvider validatorMappingProvider) {
+        if (customValidatorMappingProviders.add(validatorMappingProvider)) {
+            this.validatorMappingProvider.reset(this::createValidatorMappingProvider);
+        }
+    }
 
     /**
      * Record the set of validator classes for a given constraint annotation.
@@ -37,33 +111,80 @@ public class ConstraintCached {
      * @param annotationClass
      * @param definitionClasses
      */
+    @Deprecated
     public <A extends Annotation> void putConstraintValidator(Class<A> annotationClass,
         Class<? extends ConstraintValidator<A, ?>>[] definitionClasses) {
-        classes.put(annotationClass, definitionClasses);
+        if (ObjectUtils.isEmpty(definitionClasses)) {
+            return;
+        }
+        Validate.notNull(annotationClass, "annotationClass");
+        Stream.of(definitionClasses).map(t -> new ConstraintValidatorInfo<>(t))
+            .forEach(constraintValidatorInfo.computeIfAbsent(annotationClass, k -> new HashSet<>())::add);
     }
 
     /**
-     * Learn whether we have cached the validator classes for the requested constraint annotation.
+     * Learn whether we have cached the validator classes for the requested
+     * constraint annotation.
      * 
      * @param annotationClass
      *            to look up
      * @return boolean
      */
+    @Deprecated
     public boolean containsConstraintValidator(Class<? extends Annotation> annotationClass) {
-        return classes.containsKey(annotationClass);
+        return constraintValidatorInfo.containsKey(annotationClass);
     }
 
     /**
      * Get the cached validator classes for the requested constraint annotation.
      * 
-     * @param annotationClass
+     * @param constraintType
      *            to look up
      * @return array of {@link ConstraintValidator} implementation types
      */
     @SuppressWarnings("unchecked")
+    @Deprecated
     public <A extends Annotation> Class<? extends ConstraintValidator<A, ?>>[] getConstraintValidators(
-        Class<A> annotationClass) {
-        return (Class<? extends ConstraintValidator<A, ?>>[]) classes.get(annotationClass);
+        Class<A> constraintType) {
+        final Set<ConstraintValidatorInfo<A>> infos = infos(constraintType);
+        return infos == null ? new Class[0]
+            : infos.stream().map(ConstraintValidatorInfo::getType).toArray(Class[]::new);
     }
 
+    public <A extends Annotation> List<Class<? extends ConstraintValidator<A, ?>>> getConstraintValidatorClasses(
+        Class<A> constraintType) {
+        final Set<ConstraintValidatorInfo<A>> infos = infos(constraintType);
+        return infos == null ? Collections.emptyList()
+            : infos.stream().map(ConstraintValidatorInfo::getType).collect(ToUnmodifiable.list());
+    }
+
+    public <A extends Annotation> Set<ConstraintValidatorInfo<A>> getConstraintValidatorInfo(Class<A> constraintType) {
+        return Collections.unmodifiableSet(infos(constraintType));
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private <A extends Annotation> Set<ConstraintValidatorInfo<A>> infos(Class<A> constraintType) {
+        return (Set) constraintValidatorInfo.computeIfAbsent(constraintType,
+            c -> validatorMappingProvider.get().getValidatorMapping(c).getValidatorTypes().stream()
+                .map(ConstraintValidatorInfo::new).collect(Collectors.toSet()));
+    }
+
+    private ValidatorMappingProvider createValidatorMappingProvider() {
+        final ValidatorMappingProvider configured;
+        if (customValidatorMappingProviders.isEmpty()) {
+            configured = AnnotationDeclaredValidatorMappingProvider.INSTANCE;
+        } else {
+            final ValidatorMappingProvider custom;
+            if (customValidatorMappingProviders.size() == 1) {
+                custom = customValidatorMappingProviders.get(0);
+            } else {
+                custom = new CompositeValidatorMappingProvider(customValidatorMappingProviders);
+            }
+            configured = new DualValidationMappingProvider(AnnotationDeclaredValidatorMappingProvider.INSTANCE, custom);
+        }
+        // interpret spec as saying that default constraint validators are
+        // always present even when annotation-based validators
+        // have been excluded by custom (i.e. XML) config:
+        return new DualValidationMappingProvider(configured, ConstraintDefaults.INSTANCE);
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDefaults.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDefaults.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDefaults.java
index 3e3771e..9ea93e7 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDefaults.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDefaults.java
@@ -21,101 +21,70 @@ package org.apache.bval.jsr;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.annotation.Annotation;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
 import java.util.Properties;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import javax.validation.ConstraintValidator;
 
+import org.apache.bval.jsr.metadata.ClassLoadingValidatorMappingProvider;
+import org.apache.bval.jsr.metadata.ValidatorMapping;
 import org.apache.bval.util.StringUtils;
 import org.apache.bval.util.reflection.Reflection;
 import org.apache.commons.weaver.privilizer.Privilizing;
 import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
 
 /**
- * Description: Provides access to the default constraints/validator implementation classes built into the framework.
- * These are configured in DefaultConstraints.properties.<br/>
+ * Description: Provides access to the default constraints/validator
+ * implementation classes built into the framework. These are configured in
+ * DefaultConstraints.properties.<br/>
  */
 @Privilizing(@CallTo(Reflection.class))
-public class ConstraintDefaults {
+public class ConstraintDefaults extends ClassLoadingValidatorMappingProvider {
+    public static final ConstraintDefaults INSTANCE = new ConstraintDefaults();
+
     private static final Logger log = Logger.getLogger(ConstraintDefaults.class.getName());
     private static final String DEFAULT_CONSTRAINTS = "org/apache/bval/jsr/DefaultConstraints.properties";
 
-    /**
-     * The default constraint data stored herein.
-     */
-    private Map<String, Class<? extends ConstraintValidator<?, ?>>[]> defaultConstraints;
+    private final Properties properties;
 
     /**
      * Create a new ConstraintDefaults instance.
      */
-    public ConstraintDefaults() {
-        defaultConstraints = loadDefaultConstraints(DEFAULT_CONSTRAINTS);
-    }
-
-    /**
-     * Get the default constraint data.
-     * @return String-keyed map
-     */
-    public Map<String, Class<? extends ConstraintValidator<?, ?>>[]> getDefaultConstraints() {
-        return defaultConstraints;
-    }
-
-    /**
-     * Get the default validator implementation types for the specified constraint annotation type. 
-     * @param annotationType the annotation type
-     * @return array of {@link ConstraintValidator} implementation classes
-     */
-    @SuppressWarnings("unchecked")
-    public <A extends Annotation> Class<? extends ConstraintValidator<A, ?>>[] getValidatorClasses(
-        Class<A> annotationType) {
-        return (Class<? extends ConstraintValidator<A, ?>>[]) getDefaultConstraints().get(annotationType.getName());
+    private ConstraintDefaults() {
+        this.properties = loadProperties(DEFAULT_CONSTRAINTS);
     }
 
-    @SuppressWarnings("unchecked")
-    private Map<String, Class<? extends ConstraintValidator<?, ?>>[]> loadDefaultConstraints(String resource) {
-        final Properties constraintProperties = new Properties();
+    private Properties loadProperties(String resource) {
+        final Properties result = new Properties();
         final ClassLoader classloader = getClassLoader();
-        final InputStream stream = classloader.getResourceAsStream(resource);
-        if (stream == null) {
-            log.log(Level.WARNING, String.format("Cannot find %s", resource));
-        } else {
-            try {
-                constraintProperties.load(stream);
-            } catch (IOException e) {
-                log.log(Level.SEVERE, String.format("Cannot load %s", resource), e);
-            } finally {
-                try {
-                    stream.close();
-                } catch (final IOException e) {
-                    // no-op
-                }
+        try (final InputStream stream = classloader.getResourceAsStream(resource)) {
+            if (stream == null) {
+                log.log(Level.WARNING, String.format("Cannot find %s", resource));
+            } else {
+                result.load(stream);
             }
+        } catch (IOException e) {
+            log.log(Level.SEVERE, String.format("Cannot load %s", resource), e);
         }
+        return result;
+    }
 
-        final Map<String, Class<? extends ConstraintValidator<?, ?>>[]> loadedConstraints =
-            new HashMap<String, Class<? extends ConstraintValidator<?, ?>>[]>();
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @Override
+    public <A extends Annotation> ValidatorMapping<A> doGetValidatorMapping(Class<A> constraintType) {
 
-        for (final Map.Entry<Object, Object> entry : constraintProperties.entrySet()) {
-            final List<Class<?>> classes = new LinkedList<Class<?>>();
-            for (String className : StringUtils.split((String) entry.getValue(), ',')) {
-                try {
-                    classes.add(Reflection.toClass(className.trim(), classloader));
-                } catch (Exception e) {
-                    log.log(Level.SEVERE, String.format("Cannot find class %s", className), e);
-                }
-            }
-            loadedConstraints.put((String) entry.getKey(), classes.toArray(new Class[classes.size()]));
-        }
-        return loadedConstraints;
-    }
+        final String validators = properties.getProperty(constraintType.getName());
 
-    private ClassLoader getClassLoader() {
-        final ClassLoader classloader = Thread.currentThread().getContextClassLoader();
-        return classloader == null ? getClass().getClassLoader() : classloader;
+        if (StringUtils.isBlank(validators)) {
+            return null;
+        }
+        return new ValidatorMapping<>("built-in",
+            load(Stream.of(StringUtils.split(validators, ',')).map(String::trim),
+                (Class<ConstraintValidator<A, ?>>) (Class) ConstraintValidator.class,
+                e -> log.log(Level.SEVERE, "exception loading default constraint validators", e))
+                    .collect(Collectors.toList()));
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDescriptorImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDescriptorImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDescriptorImpl.java
index a56e1e1..c4c9d99 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDescriptorImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDescriptorImpl.java
@@ -21,6 +21,7 @@ package org.apache.bval.jsr;
 import javax.validation.ConstraintTarget;
 import javax.validation.Payload;
 import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ValidateUnwrappedValue;
 
 import java.io.Serializable;
 import java.lang.annotation.Annotation;
@@ -220,4 +221,16 @@ public class ConstraintDescriptorImpl<T extends Annotation> implements Constrain
         result = 31 * result + (template != null ? template.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public ValidateUnwrappedValue getValueUnwrapping() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public <U> U unwrap(Class<U> arg0) {
+        // TODO Auto-generated method stub
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidation.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidation.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidation.java
index 5b51141..5ba14ca 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidation.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidation.java
@@ -39,6 +39,8 @@ import javax.validation.ValidationException;
 import javax.validation.constraintvalidation.SupportedValidationTarget;
 import javax.validation.constraintvalidation.ValidationTarget;
 import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ValidateUnwrappedValue;
+
 import java.io.Serializable;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Array;
@@ -54,6 +56,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Description: Adapter between Constraint (JSR303) and Validation (Core)<br/>
@@ -328,10 +331,12 @@ public class ConstraintValidation<T extends Annotation> implements Validation, C
             throw new UnexpectedTypeException(message);
         }
         if (types.size() > 1) {
-            throw new UnexpectedTypeException(
-                String.format("Ambiguous validators for type %s. See: @%s at %s. Validators are: %s",
-                    stringForType(targetType), anno.annotationType().getSimpleName(), stringForLocation(owner, access),
-                    StringUtils.join(types, ", ")));
+            throw new UnexpectedTypeException(String.format(
+                "Ambiguous validators for type %s. See: @%s at %s. Validators are: %s",
+                stringForType(targetType),
+                anno.annotationType().getSimpleName(),
+                stringForLocation(owner, access), types.stream()
+                    .map(Object::toString).collect(Collectors.joining(", "))));
         }
     }
 
@@ -524,9 +529,13 @@ public class ConstraintValidation<T extends Annotation> implements Validation, C
      * {@inheritDoc}
      */
     @Override
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     public Set<ConstraintDescriptor<?>> getComposingConstraints() {
-        return composedConstraints == null ? Collections.EMPTY_SET : composedConstraints;
+        if (composedConstraints == null) {
+            return Collections.emptySet();
+        }
+        final Set result = composedConstraints;
+        return result;
     }
 
     /**
@@ -581,4 +590,16 @@ public class ConstraintValidation<T extends Annotation> implements Validation, C
     public void setValidated(final boolean validated) {
         this.validated = validated;
     }
+
+    @Override
+    public ValidateUnwrappedValue getValueUnwrapping() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public <U> U unwrap(Class<U> arg0) {
+        // TODO Auto-generated method stub
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidatorContextImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidatorContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidatorContextImpl.java
index 3599603..930170d 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidatorContextImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidatorContextImpl.java
@@ -25,6 +25,7 @@ import org.apache.bval.jsr.util.NodeImpl;
 import org.apache.bval.jsr.util.PathImpl;
 import org.apache.bval.model.ValidationListener;
 
+import javax.validation.ClockProvider;
 import javax.validation.ConstraintValidator;
 import javax.validation.ConstraintValidatorContext;
 import javax.validation.Path;
@@ -38,7 +39,10 @@ import java.util.List;
  * Description: Short-lived {@link ConstraintValidatorContext} implementation passed by
  * a {@link ConstraintValidation} to its adapted {@link ConstraintValidator}. <br/>
  */
-public class ConstraintValidatorContextImpl implements ConstraintValidatorContext {
+@Deprecated
+public class ConstraintValidatorContextImpl
+    extends org.apache.bval.jsr.job.ConstraintValidatorContextImpl<Object>
+    implements ConstraintValidatorContext {
     private final List<ValidationListener.Error> errorMessages = new LinkedList<ValidationListener.Error>();
 
     private final ConstraintValidation<?> constraintDescriptor;
@@ -53,6 +57,7 @@ public class ConstraintValidatorContextImpl implements ConstraintValidatorContex
      */
     public ConstraintValidatorContextImpl(GroupValidationContext<?> validationContext,
         ConstraintValidation<?> aConstraintValidation) {
+        super();
         this.validationContext = validationContext;
         this.constraintDescriptor = aConstraintValidation;
     }
@@ -154,6 +159,13 @@ public class ConstraintValidatorContextImpl implements ConstraintValidatorContex
             parent.addError(messageTemplate, propertyPath);
             return parent;
         }
+
+        @Override
+        public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(
+            String arg0, Class<?> arg1, Integer arg2) {
+            // TODO Auto-generated method stub
+            return null;
+        }
     }
 
     /**
@@ -190,4 +202,10 @@ public class ConstraintValidatorContextImpl implements ConstraintValidatorContex
     public void addError(String messageTemplate, Path propertyPath) {
         errorMessages.add(new ValidationListener.Error(messageTemplate, propertyPath, null));
     }
+
+    @Override
+    public ClockProvider getClockProvider() {
+        // TODO Auto-generated method stub
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidatorIdentity.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidatorIdentity.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidatorIdentity.java
index 1092323..572c39a 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidatorIdentity.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintValidatorIdentity.java
@@ -19,6 +19,8 @@
 
 package org.apache.bval.jsr;
 
+import java.util.Objects;
+
 import javax.validation.ConstraintValidator;
 import javax.validation.Path;
 
@@ -120,12 +122,7 @@ final class ConstraintValidatorIdentity {
      */
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((this.bean == null) ? 0 : this.bean.hashCode());
-        result = prime * result + ((this.path == null) ? 0 : this.path.hashCode());
-        result = prime * result + ((this.constraintValidator == null) ? 0 : this.constraintValidator.hashCode());
-        return result;
+        return Objects.hash(bean, path, constraintValidator);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintViolationImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintViolationImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintViolationImpl.java
index c367b8e..91ae20d 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintViolationImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintViolationImpl.java
@@ -20,15 +20,20 @@ import javax.validation.ConstraintViolation;
 import javax.validation.Path;
 import javax.validation.ValidationException;
 import javax.validation.metadata.ConstraintDescriptor;
+
+import org.apache.bval.util.Exceptions;
+
 import java.io.Serializable;
 import java.lang.annotation.ElementType;
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Description: Describe a constraint validation defect.<br/>
- * From rootBean and propertyPath, it is possible to rebuild the context of the failure
+ * From rootBean and propertyPath, it is possible to rebuild the context of the
+ * failure
  */
-class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable {
+public class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable {
     /** Serialization version */
     private static final long serialVersionUID = 1L;
 
@@ -49,8 +54,11 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable
 
     /**
      * Create a new ConstraintViolationImpl instance.
-     * @param messageTemplate - message reason (raw message)
-     * @param message - interpolated message (locale specific)
+     * 
+     * @param messageTemplate
+     *            - message reason (raw message)
+     * @param message
+     *            - interpolated message (locale specific)
      * @param rootBean
      * @param leafBean
      * @param propertyPath
@@ -79,8 +87,8 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable
     }
 
     /**
-     * {@inheritDoc}
-     * former name getInterpolatedMessage()
+     * {@inheritDoc} former name getInterpolatedMessage()
+     * 
      * @return The interpolated error message for this constraint violation.
      */
     @Override
@@ -98,6 +106,7 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable
 
     /**
      * {@inheritDoc}
+     * 
      * @return Root bean being validated
      */
     @Override
@@ -133,6 +142,7 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable
 
     /**
      * {@inheritDoc}
+     * 
      * @return The value failing to pass the constraint
      */
     @Override
@@ -142,8 +152,9 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable
 
     /**
      * {@inheritDoc}
-     * @return the property path to the value from <code>rootBean</code>
-     *         Null if the value is the rootBean itself
+     * 
+     * @return the property path to the value from <code>rootBean</code> Null if
+     *         the value is the rootBean itself
      */
     @Override
     public Path getPropertyPath() {
@@ -160,10 +171,8 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable
 
     @Override
     public <U> U unwrap(Class<U> type) {
-        if (type.isInstance(this)) {
-            return type.cast(this);
-        }
-        throw new ValidationException("Type " + type + " is not supported");
+        Exceptions.raiseUnless(type.isInstance(this), ValidationException::new, "Type %s is not supported", type);
+        return type.cast(this);
     }
 
     /**
@@ -171,45 +180,28 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable
      */
     @Override
     public String toString() {
-        return "ConstraintViolationImpl{" + "rootBean=" + rootBean + ", propertyPath='" + propertyPath + '\''
-            + ", message='" + message + '\'' + ", leafBean=" + leafBean + ", value=" + value + '}';
+        return String.format("%s{rootBean=%s, propertyPath='%s', message='%s', leafBean=%s, value=%s}",
+            ConstraintViolationImpl.class.getSimpleName(), rootBean, propertyPath, message, leafBean, value);
     }
 
     @Override
     public boolean equals(Object o) {
-        if (this == o)
+        if (this == o) {
             return true;
-        if (o == null || getClass() != o.getClass())
+        }
+        if (o == null || !getClass().equals(o.getClass())) {
             return false;
+        }
 
-        ConstraintViolationImpl that = (ConstraintViolationImpl) o;
-
-        if (constraintDescriptor != null ? !constraintDescriptor.equals(that.constraintDescriptor)
-            : that.constraintDescriptor != null)
-            return false;
-        if (elementType != that.elementType)
-            return false;
-        if (leafBean != null ? !leafBean.equals(that.leafBean) : that.leafBean != null)
-            return false;
-        if (message != null ? !message.equals(that.message) : that.message != null)
-            return false;
-        if (messageTemplate != null ? !messageTemplate.equals(that.messageTemplate) : that.messageTemplate != null)
-            return false;
-        // Probably incorrect - comparing Object[] arrays with Arrays.equals
-        if (!Arrays.equals(parameters, that.parameters))
-            return false;
-        if (propertyPath != null ? !propertyPath.equals(that.propertyPath) : that.propertyPath != null)
-            return false;
-        if (returnValue != null ? !returnValue.equals(that.returnValue) : that.returnValue != null)
-            return false;
-        if (rootBean != null ? !rootBean.equals(that.rootBean) : that.rootBean != null)
-            return false;
-        if (rootBeanClass != null ? !rootBeanClass.equals(that.rootBeanClass) : that.rootBeanClass != null)
-            return false;
-        if (value != null ? !value.equals(that.value) : that.value != null)
-            return false;
+        @SuppressWarnings("rawtypes")
+        final ConstraintViolationImpl that = (ConstraintViolationImpl) o;
 
-        return true;
+        return Objects.equals(constraintDescriptor, that.constraintDescriptor) && elementType == that.elementType
+            && Objects.equals(leafBean, that.leafBean) && Objects.equals(message, that.message)
+            && Objects.equals(messageTemplate, that.messageTemplate) && Arrays.equals(parameters, that.parameters)
+            && Objects.equals(propertyPath, that.propertyPath) && Objects.equals(returnValue, that.returnValue)
+            && Objects.equals(rootBean, that.rootBean) && Objects.equals(rootBeanClass, that.rootBeanClass)
+            && Objects.equals(value, that.value);
     }
 
     @Override
@@ -217,18 +209,10 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable
         return hashCode;
     }
 
-    public int computeHashCode() {
-        int result = messageTemplate != null ? messageTemplate.hashCode() : 0;
-        result = 31 * result + (message != null ? message.hashCode() : 0);
-        result = 31 * result + (rootBean != null ? rootBean.hashCode() : 0);
-        result = 31 * result + (rootBeanClass != null ? rootBeanClass.hashCode() : 0);
-        result = 31 * result + (leafBean != null ? leafBean.hashCode() : 0);
-        result = 31 * result + (value != null ? value.hashCode() : 0);
-        result = 31 * result + (propertyPath != null ? propertyPath.hashCode() : 0);
-        result = 31 * result + (elementType != null ? elementType.hashCode() : 0);
-        result = 31 * result + (constraintDescriptor != null ? constraintDescriptor.hashCode() : 0);
-        result = 31 * result + (returnValue != null ? returnValue.hashCode() : 0);
-        result = 31 * result + (parameters != null ? Arrays.hashCode(parameters) : 0);
+    private int computeHashCode() {
+        int result = Objects.hash(messageTemplate, message, rootBean, rootBeanClass, leafBean, value, propertyPath,
+            elementType, constraintDescriptor, returnValue);
+        result = 31 * result + (parameters == null ? 0 : Arrays.hashCode(parameters));
         return result;
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java b/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java
index 4aca48a..9474705 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java
@@ -32,8 +32,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
  * Description: create constraint instances with the default / no-arg constructor <br/>
  */
 public class DefaultConstraintValidatorFactory implements ConstraintValidatorFactory, Closeable {
-    private final Collection<BValExtension.Releasable<?>> releasables =
-        new CopyOnWriteArrayList<BValExtension.Releasable<?>>();
+    private final Collection<BValExtension.Releasable<?>> releasables = new CopyOnWriteArrayList<>();
     private volatile Boolean useCdi = null; // store it to avoid NoClassDefFoundError when cdi is not present (it is slow) + lazily (to wait cdi is started)
 
     /**
@@ -49,9 +48,7 @@ public class DefaultConstraintValidatorFactory implements ConstraintValidatorFac
                 if (useCdi == null) {
                     try {
                         useCdi = BValExtension.getBeanManager() != null;
-                    } catch (final NoClassDefFoundError error) {
-                        useCdi = Boolean.FALSE;
-                    } catch (final Exception e) {
+                    } catch (NoClassDefFoundError | Exception error) {
                         useCdi = Boolean.FALSE;
                     }
                 }
@@ -69,10 +66,7 @@ public class DefaultConstraintValidatorFactory implements ConstraintValidatorFac
                         return instance.getInstance();
                     }
                     throw new IllegalStateException("Can't create " + constraintClass.getName());
-                } catch (final Exception e) {
-                    return constraintClass.newInstance();
-                } catch (final NoClassDefFoundError error) {
-                    return constraintClass.newInstance();
+                } catch (Exception | NoClassDefFoundError e) {
                 }
             }
             return constraintClass.newInstance();

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java b/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java
index 8c77162..6a85a2a 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java
@@ -54,10 +54,10 @@ public class DefaultMessageInterpolator implements MessageInterpolator {
     private Locale defaultLocale;
 
     /** User specified resource bundles hashed against their locale. */
-    private final Map<Locale, ResourceBundle> userBundlesMap = new ConcurrentHashMap<Locale, ResourceBundle>();
+    private final Map<Locale, ResourceBundle> userBundlesMap = new ConcurrentHashMap<>();
 
     /** Builtin resource bundles hashed against their locale. */
-    private final Map<Locale, ResourceBundle> defaultBundlesMap = new ConcurrentHashMap<Locale, ResourceBundle>();
+    private final Map<Locale, ResourceBundle> defaultBundlesMap = new ConcurrentHashMap<>();
 
     private final MessageEvaluator evaluator;
 
@@ -83,12 +83,12 @@ public class DefaultMessageInterpolator implements MessageInterpolator {
             userBundlesMap.put(defaultLocale, resourceBundle);
         }
 
-        MessageEvaluator ev = null;
+        MessageEvaluator ev;
         try {
             ev = MessageEvaluator.class
                 .cast(getClass().getClassLoader().loadClass("org.apache.bval.el.ELFacade").newInstance());
         } catch (final Throwable e) { // can be exception or error
-            // no-op
+            ev = null;
         }
         evaluator = ev;
     }
@@ -170,47 +170,42 @@ public class DefaultMessageInterpolator implements MessageInterpolator {
      * @return the resource bundle or <code>null</code> if none is found.
      */
     private ResourceBundle getFileBasedResourceBundle(Locale locale) {
-        ResourceBundle rb = null;
+        ResourceBundle rb;
         final ClassLoader classLoader = Reflection.getClassLoader(DefaultMessageInterpolator.class);
         if (classLoader != null) {
             rb = loadBundle(classLoader, locale, USER_VALIDATION_MESSAGES + " not found by thread local classloader");
-        }
-
+        } else {
         // 2011-03-27 jw: No privileged action required.
         // A class can always access the classloader of itself and of subclasses.
-        if (rb == null) {
             rb = loadBundle(getClass().getClassLoader(), locale,
                 USER_VALIDATION_MESSAGES + " not found by validator classloader");
         }
         if (LOG_FINEST) {
-            if (rb != null) {
-                log.log(Level.FINEST, String.format("%s found", USER_VALIDATION_MESSAGES));
-            } else {
+            if (rb == null) {
                 log.log(Level.FINEST, String.format("%s not found. Delegating to %s", USER_VALIDATION_MESSAGES,
                     DEFAULT_VALIDATION_MESSAGES));
+            } else {
+                log.log(Level.FINEST, String.format("%s found", USER_VALIDATION_MESSAGES));
             }
         }
         return rb;
     }
 
     private ResourceBundle loadBundle(ClassLoader classLoader, Locale locale, String message) {
-        ResourceBundle rb = null;
         try {
-            rb = ResourceBundle.getBundle(USER_VALIDATION_MESSAGES, locale, classLoader);
+            return ResourceBundle.getBundle(USER_VALIDATION_MESSAGES, locale, classLoader);
         } catch (final MissingResourceException e) {
             log.fine(message);
         }
-        return rb;
+        return null;
     }
 
     private String replaceVariables(String message, ResourceBundle bundle, Locale locale, boolean recurse) {
         final Matcher matcher = messageParameterPattern.matcher(message);
         final StringBuffer sb = new StringBuffer(64);
-        String resolvedParameterValue;
         while (matcher.find()) {
             final String parameter = matcher.group(1);
-            resolvedParameterValue = resolveParameter(parameter, bundle, locale, recurse);
-
+            String resolvedParameterValue = resolveParameter(parameter, bundle, locale, recurse);
             matcher.appendReplacement(sb, sanitizeForAppendReplacement(resolvedParameterValue));
         }
         matcher.appendTail(sb);
@@ -242,13 +237,13 @@ public class DefaultMessageInterpolator implements MessageInterpolator {
     private String resolveParameter(String parameterName, ResourceBundle bundle, Locale locale, boolean recurse) {
         String parameterValue;
         try {
-            if (bundle != null) {
+            if (bundle == null) {
+                parameterValue = parameterName;
+            } else {
                 parameterValue = bundle.getString(removeCurlyBrace(parameterName));
                 if (recurse) {
                     parameterValue = replaceVariables(parameterValue, bundle, locale, recurse);
                 }
-            } else {
-                parameterValue = parameterName;
             }
         } catch (final MissingResourceException e) {
             // return parameter itself

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java b/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java
index 671b0d9..e63bdf4 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java
@@ -38,6 +38,11 @@ public class DefaultValidationProviderResolver implements ValidationProviderReso
     //TODO - Spec recommends caching per classloader
     private static final String SPI_CFG = "META-INF/services/javax.validation.spi.ValidationProvider";
 
+    private static ClassLoader getCurrentClassLoader() {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        return cl == null ? DefaultValidationProviderResolver.class.getClassLoader() : cl;
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -46,43 +51,28 @@ public class DefaultValidationProviderResolver implements ValidationProviderReso
         List<ValidationProvider<?>> providers = new ArrayList<ValidationProvider<?>>();
         try {
             // get our classloader
-            ClassLoader cl = Thread.currentThread().getContextClassLoader();
-            if (cl == null)
-                cl = DefaultValidationProviderResolver.class.getClassLoader();
+            ClassLoader cl = getCurrentClassLoader();
             // find all service provider cfgs
             Enumeration<URL> cfgs = cl.getResources(SPI_CFG);
             while (cfgs.hasMoreElements()) {
                 final URL url = cfgs.nextElement();
-                BufferedReader br = null;
-                try {
-                    br = new BufferedReader(new InputStreamReader(url.openStream()), 256);
-                    String line = br.readLine();
-                    // cfgs may contain multiple providers and/or comments
-                    while (line != null) {
-                        line = line.trim();
-                        if (!line.startsWith("#")) {
-                            try {
-                                // try loading the specified class
-                                @SuppressWarnings("rawtypes")
-                                final Class<? extends ValidationProvider> providerType =
-                                    cl.loadClass(line).asSubclass(ValidationProvider.class);
-                                // create an instance to return
-                                providers
-                                    .add(Reflection.newInstance(providerType.asSubclass(ValidationProvider.class)));
-
-                            } catch (ClassNotFoundException e) {
-                                throw new ValidationException(
-                                    "Failed to load provider " + line + " configured in file " + url, e);
-                            }
+                try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()), 256)) {
+                    br.lines().filter(s -> s.charAt(0) != '#').map(String::trim).forEach(line -> {
+                        // cfgs may contain multiple providers and/or comments
+                        try {
+                            // try loading the specified class
+                            @SuppressWarnings("rawtypes")
+                            final Class<? extends ValidationProvider> providerType =
+                                cl.loadClass(line).asSubclass(ValidationProvider.class);
+                            // create an instance to return
+                            providers.add(Reflection.newInstance(providerType));
+                        } catch (ClassNotFoundException e) {
+                            throw new ValidationException(
+                                "Failed to load provider " + line + " configured in file " + url, e);
                         }
-                        line = br.readLine();
-                    }
+                    });
                 } catch (IOException e) {
                     throw new ValidationException("Error trying to read " + url, e);
-                } finally {
-                    if (br != null) {
-                        br.close();
-                    }
                 }
             }
         } catch (IOException e) {

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/GraphBeanIdentity.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/GraphBeanIdentity.java b/bval-jsr/src/main/java/org/apache/bval/jsr/GraphBeanIdentity.java
index 3ec666e..26391e6 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/GraphBeanIdentity.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/GraphBeanIdentity.java
@@ -18,6 +18,8 @@
  */
 package org.apache.bval.jsr;
 
+import java.util.Objects;
+
 /**
  * Class that stores the needed properties to avoid circular paths when
  * validating an object graph.
@@ -80,32 +82,16 @@ public class GraphBeanIdentity {
      */
     @Override
     public boolean equals(Object obj) {
-
         if (this == obj) {
             return true;
         }
-
-        if (obj == null) {
-            return false;
-        }
-
         if (!(obj instanceof GraphBeanIdentity)) {
             return false;
         }
-
         GraphBeanIdentity other = (GraphBeanIdentity) obj;
 
-        // Bean ref must be the same
-        if (this.bean != other.bean) {
-            return false;
-        }
-
-        // Group ref must be the same
-        if (this.group != other.group) {
-            return false;
-        }
-
-        return true;
+        // Bean ref must be the same; Group ref must be the same
+        return bean == other.bean && group == other.group;
     }
 
     /**
@@ -113,11 +99,7 @@ public class GraphBeanIdentity {
      */
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((this.bean == null) ? 0 : this.bean.hashCode());
-        result = prime * result + ((this.group == null) ? 0 : this.group.hashCode());
-        return result;
+        return Objects.hash(bean, group);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/ParameterDescriptorImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ParameterDescriptorImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ParameterDescriptorImpl.java
index 187fd7e..65f3ecc 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ParameterDescriptorImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ParameterDescriptorImpl.java
@@ -21,6 +21,7 @@ import org.apache.bval.jsr.groups.GroupConversionDescriptorImpl;
 import org.apache.bval.model.MetaBean;
 import org.apache.bval.model.Validation;
 
+import javax.validation.metadata.ContainerElementTypeDescriptor;
 import javax.validation.metadata.GroupConversionDescriptor;
 import javax.validation.metadata.ParameterDescriptor;
 import java.util.Set;
@@ -86,4 +87,10 @@ public class ParameterDescriptorImpl extends ElementDescriptorImpl implements Pa
         groupConversions.add(new GroupConversionDescriptorImpl(from, to));
         super.addGroupMapping(from, to);
     }
+
+    @Override
+    public Set<ContainerElementTypeDescriptor> getConstrainedContainerElementTypes() {
+        // TODO Auto-generated method stub
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/PropertyDescriptorImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/PropertyDescriptorImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/PropertyDescriptorImpl.java
index 7f7c56d..03cf5de 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/PropertyDescriptorImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/PropertyDescriptorImpl.java
@@ -21,6 +21,9 @@ package org.apache.bval.jsr;
 import org.apache.bval.model.Features;
 import org.apache.bval.model.MetaProperty;
 
+import java.util.Set;
+
+import javax.validation.metadata.ContainerElementTypeDescriptor;
 import javax.validation.metadata.PropertyDescriptor;
 
 /**
@@ -67,4 +70,10 @@ class PropertyDescriptorImpl extends ElementDescriptorImpl implements PropertyDe
         return "PropertyDescriptorImpl{" + "returnType=" + elementClass + ", propertyPath='" + propertyPath + '\''
             + '}';
     }
+
+    @Override
+    public Set<ContainerElementTypeDescriptor> getConstrainedContainerElementTypes() {
+        // TODO Auto-generated method stub
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/ReturnValueDescriptorImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ReturnValueDescriptorImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ReturnValueDescriptorImpl.java
index b1fc72d..a6faa9b 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ReturnValueDescriptorImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ReturnValueDescriptorImpl.java
@@ -18,8 +18,10 @@ package org.apache.bval.jsr;
 
 import org.apache.bval.model.MetaBean;
 
+import javax.validation.metadata.ContainerElementTypeDescriptor;
 import javax.validation.metadata.ReturnValueDescriptor;
 import java.util.Collection;
+import java.util.Set;
 
 public class ReturnValueDescriptorImpl extends ElementDescriptorImpl implements ReturnValueDescriptor {
     public ReturnValueDescriptorImpl(final MetaBean metaBean, Class<?> returnType,
@@ -32,4 +34,10 @@ public class ReturnValueDescriptorImpl extends ElementDescriptorImpl implements
     public boolean hasConstraints() {
         return false;
     }
+
+    @Override
+    public Set<ContainerElementTypeDescriptor> getConstrainedContainerElementTypes() {
+        // TODO Auto-generated method stub
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Group.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Group.java b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Group.java
index 4f9d10a..6a211ed 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Group.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Group.java
@@ -18,6 +18,8 @@
  */
 package org.apache.bval.jsr.groups;
 
+import java.util.Objects;
+
 import javax.validation.groups.Default;
 
 /**
@@ -52,7 +54,7 @@ public final class Group {
      */
     @Override
     public String toString() {
-        return "Group{" + "group=" + group + '}';
+        return String.format("%s{group=%s}", Group.class.getSimpleName(), group);
     }
 
     /**
@@ -71,13 +73,10 @@ public final class Group {
         if (this == o) {
             return true;
         }
-        if (o == null || getClass() != o.getClass()) {
+        if (o == null || !getClass().equals(o.getClass())) {
             return false;
         }
-
-        Group group1 = (Group) o;
-
-        return group != null ? group.equals(group1.group) : group1.group == null;
+        return Objects.equals(group, ((Group) o).group);
     }
 
     /**
@@ -85,6 +84,6 @@ public final class Group {
      */
     @Override
     public int hashCode() {
-        return (group != null ? group.hashCode() : 0);
+        return Objects.hashCode(group);
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupConversionDescriptorImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupConversionDescriptorImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupConversionDescriptorImpl.java
index ba3a617..6d45ced 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupConversionDescriptorImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupConversionDescriptorImpl.java
@@ -26,10 +26,9 @@ public class GroupConversionDescriptorImpl implements GroupConversionDescriptor
 
     public GroupConversionDescriptorImpl(final Group from, final Group to) {
         this.from = from.getGroup();
-        if (this.from.getAnnotation(GroupSequence.class) != null) {
+        if (this.from.isAnnotationPresent(GroupSequence.class)) {
             throw new ConstraintDeclarationException("from() can't get a group sequence");
         }
-
         this.to = to.getGroup();
     }
 

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Groups.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Groups.java b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Groups.java
index 162bb66..3e7f008 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Groups.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Groups.java
@@ -18,25 +18,29 @@
  */
 package org.apache.bval.jsr.groups;
 
-import javax.validation.GroupDefinitionException;
-import java.util.LinkedList;
+import java.util.ArrayList;
 import java.util.List;
 
+import javax.validation.GroupDefinitionException;
+
+import org.apache.bval.util.Exceptions;
+
 /**
- * Defines the order to validate groups during validation.
- * with some inspiration from reference implementation
+ * Defines the order to validate groups during validation. with some inspiration
+ * from reference implementation
  *
  * @author Roman Stumm
  */
 public class Groups {
-    /** The list of single groups. */
-    final List<Group> groups = new LinkedList<Group>();
-
     /** The list of sequences. */
-    final List<List<Group>> sequences = new LinkedList<List<Group>>();
+    private final List<List<Group>> sequences = new ArrayList<>();
+
+    /** The list of single groups. */
+    final List<Group> groups = new ArrayList<>();
 
     /**
      * Get the Groups.
+     * 
      * @return {@link List} of {@link Group}.
      */
     public List<Group> getGroups() {
@@ -45,6 +49,7 @@ public class Groups {
 
     /**
      * Get the Group sequences.
+     * 
      * @return {@link List} of {@link List} of {@link Group}
      */
     public List<List<Group>> getSequences() {
@@ -53,7 +58,9 @@ public class Groups {
 
     /**
      * Insert a {@link Group}.
-     * @param group to insert
+     * 
+     * @param group
+     *            to insert
      */
     void insertGroup(Group group) {
         if (!groups.contains(group)) {
@@ -63,52 +70,52 @@ public class Groups {
 
     /**
      * Insert a sequence.
-     * @param groups {@link List} of {@link Group} to insert
+     * 
+     * @param groups
+     *            {@link List} of {@link Group} to insert
      */
     void insertSequence(List<Group> groups) {
-        if (groups == null || groups.isEmpty()) {
-            return;
-        }
-
-        if (!sequences.contains(groups)) {
+        if (!(groups == null || groups.isEmpty() || sequences.contains(groups))) {
             sequences.add(groups);
         }
     }
 
     /**
-     * Assert that the default group can be expanded to <code>defaultGroups</code>.
+     * Assert that the default group can be expanded to
+     * <code>defaultGroups</code>.
+     * 
      * @param defaultGroups
      */
     public void assertDefaultGroupSequenceIsExpandable(List<Group> defaultGroups) {
         for (List<Group> groupList : sequences) {
-            int idx = groupList.indexOf(Group.DEFAULT);
-            if (idx != -1) {
+            final int idx = groupList.indexOf(Group.DEFAULT);
+            if (idx >= 0) {
                 ensureExpandable(groupList, defaultGroups, idx);
             }
         }
     }
 
     private void ensureExpandable(List<Group> groupList, List<Group> defaultGroupList, int defaultGroupIndex) {
-        for (int i = 0; i < defaultGroupList.size(); i++) {
-            Group group = defaultGroupList.get(i);
+        for (int i = 0, sz = defaultGroupList.size(); i < sz; i++) {
+            final Group group = defaultGroupList.get(i);
             if (group.isDefault()) {
                 continue; // the default group is the one we want to replace
             }
-            int index = groupList.indexOf(group); // sequence contains group of default group sequence
-            if (index == -1) {
-                continue; // if group is not in the sequence
+            // sequence contains group of default group sequence
+            final int index = groupList.indexOf(group);
+            if (index < 0) {
+                // group is not in the sequence
+                continue;
             }
-
             if ((i == 0 && index == defaultGroupIndex - 1)
                 || (i == defaultGroupList.size() - 1 && index == defaultGroupIndex + 1)) {
-                // if we are at the beginning or end of he defaultGroupSequence and the
-                // matches are either directly before or after we can continue,
-                // since we basically have two groups
+                // if we are at the beginning or end of he defaultGroupSequence
+                // and the matches are either directly before or after we can
+                // continue, since we basically have two groups
                 continue;
             }
-            throw new GroupDefinitionException(
-                "Unable to expand default group list" + defaultGroupList + " into sequence " + groupList);
+            Exceptions.raise(GroupDefinitionException::new, "Unable to expand default group list %s into sequence %s",
+                defaultGroupList, groupList);
         }
     }
-
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupsComputer.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupsComputer.java b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupsComputer.java
index 398d6c3..ae6f629 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupsComputer.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupsComputer.java
@@ -18,126 +18,115 @@
  */
 package org.apache.bval.jsr.groups;
 
-import javax.validation.GroupDefinitionException;
-import javax.validation.GroupSequence;
-import javax.validation.ValidationException;
-import javax.validation.groups.Default;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
+import javax.validation.GroupDefinitionException;
+import javax.validation.GroupSequence;
+import javax.validation.ValidationException;
+import javax.validation.groups.Default;
+
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Validate;
+
 /**
  * Description: compute group order, based on the RI behavior as to guarantee
  * compatibility with interpretations of the spec.<br/>
  * Implementation is thread-safe.
  */
 public class GroupsComputer {
-    public static final Class<?>[] DEFAULT_GROUP = new Class<?>[] { Default.class };
+    public static final Class<?>[] DEFAULT_GROUP = { Default.class };
 
-    /** The default group array used in case any of the validate methods is called without a group. */
+    /**
+     * The default group array used in case any of the validate methods is
+     * called without a group.
+     */
     private static final Groups DEFAULT_GROUPS;
     static {
-        DEFAULT_GROUPS = new GroupsComputer().computeGroups(Arrays.asList(DEFAULT_GROUP));
+        DEFAULT_GROUPS = new Groups();
+        for (Class<?> g : DEFAULT_GROUP) {
+            DEFAULT_GROUPS.insertGroup(new Group(g));
+        }
     }
 
     /** caching resolved groups in a thread-safe map. */
-    private final Map<Class<?>, List<Group>> resolvedSequences = new ConcurrentHashMap<Class<?>, List<Group>>();
+    private final Map<Class<?>, List<Group>> resolvedSequences = new ConcurrentHashMap<>();
 
     /**
      * Compute groups from an array of group classes.
+     * 
      * @param groups
      * @return {@link Groups}
      */
-    public Groups computeGroups(Class<?>[] groups) {
-        if (groups == null) {
-            throw new IllegalArgumentException("null passed as group");
-        }
-
-        // if no groups is specified use the default
-        if (groups.length == 0) {
-            return DEFAULT_GROUPS;
-        }
-
+    @SafeVarargs
+    public final Groups computeGroups(Class<?>... groups) {
+        Exceptions.raiseIf(groups == null, IllegalArgumentException::new, "null validation groups specified");
         return computeGroups(Arrays.asList(groups));
     }
 
     /**
      * Main compute implementation.
+     * 
      * @param groups
      * @return {@link Groups}
      */
     protected Groups computeGroups(Collection<Class<?>> groups) {
-        if (groups == null || groups.size() == 0) {
-            throw new IllegalArgumentException("At least one group has to be specified.");
+        Validate.notNull(groups, "groups");
+
+        if (groups.isEmpty() || Arrays.asList(DEFAULT_GROUP).equals(new ArrayList<>(groups))) {
+            return DEFAULT_GROUPS;
         }
+        Exceptions.raiseIf(groups.stream().anyMatch(Objects::isNull), IllegalArgumentException::new,
+            "Null group specified");
 
         for (final Class<?> clazz : groups) {
-            if (clazz == null) {
-                throw new IllegalArgumentException("At least one group has to be specified.");
-            }
-
-            if (!clazz.isInterface()) {
-                throw new ValidationException("A group has to be an interface. " + clazz.getName() + " is not.");
-            }
+            Exceptions.raiseUnless(clazz.isInterface(), ValidationException::new,
+                "A group must be an interface. %s is not.", clazz);
         }
-
-        Groups chain = new Groups();
+        final Groups chain = new Groups();
         for (Class<?> clazz : groups) {
-            GroupSequence anno = clazz.getAnnotation(GroupSequence.class);
+            final GroupSequence anno = clazz.getAnnotation(GroupSequence.class);
             if (anno == null) {
-                Group group = new Group(clazz);
-                chain.insertGroup(group);
+                chain.insertGroup(new Group(clazz));
                 insertInheritedGroups(clazz, chain);
-            } else {
-                insertSequence(clazz, anno, chain);
+                continue;
             }
+            chain.insertSequence(
+                resolvedSequences.computeIfAbsent(clazz, g -> resolveSequence(g, anno, new HashSet<>())));
         }
-
         return chain;
     }
 
     private void insertInheritedGroups(Class<?> clazz, Groups chain) {
         for (Class<?> extendedInterface : clazz.getInterfaces()) {
-            Group group = new Group(extendedInterface);
-            chain.insertGroup(group);
+            chain.insertGroup(new Group(extendedInterface));
             insertInheritedGroups(extendedInterface, chain);
         }
     }
 
-    private void insertSequence(Class<?> clazz, GroupSequence anno, Groups chain) {
-        List<Group> sequence;
-        if (resolvedSequences.containsKey(clazz)) {
-            sequence = resolvedSequences.get(clazz);
-        } else {
-            sequence = resolveSequence(clazz, anno, new HashSet<Class<?>>());
-        }
-        chain.insertSequence(sequence);
-    }
-
     private List<Group> resolveSequence(Class<?> group, GroupSequence sequenceAnnotation,
         Set<Class<?>> processedSequences) {
-        if (processedSequences.contains(group)) {
-            throw new GroupDefinitionException("Cyclic dependency in groups definition");
-        } else {
-            processedSequences.add(group);
-        }
-        List<Group> resolvedGroupSequence = new LinkedList<Group>();
-        Class<?>[] sequenceArray = sequenceAnnotation.value();
-        for (Class<?> clazz : sequenceArray) {
-            GroupSequence anno = clazz.getAnnotation(GroupSequence.class);
+        Exceptions.raiseUnless(processedSequences.add(group), GroupDefinitionException::new,
+            "Cyclic dependency in groups definition");
+
+        final List<Group> resolvedGroupSequence = new ArrayList<>();
+        for (Class<?> clazz : sequenceAnnotation.value()) {
+            final GroupSequence anno = clazz.getAnnotation(GroupSequence.class);
             if (anno == null) {
-                resolvedGroupSequence.add(new Group(clazz)); // group part of sequence
+                // group part of sequence
+                resolvedGroupSequence.add(new Group(clazz));
             } else {
-                List<Group> tmpSequence = resolveSequence(clazz, anno, processedSequences); // recursion!
-                resolvedGroupSequence.addAll(tmpSequence);
+                // recursion!
+                resolvedGroupSequence.addAll(resolveSequence(clazz, anno, processedSequences));
             }
         }
-        resolvedSequences.put(group, resolvedGroupSequence);
         return resolvedGroupSequence;
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/parameter/DefaultParameterNameProvider.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/parameter/DefaultParameterNameProvider.java b/bval-jsr/src/main/java/org/apache/bval/jsr/parameter/DefaultParameterNameProvider.java
index 2b43bcd..dec9ae8 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/parameter/DefaultParameterNameProvider.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/parameter/DefaultParameterNameProvider.java
@@ -18,30 +18,29 @@
  */
 package org.apache.bval.jsr.parameter;
 
-import javax.validation.ParameterNameProvider;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
 import java.lang.reflect.Method;
-import java.util.ArrayList;
+import java.lang.reflect.Parameter;
 import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.ParameterNameProvider;
 
 public class DefaultParameterNameProvider implements ParameterNameProvider {
-    private static final String ARG = "arg";
+
+    private static List<String> parameterNames(Executable exe) {
+        return Stream.of(exe.getParameters()).map(Parameter::getName).collect(Collectors.toList());
+    }
 
     @Override
     public List<String> getParameterNames(Constructor<?> constructor) {
-        return names(constructor.getParameterTypes().length);
+        return parameterNames(constructor);
     }
 
     @Override
     public List<String> getParameterNames(Method method) {
-        return names(method.getParameterTypes().length);
-    }
-
-    private static List<String> names(final int length) {
-        final List<String> list = new ArrayList<String>();
-        for (int i = 0; i < length; i++) {
-            list.add(ARG + i);
-        }
-        return list;
+        return parameterNames(method);
     }
 }


[12/17] bval git commit: BV2: new validation implementation

Posted by mb...@apache.org.
BV2: new validation implementation


Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/05df7ee2
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/05df7ee2
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/05df7ee2

Branch: refs/heads/bv2
Commit: 05df7ee264cbef9199897b170e89bb7fcac58f26
Parents: a921963
Author: Matt Benson <mb...@apache.org>
Authored: Wed Feb 21 14:54:03 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Wed Feb 21 14:59:58 2018 -0600

----------------------------------------------------------------------
 .../apache/bval/jsr/ApacheFactoryContext.java   |  83 ++++++---
 .../bval/jsr/ApacheValidatorConfiguration.java  |   7 +-
 .../apache/bval/jsr/ApacheValidatorFactory.java | 171 ++++++++++++-------
 .../bval/jsr/BootstrapConfigurationImpl.java    |  29 +++-
 .../bval/jsr/CascadingPropertyValidator.java    |  96 ++++++++---
 .../org/apache/bval/jsr/ConfigurationImpl.java  | 128 ++++++++------
 .../java/org/apache/bval/jsr/ValidatorImpl.java | 141 +++++++++++++++
 7 files changed, 494 insertions(+), 161 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/05df7ee2/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 8f68b9e..1e4b263 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
@@ -18,46 +18,56 @@
  */
 package org.apache.bval.jsr;
 
-import org.apache.bval.MetaBeanFinder;
-import org.apache.bval.util.reflection.Reflection;
-import org.apache.commons.weaver.privilizer.Privilizing;
-import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
-
+import javax.validation.ClockProvider;
 import javax.validation.ConstraintValidatorFactory;
 import javax.validation.MessageInterpolator;
 import javax.validation.ParameterNameProvider;
 import javax.validation.TraversableResolver;
 import javax.validation.Validator;
 import javax.validation.ValidatorContext;
+import javax.validation.valueextraction.ValueExtractor;
+
+import org.apache.bval.MetaBeanFinder;
+import org.apache.bval.jsr.descriptor.DescriptorManager;
+import org.apache.bval.jsr.groups.GroupsComputer;
+import org.apache.bval.jsr.valueextraction.ValueExtractors;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
 
 /**
- * Description: Represents the context that is used to create
- * {@link ClassValidator} instances.
+ * Description: Represents the context that is used to create {@link ClassValidator} instances.
  */
 @Privilizing(@CallTo(Reflection.class))
 public class ApacheFactoryContext implements ValidatorContext {
+    private final Lazy<GroupsComputer> groupsComputer = new Lazy<>(GroupsComputer::new);
     private final ApacheValidatorFactory factory;
+    private final ValueExtractors valueExtractors;
     private volatile MetaBeanFinder metaBeanFinder;
 
     private MessageInterpolator messageInterpolator;
     private TraversableResolver traversableResolver;
     private ParameterNameProvider parameterNameProvider;
     private ConstraintValidatorFactory constraintValidatorFactory;
+    private ClockProvider clockProvider;
 
     /**
      * Create a new ApacheFactoryContext instance.
      * 
-     * @param factory validator factory
-     * @param metaBeanFinder meta finder
+     * @param factory
+     *            validator factory
+     * @param metaBeanFinder
+     *            meta finder
      */
     public ApacheFactoryContext(ApacheValidatorFactory factory, MetaBeanFinder metaBeanFinder) {
         this.factory = factory;
         this.metaBeanFinder = metaBeanFinder;
+        valueExtractors = factory.getValueExtractors().createChild();
     }
 
     /**
-     * Get the {@link ApacheValidatorFactory} used by this
-     * {@link ApacheFactoryContext}.
+     * Get the {@link ApacheValidatorFactory} used by this {@link ApacheFactoryContext}.
      * 
      * @return {@link ApacheValidatorFactory}
      */
@@ -75,13 +85,13 @@ public class ApacheFactoryContext implements ValidatorContext {
     }
 
     /**
-     * Discard cached metadata. Calling this method unnecessarily has the effect of severly
-     * limiting performance, therefore only do so when changes have been made that affect
-     * validation metadata, i.e. particularly NOT in response to:
+     * Discard cached metadata. Calling this method unnecessarily has the effect of severly limiting performance,
+     * therefore only do so when changes have been made that affect validation metadata, i.e. particularly NOT in
+     * response to:
      * <ul>
-     *   <li>{@link #messageInterpolator(MessageInterpolator)}</li>
-     *   <li>{@link #traversableResolver(TraversableResolver)}</li>
-     *   <li>{@link #constraintValidatorFactory(ConstraintValidatorFactory)</li>
+     * <li>{@link #messageInterpolator(MessageInterpolator)}</li>
+     * <li>{@link #traversableResolver(TraversableResolver)}</li>
+     * <li>{@link #constraintValidatorFactory(ConstraintValidatorFactory)</li>
      * </ul>
      */
     private synchronized void resetMeta() {
@@ -92,7 +102,7 @@ public class ApacheFactoryContext implements ValidatorContext {
      * {@inheritDoc}
      */
     @Override
-    public ValidatorContext messageInterpolator(MessageInterpolator messageInterpolator) {
+    public ApacheFactoryContext messageInterpolator(MessageInterpolator messageInterpolator) {
         this.messageInterpolator = messageInterpolator;
         return this;
     }
@@ -101,7 +111,7 @@ public class ApacheFactoryContext implements ValidatorContext {
      * {@inheritDoc}
      */
     @Override
-    public ValidatorContext traversableResolver(TraversableResolver traversableResolver) {
+    public ApacheFactoryContext traversableResolver(TraversableResolver traversableResolver) {
         this.traversableResolver = traversableResolver;
         return this;
     }
@@ -110,18 +120,30 @@ public class ApacheFactoryContext implements ValidatorContext {
      * {@inheritDoc}
      */
     @Override
-    public ValidatorContext constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) {
+    public ApacheFactoryContext constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) {
         this.constraintValidatorFactory = constraintValidatorFactory;
         return this;
     }
 
     @Override
-    public ValidatorContext parameterNameProvider(ParameterNameProvider parameterNameProvider) {
+    public ApacheFactoryContext parameterNameProvider(ParameterNameProvider parameterNameProvider) {
         this.parameterNameProvider = parameterNameProvider;
         resetMeta(); // needed since parameter names are a component of validation metadata
         return this;
     }
 
+    @Override
+    public ApacheFactoryContext clockProvider(ClockProvider clockProvider) {
+        this.clockProvider = clockProvider;
+        return this;
+    }
+
+    @Override
+    public ApacheFactoryContext addValueExtractor(ValueExtractor<?> extractor) {
+        valueExtractors.add(extractor);
+        return this;
+    }
+
     /**
      * Get the {@link ConstraintValidatorFactory}.
      * 
@@ -137,7 +159,7 @@ public class ApacheFactoryContext implements ValidatorContext {
      */
     @Override
     public Validator getValidator() {
-        return new ClassValidator(this);
+        return new ValidatorImpl(this);
     }
 
     /**
@@ -162,6 +184,23 @@ public class ApacheFactoryContext implements ValidatorContext {
         return parameterNameProvider == null ? factory.getParameterNameProvider() : parameterNameProvider;
     }
 
+    public ClockProvider getClockProvider() {
+        return clockProvider == null ? factory.getClockProvider() : clockProvider;
+    }
+
+    public ValueExtractors getValueExtractors() {
+        return valueExtractors;
+    }
+
+    public DescriptorManager getDescriptorManager() {
+        // TODO handle context customizations
+        return factory.getDescriptorManager();
+    }
+
+    public GroupsComputer getGroupsComputer() {
+        return groupsComputer.get();
+    }
+
     boolean isTreatMapsLikeBeans() {
         return Boolean
             .parseBoolean(factory.getProperties().get(ApacheValidatorConfiguration.Properties.TREAT_MAPS_LIKE_BEANS));

http://git-wip-us.apache.org/repos/asf/bval/blob/05df7ee2/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorConfiguration.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorConfiguration.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorConfiguration.java
index fb64d4e..81187f3 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorConfiguration.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorConfiguration.java
@@ -32,7 +32,7 @@ public interface ApacheValidatorConfiguration extends Configuration<ApacheValida
     /**
      * Proprietary property keys for {@link ConfigurationImpl}  
      */
-    public interface Properties {
+    interface Properties {
         /**
          * the location where to look for the validation.xml file.
          * default: "META-INF/validation.xml"
@@ -91,5 +91,10 @@ public interface ApacheValidatorConfiguration extends Configuration<ApacheValida
          * </ol>
          */
         String METABEAN_FACTORY_CLASSNAMES = "apache.bval.metabean-factory-classnames";
+
+        /**
+         * Size to use for caching of constraint-related information. Default is {@code 50}.
+         */
+        String CONSTRAINTS_CACHE_SIZE = "apache.bval.constraints-cache-size";
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/05df7ee2/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 5e6a611..b516a73 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
@@ -18,11 +18,39 @@
  */
 package org.apache.bval.jsr;
 
+import java.io.Closeable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.validation.ClockProvider;
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.MessageInterpolator;
+import javax.validation.ParameterNameProvider;
+import javax.validation.TraversableResolver;
+import javax.validation.Validation;
+import javax.validation.ValidationException;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.spi.ConfigurationState;
+
 import org.apache.bval.IntrospectorMetaBeanFactory;
 import org.apache.bval.MetaBeanBuilder;
 import org.apache.bval.MetaBeanFactory;
 import org.apache.bval.MetaBeanFinder;
 import org.apache.bval.MetaBeanManager;
+import org.apache.bval.jsr.descriptor.DescriptorManager;
+import org.apache.bval.jsr.metadata.MetadataBuilders;
+import org.apache.bval.jsr.util.AnnotationsManager;
+import org.apache.bval.jsr.valueextraction.ValueExtractors;
 import org.apache.bval.jsr.xml.AnnotationIgnores;
 import org.apache.bval.jsr.xml.MetaConstraint;
 import org.apache.bval.jsr.xml.ValidationMappingParser;
@@ -37,28 +65,6 @@ import org.apache.commons.weaver.privilizer.Privileged;
 import org.apache.commons.weaver.privilizer.Privilizing;
 import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
 
-import javax.validation.ConstraintValidatorFactory;
-import javax.validation.MessageInterpolator;
-import javax.validation.ParameterNameProvider;
-import javax.validation.TraversableResolver;
-import javax.validation.Validation;
-import javax.validation.ValidationException;
-import javax.validation.Validator;
-import javax.validation.ValidatorFactory;
-import javax.validation.spi.ConfigurationState;
-import java.io.Closeable;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
 /**
  * Description: a factory is a complete configurated object that can create
  * validators.<br/>
@@ -68,13 +74,17 @@ import java.util.concurrent.ConcurrentMap;
 public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
 
     private static volatile ApacheValidatorFactory DEFAULT_FACTORY;
-    private static final ConstraintDefaults DEFAULT_CONSTRAINTS = new ConstraintDefaults();
 
     private MessageInterpolator messageResolver;
     private TraversableResolver traversableResolver;
     private ConstraintValidatorFactory constraintValidatorFactory;
     private ParameterNameProvider parameterNameProvider;
+    private ClockProvider clockProvider;
     private final Map<String, String> properties;
+    private final AnnotationsManager annotationsManager;
+    private final DescriptorManager descriptorManager = new DescriptorManager(this);
+    private final MetadataBuilders metadataBuilders = new MetadataBuilders();
+    private final ValueExtractors valueExtractors = new ValueExtractors();
 
     /**
      * information from xml parsing
@@ -89,7 +99,7 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
     private final ConcurrentMap<Class<?>, List<AccessStrategy>> validAccesses;
     private final ConcurrentMap<Class<?>, List<MetaConstraint<?, ? extends Annotation>>> constraintMap;
 
-    private final Collection<Closeable> toClose = new ArrayList<Closeable>();
+    private final Collection<Closeable> toClose = new ArrayList<>();
     private final MetaBeanFinder defaultMetaBeanFinder;
 
     /**
@@ -112,7 +122,7 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
      * @return a new instance of MetaBeanManager with adequate MetaBeanFactories
      */
     protected MetaBeanFinder buildMetaBeanFinder() {
-        final List<MetaBeanFactory> builders = new ArrayList<MetaBeanFactory>();
+        final List<MetaBeanFactory> builders = new ArrayList<>();
         if (Boolean.parseBoolean(getProperties().get(ApacheValidatorConfiguration.Properties.ENABLE_INTROSPECTOR))) {
             builders.add(new IntrospectorMetaBeanFactory());
         }
@@ -121,9 +131,8 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
         if (factoryClassNames != null) {
             for (String clsName : factoryClassNames) {
                 // cast, relying on #createMetaBeanFactory to throw the exception if incompatible:
-                @SuppressWarnings("unchecked")
                 final Class<? extends MetaBeanFactory> factoryClass =
-                    (Class<? extends MetaBeanFactory>) loadClass(clsName);
+                    loadClass(clsName).asSubclass(MetaBeanFactory.class);
                 builders.add(createMetaBeanFactory(factoryClass));
             }
         }
@@ -173,24 +182,27 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
      * Create a new ApacheValidatorFactory instance.
      */
     public ApacheValidatorFactory(ConfigurationState configuration) {
-        properties = new HashMap<String, String>(configuration.getProperties());
-        defaultSequences = new HashMap<Class<?>, Class<?>[]>();
-        validAccesses = new ConcurrentHashMap<Class<?>, List<AccessStrategy>>();
-        constraintMap = new ConcurrentHashMap<Class<?>, List<MetaConstraint<?, ? extends Annotation>>>();
+        properties = new HashMap<>(configuration.getProperties());
+        defaultSequences = new HashMap<>();
+        validAccesses = new ConcurrentHashMap<>();
+        constraintMap = new ConcurrentHashMap<>();
 
         parameterNameProvider = configuration.getParameterNameProvider();
         messageResolver = configuration.getMessageInterpolator();
         traversableResolver = configuration.getTraversableResolver();
         constraintValidatorFactory = configuration.getConstraintValidatorFactory();
+        clockProvider = configuration.getClockProvider();
 
         if (ConfigurationImpl.class.isInstance(configuration)) {
-            final ConfigurationImpl impl = ConfigurationImpl.class.cast(configuration);
-            toClose.add(impl.getClosable());
+            toClose.add(ConfigurationImpl.class.cast(configuration).getClosable());
         }
 
         new ValidationMappingParser(this).processMappingConfig(configuration.getMappingStreams());
 
         defaultMetaBeanFinder = buildMetaBeanFinder();
+
+        configuration.getValueExtractors().forEach(valueExtractors::add);
+        annotationsManager = new AnnotationsManager(this);
     }
 
     /**
@@ -203,8 +215,7 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
     }
 
     /**
-     * Shortcut method to create a new Validator instance with factory's
-     * settings
+     * Shortcut method to create a new Validator instance with factory's settings
      *
      * @return the new validator instance
      */
@@ -271,6 +282,12 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
         }
     }
 
+    public void setClockProvider(final ClockProvider clockProvider) {
+        if (clockProvider != null) {
+            this.clockProvider = clockProvider;
+        }
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -307,6 +324,11 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
     }
 
     @Override
+    public ClockProvider getClockProvider() {
+        return clockProvider;
+    }
+
+    @Override
     public void close() {
         try {
             for (final Closeable c : toClose) {
@@ -319,13 +341,14 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
     }
 
     /**
-     * Return an object of the specified type to allow access to the
-     * provider-specific API. If the Bean Validation provider implementation
-     * does not support the specified class, the ValidationException is thrown.
+     * Return an object of the specified type to allow access to the provider-specific API. If the Bean Validation
+     * provider implementation does not support the specified class, the ValidationException is thrown.
      *
-     * @param type the class of the object to be returned.
+     * @param type
+     *            the class of the object to be returned.
      * @return an instance of the specified class
-     * @throws ValidationException if the provider does not support the call.
+     * @throws ValidationException
+     *             if the provider does not support the call.
      */
     @Override
     public <T> T unwrap(final Class<T> type) {
@@ -365,15 +388,6 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
     }
 
     /**
-     * Get the detected {@link ConstraintDefaults}.
-     *
-     * @return ConstraintDefaults
-     */
-    public ConstraintDefaults getDefaultConstraints() {
-        return DEFAULT_CONSTRAINTS;
-    }
-
-    /**
      * Get the detected {@link AnnotationIgnores}.
      *
      * @return AnnotationIgnores
@@ -392,8 +406,34 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
     }
 
     /**
-     * Add a meta-constraint to this {@link ApacheValidatorFactory}'s runtime
-     * customizations.
+     * Get the {@link AnnotationsManager}.
+     * 
+     * @return {@link AnnotationsManager}
+     */
+    public AnnotationsManager getAnnotationsManager() {
+        return annotationsManager;
+    }
+
+    /**
+     * Get the {@link DescriptorManager}.
+     * 
+     * @return {@link DescriptorManager}
+     */
+    public DescriptorManager getDescriptorManager() {
+        return descriptorManager;
+    }
+
+    /**
+     * Get the {@link ValueExtractors}.
+     * 
+     * @return {@link ValueExtractors}
+     */
+    public ValueExtractors getValueExtractors() {
+        return valueExtractors;
+    }
+
+    /**
+     * Add a meta-constraint to this {@link ApacheValidatorFactory}'s runtime customizations.
      *
      * @param beanClass
      * @param metaConstraint
@@ -401,7 +441,7 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
     public void addMetaConstraint(final Class<?> beanClass, final MetaConstraint<?, ?> metaConstraint) {
         List<MetaConstraint<?, ? extends Annotation>> slot = constraintMap.get(beanClass);
         if (slot == null) {
-            slot = new ArrayList<MetaConstraint<?, ? extends Annotation>>();
+            slot = new ArrayList<>();
             final List<MetaConstraint<?, ? extends Annotation>> old = constraintMap.putIfAbsent(beanClass, slot);
             if (old != null) {
                 slot = old;
@@ -420,7 +460,7 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
     public void addValid(Class<?> beanClass, AccessStrategy accessStrategy) {
         List<AccessStrategy> slot = validAccesses.get(beanClass);
         if (slot == null) {
-            slot = new ArrayList<AccessStrategy>();
+            slot = new ArrayList<>();
             final List<AccessStrategy> old = validAccesses.putIfAbsent(beanClass, slot);
             if (old != null) {
                 slot = old;
@@ -444,8 +484,7 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
      *
      * @param <T>
      * @param beanClass
-     * @return List of {@link MetaConstraint}s applicable to
-     *         <code>beanClass</code>
+     * @return List of {@link MetaConstraint}s applicable to <code>beanClass</code>
      */
     public <T> List<MetaConstraint<T, ? extends Annotation>> getMetaConstraints(Class<T> beanClass) {
         final List<MetaConstraint<?, ? extends Annotation>> slot = constraintMap.get(beanClass);
@@ -459,16 +498,15 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
     }
 
     /**
-     * Get the {@link AccessStrategy} {@link List} indicating nested bean
-     * validations that must be triggered in the course of validating a
-     * <code>beanClass</code> graph.
+     * Get the {@link AccessStrategy} {@link List} indicating nested bean validations that must be triggered in the
+     * course of validating a <code>beanClass</code> graph.
      *
      * @param beanClass
      * @return {@link List} of {@link AccessStrategy}
      */
     public List<AccessStrategy> getValidAccesses(Class<?> beanClass) {
         final List<AccessStrategy> slot = validAccesses.get(beanClass);
-        return slot == null ? Collections.<AccessStrategy> emptyList() : Collections.unmodifiableList(slot);
+        return slot == null ? Collections.emptyList() : Collections.unmodifiableList(slot);
     }
 
     /**
@@ -481,6 +519,10 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
         return safeArray(defaultSequences.get(beanClass));
     }
 
+    public MetadataBuilders getMetadataBuilders() {
+        return metadataBuilders;
+    }
+
     private static Class<?>[] safeArray(Class<?>... array) {
         return array == null || array.length == 0 ? ObjectUtils.EMPTY_CLASS_ARRAY : array.clone();
     }
@@ -519,9 +561,8 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
     }
 
     /**
-     * separate class to prevent the classloader to immediately load optional
-     * classes: XMLMetaBeanManager, XMLMetaBeanFactory, XMLMetaBeanBuilder that
-     * might not be available in the classpath
+     * separate class to prevent the classloader to immediately load optional classes: XMLMetaBeanManager,
+     * XMLMetaBeanFactory, XMLMetaBeanBuilder that might not be available in the classpath
      */
     private static class XMLMetaBeanManagerCreator {
 
@@ -530,10 +571,10 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
         }
 
         /**
-         * Create the {@link MetaBeanManager} to process JSR303 XML. Requires
-         * bval-xstream at RT.
+         * Create the {@link MetaBeanManager} to process JSR303 XML. Requires bval-xstream at RT.
          *
-         * @param builders meta bean builders
+         * @param builders
+         *            meta bean builders
          * @return {@link MetaBeanManager}
          */
         // NOTE - We return MetaBeanManager instead of XMLMetaBeanManager to

http://git-wip-us.apache.org/repos/asf/bval/blob/05df7ee2/bval-jsr/src/main/java/org/apache/bval/jsr/BootstrapConfigurationImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/BootstrapConfigurationImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/BootstrapConfigurationImpl.java
index 3a3abf1..d85ab51 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/BootstrapConfigurationImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/BootstrapConfigurationImpl.java
@@ -34,12 +34,15 @@ public class BootstrapConfigurationImpl implements BootstrapConfiguration {
     private String messageInterpolatorClassName;
     private String constraintValidatorFactoryClassName;
     private String defaultProviderClassName;
+    private String clockProviderClassName;
+    private Set<String> valueExtractorClassNames;
 
     public BootstrapConfigurationImpl(final String defaultProviderClassName,
         final String constraintValidatorFactoryClassName, final String messageInterpolatorClassName,
         final String traversableResolverClassName, final String parameterNameProviderClassName,
         final Set<String> constraintMappingResourcePaths, final boolean executableValidationEnabled,
-        final Set<ExecutableType> defaultValidatedExecutableTypes, final Map<String, String> properties) {
+        final Set<ExecutableType> defaultValidatedExecutableTypes, final Map<String, String> properties,
+        final String clockProviderClassName, final Set<String> valueExtractorClassNames) {
         this.properties = Collections.unmodifiableMap(properties);
         this.defaultValidatedExecutableTypes = Collections.unmodifiableSet(defaultValidatedExecutableTypes);
         this.executableValidationEnabled = executableValidationEnabled;
@@ -49,6 +52,8 @@ public class BootstrapConfigurationImpl implements BootstrapConfiguration {
         this.messageInterpolatorClassName = messageInterpolatorClassName;
         this.constraintValidatorFactoryClassName = constraintValidatorFactoryClassName;
         this.defaultProviderClassName = defaultProviderClassName;
+        this.clockProviderClassName = clockProviderClassName;
+        this.valueExtractorClassNames = valueExtractorClassNames;
     }
 
     @Override
@@ -78,7 +83,7 @@ public class BootstrapConfigurationImpl implements BootstrapConfiguration {
 
     @Override
     public Set<String> getConstraintMappingResourcePaths() {
-        return constraintMappingResourcePaths;
+        return Collections.unmodifiableSet(constraintMappingResourcePaths);
     }
 
     @Override
@@ -88,11 +93,27 @@ public class BootstrapConfigurationImpl implements BootstrapConfiguration {
 
     @Override
     public Set<ExecutableType> getDefaultValidatedExecutableTypes() {
-        return defaultValidatedExecutableTypes;
+        return Collections.unmodifiableSet(defaultValidatedExecutableTypes);
     }
 
     @Override
     public Map<String, String> getProperties() {
-        return properties;
+        return Collections.unmodifiableMap(properties);
+    }
+
+    /**
+     * @since 2.0
+     */
+    @Override
+    public String getClockProviderClassName() {
+        return clockProviderClassName;
+    }
+
+    /**
+     * @since 2.0
+     */
+    @Override
+    public Set<String> getValueExtractorClassNames() {
+        return Collections.unmodifiableSet(valueExtractorClassNames);
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/05df7ee2/bval-jsr/src/main/java/org/apache/bval/jsr/CascadingPropertyValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/CascadingPropertyValidator.java b/bval-jsr/src/main/java/org/apache/bval/jsr/CascadingPropertyValidator.java
index ff2e273..f183c12 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/CascadingPropertyValidator.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/CascadingPropertyValidator.java
@@ -18,7 +18,9 @@ package org.apache.bval.jsr;
 
 import javax.validation.ConstraintViolation;
 import javax.validation.Valid;
+import javax.validation.ValidationException;
 import javax.validation.Validator;
+
 import java.util.Set;
 
 /**
@@ -30,38 +32,90 @@ import java.util.Set;
  * It should be noted that {@link Validator#validateProperty(Object, String, Class...)}
  * and {@link Validator#validateValue(Class, String, Object, Class...)} are assumed
  * semantically equivalent to calling the {@link CascadingPropertyValidator}-defined
- * methods with <code>cascade == false</code>.
+ * methods with {@code cascade == false}.
  * 
  * @version $Rev: 993539 $ $Date: 2010-09-07 16:27:50 -0500 (Tue, 07 Sep 2010) $
  */
 public interface CascadingPropertyValidator extends Validator {
 
     /**
-     * Validates all constraints placed on <code>object</code>'s
-     * <code>propertyName</code> property, with optional validation cascading.
-     * 
-     * @param <T>
-     * @param object
-     * @param propertyName
-     * @param cascade
-     * @param groups
-     * @return the resulting {@link Set} of {@link ConstraintViolation}s.
+     * {@inheritDoc} Validates all constraints placed on the property of {@code object} named {@code propertyName}.
+     *
+     * @param object       object to validate
+     * @param propertyName property to validate (i.e. field and getter constraints). Nested
+     *                     properties may be referenced (e.g. prop[2].subpropA.subpropB)
+     * @param groups       group or list of groups targeted for validation (default to
+     *                     {@link javax.validation.groups.Default})
+     * @return constraint violations or an empty {@link Set} if none
+     * @throws IllegalArgumentException if {@code object} is {@code null}, if {@code propertyName null},
+     *                                  empty or not a valid object property or if {@code null} is
+     *                                  passed to the varargs {@code groups}
+     * @throws ValidationException      if a non recoverable error happens during the validation process
+     */
+    @Override
+    default <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups) {
+        return validateProperty(object, propertyName, false, groups);
+    }
+
+    /**
+     * Validates all constraints placed on the property of {@code object} named {@code propertyName}.
+     *
+     * @param object       object to validate
+     * @param propertyName property to validate (i.e. field and getter constraints). Nested
+     *                     properties may be referenced (e.g. prop[2].subpropA.subpropB)
+     * @param cascade      whether to cascade along {@link Valid} properties
+     * @param groups       group or list of groups targeted for validation (default to
+     *                     {@link javax.validation.groups.Default})
+     * @return constraint violations or an empty {@link Set} if none
+     * @throws IllegalArgumentException if {@code object} is {@code null}, if {@code propertyName null},
+     *                                  empty or not a valid object property or if {@code null} is
+     *                                  passed to the varargs {@code groups}
+     * @throws ValidationException      if a non recoverable error happens during the validation process
      */
     <T> Set<javax.validation.ConstraintViolation<T>> validateProperty(T object, String propertyName, boolean cascade,
         Class<?>... groups);
 
     /**
-     * Validates all constraints placed on <code>object</code>'s
-     * <code>propertyName</code> property, with optional validation cascading,
-     * given a hypothetical property <code>value</code>.
-     * 
-     * @param <T>
-     * @param beanType
-     * @param propertyName
-     * @param value
-     * @param cascade
-     * @param groups
-     * @return the resulting {@link Set} of {@link ConstraintViolation}s.
+     * {@inheritDoc} Validates all constraints placed on the property named {@code propertyName} of the class
+     * {@code beanType} would the property value be {@code value}.
+     * <p/>
+     * {@link ConstraintViolation} objects return {@code null} for {@link ConstraintViolation#getRootBean()} and
+     * {@link ConstraintViolation#getLeafBean()}.
+     *
+     * @param beanType     the bean type
+     * @param propertyName property to validate
+     * @param value        property value to validate
+     * @param groups       group or list of groups targeted for validation (default to
+     *                     {@link javax.validation.groups.Default})
+     * @return constraint violations or an empty {@link Set} if none
+     * @throws IllegalArgumentException if {@code beanType} is {@code null}, if
+     *                                  {@code propertyName null}, empty or not a valid object
+     *                                  property or if {@code null} is passed to the varargs {@code groups}
+     * @throws ValidationException      if a non recoverable error happens during the validation process
+     */
+    @Override
+    default <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value,
+        Class<?>... groups) {
+        return validateValue(beanType, propertyName, value, false, groups);
+    }
+
+    /**
+     * {@inheritDoc} Validates all constraints placed on the property named {@code propertyName} of the class
+     * {@code beanType} would the property value be {@code value}.
+     * <p/>
+     * {@link ConstraintViolation} objects return {@code null} for {@link ConstraintViolation#getRootBean()} and
+     * {@link ConstraintViolation#getLeafBean()}.
+     *
+     * @param beanType     the bean type
+     * @param propertyName property to validate
+     * @param value        property value to validate
+     * @param groups       group or list of groups targeted for validation (default to
+     *                     {@link javax.validation.groups.Default})
+     * @return constraint violations or an empty {@link Set} if none
+     * @throws IllegalArgumentException if {@code beanType} is {@code null}, if
+     *                                  {@code propertyName null}, empty or not a valid object
+     *                                  property or if {@code null} is passed to the varargs {@code groups}
+     * @throws ValidationException      if a non recoverable error happens during the validation process
      */
     <T> Set<javax.validation.ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value,
         boolean cascade, Class<?>... groups);

http://git-wip-us.apache.org/repos/asf/bval/blob/05df7ee2/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 7c4780f..046d6d2 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
@@ -19,9 +19,10 @@
 package org.apache.bval.jsr;
 
 import java.io.Closeable;
-import java.io.IOException;
 import java.io.InputStream;
+import java.time.Clock;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -29,6 +30,7 @@ import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 import javax.validation.BootstrapConfiguration;
+import javax.validation.ClockProvider;
 import javax.validation.ConstraintValidatorFactory;
 import javax.validation.MessageInterpolator;
 import javax.validation.ParameterNameProvider;
@@ -40,6 +42,7 @@ import javax.validation.executable.ExecutableType;
 import javax.validation.spi.BootstrapState;
 import javax.validation.spi.ConfigurationState;
 import javax.validation.spi.ValidationProvider;
+import javax.validation.valueextraction.ValueExtractor;
 
 import org.apache.bval.cdi.BValExtension;
 import org.apache.bval.jsr.parameter.DefaultParameterNameProvider;
@@ -76,29 +79,33 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
      */
     protected MessageInterpolator defaultMessageInterpolator = new DefaultMessageInterpolator();
     protected volatile MessageInterpolator messageInterpolator = defaultMessageInterpolator;
-    protected Class<? extends MessageInterpolator> messageInterpolatorClass = null;
+    protected Class<? extends MessageInterpolator> messageInterpolatorClass;
 
     /**
      * Configured {@link ConstraintValidatorFactory}
      */
     protected ConstraintValidatorFactory defaultConstraintValidatorFactory = new DefaultConstraintValidatorFactory();
     protected volatile ConstraintValidatorFactory constraintValidatorFactory = defaultConstraintValidatorFactory;
-    protected Class<? extends ConstraintValidatorFactory> constraintValidatorFactoryClass = null;
+    protected Class<? extends ConstraintValidatorFactory> constraintValidatorFactoryClass;
 
     protected TraversableResolver defaultTraversableResolver = new DefaultTraversableResolver();
     protected volatile TraversableResolver traversableResolver = defaultTraversableResolver;
-    protected Class<? extends TraversableResolver> traversableResolverClass = null;
+    protected Class<? extends TraversableResolver> traversableResolverClass;
 
     protected ParameterNameProvider defaultParameterNameProvider = new DefaultParameterNameProvider();
     protected volatile ParameterNameProvider parameterNameProvider = defaultParameterNameProvider;
-    protected Class<? extends ParameterNameProvider> parameterNameProviderClass = null;
+    protected Class<? extends ParameterNameProvider> parameterNameProviderClass;
 
     protected BootstrapConfiguration bootstrapConfiguration;
 
     protected Collection<ExecutableType> executableValidation;
 
-    private Collection<BValExtension.Releasable<?>> releasables =
-        new CopyOnWriteArrayList<BValExtension.Releasable<?>>();
+    private Collection<BValExtension.Releasable<?>> releasables = new CopyOnWriteArrayList<>();
+    protected ClockProvider defaultClockProvider = Clock::systemDefaultZone;
+    protected volatile ClockProvider clockProvider = defaultClockProvider;
+    protected Class<? extends ClockProvider> clockProviderClass;
+
+    protected Set<ValueExtractor<?>> valueExtractors = new HashSet<>();
 
     private boolean beforeCdi = false;
 
@@ -109,8 +116,8 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
     private boolean prepared = false;
     // END DEFAULTS
 
-    private Set<InputStream> mappingStreams = new HashSet<InputStream>();
-    private Map<String, String> properties = new HashMap<String, String>();
+    private Set<InputStream> mappingStreams = new HashSet<>();
+    private Map<String, String> properties = new HashMap<>();
     private boolean ignoreXmlConfiguration = false;
 
     private volatile ValidationParser parser;
@@ -134,6 +141,7 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
         } else {
             throw new ValidationException("either provider or state are required");
         }
+        initializePropertyDefaults();
     }
 
     /**
@@ -141,13 +149,11 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
      */
     @Override
     public ApacheValidatorConfiguration traversableResolver(TraversableResolver resolver) {
-        if (resolver == null) {
-            return this;
+        if (resolver != null) {
+            this.traversableResolverClass = null;
+            this.traversableResolver = resolver;
+            this.prepared = false;
         }
-
-        this.traversableResolverClass = null;
-        this.traversableResolver = resolver;
-        this.prepared = false;
         return this;
     }
 
@@ -169,13 +175,11 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
      */
     @Override
     public ConfigurationImpl messageInterpolator(MessageInterpolator resolver) {
-        if (resolver == null) {
-            return this;
+        if (resolver != null) {
+            this.messageInterpolatorClass = null;
+            this.messageInterpolator = resolver;
+            this.prepared = false;
         }
-
-        this.messageInterpolatorClass = null;
-        this.messageInterpolator = resolver;
-        this.prepared = false;
         return this;
     }
 
@@ -184,23 +188,20 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
      */
     @Override
     public ConfigurationImpl constraintValidatorFactory(ConstraintValidatorFactory constraintFactory) {
-        if (constraintFactory == null) {
-            return this;
+        if (constraintFactory != null) {
+            this.constraintValidatorFactoryClass = null;
+            this.constraintValidatorFactory = constraintFactory;
+            this.prepared = false;
         }
-
-        this.constraintValidatorFactoryClass = null;
-        this.constraintValidatorFactory = constraintFactory;
-        this.prepared = false;
         return this;
     }
 
     @Override
     public ApacheValidatorConfiguration parameterNameProvider(ParameterNameProvider parameterNameProvider) {
-        if (parameterNameProvider == null) {
-            return this;
+        if (parameterNameProvider != null) {
+            this.parameterNameProviderClass = null;
+            this.parameterNameProvider = parameterNameProvider;
         }
-        this.parameterNameProviderClass = null;
-        this.parameterNameProvider = parameterNameProvider;
         return this;
     }
 
@@ -213,10 +214,9 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
      */
     @Override
     public ApacheValidatorConfiguration addMapping(InputStream stream) {
-        if (stream == null) {
-            return this;
+        if (stream != null) {
+            mappingStreams.add(IOs.convertToMarkableInputStream(stream));
         }
-        mappingStreams.add(IOs.convertToMarkableInputStream(stream));
         return this;
     }
 
@@ -297,7 +297,6 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
         if (beforeCdi) {
             return defaultMessageInterpolator;
         }
-
         if (messageInterpolator == defaultMessageInterpolator && messageInterpolatorClass != null) {
             synchronized (this) {
                 if (messageInterpolator == defaultMessageInterpolator && messageInterpolatorClass != null) {
@@ -336,7 +335,6 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
         if (prepared) {
             return this;
         }
-
         createBootstrapConfiguration();
         parser.applyConfigWithInstantiation(this); // instantiate the config if needed
 
@@ -367,7 +365,6 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
         if (beforeCdi) {
             return constraintValidatorFactory;
         }
-
         if (constraintValidatorFactory == defaultConstraintValidatorFactory
             && constraintValidatorFactoryClass != null) {
             synchronized (this) {
@@ -388,7 +385,6 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
         if (beforeCdi) {
             return defaultTraversableResolver;
         }
-
         if (traversableResolver == defaultTraversableResolver && traversableResolverClass != null) {
             synchronized (this) {
                 if (traversableResolver == defaultTraversableResolver && traversableResolverClass != null) {
@@ -404,7 +400,6 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
         if (beforeCdi) {
             return defaultParameterNameProvider;
         }
-
         if (parameterNameProvider == defaultParameterNameProvider && parameterNameProviderClass != null) {
             synchronized (this) {
                 if (parameterNameProvider == defaultParameterNameProvider && parameterNameProviderClass != null) {
@@ -452,14 +447,11 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
     }
 
     public Closeable getClosable() {
-        return new Closeable() {
-            @Override
-            public void close() throws IOException {
-                for (final BValExtension.Releasable<?> releasable : releasables) {
-                    releasable.release();
-                }
-                releasables.clear();
+        return () -> {
+            for (final BValExtension.Releasable<?> releasable : releasables) {
+                releasable.release();
             }
+            releasables.clear();
         };
     }
 
@@ -469,8 +461,7 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
             final BValExtension.Releasable<T> releasable = BValExtension.inject(cls);
             releasables.add(releasable);
             return releasable.getInstance();
-        } catch (final Exception e) {
-        } catch (final NoClassDefFoundError error) {
+        } catch (Exception | NoClassDefFoundError e) {
         }
         try {
             return cls.newInstance();
@@ -494,4 +485,45 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur
     public void parameterNameProviderClass(final Class<? extends ParameterNameProvider> clazz) {
         parameterNameProviderClass = clazz;
     }
+
+    @Override
+    public ApacheValidatorConfiguration clockProvider(ClockProvider clockProvider) {
+        this.clockProvider = clockProvider;
+        return this;
+    }
+
+    @Override
+    public ApacheValidatorConfiguration addValueExtractor(ValueExtractor<?> extractor) {
+        valueExtractors.add(extractor);
+        return this;
+    }
+
+    @Override
+    public ClockProvider getDefaultClockProvider() {
+        return defaultClockProvider;
+    }
+
+    @Override
+    public Set<ValueExtractor<?>> getValueExtractors() {
+        return Collections.unmodifiableSet(valueExtractors);
+    }
+
+    @Override
+    public ClockProvider getClockProvider() {
+        if (beforeCdi) {
+            return defaultClockProvider;
+        }
+        if (clockProvider == defaultClockProvider && clockProviderClass != null) {
+            synchronized (this) {
+                if (clockProvider == defaultClockProvider && clockProviderClass != null) {
+                    clockProvider = newInstance(clockProviderClass);
+                }
+            }
+        }
+        return clockProvider;
+    }
+
+    protected void initializePropertyDefaults() {
+        properties.put(Properties.CONSTRAINTS_CACHE_SIZE, Integer.toString(50));
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/05df7ee2/bval-jsr/src/main/java/org/apache/bval/jsr/ValidatorImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ValidatorImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ValidatorImpl.java
new file mode 100644
index 0000000..606e191
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ValidatorImpl.java
@@ -0,0 +1,141 @@
+/*
+ * 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;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ValidationException;
+import javax.validation.executable.ExecutableValidator;
+import javax.validation.metadata.BeanDescriptor;
+
+import org.apache.bval.jsr.job.ValidationJobFactory;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+
+public class ValidatorImpl implements CascadingPropertyValidator, ExecutableValidator {
+
+    private final ApacheFactoryContext validatorContext;
+    private final ValidationJobFactory validationJobFactory;
+
+    ValidatorImpl(ApacheFactoryContext validatorContext) {
+        super();
+        this.validatorContext = Validate.notNull(validatorContext, "validatorContext");
+        this.validationJobFactory = new ValidationJobFactory(validatorContext);
+    }
+
+    @Override
+    public BeanDescriptor getConstraintsForClass(Class<?> clazz) {
+        return validatorContext.getDescriptorManager().getBeanDescriptor(clazz);
+    }
+
+    @Override
+    public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
+        return validationJobFactory.validateBean(object, groups).getResults();
+    }
+
+    @Override
+    public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, boolean cascade,
+        Class<?>... groups) {
+        return validationJobFactory.validateProperty(object, propertyName, groups).cascade(cascade).getResults();
+    }
+
+    @Override
+    public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value,
+        boolean cascade, Class<?>... groups) {
+        return validationJobFactory.validateValue(beanType, propertyName, value, groups).cascade(cascade).getResults();
+    }
+
+    @Override
+    public ExecutableValidator forExecutables() {
+        return this;
+    }
+
+    @Override
+    public <T> Set<ConstraintViolation<T>> validateParameters(T object, Method method, Object[] parameterValues,
+        Class<?>... groups) {
+        return validationJobFactory.validateParameters(object, method, parameterValues, groups).getResults();
+    }
+
+    @Override
+    public <T> Set<ConstraintViolation<T>> validateReturnValue(T object, Method method, Object returnValue,
+        Class<?>... groups) {
+        return validationJobFactory.validateReturnValue(object, method, returnValue, groups).getResults();
+    }
+
+    @Override
+    public <T> Set<ConstraintViolation<T>> validateConstructorParameters(Constructor<? extends T> constructor,
+        Object[] parameterValues, Class<?>... groups) {
+        return validationJobFactory.<T> validateConstructorParameters(constructor, parameterValues, groups)
+            .getResults();
+    }
+
+    @Override
+    public <T> Set<ConstraintViolation<T>> validateConstructorReturnValue(Constructor<? extends T> constructor,
+        T createdObject, Class<?>... groups) {
+        return validationJobFactory.<T> validateConstructorReturnValue(constructor, createdObject, groups).getResults();
+    }
+
+    @Override
+    public <T> T unwrap(Class<T> type) {
+        // FIXME 2011-03-27 jw:
+        // This code is unsecure.
+        // It should allow only a fixed set of classes.
+        // Can't fix this because don't know which classes this method should support.
+
+        if (type.isAssignableFrom(getClass())) {
+            @SuppressWarnings("unchecked")
+            final T result = (T) this;
+            return result;
+        }
+        if (!(type.isInterface() || Modifier.isAbstract(type.getModifiers()))) {
+            return newInstance(type);
+        }
+        try {
+            final Class<?> cls = Reflection.toClass(type.getName() + "Impl");
+            if (type.isAssignableFrom(cls)) {
+                @SuppressWarnings("unchecked")
+                final Class<? extends T> implClass = (Class<? extends T>) cls;
+                return newInstance(implClass);
+            }
+        } catch (ClassNotFoundException e) {
+        }
+        throw new ValidationException("Type " + type + " not supported");
+    }
+
+    private <T> T newInstance(final Class<T> cls) {
+        final Constructor<T> cons = Reflection.getDeclaredConstructor(cls, ApacheFactoryContext.class);
+        if (cons == null) {
+            throw new ValidationException("Cannot instantiate " + cls);
+        }
+        final boolean mustUnset = Reflection.setAccessible(cons, true);
+        try {
+            return cons.newInstance(validatorContext);
+        } catch (final Exception ex) {
+            throw new ValidationException("Cannot instantiate " + cls, ex);
+        } finally {
+            if (mustUnset) {
+                Reflection.setAccessible(cons, false);
+            }
+        }
+    }
+}


[13/17] bval git commit: BV2: validation job model

Posted by mb...@apache.org.
BV2: validation job model


Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/a921963f
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/a921963f
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/a921963f

Branch: refs/heads/bv2
Commit: a921963f7e6d4766288c583278641cfc1add3620
Parents: 40ac09f
Author: Matt Benson <mb...@apache.org>
Authored: Wed Feb 21 14:51:34 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Wed Feb 21 14:59:58 2018 -0600

----------------------------------------------------------------------
 .../java/org/apache/bval/jsr/GraphContext.java  |  95 ++++
 .../jsr/job/ConstraintValidatorContextImpl.java | 195 +++++++
 .../org/apache/bval/jsr/job/ValidateBean.java   |  60 +++
 .../apache/bval/jsr/job/ValidateParameters.java | 188 +++++++
 .../apache/bval/jsr/job/ValidateProperty.java   | 522 +++++++++++++++++++
 .../bval/jsr/job/ValidateReturnValue.java       | 126 +++++
 .../org/apache/bval/jsr/job/ValidationJob.java  | 380 ++++++++++++++
 .../bval/jsr/job/ValidationJobFactory.java      | 112 ++++
 8 files changed, 1678 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/a921963f/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java b/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java
new file mode 100644
index 0000000..26350d6
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java
@@ -0,0 +1,95 @@
+/*
+ * 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;
+
+import javax.validation.Path;
+
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.jsr.util.PathImpl;
+import org.apache.bval.util.Validate;
+
+public class GraphContext {
+
+    private final ApacheFactoryContext validatorContext;
+    private final PathImpl path;
+    private final Object value;
+    private final GraphContext parent;
+
+    public GraphContext(ApacheFactoryContext validatorContext, PathImpl path, Object value) {
+        this(validatorContext, path, value, null);
+    }
+
+    private GraphContext(ApacheFactoryContext validatorContext, PathImpl path, Object value, GraphContext parent) {
+        super();
+        this.validatorContext = Validate.notNull(validatorContext, "validatorContext");
+        this.path = Validate.notNull(path, "path");
+        this.value = value;
+        this.parent = parent;
+    }
+
+    public ApacheFactoryContext getValidatorContext() {
+        return validatorContext;
+    }
+
+    public PathImpl getPath() {
+        return PathImpl.copy(path);
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    public GraphContext child(NodeImpl node, Object value) {
+        Validate.notNull(node, "node");
+        final PathImpl p = PathImpl.copy(path);
+        p.addNode(node);
+        return new GraphContext(validatorContext, p, value, this);
+    }
+
+    public GraphContext child(Path p, Object value) {
+        Validate.notNull(p, "Path");
+        final PathImpl impl = PathImpl.copy(p);
+        Validate.isTrue(impl.isSubPathOf(path), "%s is not a subpath of %s", p, path);
+        return new GraphContext(validatorContext, impl, value, this);
+    }
+
+    public boolean isRoot() {
+        return parent == null;
+    }
+
+    public boolean isRecursive() {
+        GraphContext c = parent;
+        while (c != null) {
+            if (c.value == value) {
+                return true;
+            }
+            c = c.parent;
+        }
+        return false;
+    }
+
+    public GraphContext getParent() {
+        return parent;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s: %s at '%s'", getClass().getSimpleName(), value, path);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a921963f/bval-jsr/src/main/java/org/apache/bval/jsr/job/ConstraintValidatorContextImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ConstraintValidatorContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ConstraintValidatorContextImpl.java
new file mode 100644
index 0000000..c3cc3a7
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ConstraintValidatorContextImpl.java
@@ -0,0 +1,195 @@
+/*
+ * 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.util.HashSet;
+import java.util.Set;
+
+import javax.validation.ClockProvider;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintViolation;
+import javax.validation.MessageInterpolator;
+import javax.validation.Path;
+import javax.validation.ValidationException;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.CrossParameterDescriptor;
+
+import org.apache.bval.jsr.descriptor.ComposedD;
+import org.apache.bval.jsr.descriptor.ConstraintD;
+import org.apache.bval.jsr.descriptor.CrossParameterD;
+import org.apache.bval.jsr.util.ContainerElementNodeBuilderCustomizableContextImpl;
+import org.apache.bval.jsr.util.LeafNodeBuilderCustomizableContextImpl;
+import org.apache.bval.jsr.util.NodeBuilderCustomizableContextImpl;
+import org.apache.bval.jsr.util.NodeBuilderDefinedContextImpl;
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.jsr.util.PathImpl;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.Validate;
+
+public class ConstraintValidatorContextImpl<T> implements ConstraintValidatorContext, MessageInterpolator.Context {
+    private class ConstraintViolationBuilderImpl implements ConstraintValidatorContext.ConstraintViolationBuilder {
+        private final String template;
+        private final PathImpl path;
+
+        ConstraintViolationBuilderImpl(String template, PathImpl path) {
+            this.template = template;
+            this.path = path;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public NodeBuilderDefinedContext addNode(String name) {
+            PathImpl p;
+            if (path.isRootPath()) {
+                p = PathImpl.create();
+                p.getLeafNode().setName(name);
+            } else {
+                p = PathImpl.copy(path);
+                p.addNode(new NodeImpl(name));
+            }
+            return new NodeBuilderDefinedContextImpl(ConstraintValidatorContextImpl.this, template, p);
+        }
+
+        @Override
+        public NodeBuilderCustomizableContext addPropertyNode(String name) {
+            return new NodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl.this, template, path, name);
+        }
+
+        @Override
+        public LeafNodeBuilderCustomizableContext addBeanNode() {
+            return new LeafNodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl.this, template, path);
+        }
+
+        @Override
+        public NodeBuilderDefinedContext addParameterNode(int index) {
+            Exceptions.raiseUnless(frame.descriptor instanceof CrossParameterDescriptor, ValidationException::new,
+                "Cannot add parameter node for %s", frame.descriptor.getClass().getName());
+
+            final CrossParameterD<?, ?> crossParameter =
+                ComposedD.unwrap(frame.descriptor, CrossParameterD.class).findFirst().get();
+
+            final String parameterName = crossParameter.getParent().getParameterDescriptors().get(index).getName();
+
+            final NodeImpl node = new NodeImpl.ParameterNodeImpl(parameterName, index);
+            if (!path.isRootPath()) {
+                path.removeLeafNode();
+            }
+            path.addNode(node);
+            return new NodeBuilderDefinedContextImpl(ConstraintValidatorContextImpl.this, template, path);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public ConstraintValidatorContext addConstraintViolation() {
+            addError(template, path);
+            return ConstraintValidatorContextImpl.this;
+        }
+
+        @Override
+        public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name,
+            Class<?> containerType, Integer typeArgumentIndex) {
+            return new ContainerElementNodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl.this, template,
+                path, name, containerType, typeArgumentIndex);
+        }
+    }
+
+    private final ValidationJob<T>.Frame<?> frame;
+    private final ConstraintD<?> constraint;
+    private final Lazy<Set<ConstraintViolation<T>>> violations = new Lazy<>(HashSet::new);
+    private boolean defaultConstraintViolationDisabled;
+
+    /**
+     * Temporary for code migration
+     */
+    // TODO delete
+    @Deprecated
+    protected ConstraintValidatorContextImpl() {
+        this.frame = null;
+        this.constraint = null;
+    }
+
+    ConstraintValidatorContextImpl(ValidationJob<T>.Frame<?> frame, ConstraintD<?> constraint) {
+        super();
+        this.frame = Validate.notNull(frame, "frame");
+        this.constraint = Validate.notNull(constraint, "constraint");
+    }
+
+    @Override
+    public void disableDefaultConstraintViolation() {
+        this.defaultConstraintViolationDisabled = true;
+    }
+
+    @Override
+    public String getDefaultConstraintMessageTemplate() {
+        return constraint.getMessageTemplate();
+    }
+
+    @Override
+    public ConstraintViolationBuilder buildConstraintViolationWithTemplate(String messageTemplate) {
+        return new ConstraintViolationBuilderImpl(messageTemplate, frame.context.getPath());
+    }
+
+    @Override
+    public ClockProvider getClockProvider() {
+        return frame.getJob().validatorContext.getClockProvider();
+    }
+
+    @Override
+    public <U> U unwrap(Class<U> type) {
+        try {
+            return type.cast(this);
+        } catch (ClassCastException e) {
+            throw new ValidationException(e);
+        }
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void addError(String messageTemplate, Path propertyPath) {
+        violations.get().add(((ValidationJob) frame.getJob()).createViolation(messageTemplate, this, propertyPath));
+    }
+
+    ValidationJob<T>.Frame<?> getFrame() {
+        return frame;
+    }
+
+    Set<ConstraintViolation<T>> getRequiredViolations() {
+        if (!violations.optional().isPresent()) {
+            Exceptions.raiseIf(defaultConstraintViolationDisabled, ValidationException::new,
+                "Expected custom constraint violation(s)");
+
+            addError(getDefaultConstraintMessageTemplate(), frame.context.getPath());
+        }
+        return violations.get();
+    }
+
+    @Override
+    public ConstraintDescriptor<?> getConstraintDescriptor() {
+        return constraint;
+    }
+
+    @Override
+    public Object getValidatedValue() {
+        return frame.context.getValue();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a921963f/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateBean.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateBean.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateBean.java
new file mode 100644
index 0000000..dc3fab5
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateBean.java
@@ -0,0 +1,60 @@
+/*
+ * 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 javax.validation.Path;
+
+import org.apache.bval.jsr.ApacheFactoryContext;
+import org.apache.bval.jsr.ConstraintViolationImpl;
+import org.apache.bval.jsr.GraphContext;
+import org.apache.bval.jsr.descriptor.BeanD;
+import org.apache.bval.jsr.descriptor.ConstraintD;
+import org.apache.bval.jsr.util.PathImpl;
+import org.apache.bval.util.Validate;
+
+public final class ValidateBean<T> extends ValidationJob<T> {
+
+    private final T bean;
+
+    ValidateBean(ApacheFactoryContext validatorContext, T bean, Class<?>[] groups) {
+        super(validatorContext, groups);
+        this.bean = Validate.notNull(bean, IllegalArgumentException::new, "bean");
+    }
+
+    @Override
+    protected Frame<BeanD> computeBaseFrame() {
+        return new BeanFrame(new GraphContext(validatorContext, PathImpl.create(), bean));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected Class<T> getRootBeanClass() {
+        return (Class<T>) bean.getClass();
+    }
+
+    @Override
+    ConstraintViolationImpl<T> createViolation(String messageTemplate, ConstraintValidatorContextImpl<T> context,
+        Path propertyPath) {
+        final String message = validatorContext.getMessageInterpolator().interpolate(messageTemplate, context);
+
+        return new ConstraintViolationImpl<>(messageTemplate, message, bean, context.getFrame().getBean(), propertyPath,
+            context.getFrame().context.getValue(), context.getConstraintDescriptor(), getRootBeanClass(),
+            context.getConstraintDescriptor().unwrap(ConstraintD.class).getDeclaredOn(), null, null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a921963f/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
new file mode 100644
index 0000000..c0d866b
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java
@@ -0,0 +1,188 @@
+/*
+ * 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.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.stream.IntStream;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ParameterNameProvider;
+import javax.validation.Path;
+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.CrossParameterD;
+import org.apache.bval.jsr.descriptor.ParameterD;
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.jsr.util.PathImpl;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.TypeUtils;
+
+public abstract class ValidateParameters<E extends Executable, T> extends ValidationJob<T> {
+
+    public static class ForMethod<T> extends ValidateParameters<Method, T> {
+
+        private final T object;
+
+        ForMethod(ApacheFactoryContext validatorContext, T object, Method executable, Object[] parameterValues,
+            Class<?>[] groups) {
+            super(validatorContext, object, executable, parameterValues, groups);
+            this.object = Validate.notNull(object, IllegalArgumentException::new, "object");
+        }
+
+        @Override
+        protected ExecutableDescriptor describe() {
+            return validatorContext.getDescriptorManager().getBeanDescriptor(object.getClass())
+                .getConstraintsForMethod(executable.getName(), executable.getParameterTypes());
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        protected Class<T> getRootBeanClass() {
+            return (Class<T>) object.getClass();
+        }
+
+        @Override
+        protected List<String> getParameterNames(ParameterNameProvider parameterNameProvider) {
+            return parameterNameProvider.getParameterNames(executable);
+        }
+
+        @Override
+        protected T getRootBean() {
+            return object;
+        }
+    }
+
+    public static class ForConstructor<T> extends ValidateParameters<Constructor<? extends T>, T> {
+
+        ForConstructor(ApacheFactoryContext validatorContext, Constructor<? extends T> executable,
+            Object[] parameterValues, Class<?>[] groups) {
+            super(validatorContext, null, executable, parameterValues, groups);
+        }
+
+        @Override
+        protected ExecutableDescriptor describe() {
+            return validatorContext.getDescriptorManager().getBeanDescriptor(executable.getDeclaringClass())
+                .getConstraintsForConstructor(executable.getParameterTypes());
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        protected Class<T> getRootBeanClass() {
+            return (Class<T>) executable.getDeclaringClass();
+        }
+
+        @Override
+        protected List<String> getParameterNames(ParameterNameProvider parameterNameProvider) {
+            return parameterNameProvider.getParameterNames(executable);
+        }
+
+        @Override
+        protected T getRootBean() {
+            return null;
+        }
+    }
+
+    class ParametersFrame extends Frame<CrossParameterD<?, ?>> {
+        private final ExecutableDescriptor executableDescriptor;
+
+        protected ParametersFrame(ExecutableDescriptor executableDescriptor, GraphContext context) {
+            super(null, (CrossParameterD<?, ?>) Validate.notNull(executableDescriptor, "executableDescriptor")
+                .getCrossParameterDescriptor(), context);
+            this.executableDescriptor = executableDescriptor;
+        }
+
+        @Override
+        void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+            executableDescriptor.getParameterDescriptors().stream()
+                .map(pd -> new SproutFrame<ParameterD<?>>(this, (ParameterD<?>) pd, parameter(pd.getIndex())))
+                .forEach(f -> f.process(group, sink));
+        }
+
+        @Override
+        Object getBean() {
+            return object;
+        }
+    }
+
+    private static final String PARAMETERS_DO_NOT_MATCH = "Parameters do not match";
+
+    protected final T object;
+    protected final E executable;
+    protected final Lazy<List<String>> parameterNames =
+        new Lazy<>(() -> getParameterNames(validatorContext.getParameterNameProvider()));
+
+    private final Object[] parameterValues;
+
+    ValidateParameters(ApacheFactoryContext validatorContext, T object, E executable, Object[] parameterValues,
+        Class<?>[] groups) {
+        super(validatorContext, groups);
+        this.object = object;
+        this.executable = Validate.notNull(executable, IllegalArgumentException::new, "executable");
+        this.parameterValues =
+            Validate.notNull(parameterValues, IllegalArgumentException::new, "parameterValues").clone();
+
+        final Type[] genericParameterTypes = executable.getGenericParameterTypes();
+        Exceptions.raiseUnless(parameterValues.length == genericParameterTypes.length, IllegalArgumentException::new,
+            PARAMETERS_DO_NOT_MATCH);
+        IntStream.range(0, genericParameterTypes.length)
+            .forEach(n -> Exceptions.raiseUnless(TypeUtils.isInstance(parameterValues[n], genericParameterTypes[n]),
+                IllegalArgumentException::new, PARAMETERS_DO_NOT_MATCH));
+    }
+
+    @Override
+    protected Frame<?> computeBaseFrame() {
+        final PathImpl cp = PathImpl.create();
+        cp.addNode(new NodeImpl.CrossParameterNodeImpl());
+        return new ParametersFrame(describe(), new GraphContext(validatorContext, cp, parameterValues));
+    }
+
+    protected abstract ExecutableDescriptor describe();
+
+    protected abstract List<String> getParameterNames(ParameterNameProvider parameterNameProvider);
+
+    protected abstract T getRootBean();
+
+    @Override
+    ConstraintViolationImpl<T> createViolation(String messageTemplate, ConstraintValidatorContextImpl<T> context,
+        Path propertyPath) {
+
+        final String message = validatorContext.getMessageInterpolator().interpolate(messageTemplate, context);
+
+        return new ConstraintViolationImpl<T>(messageTemplate, message, getRootBean(), context.getFrame().getBean(),
+            propertyPath, context.getFrame().context.getValue(), context.getConstraintDescriptor(), getRootBeanClass(),
+            context.getConstraintDescriptor().unwrap(ConstraintD.class).getDeclaredOn(), null, parameterValues);
+    }
+
+    private GraphContext parameter(int i) {
+        final PathImpl path = PathImpl.create();
+        path.addNode(new NodeImpl.ParameterNodeImpl(parameterNames.get().get(i), i));
+        return new GraphContext(validatorContext, path, parameterValues[i]);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a921963f/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateProperty.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateProperty.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateProperty.java
new file mode 100644
index 0000000..a8fbdbc
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateProperty.java
@@ -0,0 +1,522 @@
+/*
+ * 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.reflect.Array;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Arrays;
+import java.util.List;
+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.function.Predicate;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Path;
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.CascadableDescriptor;
+import javax.validation.metadata.ContainerDescriptor;
+import javax.validation.metadata.ContainerElementTypeDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+import javax.validation.metadata.PropertyDescriptor;
+import javax.validation.valueextraction.ValueExtractor;
+import javax.validation.valueextraction.ValueExtractor.ValueReceiver;
+
+import org.apache.bval.jsr.ApacheFactoryContext;
+import org.apache.bval.jsr.ConstraintViolationImpl;
+import org.apache.bval.jsr.GraphContext;
+import org.apache.bval.jsr.descriptor.BeanD;
+import org.apache.bval.jsr.descriptor.CascadableContainerD;
+import org.apache.bval.jsr.descriptor.ComposedD;
+import org.apache.bval.jsr.descriptor.ConstraintD;
+import org.apache.bval.jsr.descriptor.ContainerElementTypeD;
+import org.apache.bval.jsr.descriptor.ElementD;
+import org.apache.bval.jsr.descriptor.PropertyD;
+import org.apache.bval.jsr.metadata.ContainerElementKey;
+import org.apache.bval.jsr.util.PathImpl;
+import org.apache.bval.jsr.util.PathNavigation;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.ObjectWrapper;
+import org.apache.bval.util.StringUtils;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.TypeUtils;
+
+public final class ValidateProperty<T> extends ValidationJob<T> {
+
+    interface Strategy<T> {
+        default PathNavigation.Callback<?> callback(PathImpl.Builder pathBuilder, FindDescriptor findDescriptor) {
+            return new PathNavigation.CompositeCallbackProcedure(Arrays.asList(pathBuilder, findDescriptor));
+        }
+
+        default T getRootBean() {
+            return null;
+        }
+
+        ValidateProperty<T>.Frame<?> frame(ValidateProperty<T> job, PathImpl path);
+    }
+
+    static class ForBeanProperty<T> implements Strategy<T> {
+        final ApacheFactoryContext validatorContext;
+        final T rootBean;
+        final GraphContext rootContext;
+        final ObjectWrapper<GraphContext> leafContext;
+        final ObjectWrapper<Object> value;
+
+        ForBeanProperty(ApacheFactoryContext validatorContext, T bean) {
+            super();
+            this.validatorContext = validatorContext;
+            this.rootBean = bean;
+            this.rootContext = new GraphContext(validatorContext, PathImpl.create(), bean);
+            this.leafContext = new ObjectWrapper<>(rootContext);
+            this.value = new ObjectWrapper<>(bean);
+        }
+
+        @Override
+        public PathNavigation.Callback<?> callback(PathImpl.Builder pathBuilder, FindDescriptor findDescriptor) {
+            return new WalkGraph(validatorContext, pathBuilder, findDescriptor, value,
+                (p, v) -> leafContext.accept(p.isRootPath() ? rootContext : rootContext.child(p, v)));
+        }
+
+        @Override
+        public T getRootBean() {
+            return rootBean;
+        }
+
+        public GraphContext baseContext(PathImpl path, ApacheFactoryContext validatorContext) {
+            return new GraphContext(validatorContext, PathImpl.create(), rootBean).child(path, value.get());
+        }
+
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        @Override
+        public ValidationJob<T>.Frame<?> frame(ValidateProperty<T> job, PathImpl path) {
+            if (job.descriptor instanceof BeanDescriptor) {
+                return job.new LeafFrame(leafContext.get());
+            }
+            return job.new PropertyFrame(job.new BeanFrame(leafContext.get()), job.descriptor,
+                leafContext.get().child(path, value.get()));
+        }
+    }
+
+    static class ForPropertyValue<T> implements Strategy<T> {
+        final Object value;
+
+        ForPropertyValue(Object value) {
+            super();
+            this.value = value;
+        }
+
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        @Override
+        public ValidationJob<T>.Frame<?> frame(ValidateProperty<T> job, PathImpl path) {
+            final GraphContext context = new GraphContext(job.validatorContext, path, value);
+            if (job.descriptor instanceof BeanDescriptor) {
+                return job.new LeafFrame(context);
+            }
+            return job.new PropertyFrame(null, job.descriptor, context);
+        }
+    }
+
+    private interface Step {
+        Type type();
+
+        ElementD<?, ?> element();
+    }
+
+    private static class DescriptorWrapper implements Step {
+        final ElementD<?, ?> wrapped;
+
+        DescriptorWrapper(ElementDescriptor wrapped) {
+            super();
+            this.wrapped = (ElementD<?, ?>) wrapped;
+        }
+
+        @Override
+        public Type type() {
+            return wrapped.getGenericType();
+        }
+
+        @Override
+        public ElementD<?, ?> element() {
+            return wrapped;
+        }
+    }
+
+    private static class TypeWrapper implements Step {
+        final ApacheFactoryContext validatorContext;
+        final Type type;
+
+        TypeWrapper(ApacheFactoryContext validatorContext, Type type) {
+            super();
+            this.validatorContext = validatorContext;
+            this.type = type;
+        }
+
+        @Override
+        public Type type() {
+            return type;
+        }
+
+        @Override
+        public ElementD<?, ?> element() {
+            final Class<?> beanClass = TypeUtils.getRawType(type, null);
+            return beanClass == null ? null
+                : (BeanD) validatorContext.getDescriptorManager().getBeanDescriptor(beanClass);
+        }
+    }
+
+    private static class FindDescriptor implements PathNavigation.Callback<ElementD<?, ?>> {
+        private final ApacheFactoryContext validatorContext;
+        Step current;
+
+        FindDescriptor(ApacheFactoryContext validatorContext, Class<?> beanClass) {
+            this.validatorContext = validatorContext;
+            this.current = new DescriptorWrapper(validatorContext.getDescriptorManager().getBeanDescriptor(beanClass));
+        }
+
+        @Override
+        public void handleProperty(String name) {
+            final ElementDescriptor element = current.element();
+            final BeanD bean;
+            if (element instanceof BeanD) {
+                bean = (BeanD) element;
+            } else {
+                bean = (BeanD) validatorContext.getDescriptorManager().getBeanDescriptor(element.getElementClass());
+            }
+            final PropertyDescriptor property = bean.getProperty(name);
+            Exceptions.raiseIf(property == null, IllegalArgumentException::new, "Unknown property %s of %s", name,
+                bean.getElementClass());
+            current = new DescriptorWrapper(property);
+        }
+
+        @Override
+        public void handleIndexOrKey(String value) {
+            handleGenericInIterable();
+        }
+
+        @Override
+        public void handleGenericInIterable() {
+            final ElementDescriptor desc = current.element();
+            if (desc instanceof CascadableContainerD<?, ?>) {
+                final Step containerElement = handleContainerElement((CascadableContainerD<?, ?>) desc);
+                if (containerElement != null) {
+                    current = containerElement;
+                    return;
+                }
+            }
+            current = handleElementByType(current.type());
+        }
+
+        private Step handleContainerElement(CascadableContainerD<?, ?> desc) {
+            final Set<ContainerElementTypeDescriptor> containerElements = desc.getConstrainedContainerElementTypes();
+            if (containerElements.isEmpty()) {
+                return null;
+            }
+            final ContainerElementTypeDescriptor element;
+            if (containerElements.size() == 1) {
+                element = containerElements.iterator().next();
+            } else {
+                final Predicate<ContainerElementKey> wellKnown =
+                    k -> k.represents(MAP_VALUE) || k.represents(ITERABLE_ELEMENT);
+
+                final Optional<ContainerElementTypeD> found =
+                    containerElements.stream().map(ContainerElementTypeD.class::cast)
+                        .filter(d -> d.getKey().getAssignableKeys().stream().anyMatch(wellKnown)).findFirst();
+
+                if (!found.isPresent()) {
+                    return null;
+                }
+                element = found.get();
+            }
+            return new DescriptorWrapper(element);
+        }
+
+        private Step handleElementByType(Type type) {
+            Type elementType;
+
+            if (TypeUtils.isArrayType(type)) {
+                elementType = TypeUtils.getArrayComponentType(type);
+            } else if (TypeUtils.isAssignable(type, Map.class)) {
+                elementType =
+                    Optional.ofNullable(TypeUtils.getTypeArguments(type, Map.class).get(MAP_VALUE)).orElse(MAP_VALUE);
+            } else if (TypeUtils.isAssignable(type, Iterable.class)) {
+                elementType =
+                    Optional.ofNullable(TypeUtils.getTypeArguments(type, Iterable.class).get(ITERABLE_ELEMENT))
+                        .orElse(ITERABLE_ELEMENT);
+            } else {
+                elementType = null;
+            }
+            Exceptions.raiseIf(elementType == null, IllegalArgumentException::new,
+                "Unable to resolve element type of %s", type);
+
+            return new TypeWrapper(validatorContext, elementType);
+        }
+
+        @Override
+        public ElementD<?, ?> result() {
+            return current.element();
+        }
+    }
+
+    private static class WalkGraph extends PathNavigation.CallbackProcedure {
+        final ApacheFactoryContext validatorContext;
+        final PathImpl.Builder pathBuilder;
+        final FindDescriptor findDescriptor;
+        final ObjectWrapper<Object> value;
+        final BiConsumer<PathImpl, Object> recordLeaf;
+
+        WalkGraph(ApacheFactoryContext validatorContext, PathImpl.Builder pathBuilder, FindDescriptor findDescriptor,
+            ObjectWrapper<Object> value, BiConsumer<PathImpl, Object> recordLeaf) {
+            this.validatorContext = validatorContext;
+            this.pathBuilder = pathBuilder;
+            this.findDescriptor = findDescriptor;
+            this.value = value;
+            this.recordLeaf = recordLeaf;
+        }
+
+        @Override
+        public void handleProperty(String name) {
+            final PathImpl p = PathImpl.copy(pathBuilder.result());
+            pathBuilder.handleProperty(name);
+            if (value.optional().isPresent()) {
+                recordLeaf.accept(p, value.get());
+
+                findDescriptor.handleProperty(name);
+
+                final PropertyD<?> propertyD =
+                    ComposedD.unwrap(findDescriptor.current.element(), PropertyD.class).findFirst().get();
+                try {
+                    value.accept(propertyD.getValue(value.get()));
+                } catch (Exception e) {
+                    Exceptions.raise(IllegalStateException::new, e, "Unable to get value of property %s",
+                        propertyD.getPropertyName());
+                }
+            }
+        }
+
+        @Override
+        public void handleIndexOrKey(final String indexOrKey) {
+            pathBuilder.handleIndexOrKey(indexOrKey);
+            findDescriptor.handleIndexOrKey(indexOrKey);
+            if (value.optional().isPresent()) {
+                ElementDescriptor element = findDescriptor.current.element();
+                if (element instanceof ContainerElementTypeD) {
+                    value.accept(handleContainer(value.get(), ((ContainerElementTypeD) element).getKey(), indexOrKey));
+                } else {
+                    value.accept(handleBasic(value.get(), indexOrKey));
+
+                    if (element == null && value.optional().isPresent()) {
+                        // no generic info available at some previous index level; fall back to runtime type of value
+                        // and repair structure of findDescriptor:
+                        findDescriptor.current = new TypeWrapper(validatorContext, value.get().getClass());
+                        element = findDescriptor.current.element();
+                    }
+                    if (element instanceof BeanDescriptor) {
+                        recordLeaf.accept(PathImpl.copy(pathBuilder.result()), value.get());
+                    }
+                }
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        private Object handleContainer(Object o, ContainerElementKey key, String indexOrKey) {
+            @SuppressWarnings("rawtypes")
+            final ValueExtractor valueExtractor = validatorContext.getValueExtractors().find(key);
+
+            final ObjectWrapper<Object> result = new ObjectWrapper<>();
+            valueExtractor.extractValues(o, new ValueReceiver() {
+
+                @Override
+                public void indexedValue(String nodeName, int index, Object object) {
+                    if (Integer.toString(index).equals(indexOrKey)) {
+                        result.accept(object);
+                    }
+                }
+
+                @Override
+                public void iterableValue(String nodeName, Object object) {
+                    // ?
+                    result.accept(object);
+                }
+
+                @Override
+                public void keyedValue(String nodeName, Object key, Object object) {
+                    if (String.valueOf(key).equals(indexOrKey)) {
+                        result.accept(object);
+                    }
+                }
+
+                @Override
+                public void value(String nodeName, Object object) {
+                    // ?
+                    result.accept(object);
+                }
+            });
+            return result.get();
+        }
+
+        private Object handleBasic(Object o, String indexOrKey) {
+            if (Map.class.isInstance(o)) {
+                for (Map.Entry<?, ?> e : ((Map<?, ?>) o).entrySet()) {
+                    if (String.valueOf(e.getKey()).equals(indexOrKey)) {
+                        return e.getValue();
+                    }
+                }
+            } else {
+                try {
+                    final int index = Integer.parseInt(indexOrKey);
+                    Exceptions.raiseIf(index < 0, IllegalArgumentException::new, "Invalid index %d", index);
+                    if (o != null && TypeUtils.isArrayType(o.getClass())) {
+                        if (Array.getLength(o) > index) {
+                            return Array.get(o, index);
+                        }
+                    } else if (List.class.isInstance(o)) {
+                        final List<?> l = (List<?>) o;
+                        if (l.size() > index) {
+                            return l.get(index);
+                        }
+                    } else if (Iterable.class.isInstance(o)) {
+                        int i = -1;
+                        for (Object e : (Iterable<?>) o) {
+                            if (++i == index) {
+                                return e;
+                            }
+                        }
+                    }
+                } catch (NumberFormatException e) {
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public void handleGenericInIterable() {
+            throw new UnsupportedOperationException("Cannot resolve generic inIterable against actual object graph");
+        }
+    }
+
+    class LeafFrame extends BeanFrame {
+
+        LeafFrame(GraphContext context) {
+            super(context);
+        }
+
+        @Override
+        protected ValidationJob<T>.Frame<?> propertyFrame(PropertyD<?> d, GraphContext context) {
+            return new PropertyFrame<>(this, d, context);
+        }
+    }
+
+    class PropertyFrame<D extends ElementD<?, ?> & CascadableDescriptor & ContainerDescriptor> extends SproutFrame<D> {
+
+        PropertyFrame(ValidationJob<T>.Frame<?> parent, D descriptor, GraphContext context) {
+            super(parent, descriptor, context);
+        }
+
+        @Override
+        void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+            if (cascade) {
+                super.recurse(group, sink);
+            }
+        }
+    }
+
+    private static final TypeVariable<?> MAP_VALUE = Map.class.getTypeParameters()[1];
+    private static final TypeVariable<?> ITERABLE_ELEMENT = Iterable.class.getTypeParameters()[0];
+
+    private final Strategy<T> strategy;
+    private final Class<T> rootBeanClass;
+    private final PathImpl propertyPath;
+    private final T rootBean;
+    private ElementD<?, ?> descriptor;
+    private boolean cascade;
+
+    private ValidateProperty(Strategy<T> strategy, ApacheFactoryContext validatorContext, Class<T> rootBeanClass,
+        String property, Class<?>[] groups) {
+        super(validatorContext, groups);
+
+        Exceptions.raiseIf(StringUtils.isBlank(property), IllegalArgumentException::new,
+            "property cannot be null/empty/blank");
+
+        this.strategy = strategy;
+        this.rootBeanClass = Validate.notNull(rootBeanClass, IllegalArgumentException::new, "rootBeanClass");
+
+        final PathImpl.Builder pathBuilder = new PathImpl.Builder();
+        final FindDescriptor findDescriptor = new FindDescriptor(validatorContext, rootBeanClass);
+
+        PathNavigation.navigate(property, strategy.callback(pathBuilder, findDescriptor));
+
+        this.propertyPath = pathBuilder.result();
+        this.descriptor = findDescriptor.result();
+        this.rootBean = strategy.getRootBean();
+    }
+
+    ValidateProperty(ApacheFactoryContext validatorContext, Class<T> rootBeanClass, String property, Object value,
+        Class<?>[] groups) {
+        this(new ForPropertyValue<>(value), validatorContext, rootBeanClass, property, groups);
+        if (descriptor == null) {
+            // should only occur when the root class is raw
+            descriptor = (ElementD<?, ?>) validatorContext.getDescriptorManager()
+                .getBeanDescriptor(value == null ? Object.class : value.getClass());
+        } else {
+            final Class<?> propertyType = descriptor.getElementClass();
+            Exceptions.raiseUnless(TypeUtils.isInstance(value, propertyType), IllegalArgumentException::new,
+                "%s is not an instance of %s", value, propertyType);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    ValidateProperty(ApacheFactoryContext validatorContext, T bean, String property, Class<?>[] groups)
+        throws Exception {
+        this(new ForBeanProperty<>(validatorContext, bean), validatorContext,
+            (Class<T>) Validate.notNull(bean, IllegalArgumentException::new, "bean").getClass(), property, groups);
+
+        Exceptions.raiseIf(descriptor == null, IllegalArgumentException::new,
+            "Could not resolve property name/path: %s", property);
+    }
+
+    public ValidateProperty<T> cascade(boolean cascade) {
+        this.cascade = cascade;
+        return this;
+    }
+
+    @Override
+    protected Frame<?> computeBaseFrame() {
+        // TODO assign bean as its own property and figure out what to do
+
+        return strategy.frame(this, propertyPath);
+    }
+
+    @Override
+    protected Class<T> getRootBeanClass() {
+        return rootBeanClass;
+    }
+
+    @Override
+    ConstraintViolationImpl<T> createViolation(String messageTemplate, ConstraintValidatorContextImpl<T> context,
+        Path propertyPath) {
+        final String message = validatorContext.getMessageInterpolator().interpolate(messageTemplate, context);
+
+        return new ConstraintViolationImpl<>(messageTemplate, message, rootBean, context.getFrame().getBean(),
+            propertyPath, context.getFrame().context.getValue(), context.getConstraintDescriptor(), rootBeanClass,
+            context.getConstraintDescriptor().unwrap(ConstraintD.class).getDeclaredOn(), null, null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a921963f/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
new file mode 100644
index 0000000..e71e7ae
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateReturnValue.java
@@ -0,0 +1,126 @@
+package org.apache.bval.jsr.job;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+import javax.validation.Path;
+
+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.Metas;
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.jsr.util.PathImpl;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.TypeUtils;
+
+public abstract class ValidateReturnValue<E extends Executable, T> extends ValidationJob<T> {
+    public static class ForMethod<T> extends ValidateReturnValue<Method, T> {
+        private final T object;
+
+        ForMethod(ApacheFactoryContext validatorContext, T object, Method method, Object returnValue,
+            Class<?>[] groups) {
+            super(validatorContext,
+                new Metas.ForMethod(Validate.notNull(method, IllegalArgumentException::new, "method")), returnValue,
+                groups);
+            this.object = Validate.notNull(object, IllegalArgumentException::new, "object");
+        }
+
+        @Override
+        protected T getRootBean() {
+            return object;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        protected Class<T> getRootBeanClass() {
+            return (Class<T>) object.getClass();
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        protected ExecutableD<Method, ?, ?> describe() {
+            return (ExecutableD<Method, ?, ?>) validatorContext.getDescriptorManager()
+                .getBeanDescriptor(object.getClass())
+                .getConstraintsForMethod(executable.getName(), executable.getParameterTypes());
+        }
+    }
+
+    public static class ForConstructor<T> extends ValidateReturnValue<Constructor<?>, T> {
+
+        ForConstructor(ApacheFactoryContext validatorContext, Constructor<? extends T> ctor, Object returnValue,
+            Class<?>[] groups) {
+            super(validatorContext,
+                new Metas.ForConstructor(Validate.notNull(ctor, IllegalArgumentException::new, "ctor")), returnValue,
+                groups);
+        }
+
+        @Override
+        protected T getRootBean() {
+            return null;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        protected Class<T> getRootBeanClass() {
+            return (Class<T>) executable.getDeclaringClass();
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        protected ExecutableD<Constructor<T>, ?, ?> describe() {
+            return (ExecutableD<Constructor<T>, ?, ?>) validatorContext.getDescriptorManager()
+                .getBeanDescriptor(executable.getDeclaringClass())
+                .getConstraintsForConstructor(executable.getParameterTypes());
+        }
+    }
+
+    protected final E executable;
+    private final Object returnValue;
+
+    ValidateReturnValue(ApacheFactoryContext validatorContext, Metas<E> meta, Object returnValue, Class<?>[] groups) {
+        super(validatorContext, groups);
+
+        final Type type = Validate.notNull(meta, "meta").getType();
+        Exceptions.raiseUnless(TypeUtils.isInstance(returnValue, type), IllegalArgumentException::new,
+            "%s is not an instance of %s", returnValue, type);
+
+        this.executable = meta.getHost();
+        this.returnValue = returnValue;
+    }
+
+    @Override
+    protected Frame<?> computeBaseFrame() {
+        final PathImpl path = PathImpl.create();
+        path.addNode(new NodeImpl.ReturnValueNodeImpl());
+
+        return new SproutFrame<ReturnValueD<?, ?>>((ReturnValueD<?, ?>) describe().getReturnValueDescriptor(),
+            new GraphContext(validatorContext, path, returnValue)) {
+            @Override
+            Object getBean() {
+                return getRootBean();
+            }
+        };
+    }
+
+    @Override
+    ConstraintViolationImpl<T> createViolation(String messageTemplate, ConstraintValidatorContextImpl<T> context,
+        Path propertyPath) {
+
+        final String message = validatorContext.getMessageInterpolator().interpolate(messageTemplate, context);
+
+        return new ConstraintViolationImpl<>(messageTemplate, message, getRootBean(), context.getFrame().getBean(),
+            propertyPath, context.getFrame().context.getValue(), context.getConstraintDescriptor(), getRootBeanClass(),
+            context.getConstraintDescriptor().unwrap(ConstraintD.class).getDeclaredOn(), returnValue, null);
+    }
+
+    protected abstract ExecutableD<?, ?, ?> describe();
+
+    protected abstract T getRootBean();
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a921963f/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
new file mode 100644
index 0000000..9221184
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java
@@ -0,0 +1,380 @@
+/*
+ * 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.reflect.Array;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintViolation;
+import javax.validation.Path;
+import javax.validation.TraversableResolver;
+import javax.validation.UnexpectedTypeException;
+import javax.validation.ValidationException;
+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 org.apache.bval.jsr.ApacheFactoryContext;
+import org.apache.bval.jsr.ConstraintViolationImpl;
+import org.apache.bval.jsr.GraphContext;
+import org.apache.bval.jsr.descriptor.BeanD;
+import org.apache.bval.jsr.descriptor.CascadableContainerD;
+import org.apache.bval.jsr.descriptor.ComposedD;
+import org.apache.bval.jsr.descriptor.ConstraintD;
+import org.apache.bval.jsr.descriptor.ElementD;
+import org.apache.bval.jsr.descriptor.PropertyD;
+import org.apache.bval.jsr.groups.Group;
+import org.apache.bval.jsr.groups.Groups;
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.jsr.util.PathImpl;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.Validate;
+
+public abstract class ValidationJob<T> {
+
+    public abstract class Frame<D extends ElementD<?, ?>> {
+        protected final Frame<?> parent;
+        protected final D descriptor;
+        protected final GraphContext context;
+
+        protected Frame(Frame<?> parent, D descriptor, GraphContext context) {
+            super();
+            this.parent = parent;
+            this.descriptor = Validate.notNull(descriptor, "descriptor");
+            this.context = Validate.notNull(context, "context");
+        }
+
+        final ValidationJob<T> getJob() {
+            return ValidationJob.this;
+        }
+
+        final void process(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+            Validate.notNull(sink, "sink");
+
+            each(expand(group), this::validateDescriptorConstraints, sink);
+            recurse(group, sink);
+        }
+
+        abstract void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink);
+
+        abstract Object getBean();
+
+        protected void validateDescriptorConstraints(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+            constraintsFrom(descriptor.findConstraints().unorderedAndMatchingGroups(group))
+                .forEach(c -> validate(c, sink));
+        }
+
+        @SuppressWarnings("unchecked")
+        private Stream<ConstraintD<?>> constraintsFrom(ConstraintFinder finder) {
+            // our ConstraintFinder implementation is a Stream supplier; reference without exposing it beyond its
+            // package:
+            if (finder instanceof Supplier<?>) {
+                return (Stream<ConstraintD<?>>) ((Supplier<?>) finder).get();
+            }
+            return finder.getConstraintDescriptors().stream().map(ConstraintD.class::cast);
+        }
+
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        private boolean validate(ConstraintD<?> constraint, Consumer<ConstraintViolation<T>> sink) {
+            if (!validatedPathsByConstraint
+                .computeIfAbsent(constraint, k -> new ConcurrentSkipListSet<>(COMPARE_TO_STRING))
+                .add(context.getPath())) {
+                // seen, ignore:
+                return true;
+            }
+            final ConstraintValidatorContextImpl<T> constraintValidatorContext =
+                new ConstraintValidatorContextImpl<>(this, constraint);
+
+            final ConstraintValidator constraintValidator = getConstraintValidator(constraint);
+
+            final boolean valid;
+            if (constraintValidator == null) {
+                // null validator without exception implies composition:
+                valid = true;
+            } else {
+                constraintValidator.initialize(constraint.getAnnotation());
+                valid = constraintValidator.isValid(context.getValue(), constraintValidatorContext);
+            }
+            if (!valid) {
+                constraintValidatorContext.getRequiredViolations().forEach(sink);
+            }
+            if (valid || !constraint.isReportAsSingleViolation()) {
+                final boolean compositionValid = validateComposed(constraint, sink);
+
+                if (!compositionValid) {
+                    if (valid && constraint.isReportAsSingleViolation()) {
+                        constraintValidatorContext.getRequiredViolations().forEach(sink);
+                    }
+                    return false;
+                }
+            }
+            return valid;
+        }
+
+        private boolean validateComposed(ConstraintD<?> constraint, Consumer<ConstraintViolation<T>> sink) {
+            if (constraint.getComposingConstraints().isEmpty()) {
+                return true;
+            }
+            final Consumer<ConstraintViolation<T>> effectiveSink = constraint.isReportAsSingleViolation() ? cv -> {
+            } : sink;
+
+            // collect validation results to set of Boolean, ensuring all are evaluated:
+            final Set<Boolean> results = constraint.getComposingConstraints().stream().map(ConstraintD.class::cast)
+                .map(c -> validate(c, effectiveSink)).collect(Collectors.toSet());
+
+            return Collections.singleton(Boolean.TRUE).equals(results);
+        }
+
+        @SuppressWarnings({ "rawtypes" })
+        private ConstraintValidator getConstraintValidator(ConstraintD<?> constraint) {
+            final Class<? extends ConstraintValidator> constraintValidatorClass =
+                constraint.getConstraintValidatorClass();
+
+            if (constraintValidatorClass == null) {
+                Exceptions.raiseIf(constraint.getComposingConstraints().isEmpty(), UnexpectedTypeException::new,
+                    "No %s type located for non-composed constraint %s", ConstraintValidator.class.getSimpleName(),
+                    constraint);
+                return null;
+            }
+            ConstraintValidator constraintValidator = null;
+            Exception cause = null;
+            try {
+                constraintValidator =
+                    validatorContext.getConstraintValidatorFactory().getInstance(constraintValidatorClass);
+            } catch (Exception e) {
+                cause = e;
+            }
+            Exceptions.raiseIf(constraintValidator == null, ValidationException::new, cause,
+                "Unable to get %s instance from %s", constraintValidatorClass.getName(),
+                validatorContext.getConstraintValidatorFactory());
+
+            return constraintValidator;
+        }
+
+        protected Stream<Class<?>> expand(Class<?> group) {
+            if (Default.class.equals(group)) {
+                final List<Class<?>> groupSequence = descriptor.getGroupSequence();
+                if (groupSequence != null) {
+                    return groupSequence.stream();
+                }
+            }
+            return Stream.of(group);
+        }
+    }
+
+    public class BeanFrame extends Frame<BeanD> {
+
+        BeanFrame(GraphContext context) {
+            this(null, context);
+        }
+
+        BeanFrame(Frame<?> parent, GraphContext context) {
+            super(parent, getBeanDescriptor(context.getValue()), context);
+        }
+
+        @Override
+        void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+            // bean frame has to do some convoluted things to properly handle groups and recursion; skipping
+            // frame#process() on properties:
+            final List<Frame<?>> propertyFrames = propertyFrames();
+
+            each(expand(group), (g, s) -> propertyFrames.forEach(f -> f.validateDescriptorConstraints(g, s)), sink);
+            propertyFrames.forEach(f -> f.recurse(group, sink));
+        }
+
+        protected Frame<?> propertyFrame(PropertyD<?> d, GraphContext context) {
+            return new SproutFrame<>(this, d, context);
+        }
+
+        @Override
+        Object getBean() {
+            return context.getValue();
+        }
+
+        private List<Frame<?>> propertyFrames() {
+            final Stream<PropertyD<?>> properties = descriptor.getConstrainedProperties().stream()
+                .flatMap(d -> ComposedD.unwrap(d, PropertyD.class)).map(d -> (PropertyD<?>) d);
+
+            final TraversableResolver traversableResolver = validatorContext.getTraversableResolver();
+
+            final Stream<PropertyD<?>> reachableProperties =
+                properties.filter(d -> traversableResolver.isReachable(context.getValue(),
+                    new NodeImpl.PropertyNodeImpl(d.getPropertyName()), getRootBeanClass(), context.getPath(),
+                    d.getElementType()));
+
+            return reachableProperties.flatMap(
+                d -> d.read(context).filter(context -> !context.isRecursive()).map(child -> propertyFrame(d, child)))
+                .collect(Collectors.toList());
+        }
+    }
+
+    public class SproutFrame<D extends ElementD<?, ?> & CascadableDescriptor & ContainerDescriptor> extends Frame<D> {
+
+        public SproutFrame(D descriptor, GraphContext context) {
+            this(null, descriptor, context);
+        }
+
+        public SproutFrame(Frame<?> parent, D descriptor, GraphContext context) {
+            super(parent, descriptor, context);
+        }
+
+        @Override
+        void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            final Stream<CascadableContainerD<?, ?>> containerElements =
+                descriptor.getConstrainedContainerElementTypes().stream()
+                    .flatMap(d -> ComposedD.unwrap(d, (Class) CascadableContainerD.class));
+
+            containerElements.flatMap(d -> d.read(context).map(child -> new SproutFrame<>(this, d, child)))
+                .forEach(f -> f.process(group, sink));
+
+            if (!descriptor.isCascaded()) {
+                return;
+            }
+            if (descriptor instanceof PropertyDescriptor) {
+                final TraversableResolver traversableResolver = validatorContext.getTraversableResolver();
+
+                final PathImpl pathToTraversableObject = PathImpl.copy(context.getPath());
+                final NodeImpl traversableProperty = pathToTraversableObject.removeLeafNode();
+
+                if (!traversableResolver.isCascadable(context.getValue(), traversableProperty, getRootBeanClass(),
+                    pathToTraversableObject, ((PropertyD<?>) descriptor).getElementType())) {
+                    return;
+                }
+            }
+            multiplex().filter(context -> context.getValue() != null).map(context -> new BeanFrame(this, context))
+                .forEach(b -> b.process(group, sink));
+        }
+
+        private Stream<GraphContext> multiplex() {
+            final Object value = context.getValue();
+            if (value == null) {
+                return Stream.empty();
+            }
+            if (Map.class.isInstance(value)) {
+                return ((Map<?, ?>) value).entrySet().stream()
+                    .map(e -> context.child(NodeImpl.atKey(e.getKey()), e.getValue()));
+            }
+            if (value.getClass().isArray()) {
+                return IntStream.range(0, Array.getLength(value))
+                    .mapToObj(i -> context.child(NodeImpl.atIndex(i), Array.get(value, i)));
+            }
+            if (List.class.isInstance(value)) {
+                final List<?> l = (List<?>) value;
+                return IntStream.range(0, l.size()).mapToObj(i -> context.child(NodeImpl.atIndex(i), l.get(i)));
+            }
+            if (Iterable.class.isInstance(value)) {
+                final Stream.Builder<Object> b = Stream.builder();
+                ((Iterable<?>) value).forEach(b);
+                return b.build().map(o -> context.child(NodeImpl.atIndex(null), o));
+            }
+            return Stream.of(context);
+        }
+
+        @Override
+        Object getBean() {
+            return Optional.ofNullable(parent).map(Frame::getBean).orElse(null);
+        }
+    }
+
+    private static final Comparator<Path> COMPARE_TO_STRING = Comparator.comparing(Object::toString);
+
+    protected final ApacheFactoryContext validatorContext;
+
+    private final Groups groups;
+    private final Lazy<Set<ConstraintViolation<T>>> results = new Lazy<>(LinkedHashSet::new);
+
+    private ConcurrentMap<ConstraintD<?>, Set<Path>> validatedPathsByConstraint;
+
+    ValidationJob(ApacheFactoryContext validatorContext, Class<?>[] groups) {
+        super();
+        this.validatorContext = Validate.notNull(validatorContext, "validatorContext");
+        this.groups = validatorContext.getGroupsComputer().computeGroups(groups);
+    }
+
+    public final Set<ConstraintViolation<T>> getResults() {
+        if (results.optional().isPresent()) {
+            return results.get();
+        }
+        final Frame<?> baseFrame = computeBaseFrame();
+        Validate.validState(baseFrame != null, "%s computed null baseFrame", getClass().getName());
+
+        final Consumer<ConstraintViolation<T>> sink = results.consumer(Set::add);
+
+        validatedPathsByConstraint = new ConcurrentHashMap<>();
+
+        try {
+            groups.getGroups().stream().map(Group::getGroup).forEach(g -> baseFrame.process(g, sink));
+
+            sequences: for (List<Group> seq : groups.getSequences()) {
+                final boolean proceed = each(seq.stream().map(Group::getGroup), baseFrame::process, sink);
+                if (!proceed) {
+                    break sequences;
+                }
+            }
+        } finally {
+            validatedPathsByConstraint = null;
+        }
+        return results.optional().map(Collections::unmodifiableSet).orElse(Collections.emptySet());
+    }
+
+    private boolean each(Stream<Class<?>> groupSequence, BiConsumer<Class<?>, Consumer<ConstraintViolation<T>>> closure,
+        Consumer<ConstraintViolation<T>> sink) {
+        final Lazy<Set<ConstraintViolation<T>>> sequenceViolations = new Lazy<>(LinkedHashSet::new);
+        for (Class<?> g : (Iterable<Class<?>>) () -> groupSequence.iterator()) {
+            closure.accept(g, sequenceViolations.consumer(Set::add));
+            if (sequenceViolations.optional().isPresent()) {
+                sequenceViolations.get().forEach(sink);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private BeanD getBeanDescriptor(Object bean) {
+        return (BeanD) validatorContext.getFactory().getDescriptorManager()
+            .getBeanDescriptor(Validate.notNull(bean, "bean").getClass());
+    }
+
+    abstract ConstraintViolationImpl<T> createViolation(String messageTemplate,
+        ConstraintValidatorContextImpl<T> context, Path propertyPath);
+
+    protected abstract Frame<?> computeBaseFrame();
+
+    protected abstract Class<T> getRootBeanClass();
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a921963f/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJobFactory.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJobFactory.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJobFactory.java
new file mode 100644
index 0000000..2a23192
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJobFactory.java
@@ -0,0 +1,112 @@
+/*
+ * 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.reflect.Constructor;
+import java.lang.reflect.Method;
+
+import javax.validation.ValidationException;
+import javax.validation.Validator;
+import javax.validation.executable.ExecutableValidator;
+
+import org.apache.bval.jsr.ApacheFactoryContext;
+import org.apache.bval.util.Validate;
+
+/**
+ * Creates {@link ValidationJob} instances.
+ */
+public class ValidationJobFactory {
+
+    private final ApacheFactoryContext validatorContext;
+
+    /**
+     * Create a new {@link ValidationJobFactory}.
+     * 
+     * @param validatorContext
+     */
+    public ValidationJobFactory(ApacheFactoryContext validatorContext) {
+        super();
+        this.validatorContext = Validate.notNull(validatorContext, "validatorContext");
+    }
+
+    /**
+     * @see Validator#validate(Object, Class...)
+     */
+    public <T> ValidateBean<T> validateBean(T bean, Class<?>... groups) {
+        return new ValidateBean<>(validatorContext, bean, groups);
+    }
+
+    /**
+     * @see Validator#validateProperty(Object, String, Class...)
+     */
+    public <T> ValidateProperty<T> validateProperty(T bean, String property, Class<?>... groups) {
+        try {
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            final ValidateProperty<T> result = new ValidateProperty(validatorContext, bean, property, groups);
+            return result;
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ValidationException(e);
+        }
+    }
+
+    /**
+     * @see Validator#validateValue(Class, String, Object, Class...)
+     */
+    public <T> ValidateProperty<T> validateValue(Class<T> rootBeanClass, String property, Object value,
+        Class<?>... groups) {
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        final ValidateProperty<T> result =
+            new ValidateProperty(validatorContext, rootBeanClass, property, value, groups);
+        return result;
+    }
+
+    /**
+     * @see ExecutableValidator#validateParameters(Object, Method, Object[], Class...)
+     */
+    public <T> ValidateParameters.ForMethod<T> validateParameters(T object, Method method, Object[] parameterValues,
+        Class<?>... groups) {
+        return new ValidateParameters.ForMethod<T>(validatorContext, object, method, parameterValues, groups);
+    }
+
+    /**
+     * @see ExecutableValidator#validateReturnValue(Object, Method, Object, Class...)
+     */
+    public <T> ValidateReturnValue.ForMethod<T> validateReturnValue(T object, Method method, Object returnValue,
+        Class<?>... groups) {
+        return new ValidateReturnValue.ForMethod<>(validatorContext, object, method, returnValue, groups);
+    }
+
+    /**
+     * @see ExecutableValidator#validateConstructorParameters(Constructor, Object[], Class...)
+     */
+    public <T> ValidateParameters.ForConstructor<T> validateConstructorParameters(Constructor<? extends T> constructor,
+        Object[] parameterValues, Class<?>... groups) {
+        return new ValidateParameters.ForConstructor<T>(validatorContext, constructor, parameterValues, groups);
+    }
+
+    /**
+     * @see ExecutableValidator#validateConstructorReturnValue(Constructor, Object, Class...)
+     */
+    public <T> ValidateReturnValue.ForConstructor<T> validateConstructorReturnValue(
+        Constructor<? extends T> constructor, T createdObject, Class<?>... groups) {
+        return new ValidateReturnValue.ForConstructor<T>(validatorContext, constructor, createdObject, groups);
+    }
+}


[02/17] bval git commit: BV2: utility classes

Posted by mb...@apache.org.
http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeContextBuilderImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeContextBuilderImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeContextBuilderImpl.java
index 80e5b8f..f32db9f 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeContextBuilderImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeContextBuilderImpl.java
@@ -18,18 +18,19 @@
  */
 package org.apache.bval.jsr.util;
 
-import org.apache.bval.jsr.ConstraintValidatorContextImpl;
+import org.apache.bval.jsr.job.ConstraintValidatorContextImpl;
 
 import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext;
 import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder;
 
 /**
  * Description: Implementation of {@link NodeContextBuilder}.<br/>
  */
-final class NodeContextBuilderImpl implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder {
-    private final ConstraintValidatorContextImpl parent;
-    private final String messageTemplate;
-    private final PathImpl propertyPath;
+public final class NodeContextBuilderImpl implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder {
+    private final ConstraintValidatorContextImpl<?> context;
+    private final String template;
+    private final PathImpl path;
     // The name of the last "added" node, it will only be added if it has a non-null name
     // The actual incorporation in the path will take place when the definition of the current leaf node is complete
     private final NodeImpl node;
@@ -40,10 +41,10 @@ final class NodeContextBuilderImpl implements ConstraintValidatorContext.Constra
      * @param template
      * @param path
      */
-    NodeContextBuilderImpl(ConstraintValidatorContextImpl contextImpl, String template, PathImpl path, NodeImpl node) {
-        parent = contextImpl;
-        messageTemplate = template;
-        propertyPath = path;
+    NodeContextBuilderImpl(ConstraintValidatorContextImpl<?> contextImpl, String template, PathImpl path, NodeImpl node) {
+        this.context = contextImpl;
+        this.template = template;
+        this.path = path;
         this.node = node;
     }
 
@@ -53,8 +54,8 @@ final class NodeContextBuilderImpl implements ConstraintValidatorContext.Constra
     @Override
     public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext atKey(Object key) {
         node.setKey(key);
-        propertyPath.addNode(node);
-        return new NodeBuilderDefinedContextImpl(parent, messageTemplate, propertyPath);
+        path.addNode(node);
+        return new NodeBuilderDefinedContextImpl(context, template, path);
     }
 
     /**
@@ -63,8 +64,8 @@ final class NodeContextBuilderImpl implements ConstraintValidatorContext.Constra
     @Override
     public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext atIndex(Integer index) {
         node.setIndex(index);
-        propertyPath.addNode(node);
-        return new NodeBuilderDefinedContextImpl(parent, messageTemplate, propertyPath);
+        path.addNode(node);
+        return new NodeBuilderDefinedContextImpl(context, template, path);
     }
 
     /**
@@ -78,14 +79,14 @@ final class NodeContextBuilderImpl implements ConstraintValidatorContext.Constra
     @Override
     public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addPropertyNode(
         String name) {
-        propertyPath.addNode(node);
-        return new NodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath, name);
+        path.addNode(node);
+        return new NodeBuilderCustomizableContextImpl(context, template, path, name);
     }
 
     @Override
     public ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext addBeanNode() {
-        propertyPath.addNode(node);
-        return new LeafNodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath);
+        path.addNode(node);
+        return new LeafNodeBuilderCustomizableContextImpl(context, template, path);
     }
 
     /**
@@ -93,9 +94,18 @@ final class NodeContextBuilderImpl implements ConstraintValidatorContext.Constra
      */
     @Override
     public ConstraintValidatorContext addConstraintViolation() {
-        propertyPath.addNode(node);
-        parent.addError(messageTemplate, propertyPath);
-        return parent;
+        path.addNode(node);
+        context.addError(template, path);
+        return context;
     }
 
-}
\ No newline at end of file
+    @Override
+    public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType,
+        Integer typeArgumentIndex) {
+        final NodeImpl node = new NodeImpl.ContainerElementNodeImpl(name, containerType, typeArgumentIndex);
+        path.addNode(node);
+        return new ContainerElementNodeBuilderCustomizableContextImpl(context, template, path, name, containerType,
+            typeArgumentIndex);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java
index 5a08f0e..96f9421 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java
@@ -21,16 +21,25 @@ package org.apache.bval.jsr.util;
 import javax.validation.ElementKind;
 import javax.validation.Path;
 import javax.validation.Path.Node;
+
+import org.apache.bval.util.Exceptions;
+
 import java.io.Serializable;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Consumer;
 
 public class NodeImpl implements Path.Node, Serializable {
 
     private static final long serialVersionUID = 1L;
     private static final String INDEX_OPEN = "[";
     private static final String INDEX_CLOSE = "]";
-    private List<Class<?>> parameterTypes;
+
+    private static <T extends Path.Node> Optional<T> optional(Class<T> type, Object o) {
+        return Optional.ofNullable(o).filter(type::isInstance).map(type::cast);
+    }
 
     /**
      * Append a Node to the specified StringBuilder.
@@ -63,7 +72,7 @@ public class NodeImpl implements Path.Node, Serializable {
      * @return NodeImpl
      */
     public static NodeImpl atIndex(Integer index) {
-        NodeImpl result = new NodeImpl();
+        final NodeImpl result = new NodeImpl();
         result.setIndex(index);
         return result;
     }
@@ -74,7 +83,7 @@ public class NodeImpl implements Path.Node, Serializable {
      * @return NodeImpl
      */
     public static NodeImpl atKey(Object key) {
-        NodeImpl result = new NodeImpl();
+        final NodeImpl result = new NodeImpl();
         result.setKey(key);
         return result;
     }
@@ -85,6 +94,9 @@ public class NodeImpl implements Path.Node, Serializable {
     private int parameterIndex;
     private Object key;
     private ElementKind kind;
+    private List<Class<?>> parameterTypes;
+    private Class<?> containerType;
+    private Integer typeArgumentIndex;
 
     /**
      * Create a new NodeImpl instance.
@@ -99,13 +111,18 @@ public class NodeImpl implements Path.Node, Serializable {
      * @param node
      */
     NodeImpl(Path.Node node) {
-        this.name = node.getName();
+        this(node.getName());
         this.inIterable = node.isInIterable();
         this.index = node.getIndex();
         this.key = node.getKey();
         this.kind = node.getKind();
     }
 
+    <T extends Path.Node> NodeImpl(Path.Node node, Class<T> nodeType, Consumer<T> handler) {
+        this(node);
+        Optional.of(node).filter(nodeType::isInstance).map(nodeType::cast).ifPresent(handler);
+    }
+
     private NodeImpl() {
     }
 
@@ -191,10 +208,8 @@ public class NodeImpl implements Path.Node, Serializable {
 
     @Override
     public <T extends Node> T as(final Class<T> nodeType) {
-        if (nodeType.isInstance(this)) {
-            return nodeType.cast(this);
-        }
-        throw new ClassCastException("Type " + nodeType + " not supported");
+        Exceptions.raiseUnless(nodeType.isInstance(this), ClassCastException::new, "Type %s not supported", nodeType);
+        return nodeType.cast(this);
     }
 
     /**
@@ -213,29 +228,13 @@ public class NodeImpl implements Path.Node, Serializable {
         if (this == o) {
             return true;
         }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        NodeImpl node = (NodeImpl) o;
-
-        if (inIterable != node.inIterable) {
-            return false;
-        }
-        if (index != null ? !index.equals(node.index) : node.index != null) {
-            return false;
-        }
-        if (key != null ? !key.equals(node.key) : node.key != null) {
-            return false;
-        }
-        if (name != null ? !name.equals(node.name) : node.name != null) {
-            return false;
-        }
-        if (kind != null ? !kind.equals(node.kind) : node.kind != null) {
+        if (o == null || !getClass().equals(o.getClass())) {
             return false;
         }
+        final NodeImpl node = (NodeImpl) o;
 
-        return true;
+        return inIterable == node.inIterable && Objects.equals(index, node.index) && Objects.equals(key, node.key)
+            && Objects.equals(name, node.name) && kind == node.kind;
     }
 
     /**
@@ -243,12 +242,7 @@ public class NodeImpl implements Path.Node, Serializable {
      */
     @Override
     public int hashCode() {
-        int result = name != null ? name.hashCode() : 0;
-        result = 31 * result + (inIterable ? 1 : 0);
-        result = 31 * result + (index != null ? index.hashCode() : 0);
-        result = 31 * result + (key != null ? key.hashCode() : 0);
-        result = 31 * result + (kind != null ? kind.hashCode() : 0);
-        return result;
+        return Objects.hash(name, Boolean.valueOf(inIterable), index, key, kind);
     }
 
     public int getParameterIndex() {
@@ -263,12 +257,24 @@ public class NodeImpl implements Path.Node, Serializable {
         this.parameterTypes = parameterTypes;
     }
 
+    public Class<?> getContainerClass() {
+        return containerType;
+    }
+
+    public Integer getTypeArgumentIndex() {
+        return typeArgumentIndex;
+    }
+
+    public void inContainer(Class<?> containerType, Integer typeArgumentIndex) {
+        this.containerType = containerType;
+        this.typeArgumentIndex = typeArgumentIndex;
+    }
+
+    @SuppressWarnings("serial")
     public static class ParameterNodeImpl extends NodeImpl implements Path.ParameterNode {
         public ParameterNodeImpl(final Node cast) {
             super(cast);
-            if (ParameterNodeImpl.class.isInstance(cast)) {
-                setParameterIndex(ParameterNodeImpl.class.cast(cast).getParameterIndex());
-            }
+            optional(Path.ParameterNode.class, cast).ifPresent(n -> setParameterIndex(n.getParameterIndex()));
         }
 
         public ParameterNodeImpl(final String name, final int idx) {
@@ -282,12 +288,11 @@ public class NodeImpl implements Path.Node, Serializable {
         }
     }
 
+    @SuppressWarnings("serial")
     public static class ConstructorNodeImpl extends NodeImpl implements Path.ConstructorNode {
         public ConstructorNodeImpl(final Node cast) {
             super(cast);
-            if (NodeImpl.class.isInstance(cast)) {
-                setParameterTypes(NodeImpl.class.cast(cast).parameterTypes);
-            }
+            optional(Path.ConstructorNode.class, cast).ifPresent(n -> setParameterTypes(n.getParameterTypes()));
         }
 
         public ConstructorNodeImpl(final String simpleName, List<Class<?>> paramTypes) {
@@ -301,6 +306,7 @@ public class NodeImpl implements Path.Node, Serializable {
         }
     }
 
+    @SuppressWarnings("serial")
     public static class CrossParameterNodeImpl extends NodeImpl implements Path.CrossParameterNode {
         public CrossParameterNodeImpl() {
             super("<cross-parameter>");
@@ -316,12 +322,11 @@ public class NodeImpl implements Path.Node, Serializable {
         }
     }
 
+    @SuppressWarnings("serial")
     public static class MethodNodeImpl extends NodeImpl implements Path.MethodNode {
         public MethodNodeImpl(final Node cast) {
             super(cast);
-            if (MethodNodeImpl.class.isInstance(cast)) {
-                setParameterTypes(MethodNodeImpl.class.cast(cast).getParameterTypes());
-            }
+            optional(Path.MethodNode.class, cast).ifPresent(n -> setParameterTypes(n.getParameterTypes()));
         }
 
         public MethodNodeImpl(final String name, final List<Class<?>> classes) {
@@ -335,6 +340,7 @@ public class NodeImpl implements Path.Node, Serializable {
         }
     }
 
+    @SuppressWarnings("serial")
     public static class ReturnValueNodeImpl extends NodeImpl implements Path.ReturnValueNode {
         public ReturnValueNodeImpl(final Node cast) {
             super(cast);
@@ -350,6 +356,7 @@ public class NodeImpl implements Path.Node, Serializable {
         }
     }
 
+    @SuppressWarnings("serial")
     public static class PropertyNodeImpl extends NodeImpl implements Path.PropertyNode {
         public PropertyNodeImpl(final String name) {
             super(name);
@@ -357,6 +364,8 @@ public class NodeImpl implements Path.Node, Serializable {
 
         public PropertyNodeImpl(final Node cast) {
             super(cast);
+            optional(Path.PropertyNode.class, cast)
+                .ifPresent(n -> inContainer(n.getContainerClass(), n.getTypeArgumentIndex()));
         }
 
         @Override
@@ -365,6 +374,7 @@ public class NodeImpl implements Path.Node, Serializable {
         }
     }
 
+    @SuppressWarnings("serial")
     public static class BeanNodeImpl extends NodeImpl implements Path.BeanNode {
         public BeanNodeImpl() {
             // no-op
@@ -372,6 +382,8 @@ public class NodeImpl implements Path.Node, Serializable {
 
         public BeanNodeImpl(final Node cast) {
             super(cast);
+            optional(Path.BeanNode.class, cast)
+                .ifPresent(n -> inContainer(n.getContainerClass(), n.getTypeArgumentIndex()));
         }
 
         @Override
@@ -379,4 +391,24 @@ public class NodeImpl implements Path.Node, Serializable {
             return ElementKind.BEAN;
         }
     }
+
+    @SuppressWarnings("serial")
+    public static class ContainerElementNodeImpl extends NodeImpl implements Path.ContainerElementNode {
+
+        public ContainerElementNodeImpl(String name, Class<?> containerType, Integer typeArgumentIndex) {
+            super(name);
+            inContainer(containerType, typeArgumentIndex);
+        }
+
+        public ContainerElementNodeImpl(final Node cast) {
+            super(cast);
+            optional(Path.ContainerElementNode.class, cast)
+                .ifPresent(n -> inContainer(n.getContainerClass(), n.getTypeArgumentIndex()));
+        }
+
+        @Override
+        public ElementKind getKind() {
+            return ElementKind.CONTAINER_ELEMENT;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java
index 59fba83..430d257 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java
@@ -18,16 +18,19 @@
  */
 package org.apache.bval.jsr.util;
 
-import javax.validation.Path;
 import java.io.Serializable;
-import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Objects;
+
+import javax.validation.Path;
+
+import org.apache.bval.util.Exceptions;
 
 /**
- * Description: object holding the property path as a list of nodes.
- * (Implementation partially based on reference implementation)
- * <br/>
+ * Description: object holding the property path as a list of nodes. (Implementation partially based on reference
+ * implementation) <br/>
  * This class is not synchronized.
  * 
  * @version $Rev: 1498347 $ $Date: 2013-07-01 12:06:18 +0200 (lun., 01 juil. 2013) $
@@ -41,8 +44,8 @@ public class PathImpl implements Path, Serializable {
     /**
      * Builds non-root paths from expressions.
      */
-    private static class PathImplBuilder implements PathNavigation.Callback<PathImpl> {
-        PathImpl result = new PathImpl();
+    public static class Builder implements PathNavigation.Callback<PathImpl> {
+        private final PathImpl result = PathImpl.create();
 
         /**
          * {@inheritDoc}
@@ -72,9 +75,6 @@ public class PathImpl implements Path, Serializable {
          */
         @Override
         public PathImpl result() {
-            if (result.nodeList.isEmpty()) {
-                throw new IllegalStateException();
-            }
             return result;
         }
 
@@ -85,11 +85,8 @@ public class PathImpl implements Path, Serializable {
         public void handleGenericInIterable() {
             result.addNode(NodeImpl.atIndex(null));
         }
-
     }
 
-    private final List<NodeImpl> nodeList;
-
     /**
      * Returns a {@code Path} instance representing the path described by the given string. To create a root node the
      * empty string should be passed. Note: This signature is to maintain pluggability with the RI impl.
@@ -102,7 +99,7 @@ public class PathImpl implements Path, Serializable {
         if (propertyPath == null || propertyPath.isEmpty()) {
             return create();
         }
-        return PathNavigation.navigateAndReturn(propertyPath, new PathImplBuilder());
+        return PathNavigation.navigateAndReturn(propertyPath, new Builder());
     }
 
     /**
@@ -127,6 +124,10 @@ public class PathImpl implements Path, Serializable {
         return path == null ? null : new PathImpl(path);
     }
 
+    public static PathImpl of(Path path) {
+        return path instanceof PathImpl ? (PathImpl) path : copy(path);
+    }
+
     private static NodeImpl newNode(final Node cast) {
         if (PropertyNode.class.isInstance(cast)) {
             return new NodeImpl.PropertyNodeImpl(cast);
@@ -140,9 +141,6 @@ public class PathImpl implements Path, Serializable {
         if (ConstructorNode.class.isInstance(cast)) {
             return new NodeImpl.ConstructorNodeImpl(cast);
         }
-        if (ConstructorNode.class.isInstance(cast)) {
-            return new NodeImpl.ConstructorNodeImpl(cast);
-        }
         if (ReturnValueNode.class.isInstance(cast)) {
             return new NodeImpl.ReturnValueNodeImpl(cast);
         }
@@ -152,18 +150,19 @@ public class PathImpl implements Path, Serializable {
         if (CrossParameterNode.class.isInstance(cast)) {
             return new NodeImpl.CrossParameterNodeImpl(cast);
         }
+        if (ContainerElementNode.class.isInstance(cast)) {
+            return new NodeImpl.ContainerElementNodeImpl(cast);
+        }
         return new NodeImpl(cast);
     }
 
+    private final LinkedList<NodeImpl> nodeList = new LinkedList<>();
+
     private PathImpl() {
-        nodeList = new ArrayList<NodeImpl>();
     }
 
-    private PathImpl(Iterable<Node> path) {
-        this();
-        for (final Node node : path) {
-            nodeList.add(newNode(node));
-        }
+    private PathImpl(Iterable<? extends Node> nodes) {
+        nodes.forEach(n -> nodeList.add(newNode(n)));
     }
 
     /**
@@ -176,7 +175,7 @@ public class PathImpl implements Path, Serializable {
         if (nodeList.size() != 1) {
             return false;
         }
-        Path.Node first = nodeList.get(0);
+        final Path.Node first = nodeList.peekFirst();
         return !first.isInIterable() && first.getName() == null;
     }
 
@@ -186,13 +185,10 @@ public class PathImpl implements Path, Serializable {
      * @return PathImpl
      */
     public PathImpl getPathWithoutLeafNode() {
-        List<Node> nodes = new ArrayList<Node>(nodeList);
-        PathImpl path = null;
-        if (nodes.size() > 1) {
-            nodes.remove(nodes.size() - 1);
-            path = new PathImpl(nodes);
+        if (nodeList.size() < 2) {
+            return null;
         }
-        return path;
+        return new PathImpl(nodeList.subList(0, nodeList.size() - 1));
     }
 
     /**
@@ -202,12 +198,11 @@ public class PathImpl implements Path, Serializable {
      *            to add
      */
     public void addNode(Node node) {
-        NodeImpl impl = node instanceof NodeImpl ? (NodeImpl) node : newNode(node);
+        final NodeImpl impl = node instanceof NodeImpl ? (NodeImpl) node : newNode(node);
         if (isRootPath()) {
-            nodeList.set(0, impl);
-        } else {
-            nodeList.add(impl);
+            nodeList.pop();
         }
+        nodeList.add(impl);
     }
 
     /**
@@ -229,7 +224,6 @@ public class PathImpl implements Path, Serializable {
                 return;
             }
         }
-
         final NodeImpl node;
         if ("<cross-parameter>".equals(name)) {
             node = new NodeImpl.CrossParameterNodeImpl();
@@ -237,7 +231,6 @@ public class PathImpl implements Path, Serializable {
             node = new NodeImpl.PropertyNodeImpl(name);
         }
         addNode(node);
-
     }
 
     /**
@@ -248,11 +241,10 @@ public class PathImpl implements Path, Serializable {
      *             if no nodes are found
      */
     public NodeImpl removeLeafNode() {
-        if (isRootPath() || nodeList.isEmpty()) {
-            throw new IllegalStateException("No nodes in path!");
-        }
+        Exceptions.raiseIf(isRootPath() || nodeList.isEmpty(), IllegalStateException::new, "No nodes in path!");
+
         try {
-            return nodeList.remove(nodeList.size() - 1);
+            return nodeList.removeLast();
         } finally {
             if (nodeList.isEmpty()) {
                 nodeList.add(new NodeImpl((String) null));
@@ -269,7 +261,7 @@ public class PathImpl implements Path, Serializable {
         if (nodeList.isEmpty()) {
             return null;
         }
-        return (NodeImpl) nodeList.get(nodeList.size() - 1);
+        return nodeList.peekLast();
     }
 
     /**
@@ -292,14 +284,14 @@ public class PathImpl implements Path, Serializable {
         if (path instanceof PathImpl && ((PathImpl) path).isRootPath()) {
             return true;
         }
-        Iterator<Node> pathIter = path.iterator();
-        Iterator<Node> thisIter = iterator();
+        final Iterator<Node> pathIter = path.iterator();
+        final Iterator<Node> thisIter = iterator();
         while (pathIter.hasNext()) {
-            Node pathNode = pathIter.next();
+            final Node pathNode = pathIter.next();
             if (!thisIter.hasNext()) {
                 return false;
             }
-            Node thisNode = thisIter.next();
+            final Node thisNode = thisIter.next();
             if (pathNode.isInIterable()) {
                 if (!thisNode.isInIterable()) {
                     return false;
@@ -328,7 +320,7 @@ public class PathImpl implements Path, Serializable {
      */
     @Override
     public String toString() {
-        StringBuilder builder = new StringBuilder();
+        final StringBuilder builder = new StringBuilder();
         for (Path.Node node : this) {
             NodeImpl.appendNode(node, builder);
         }
@@ -346,9 +338,7 @@ public class PathImpl implements Path, Serializable {
         if (o == null || !getClass().equals(o.getClass())) {
             return false;
         }
-
-        PathImpl path = (PathImpl) o;
-        return nodeList == path.nodeList || nodeList != null && nodeList.equals(path.nodeList);
+        return Objects.equals(nodeList, ((PathImpl) o).nodeList);
     }
 
     /**
@@ -356,7 +346,6 @@ public class PathImpl implements Path, Serializable {
      */
     @Override
     public int hashCode() {
-        return nodeList == null ? 0 : nodeList.hashCode();
+        return Objects.hashCode(nodeList);
     }
-
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathNavigation.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathNavigation.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathNavigation.java
index 36fb919..7ba6bc3 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathNavigation.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathNavigation.java
@@ -18,12 +18,17 @@ package org.apache.bval.jsr.util;
 
 import javax.validation.ValidationException;
 
+import org.apache.bval.util.Exceptions;
 import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.commons.lang3.Validate;
 
 import java.io.IOException;
 import java.io.StringWriter;
 import java.io.Writer;
 import java.text.ParsePosition;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.logging.Logger;
 
 /**
@@ -67,13 +72,13 @@ public class PathNavigation {
     /**
      * Callback "procedure" that always returns null.
      */
-    public static abstract class CallbackProcedure implements Callback<Object> {
+    public static abstract class CallbackProcedure implements Callback<Void> {
 
         /**
          * {@inheritDoc}
          */
         @Override
-        public final Object result() {
+        public final Void result() {
             complete();
             return null;
         }
@@ -85,6 +90,34 @@ public class PathNavigation {
         }
     }
 
+    public static class CompositeCallbackProcedure extends CallbackProcedure {
+        private final List<Callback<?>> delegates;
+
+        public CompositeCallbackProcedure(Callback<?>... delegates) {
+            this(new ArrayList<>(Arrays.asList(delegates)));
+        }
+
+        public CompositeCallbackProcedure(List<Callback<?>> delegates) {
+            super();
+            this.delegates = Validate.notNull(delegates);
+        }
+
+        @Override
+        public void handleProperty(String name) {
+            delegates.forEach(d -> d.handleProperty(name));
+        }
+
+        @Override
+        public void handleIndexOrKey(String value) {
+            delegates.forEach(d -> d.handleIndexOrKey(value));
+        }
+
+        @Override
+        public void handleGenericInIterable() {
+            delegates.forEach(Callback::handleGenericInIterable);
+        }
+    }
+
     private static class QuotedStringParser {
         String parseQuotedString(CharSequence path, PathPosition pos) throws Exception {
             final int len = path.length();
@@ -118,8 +151,7 @@ public class PathNavigation {
 
         @Override
         protected void handleNextChar(CharSequence path, PathPosition pos, Writer target) throws IOException {
-            final int 
-                codePoints = StringEscapeUtils.UNESCAPE_JAVA.translate(path, pos.getIndex(), target);
+            final int codePoints = StringEscapeUtils.UNESCAPE_JAVA.translate(path, pos.getIndex(), target);
             if (codePoints == 0) {
                 super.handleNextChar(path, pos, target);
             } else {
@@ -128,13 +160,12 @@ public class PathNavigation {
                 }
             }
         }
-
     }
 
     private static final Logger LOG = Logger.getLogger(PathNavigation.class.getName());
 
     private static final QuotedStringParser QUOTED_STRING_PARSER;
-    
+
     static {
         QuotedStringParser quotedStringParser;
         try {
@@ -164,10 +195,10 @@ public class PathNavigation {
     public static <T> T navigateAndReturn(CharSequence propertyPath, Callback<? extends T> callback) {
         try {
             parse(propertyPath == null ? "" : propertyPath, new PathPosition(callback));
-        } catch (ValidationException ex) {
+        } catch (ValidationException | IllegalArgumentException ex) {
             throw ex;
-        } catch (Exception ex) {
-            throw new ValidationException(String.format("invalid property: %s", propertyPath), ex);
+        } catch (Exception e) {
+            Exceptions.raise(ValidationException::new, e, "invalid property: %s", propertyPath);
         }
         return callback.result();
     }
@@ -190,23 +221,21 @@ public class PathNavigation {
             char c = path.charAt(here);
             switch (c) {
             case ']':
-                throw new IllegalStateException(String.format("Position %s: unexpected '%s'", here, c));
+                Exceptions.raise(IllegalStateException::new, "Position %s: unexpected '%s'", here, c);
             case '[':
                 handleIndex(path, pos.next());
                 break;
             case '.':
-                if (sep) {
-                    throw new IllegalStateException(
-                        String.format("Position %s: expected property, index/key, or end of expression", here));
-                }
+                Exceptions.raiseIf(sep, IllegalStateException::new,
+                    "Position %s: expected property, index/key, or end of expression", here);
+
                 sep = true;
                 pos.next();
                 // fall through:
             default:
-                if (!sep) {
-                    throw new IllegalStateException(String.format(
-                        "Position %s: expected property path separator, index/key, or end of expression", here));
-                }
+                Exceptions.raiseUnless(sep, IllegalStateException::new,
+                    "Position %s: expected property path separator, index/key, or end of expression", here);
+
                 pos.handleProperty(parseProperty(path, pos));
             }
             sep = false;
@@ -214,8 +243,8 @@ public class PathNavigation {
     }
 
     private static String parseProperty(CharSequence path, PathPosition pos) throws Exception {
-        int len = path.length();
-        int start = pos.getIndex();
+        final int len = path.length();
+        final int start = pos.getIndex();
         loop: while (pos.getIndex() < len) {
             switch (path.charAt(pos.getIndex())) {
             case '[':
@@ -225,27 +254,28 @@ public class PathNavigation {
             }
             pos.next();
         }
-        if (pos.getIndex() > start) {
-            return path.subSequence(start, pos.getIndex()).toString();
-        }
-        throw new IllegalStateException(String.format("Position %s: expected property", start));
+        Exceptions.raiseIf(pos.getIndex() == start, IllegalStateException::new, "Position %s: expected property",
+            start);
+
+        return path.subSequence(start, pos.getIndex()).toString();
     }
 
     /**
      * Handles an index/key. If the text contained between [] is surrounded by a pair of " or ', it will be treated as a
-     * string which may contain Java escape sequences. This function is only available if commons-lang3 is available on the classpath!
+     * string which may contain Java escape sequences. This function is only available if commons-lang3 is available on
+     * the classpath!
      * 
      * @param path
      * @param pos
      * @throws Exception
      */
     private static void handleIndex(CharSequence path, PathPosition pos) throws Exception {
-        int len = path.length();
-        int start = pos.getIndex();
+        final int len = path.length();
+        final int start = pos.getIndex();
         if (start < len) {
-            char first = path.charAt(pos.getIndex());
+            final char first = path.charAt(pos.getIndex());
             if (first == '"' || first == '\'') {
-                String s = QUOTED_STRING_PARSER.parseQuotedString(path, pos);
+                final String s = QUOTED_STRING_PARSER.parseQuotedString(path, pos);
                 if (s != null && path.charAt(pos.getIndex()) == ']') {
                     pos.handleIndexOrKey(s);
                     pos.next();
@@ -254,7 +284,7 @@ public class PathNavigation {
             }
             // no quoted string; match ] greedily
             while (pos.getIndex() < len) {
-                int here = pos.getIndex();
+                final int here = pos.getIndex();
                 try {
                     if (path.charAt(here) == ']') {
                         if (here == start) {
@@ -269,13 +299,13 @@ public class PathNavigation {
                 }
             }
         }
-        throw new IllegalStateException(String.format("Position %s: unparsable index", start));
+        Exceptions.raise(IllegalStateException::new, "Position %s: unparsable index", start);
     }
 
     /**
      * ParsePosition/Callback
      */
-    private static class PathPosition extends ParsePosition implements Callback<Object> {
+    private static class PathPosition extends ParsePosition implements Callback<Void> {
         final Callback<?> delegate;
 
         /**
@@ -336,7 +366,7 @@ public class PathNavigation {
          * {@inheritDoc}
          */
         @Override
-        public Object result() {
+        public Void result() {
             return null;
         }
 
@@ -344,9 +374,8 @@ public class PathNavigation {
          * {@inheritDoc}
          */
         /*
-         * Override equals to make findbugs happy;
-         * would simply ignore but doesn't seem to be possible at the inner class level
-         * without attaching the filter to the containing class.
+         * Override equals to make findbugs happy; would simply ignore but doesn't seem to be possible at the inner
+         * class level without attaching the filter to the containing class.
          */
         @Override
         public boolean equals(Object obj) {
@@ -364,5 +393,4 @@ public class PathNavigation {
             return super.hashCode();
         }
     }
-
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/Proxies.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/Proxies.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/Proxies.java
index 1fa033c..1d0c1ee 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/Proxies.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/Proxies.java
@@ -26,7 +26,7 @@ public final class Proxies {
     private static final Set<String> KNOWN_PROXY_CLASSNAMES;
 
     static {
-        final Set<String> s = new HashSet<String>();
+        final Set<String> s = new HashSet<>();
         s.add("org.jboss.weld.bean.proxy.ProxyObject");
         KNOWN_PROXY_CLASSNAMES = Collections.unmodifiableSet(s);
     }

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/ToUnmodifiable.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ToUnmodifiable.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ToUnmodifiable.java
new file mode 100644
index 0000000..a470e0d
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ToUnmodifiable.java
@@ -0,0 +1,34 @@
+package org.apache.bval.jsr.util;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+/**
+ * Utility {@link Collector} definitions.
+ */
+public class ToUnmodifiable {
+
+    public static <T> Collector<T, ?, Set<T>> set(Supplier<Set<T>> set) {
+        return Collectors.collectingAndThen(Collectors.toCollection(set), Collections::unmodifiableSet);
+    }
+    
+    public static <T> Collector<T, ?, Set<T>> set() {
+        return Collectors.collectingAndThen(Collectors.toCollection(LinkedHashSet::new), Collections::unmodifiableSet);
+    }
+
+    public static <T> Collector<T, ?, List<T>> list() {
+        return Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList);
+    }
+
+    public static <T, K, U> Collector<T, ?, Map<K, U>> map(Function<? super T, ? extends K> keyMapper,
+        Function<? super T, ? extends U> valueMapper) {
+        return Collectors.collectingAndThen(Collectors.toMap(keyMapper, valueMapper), Collections::unmodifiableMap);
+    }
+}


[05/17] bval git commit: BV2: xml processing changes

Posted by mb...@apache.org.
BV2: xml processing changes


Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/5c09f0dd
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/5c09f0dd
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/5c09f0dd

Branch: refs/heads/bv2
Commit: 5c09f0dd1bfa62aa444287fbf2c1882f539b02d6
Parents: af6ee90
Author: Matt Benson <mb...@apache.org>
Authored: Wed Feb 21 14:29:48 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Wed Feb 21 14:44:27 2018 -0600

----------------------------------------------------------------------
 .../apache/bval/jsr/xml/AnnotationProxy.java    |  59 +-
 .../bval/jsr/xml/AnnotationProxyBuilder.java    | 104 ++-
 .../org/apache/bval/jsr/xml/SchemaManager.java  | 286 +++++++
 .../bval/jsr/xml/ValidationMappingParser.java   | 749 ++-----------------
 .../apache/bval/jsr/xml/ValidationParser.java   | 330 ++++----
 .../java/org/apache/bval/jsr/xml/XmlUtils.java  |  67 ++
 bval-jsr/src/main/xjb/binding-customization.xjb |   4 +-
 .../main/xsd/validation-configuration-2.0.xsd   |  75 ++
 .../src/main/xsd/validation-mapping-2.0.xsd     | 297 ++++++++
 .../test/java/org/apache/bval/jsr/xml/Demo.java |  40 +
 .../bval/jsr/xml/ValidationParserTest.java      |  18 +-
 .../src/test/resources/sample-validation11.xml  |  30 +
 .../src/test/resources/sample-validation2.xml   |  30 +
 13 files changed, 1116 insertions(+), 973 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/5c09f0dd/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxy.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxy.java b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxy.java
index 77aed70..96a2b46 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxy.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxy.java
@@ -16,20 +16,21 @@
  */
 package org.apache.bval.jsr.xml;
 
-import javax.validation.Valid;
 import java.io.Serializable;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.SortedSet;
-import java.util.TreeSet;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+import javax.validation.Valid;
+
+import org.apache.bval.util.Exceptions;
 
 /**
  * Description: <br/>
- * InvocationHandler implementation of <code>Annotation</code> that pretends it
- * is a "real" source code annotation.
+ * InvocationHandler implementation of <code>Annotation</code> that pretends it is a "real" source code annotation.
  * <p/>
  */
 class AnnotationProxy implements Annotation, InvocationHandler, Serializable {
@@ -38,7 +39,7 @@ class AnnotationProxy implements Annotation, InvocationHandler, Serializable {
     private static final long serialVersionUID = 1L;
 
     private final Class<? extends Annotation> annotationType;
-    private final Map<String, Object> values;
+    private final SortedMap<String, Object> values;
 
     /**
      * Create a new AnnotationProxy instance.
@@ -46,28 +47,23 @@ class AnnotationProxy implements Annotation, InvocationHandler, Serializable {
      * @param <A>
      * @param descriptor
      */
-    public <A extends Annotation> AnnotationProxy(AnnotationProxyBuilder<A> descriptor) {
+    <A extends Annotation> AnnotationProxy(AnnotationProxyBuilder<A> descriptor) {
         this.annotationType = descriptor.getType();
-        values = getAnnotationValues(descriptor);
-    }
-
-    private <A extends Annotation> Map<String, Object> getAnnotationValues(AnnotationProxyBuilder<A> descriptor) {
-        final Map<String, Object> result = new HashMap<String, Object>();
+        values = new TreeMap<>();
         int processedValuesFromDescriptor = 0;
         for (final Method m : descriptor.getMethods()) {
             if (descriptor.contains(m.getName())) {
-                result.put(m.getName(), descriptor.getValue(m.getName()));
+                values.put(m.getName(), descriptor.getValue(m.getName()));
                 processedValuesFromDescriptor++;
-            } else if (m.getDefaultValue() != null) {
-                result.put(m.getName(), m.getDefaultValue());
             } else {
-                throw new IllegalArgumentException("No value provided for " + m.getName());
+                Exceptions.raiseIf(m.getDefaultValue() == null, IllegalArgumentException::new,
+                    "No value provided for %s", m.getName());
+                values.put(m.getName(), m.getDefaultValue());
             }
         }
-        if (processedValuesFromDescriptor != descriptor.size() && !Valid.class.equals(annotationType)) {
-            throw new RuntimeException("Trying to instanciate " + annotationType + " with unknown paramters.");
-        }
-        return result;
+        Exceptions.raiseUnless(processedValuesFromDescriptor == descriptor.size() || Valid.class.equals(annotationType),
+            IllegalArgumentException::new, "Trying to instantiate %s with unknown parameters.",
+            annotationType.getName());
     }
 
     /**
@@ -94,22 +90,7 @@ class AnnotationProxy implements Annotation, InvocationHandler, Serializable {
      */
     @Override
     public String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append('@').append(annotationType().getName()).append('(');
-        boolean comma = false;
-        for (String m : getMethodsSorted()) {
-            if (comma)
-                result.append(", ");
-            result.append(m).append('=').append(values.get(m));
-            comma = true;
-        }
-        result.append(")");
-        return result.toString();
-    }
-
-    private SortedSet<String> getMethodsSorted() {
-        SortedSet<String> result = new TreeSet<String>();
-        result.addAll(values.keySet());
-        return result;
+        return values.entrySet().stream().map(e -> String.format("%s=%s", e.getKey(), e.getValue()))
+            .collect(Collectors.joining(", ", String.format("@%s(", annotationType().getName()), ")"));
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/5c09f0dd/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxyBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxyBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxyBuilder.java
index dedfabc..02383cb 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxyBuilder.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/AnnotationProxyBuilder.java
@@ -30,10 +30,12 @@ import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 import javax.enterprise.util.AnnotationLiteral;
+import javax.validation.ConstraintTarget;
 import javax.validation.Payload;
 import javax.validation.Valid;
 import javax.validation.ValidationException;
@@ -45,11 +47,21 @@ import javax.validation.groups.ConvertGroup;
  */
 @Privilizing(@CallTo(Reflection.class))
 public final class AnnotationProxyBuilder<A extends Annotation> {
-    private static final ConcurrentMap<Class<?>, Method[]> METHODS_CACHE = new ConcurrentHashMap<Class<?>, Method[]>();
+    private static final ConcurrentMap<Class<?>, Method[]> METHODS_CACHE = new ConcurrentHashMap<>();
+
+    public static <A> Method[] findMethods(final Class<A> annotationType) {
+        // cache only built-in constraints to avoid memory leaks:
+        // TODO use configurable cache size property?
+        if (annotationType.getName().startsWith("javax.validation.constraints.")) {
+            return METHODS_CACHE.computeIfAbsent(annotationType, Reflection::getDeclaredMethods);
+        }
+        return Reflection.getDeclaredMethods(annotationType);
+    }
 
     private final Class<A> type;
-    private final Map<String, Object> elements = new HashMap<String, Object>();
+    private final Map<String, Object> elements = new HashMap<>();
     private final Method[] methods;
+    private boolean changed;
 
     /**
      * Create a new AnnotationProxyBuilder instance.
@@ -61,21 +73,6 @@ public final class AnnotationProxyBuilder<A extends Annotation> {
         this.methods = findMethods(annotationType);
     }
 
-    public static <A> Method[] findMethods(final Class<A> annotationType) {
-        if (annotationType.getName().startsWith("javax.validation.constraints.")) { // cache built-in constraints only to avoid mem leaks
-            Method[] mtd = METHODS_CACHE.get(annotationType);
-            if (mtd == null) {
-                final Method[] value = Reflection.getDeclaredMethods(annotationType);
-                mtd = METHODS_CACHE.putIfAbsent(annotationType, value);
-                if (mtd == null) {
-                    mtd = value;
-                }
-            }
-            return mtd;
-        }
-        return Reflection.getDeclaredMethods(annotationType);
-    }
-
     /**
      * Create a new AnnotationProxyBuilder instance.
      *
@@ -84,16 +81,15 @@ public final class AnnotationProxyBuilder<A extends Annotation> {
      */
     public AnnotationProxyBuilder(Class<A> annotationType, Map<String, Object> elements) {
         this(annotationType);
-        for (Map.Entry<String, Object> entry : elements.entrySet()) {
-            this.elements.put(entry.getKey(), entry.getValue());
-        }
+        elements.forEach(this.elements::put);
     }
 
     /**
      * Create a builder initially configured to create an annotation equivalent
-     * to <code>annot</code>.
+     * to {@code annot}.
      * 
-     * @param annot Annotation to be replicated.
+     * @param annot
+     *            Annotation to be replicated.
      */
     @SuppressWarnings("unchecked")
     public AnnotationProxyBuilder(A annot) {
@@ -102,8 +98,7 @@ public final class AnnotationProxyBuilder<A extends Annotation> {
         for (Method m : methods) {
             final boolean mustUnset = Reflection.setAccessible(m, true);
             try {
-                Object value = m.invoke(annot);
-                this.elements.put(m.getName(), value);
+                this.elements.put(m.getName(), m.invoke(annot));
             } catch (Exception e) {
                 throw new ValidationException("Cannot access annotation " + annot + " element: " + m.getName(), e);
             } finally {
@@ -124,8 +119,22 @@ public final class AnnotationProxyBuilder<A extends Annotation> {
      * @param elementName
      * @param value
      */
-    public void putValue(String elementName, Object value) {
-        elements.put(elementName, value);
+    @Deprecated
+    public Object putValue(String elementName, Object value) {
+        return elements.put(elementName, value);
+    }
+
+    /**
+     * Add an element to the configuration.
+     *
+     * @param elementName
+     * @param value
+     * @return whether any change occurred
+     */
+    public boolean setValue(String elementName, Object value) {
+        final boolean result = !Objects.equals(elements.put(elementName, value), value);
+        changed |= result;
+        return result;
     }
 
     /**
@@ -171,27 +180,42 @@ public final class AnnotationProxyBuilder<A extends Annotation> {
      * Configure the well-known JSR303 "message" element.
      *
      * @param message
+     * @return
      */
-    public void setMessage(String message) {
-        ConstraintAnnotationAttributes.MESSAGE.put(elements, message);
+    public boolean setMessage(String message) {
+        return setValue(ConstraintAnnotationAttributes.MESSAGE.getAttributeName(), message);
     }
 
     /**
      * Configure the well-known JSR303 "groups" element.
      *
      * @param groups
+     * @return
      */
-    public void setGroups(Class<?>[] groups) {
-        ConstraintAnnotationAttributes.GROUPS.put(elements, groups);
+    public boolean setGroups(Class<?>[] groups) {
+        return setValue(ConstraintAnnotationAttributes.GROUPS.getAttributeName(), groups);
     }
 
     /**
      * Configure the well-known JSR303 "payload" element.
      * 
      * @param payload
+     * @return
+     */
+    public boolean setPayload(Class<? extends Payload>[] payload) {
+        return setValue(ConstraintAnnotationAttributes.PAYLOAD.getAttributeName(), payload);
+    }
+
+    /**
+     * Configure the well-known "validationAppliesTo" element.
+     * 
+     * @param constraintTarget
      */
-    public void setPayload(Class<? extends Payload>[] payload) {
-        ConstraintAnnotationAttributes.PAYLOAD.put(elements, payload);
+    public boolean setValidationAppliesTo(ConstraintTarget constraintTarget) {
+        return setValue(ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO.getAttributeName(), constraintTarget);
+    }
+    public boolean isChanged() {
+        return changed;
     }
 
     /**
@@ -203,16 +227,22 @@ public final class AnnotationProxyBuilder<A extends Annotation> {
         final ClassLoader classLoader = Reflection.getClassLoader(getType());
         @SuppressWarnings("unchecked")
         final Class<A> proxyClass = (Class<A>) Proxy.getProxyClass(classLoader, getType());
-        final InvocationHandler handler = new AnnotationProxy(this);
-        return doCreateAnnotation(proxyClass, handler);
+        return doCreateAnnotation(proxyClass, new AnnotationProxy(this));
     }
 
     @Privileged
     private A doCreateAnnotation(final Class<A> proxyClass, final InvocationHandler handler) {
         try {
-            Constructor<A> constructor = proxyClass.getConstructor(InvocationHandler.class);
-            Reflection.setAccessible(constructor, true); // java 8
-            return constructor.newInstance(handler);
+            final Constructor<A> constructor = proxyClass.getConstructor(InvocationHandler.class);
+            final boolean mustUnset = Reflection.setAccessible(constructor, true); // java
+                                                                                   // 8
+            try {
+                return constructor.newInstance(handler);
+            } finally {
+                if (mustUnset) {
+                    Reflection.setAccessible(constructor, false);
+                }
+            }
         } catch (Exception e) {
             throw new ValidationException("Unable to create annotation for configured constraint", e);
         }

http://git-wip-us.apache.org/repos/asf/bval/blob/5c09f0dd/bval-jsr/src/main/java/org/apache/bval/jsr/xml/SchemaManager.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/SchemaManager.java b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/SchemaManager.java
new file mode 100644
index 0000000..a502b7e
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/SchemaManager.java
@@ -0,0 +1,286 @@
+/*
+ * 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.net.URL;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.function.Function;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.UnmarshallerHandler;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.ValidatorHandler;
+
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.reflection.Reflection;
+import org.w3c.dom.Document;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+/**
+ * Unmarshals XML converging on latest schema version. Presumes backward compatiblity between schemae.
+ */
+public class SchemaManager {
+    public static class Builder {
+        private final SortedMap<Key, Lazy<Schema>> data = new TreeMap<>();
+
+        public Builder add(String version, String ns, String resource) {
+            data.put(new Key(version, ns), new Lazy<>(() -> SchemaManager.loadSchema(resource)));
+            return this;
+        }
+
+        public SchemaManager build() {
+            return new SchemaManager(new TreeMap<>(data));
+        }
+    }
+
+    private static class Key implements Comparable<Key> {
+        private static final Comparator<Key> CMP = Comparator.comparing(Key::getVersion).thenComparing(Key::getNs);
+
+        final String version;
+        final String ns;
+
+        Key(String version, String ns) {
+            super();
+            this.version = Objects.toString(version, "");
+            this.ns = Objects.toString(ns, "");
+        }
+
+        public String getVersion() {
+            return version;
+        }
+
+        public String getNs() {
+            return ns;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            return Optional.ofNullable(obj).filter(SchemaManager.Key.class::isInstance)
+                .map(SchemaManager.Key.class::cast)
+                .filter(k -> Objects.equals(this.version, k.version) && Objects.equals(this.ns, k.ns)).isPresent();
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(version, ns);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s:%s", version, ns);
+        }
+
+        @Override
+        public int compareTo(Key o) {
+            return CMP.compare(this, o);
+        }
+    }
+
+    private class DynamicValidatorHandler extends XMLFilterImpl {
+        ContentHandler ch;
+        SAXParseException e;
+
+        @Override
+        public void setContentHandler(ContentHandler handler) {
+            super.setContentHandler(handler);
+            this.ch = handler;
+        }
+
+        @Override
+        public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
+            if (getContentHandler() == ch) {
+                final Key schemaKey = new Key(Objects.toString(atts.getValue("version"), ""), uri);
+                if (data.containsKey(schemaKey)) {
+                    final Schema schema = data.get(schemaKey).get();
+                    final ValidatorHandler vh = schema.newValidatorHandler();
+                    vh.startDocument();
+                    vh.setContentHandler(ch);
+                    super.setContentHandler(vh);
+                }
+            }
+            try {
+                super.startElement(uri, localName, qName, atts);
+            } catch (SAXParseException e) {
+                this.e = e;
+            }
+        }
+
+        @Override
+        public void error(SAXParseException e) throws SAXException {
+            this.e = e;
+            super.error(e);
+        }
+
+        @Override
+        public void fatalError(SAXParseException e) throws SAXException {
+            this.e = e;
+            super.fatalError(e);
+        }
+
+        void validate() throws SAXParseException {
+            if (e != null) {
+                throw e;
+            }
+        }
+    }
+
+    //@formatter:off
+    private enum XmlAttributeType {
+        CDATA, ID, IDREF, IDREFS, NMTOKEN, NMTOKENS, ENTITY, ENTITIES, NOTATION;
+        //@formatter:on
+    }
+
+    private class SchemaRewriter extends XMLFilterImpl {
+        private boolean root = true;
+
+        @Override
+        public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
+            final Key schemaKey = new Key(Objects.toString(atts.getValue("version"), ""), uri);
+
+            if (!target.equals(schemaKey) && data.containsKey(schemaKey)) {
+                uri = target.ns;
+                if (root) {
+                    atts = rewrite(atts);
+                    root = false;
+                }
+            }
+            super.startElement(uri, localName, qName, atts);
+        }
+
+        private Attributes rewrite(Attributes atts) {
+            final AttributesImpl result;
+            if (atts instanceof AttributesImpl) {
+                result = (AttributesImpl) atts;
+            } else {
+                result = new AttributesImpl(atts);
+            }
+            set(result, "", VERSION_ATTRIBUTE, "", XmlAttributeType.CDATA, target.version);
+            return result;
+        }
+
+        private void set(AttributesImpl attrs, String uri, String localName, String qName, XmlAttributeType type,
+            String value) {
+            for (int i = 0, sz = attrs.getLength(); i < sz; i++) {
+                if (Objects.equals(qName, attrs.getQName(i))
+                    || Objects.equals(uri, attrs.getURI(i)) && Objects.equals(localName, attrs.getLocalName(i))) {
+                    attrs.setAttribute(i, uri, localName, qName, type.name(), value);
+                    return;
+                }
+            }
+            attrs.addAttribute(uri, localName, qName, type.name(), value);
+        }
+    }
+
+    public static final String VERSION_ATTRIBUTE = "version";
+
+    private static final Logger log = Logger.getLogger(SchemaManager.class.getName());
+    private static final SchemaFactory SCHEMA_FACTORY = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+    private static final SAXParserFactory SAX_PARSER_FACTORY;
+
+    static {
+        SAX_PARSER_FACTORY = SAXParserFactory.newInstance();
+        SAX_PARSER_FACTORY.setNamespaceAware(true);
+    }
+
+    static Schema loadSchema(String resource) {
+        final URL schemaUrl = Reflection.getClassLoader(XmlUtils.class).getResource(resource);
+        try {
+            return SCHEMA_FACTORY.newSchema(schemaUrl);
+        } catch (SAXException e) {
+            log.log(Level.WARNING, String.format("Unable to parse schema: %s", resource), e);
+            return null;
+        }
+    }
+
+    private static Class<?> getObjectFactory(Class<?> type) throws ClassNotFoundException {
+        final String className = String.format("%s.%s", type.getPackage().getName(), "ObjectFactory");
+        return Reflection.toClass(className, type.getClassLoader());
+    }
+
+    private final Key target;
+    private final SortedMap<Key, Lazy<Schema>> data;
+    private final String description;
+
+    private SchemaManager(SortedMap<Key, Lazy<Schema>> data) {
+        super();
+        this.data = Collections.unmodifiableSortedMap(data);
+        this.target = data.lastKey();
+        this.description = target.ns.substring(target.ns.lastIndexOf('/') + 1);
+    }
+
+    public Optional<Schema> getSchema(String ns, String version) {
+        return Optional.of(new Key(version, ns)).map(data::get).map(Lazy::get);
+    }
+
+    public Optional<Schema> getSchema(Document document) {
+        return Optional.ofNullable(document).map(Document::getDocumentElement)
+            .map(e -> getSchema(e.getAttribute(XMLConstants.XMLNS_ATTRIBUTE), e.getAttribute(VERSION_ATTRIBUTE))).get();
+    }
+
+    public <E extends Exception> Schema requireSchema(Document document, Function<String, E> exc) throws E {
+        return getSchema(document).orElseThrow(() -> Objects.requireNonNull(exc, "exc")
+            .apply(String.format("Unknown %s schema", Objects.toString(description, ""))));
+    }
+
+    public <T> T unmarshal(InputSource input, Class<T> type) throws Exception {
+        final XMLReader xmlReader = SAX_PARSER_FACTORY.newSAXParser().getXMLReader();
+
+        // validate specified schema:
+        final DynamicValidatorHandler schemaValidator = new DynamicValidatorHandler();
+        xmlReader.setContentHandler(schemaValidator);
+
+        // rewrite to latest schema, if required:
+        final SchemaRewriter schemaRewriter = new SchemaRewriter();
+        schemaValidator.setContentHandler(schemaRewriter);
+
+        JAXBContext jc = JAXBContext.newInstance(getObjectFactory(type));
+        // unmarshal:
+        final UnmarshallerHandler unmarshallerHandler = jc.createUnmarshaller().getUnmarshallerHandler();
+        schemaRewriter.setContentHandler(unmarshallerHandler);
+
+        xmlReader.parse(input);
+
+        schemaValidator.validate();
+
+        @SuppressWarnings("unchecked")
+        final JAXBElement<T> result = (JAXBElement<T>) unmarshallerHandler.getResult();
+        return result.getValue();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/5c09f0dd/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 b260a9e..3a86142 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
@@ -18,730 +18,100 @@ package org.apache.bval.jsr.xml;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.Serializable;
 import java.lang.annotation.Annotation;
-import java.lang.reflect.Array;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Member;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
-import javax.validation.Constraint;
-import javax.validation.ConstraintValidator;
-import javax.validation.Payload;
 import javax.validation.ValidationException;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.transform.stream.StreamSource;
-import javax.xml.validation.Schema;
 
 import org.apache.bval.jsr.ApacheValidatorFactory;
-import org.apache.bval.jsr.ConstraintAnnotationAttributes;
-import org.apache.bval.jsr.util.IOs;
-import org.apache.bval.util.FieldAccess;
-import org.apache.bval.util.MethodAccess;
-import org.apache.bval.util.ObjectUtils;
-import org.apache.bval.util.StringUtils;
+import org.apache.bval.jsr.metadata.XmlBuilder;
+import org.apache.bval.jsr.metadata.XmlValidationMappingProvider;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Validate;
 import org.apache.bval.util.reflection.Reflection;
-import org.apache.commons.weaver.privilizer.Privileged;
 import org.apache.commons.weaver.privilizer.Privilizing;
 import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+import org.xml.sax.InputSource;
 
 /**
- * Uses JAXB to parse constraints.xml based on validation-mapping-1.0.xsd.<br>
+ * Uses JAXB to parse constraints.xml based on the validation-mapping XML
+ * schema.
  */
 @Privilizing(@CallTo(Reflection.class))
 public class ValidationMappingParser {
-    private static final String VALIDATION_MAPPING_XSD = "META-INF/validation-mapping-1.1.xsd";
+    private static final SchemaManager SCHEMA_MANAGER = new SchemaManager.Builder()
+        .add(null, "http://jboss.org/xml/ns/javax/validation/mapping", "META-INF/validation-mapping-1.0.xsd")
+        .add(XmlBuilder.Version.v11.getId(), "http://jboss.org/xml/ns/javax/validation/mapping",
+            "META-INF/validation-mapping-1.1.xsd")
+        .add(XmlBuilder.Version.v20.getId(), "http://xmlns.jcp.org/xml/ns/javax/validation/mapping",
+            "META-INF/validation-mapping-2.0.xsd")
+        .build();
 
-    private static final Set<ConstraintAnnotationAttributes> RESERVED_PARAMS = Collections
-        .unmodifiableSet(EnumSet.of(ConstraintAnnotationAttributes.GROUPS, ConstraintAnnotationAttributes.MESSAGE,
-            ConstraintAnnotationAttributes.PAYLOAD, ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO));
-
-    private final Set<Class<?>> processedClasses;
     private final ApacheValidatorFactory factory;
 
     public ValidationMappingParser(ApacheValidatorFactory factory) {
-        this.factory = factory;
-        this.processedClasses = new HashSet<Class<?>>();
+        this.factory = Validate.notNull(factory, "factory");
     }
 
     /**
-     * Parse files with constraint mappings and collect information in the factory.
-     *  
-     * @param xmlStreams - one or more contraints.xml file streams to parse
+     * Parse files with constraint mappings and collect information in the
+     * factory.
+     * 
+     * @param xmlStreams
+     *            - one or more contraints.xml file streams to parse
      */
     public void processMappingConfig(Set<InputStream> xmlStreams) throws ValidationException {
         for (final InputStream xmlStream : xmlStreams) {
-            ConstraintMappingsType mapping = parseXmlMappings(xmlStream);
-
-            final String defaultPackage = mapping.getDefaultPackage();
-            processConstraintDefinitions(mapping.getConstraintDefinition(), defaultPackage);
-            for (final BeanType bean : mapping.getBean()) {
-                Class<?> beanClass = loadClass(bean.getClazz(), defaultPackage);
-                if (!processedClasses.add(beanClass)) {
-                    // spec: A given class must not be described more than once amongst all
-                    //  the XML mapping descriptors.
-                    throw new ValidationException(beanClass.getName() + " has already be configured in xml.");
-                }
-
-                boolean ignoreAnnotations = bean.getIgnoreAnnotations() == null ? true : bean.getIgnoreAnnotations();
-                factory.getAnnotationIgnores().setDefaultIgnoreAnnotation(beanClass, ignoreAnnotations);
-                processClassLevel(bean.getClassType(), beanClass, defaultPackage);
-                processConstructorLevel(bean.getConstructor(), beanClass, defaultPackage, ignoreAnnotations);
-                processFieldLevel(bean.getField(), beanClass, defaultPackage, ignoreAnnotations);
-                final Collection<String> potentialMethodName =
-                    processPropertyLevel(bean.getGetter(), beanClass, defaultPackage, ignoreAnnotations);
-                processMethodLevel(bean.getMethod(), beanClass, defaultPackage, ignoreAnnotations, potentialMethodName);
-                processedClasses.add(beanClass);
-            }
+            final ConstraintMappingsType mapping = parseXmlMappings(xmlStream);
+            processConstraintDefinitions(mapping.getConstraintDefinition(), mapping.getDefaultPackage());
+            new XmlBuilder(mapping).forBeans().forEach(factory.getMetadataBuilders()::registerCustomBuilder);
         }
     }
 
-    /** @param in XML stream to parse using the validation-mapping-1.0.xsd */
+    /**
+     * @param in
+     *            XML stream to parse using the validation-mapping-1.0.xsd
+     */
     private ConstraintMappingsType parseXmlMappings(final InputStream in) {
-        ConstraintMappingsType mappings;
         try {
-            final JAXBContext jc = JAXBContext.newInstance(ConstraintMappingsType.class);
-            final Unmarshaller unmarshaller = jc.createUnmarshaller();
-            unmarshaller.setSchema(getSchema());
-            final StreamSource stream = new StreamSource(in);
-            final JAXBElement<ConstraintMappingsType> root =
-                unmarshaller.unmarshal(stream, ConstraintMappingsType.class);
-            mappings = root.getValue();
-        } catch (final JAXBException e) {
+            return SCHEMA_MANAGER.unmarshal(new InputSource(in), ConstraintMappingsType.class);
+        } catch (Exception e) {
             throw new ValidationException("Failed to parse XML deployment descriptor file.", e);
         } finally {
-            IOs.closeQuietly(in);
             try {
-                in.reset(); // can be read several times + we ensured it was re-readable in addMapping()
+                in.reset(); // can be read several times + we ensured it was
+                            // re-readable in addMapping()
             } catch (final IOException e) {
                 // no-op
             }
         }
-        return mappings;
-    }
-
-    /** @return validation-mapping-1.0.xsd based schema */
-    private Schema getSchema() {
-        return ValidationParser.getSchema(VALIDATION_MAPPING_XSD);
-    }
-
-    private void processClassLevel(ClassType classType, Class<?> beanClass, String defaultPackage) {
-        if (classType == null) {
-            return;
-        }
-
-        // ignore annotation
-        if (classType.getIgnoreAnnotations() != null) {
-            factory.getAnnotationIgnores().setIgnoreAnnotationsOnClass(beanClass, classType.getIgnoreAnnotations());
-        }
-
-        // group sequence
-        Class<?>[] groupSequence = createGroupSequence(classType.getGroupSequence(), defaultPackage);
-        if (groupSequence != null) {
-            factory.addDefaultSequence(beanClass, groupSequence);
-        }
-
-        // constraints
-        for (ConstraintType constraint : classType.getConstraint()) {
-            MetaConstraint<?, ?> metaConstraint = createConstraint(constraint, beanClass, null, defaultPackage);
-            factory.addMetaConstraint(beanClass, metaConstraint);
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private <A extends Annotation, T> MetaConstraint<?, ?> createConstraint(final ConstraintType constraint,
-        final Class<T> beanClass, final Member member, final String defaultPackage) {
-
-        final Class<A> annotationClass = (Class<A>) loadClass(constraint.getAnnotation(), defaultPackage);
-        final AnnotationProxyBuilder<A> annoBuilder = new AnnotationProxyBuilder<A>(annotationClass);
-
-        if (constraint.getMessage() != null) {
-            annoBuilder.setMessage(constraint.getMessage());
-        }
-        annoBuilder.setGroups(getGroups(constraint.getGroups(), defaultPackage));
-        annoBuilder.setPayload(getPayload(constraint.getPayload(), defaultPackage));
-
-        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, defaultPackage);
-            annoBuilder.putValue(name, elementValue);
-        }
-        return new MetaConstraint<T, A>(beanClass, member, annoBuilder.createAnnotation());
-    }
-
-    private void checkValidName(String name) {
-        for (ConstraintAnnotationAttributes attr : RESERVED_PARAMS) {
-            if (attr.getAttributeName().equals(name)) {
-                throw new ValidationException(name + " is a reserved parameter name.");
-            }
-        }
-    }
-
-    private <A extends Annotation> Class<?> getAnnotationParameterType(final Class<A> annotationClass,
-        final String name) {
-        final Method m = Reflection.getPublicMethod(annotationClass, name);
-        if (m == null) {
-            throw new ValidationException(
-                "Annotation of type " + annotationClass.getName() + " does not contain a parameter " + name + ".");
-        }
-        return m.getReturnType();
-    }
-
-    private Object getElementValue(ElementType elementType, Class<?> returnType, String defaultPackage) {
-        removeEmptyContentElements(elementType);
-
-        boolean isArray = returnType.isArray();
-        if (!isArray) {
-            if (elementType.getContent().size() != 1) {
-                throw new ValidationException("Attempt to specify an array where single value is expected.");
-            }
-            return getSingleValue(elementType.getContent().get(0), returnType, defaultPackage);
-        }
-        List<Object> values = new ArrayList<Object>();
-        for (Serializable s : elementType.getContent()) {
-            values.add(getSingleValue(s, returnType.getComponentType(), defaultPackage));
-        }
-        return values.toArray((Object[]) Array.newInstance(returnType.getComponentType(), values.size()));
-    }
-
-    private void removeEmptyContentElements(ElementType elementType) {
-        List<Serializable> contentToDelete = new ArrayList<Serializable>();
-        for (Serializable content : elementType.getContent()) {
-            if (content instanceof String && ((String) content).matches("[\\n ].*")) {
-                contentToDelete.add(content);
-            }
-        }
-        elementType.getContent().removeAll(contentToDelete);
-    }
-
-    @SuppressWarnings("unchecked")
-    private Object getSingleValue(Serializable serializable, Class<?> returnType, String defaultPackage) {
-        if (serializable instanceof String) {
-            String value = (String) serializable;
-            return convertToResultType(returnType, value, defaultPackage);
-        }
-        if (serializable instanceof JAXBElement<?>) {
-            JAXBElement<?> elem = (JAXBElement<?>) serializable;
-            if (String.class.equals(elem.getDeclaredType())) {
-                String value = (String) elem.getValue();
-                return convertToResultType(returnType, value, defaultPackage);
-            }
-            if (AnnotationType.class.equals(elem.getDeclaredType())) {
-                AnnotationType annotationType = (AnnotationType) elem.getValue();
-                try {
-                    Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) returnType;
-                    return createAnnotation(annotationType, annotationClass, defaultPackage);
-                } catch (ClassCastException e) {
-                    throw new ValidationException("Unexpected parameter value");
-                }
-            }
-        }
-        throw new ValidationException("Unexpected parameter value");
     }
 
-    private Object convertToResultType(Class<?> returnType, String value, String defaultPackage) {
-        /**
-         * Class is represented by the fully qualified class name of the class.
-         * spec: Note that if the raw string is unqualified,
-         * default package is taken into account.
-         */
-        if (returnType.equals(String.class)) {
-            return value;
-        }
-        if (returnType.equals(Class.class)) {
-            ClassLoader cl = Reflection.getClassLoader(ValidationMappingParser.class);
-            try {
-                return Reflection.toClass(toQualifiedClassName(value, defaultPackage), cl);
-            } catch (Exception e) {
-                throw new ValidationException(e);
-            }
-        }
-        if (returnType.isEnum()) {
-            try {
-                @SuppressWarnings({ "rawtypes", "unchecked" })
-                final Enum e = Enum.valueOf(returnType.asSubclass(Enum.class), value);
-                return e;
-            } catch (IllegalArgumentException e) {
-                throw new ValidationException(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);
-        }
-        if (Character.class.equals(returnType) || char.class.equals(returnType)) {
-            if (value.length() > 1) {
-                throw new IllegalArgumentException("a char has a length of 1");
-            }
-            return value.charAt(0);
-        }
-        throw new ValidationException(String.format("Unknown annotation value type %s", returnType.getName()));
-    }
-
-    private <A extends Annotation> Annotation createAnnotation(AnnotationType annotationType, Class<A> returnType,
+    private void processConstraintDefinitions(List<ConstraintDefinitionType> constraintDefinitionList,
         String defaultPackage) {
-        AnnotationProxyBuilder<A> metaAnnotation = new AnnotationProxyBuilder<A>(returnType);
-        for (ElementType elementType : annotationType.getElement()) {
-            String name = elementType.getName();
-            Class<?> parameterType = getAnnotationParameterType(returnType, name);
-            Object elementValue = getElementValue(elementType, parameterType, defaultPackage);
-            metaAnnotation.putValue(name, elementValue);
-        }
-        return metaAnnotation.createAnnotation();
-    }
 
-    private Class<?>[] getGroups(GroupsType groupsType, String defaultPackage) {
-        if (groupsType == null) {
-            return ObjectUtils.EMPTY_CLASS_ARRAY;
-        }
+        final Map<Class<? extends Annotation>, ValidatedByType> validatorMappings = new HashMap<>();
 
-        List<Class<?>> groupList = new ArrayList<Class<?>>();
-        for (String groupClass : groupsType.getValue()) {
-            groupList.add(loadClass(groupClass, defaultPackage));
-        }
-        return groupList.toArray(new Class[groupList.size()]);
-    }
-
-    @SuppressWarnings("unchecked")
-    private Class<? extends Payload>[] getPayload(PayloadType payloadType, String defaultPackage) {
-        if (payloadType == null) {
-            return new Class[] {};
-        }
-
-        List<Class<? extends Payload>> payloadList = new ArrayList<Class<? extends Payload>>();
-        for (String groupClass : payloadType.getValue()) {
-            Class<?> payload = loadClass(groupClass, defaultPackage);
-            if (!Payload.class.isAssignableFrom(payload)) {
-                throw new ValidationException(
-                    "Specified payload class " + payload.getName() + " does not implement javax.validation.Payload");
-            }
-            payloadList.add((Class<? extends Payload>) payload);
-        }
-        return payloadList.toArray(new Class[payloadList.size()]);
-    }
-
-    private Class<?>[] createGroupSequence(GroupSequenceType groupSequenceType, String defaultPackage) {
-        if (groupSequenceType != null) {
-            Class<?>[] groupSequence = new Class<?>[groupSequenceType.getValue().size()];
-            int i = 0;
-            for (String groupName : groupSequenceType.getValue()) {
-                Class<?> group = loadClass(groupName, defaultPackage);
-                groupSequence[i++] = group;
-            }
-            return groupSequence;
-        }
-        return null;
-    }
-
-    private <A> void processMethodLevel(final List<MethodType> methods, final Class<A> beanClass,
-        final String defaultPackage, final boolean parentIgnoreAnn, final Collection<String> getters) {
-        final List<String> methodNames = new ArrayList<String>();
-        for (final MethodType methodType : methods) {
-            final String methodName = methodType.getName();
-            if (methodNames.contains(methodName) || getters.contains(methodName)) {
-                throw new ValidationException(
-                    methodName + " is defined more than once in mapping xml for bean " + beanClass.getName());
-            }
-            methodNames.add(methodName);
-
-            final Method method =
-                Reflection.getDeclaredMethod(beanClass, methodName, toTypes(methodType.getParameter(), defaultPackage));
-            if (method == null) {
-                throw new ValidationException(beanClass.getName() + " does not contain the method  " + methodName);
-            }
-
-            // ignore annotations
-            final boolean ignoreMethodAnnotation =
-                methodType.getIgnoreAnnotations() == null ? parentIgnoreAnn : methodType.getIgnoreAnnotations();
-            factory.getAnnotationIgnores().setIgnoreAnnotationsOnMember(method, ignoreMethodAnnotation);
-
-            final boolean ignoreAnn;
-            if (methodType.getIgnoreAnnotations() == null) {
-                ignoreAnn = parentIgnoreAnn;
-            } else {
-                ignoreAnn = methodType.getIgnoreAnnotations();
-            }
-
-            // constraints
-            int i = 0;
-            for (final ParameterType p : methodType.getParameter()) {
-                for (final ConstraintType constraintType : p.getConstraint()) {
-                    final MetaConstraint<?, ?> constraint =
-                        createConstraint(constraintType, beanClass, method, defaultPackage);
-                    constraint.setIndex(i);
-                    factory.addMetaConstraint(beanClass, constraint);
-                }
-                if (p.getValid() != null) {
-                    final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, method,
-                        AnnotationProxyBuilder.ValidAnnotation.INSTANCE);
-                    constraint.setIndex(i);
-                    factory.addMetaConstraint(beanClass, constraint);
-                }
-
-                if (p.getConvertGroup() != null) {
-                    for (final GroupConversionType groupConversion : p.getConvertGroup()) {
-                        final Class<?> from = loadClass(groupConversion.getFrom(), defaultPackage);
-                        final Class<?> to = loadClass(groupConversion.getTo(), defaultPackage);
-                        final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, method,
-                            new AnnotationProxyBuilder.ConvertGroupAnnotation(from, to));
-                        constraint.setIndex(i);
-                        factory.addMetaConstraint(beanClass, constraint);
-                    }
-                }
-
-                boolean ignoreParametersAnnotation =
-                    p.getIgnoreAnnotations() == null ? ignoreMethodAnnotation : p.getIgnoreAnnotations();
-                factory.getAnnotationIgnores().setIgnoreAnnotationsOnParameter(method, i, ignoreParametersAnnotation);
-
-                i++;
-            }
-
-            final ReturnValueType returnValue = methodType.getReturnValue();
-            if (returnValue != null) {
-                for (final ConstraintType constraintType : returnValue.getConstraint()) {
-                    final MetaConstraint<?, ?> constraint =
-                        createConstraint(constraintType, beanClass, method, defaultPackage);
-                    factory.addMetaConstraint(beanClass, constraint);
-                }
-                if (returnValue.getValid() != null) {
-                    final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, method,
-                        AnnotationProxyBuilder.ValidAnnotation.INSTANCE);
-                    factory.addMetaConstraint(beanClass, constraint);
-                }
-
-                if (returnValue.getConvertGroup() != null) {
-                    for (final GroupConversionType groupConversion : returnValue.getConvertGroup()) {
-                        final Class<?> from = loadClass(groupConversion.getFrom(), defaultPackage);
-                        final Class<?> to = loadClass(groupConversion.getTo(), defaultPackage);
-                        final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, method,
-                            new AnnotationProxyBuilder.ConvertGroupAnnotation(from, to));
-                        factory.addMetaConstraint(beanClass, constraint);
-                    }
-                }
-                factory.getAnnotationIgnores().setIgnoreAnnotationOnReturn(method,
-                    returnValue.getIgnoreAnnotations() == null ? ignoreAnn : returnValue.getIgnoreAnnotations());
-            }
-
-            final CrossParameterType crossParameter = methodType.getCrossParameter();
-            if (crossParameter != null) {
-                for (final ConstraintType constraintType : crossParameter.getConstraint()) {
-                    final MetaConstraint<?, ?> constraint =
-                        createConstraint(constraintType, beanClass, method, defaultPackage);
-                    factory.addMetaConstraint(beanClass, constraint);
-                }
-                factory.getAnnotationIgnores().setIgnoreAnnotationOnCrossParameter(method,
-                    crossParameter.getIgnoreAnnotations() != null ? crossParameter.getIgnoreAnnotations() : ignoreAnn);
-            }
-        }
-    }
-
-    private <A> void processConstructorLevel(final List<ConstructorType> constructors, final Class<A> beanClass,
-        final String defaultPackage, final boolean parentIgnore) {
-        for (final ConstructorType constructorType : constructors) {
-            final Constructor<?> constructor =
-                Reflection.getDeclaredConstructor(beanClass, toTypes(constructorType.getParameter(), defaultPackage));
-            if (constructor == null) {
-                throw new ValidationException(
-                    beanClass.getName() + " does not contain the constructor  " + constructorType);
-            }
-
-            // ignore annotations
-            final boolean ignoreMethodAnnotation =
-                constructorType.getIgnoreAnnotations() == null ? parentIgnore : constructorType.getIgnoreAnnotations();
-            factory.getAnnotationIgnores().setIgnoreAnnotationsOnMember(constructor, ignoreMethodAnnotation);
-
-            final boolean ignoreAnn;
-            if (constructorType.getIgnoreAnnotations() == null) {
-                ignoreAnn = parentIgnore;
-            } else {
-                ignoreAnn = constructorType.getIgnoreAnnotations();
-            }
-
-            // constraints
-            int i = 0;
-            for (final ParameterType p : constructorType.getParameter()) {
-                for (final ConstraintType constraintType : p.getConstraint()) {
-                    final MetaConstraint<?, ?> constraint =
-                        createConstraint(constraintType, beanClass, constructor, defaultPackage);
-                    constraint.setIndex(i);
-                    factory.addMetaConstraint(beanClass, constraint);
-                }
-                if (p.getValid() != null) {
-                    final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, constructor,
-                        AnnotationProxyBuilder.ValidAnnotation.INSTANCE);
-                    constraint.setIndex(i);
-                    factory.addMetaConstraint(beanClass, constraint);
-                }
-
-                if (p.getConvertGroup() != null) {
-                    for (final GroupConversionType groupConversion : p.getConvertGroup()) {
-                        final Class<?> from = loadClass(groupConversion.getFrom(), defaultPackage);
-                        final Class<?> to = loadClass(groupConversion.getTo(), defaultPackage);
-                        final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass,
-                            constructor, new AnnotationProxyBuilder.ConvertGroupAnnotation(from, to));
-                        constraint.setIndex(i);
-                        factory.addMetaConstraint(beanClass, constraint);
-                    }
-                }
-
-                boolean ignoreParametersAnnotation =
-                    p.getIgnoreAnnotations() == null ? ignoreMethodAnnotation : p.getIgnoreAnnotations();
-                if (ignoreParametersAnnotation || (ignoreMethodAnnotation && p.getIgnoreAnnotations() == null)) {
-                    // TODO what ?
-                }
-                factory.getAnnotationIgnores().setIgnoreAnnotationsOnParameter(constructor, i,
-                    p.getIgnoreAnnotations() != null ? p.getIgnoreAnnotations() : ignoreAnn);
-
-                i++;
-            }
-
-            final ReturnValueType returnValue = constructorType.getReturnValue();
-            if (returnValue != null) {
-                for (final ConstraintType constraintType : returnValue.getConstraint()) {
-                    final MetaConstraint<?, ?> constraint =
-                        createConstraint(constraintType, beanClass, constructor, defaultPackage);
-                    constraint.setIndex(-1);
-                    factory.addMetaConstraint(beanClass, constraint);
-                }
-                if (returnValue.getValid() != null) {
-                    final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, constructor,
-                        AnnotationProxyBuilder.ValidAnnotation.INSTANCE);
-                    constraint.setIndex(-1);
-                    factory.addMetaConstraint(beanClass, constraint);
-                }
-
-                if (returnValue.getConvertGroup() != null) {
-                    for (final GroupConversionType groupConversion : returnValue.getConvertGroup()) {
-                        final Class<?> from = loadClass(groupConversion.getFrom(), defaultPackage);
-                        final Class<?> to = loadClass(groupConversion.getTo(), defaultPackage);
-                        final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass,
-                            constructor, new AnnotationProxyBuilder.ConvertGroupAnnotation(from, to));
-                        constraint.setIndex(-1);
-                        factory.addMetaConstraint(beanClass, constraint);
-                    }
-                }
-                factory.getAnnotationIgnores().setIgnoreAnnotationOnReturn(constructor,
-                    returnValue.getIgnoreAnnotations() != null ? returnValue.getIgnoreAnnotations() : ignoreAnn);
-            }
-
-            final CrossParameterType crossParameter = constructorType.getCrossParameter();
-            if (crossParameter != null) {
-                for (final ConstraintType constraintType : crossParameter.getConstraint()) {
-                    final MetaConstraint<?, ?> constraint =
-                        createConstraint(constraintType, beanClass, constructor, defaultPackage);
-                    factory.addMetaConstraint(beanClass, constraint);
-                }
-                factory.getAnnotationIgnores().setIgnoreAnnotationOnCrossParameter(constructor,
-                    crossParameter.getIgnoreAnnotations() != null ? crossParameter.getIgnoreAnnotations() : ignoreAnn);
-            }
-        }
-    }
-
-    private Class<?>[] toTypes(final List<ParameterType> parameter, final String defaultPck) {
-        if (parameter == null) {
-            return null;
-        }
-        final Class<?>[] types = new Class<?>[parameter.size()];
-        int i = 0;
-        for (final ParameterType type : parameter) {
-            types[i++] = loadClass(type.getType(), defaultPck);
-        }
-        return types;
-    }
-
-    private <A> void processFieldLevel(List<FieldType> fields, Class<A> beanClass, String defaultPackage,
-        boolean ignoreAnnotations) {
-        final List<String> fieldNames = new ArrayList<String>();
-        for (FieldType fieldType : fields) {
-            String fieldName = fieldType.getName();
-            if (fieldNames.contains(fieldName)) {
-                throw new ValidationException(
-                    fieldName + " is defined more than once in mapping xml for bean " + beanClass.getName());
-            }
-            fieldNames.add(fieldName);
-
-            final Field field = Reflection.getDeclaredField(beanClass, fieldName);
-            if (field == null) {
-                throw new ValidationException(beanClass.getName() + " does not contain the fieldType  " + fieldName);
-            }
-
-            // ignore annotations
-            final boolean ignoreFieldAnnotation =
-                fieldType.getIgnoreAnnotations() == null ? ignoreAnnotations : fieldType.getIgnoreAnnotations();
-            factory.getAnnotationIgnores().setIgnoreAnnotationsOnMember(field, ignoreFieldAnnotation);
-
-            // valid
-            if (fieldType.getValid() != null) {
-                factory.addValid(beanClass, new FieldAccess(field));
-            }
-
-            for (final GroupConversionType conversion : fieldType.getConvertGroup()) {
-                final Class<?> from = loadClass(conversion.getFrom(), defaultPackage);
-                final Class<?> to = loadClass(conversion.getTo(), defaultPackage);
-                final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, field,
-                    new AnnotationProxyBuilder.ConvertGroupAnnotation(from, to));
-                factory.addMetaConstraint(beanClass, constraint);
-            }
-
-            // constraints
-            for (ConstraintType constraintType : fieldType.getConstraint()) {
-                MetaConstraint<?, ?> constraint = createConstraint(constraintType, beanClass, field, defaultPackage);
-                factory.addMetaConstraint(beanClass, constraint);
-            }
-        }
-    }
-
-    private <A> Collection<String> processPropertyLevel(List<GetterType> getters, Class<A> beanClass,
-        String defaultPackage, boolean ignoreAnnotatino) {
-        List<String> getterNames = new ArrayList<String>();
-        for (GetterType getterType : getters) {
-            final String getterName = getterType.getName();
-            final String methodName = "get" + StringUtils.capitalize(getterType.getName());
-            if (getterNames.contains(methodName)) {
-                throw new ValidationException(
-                    getterName + " is defined more than once in mapping xml for bean " + beanClass.getName());
-            }
-            getterNames.add(methodName);
-
-            final Method method = getGetter(beanClass, getterName);
-            if (method == null) {
-                throw new ValidationException(beanClass.getName() + " does not contain the property  " + getterName);
-            }
-
-            // ignore annotations
-            final boolean ignoreGetterAnnotation =
-                getterType.getIgnoreAnnotations() == null ? ignoreAnnotatino : getterType.getIgnoreAnnotations();
-            factory.getAnnotationIgnores().setIgnoreAnnotationsOnMember(method, ignoreGetterAnnotation);
-
-            // valid
-            if (getterType.getValid() != null) {
-                factory.addValid(beanClass, new MethodAccess(getterName, method));
-            }
-
-            // ConvertGroup
-            for (final GroupConversionType conversion : getterType.getConvertGroup()) {
-                final Class<?> from = loadClass(conversion.getFrom(), defaultPackage);
-                final Class<?> to = loadClass(conversion.getTo(), defaultPackage);
-                final MetaConstraint<?, ?> constraint = new MetaConstraint<A, Annotation>(beanClass, method,
-                    new AnnotationProxyBuilder.ConvertGroupAnnotation(from, to));
-                factory.addMetaConstraint(beanClass, constraint);
-            }
-
-            // constraints
-            for (ConstraintType constraintType : getterType.getConstraint()) {
-                MetaConstraint<?, ?> metaConstraint =
-                    createConstraint(constraintType, beanClass, method, defaultPackage);
-                factory.addMetaConstraint(beanClass, metaConstraint);
-            }
-        }
-
-        return getterNames;
-    }
-
-    @SuppressWarnings("unchecked")
-    private void processConstraintDefinitions(List<ConstraintDefinitionType> constraintDefinitionList,
-        String defaultPackage) {
         for (ConstraintDefinitionType constraintDefinition : constraintDefinitionList) {
-            String annotationClassName = constraintDefinition.getAnnotation();
+            final String annotationClassName = constraintDefinition.getAnnotation();
 
-            Class<?> clazz = loadClass(annotationClassName, defaultPackage);
-            if (!clazz.isAnnotation()) {
-                throw new ValidationException(annotationClassName + " is not an annotation");
-            }
-            Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) clazz;
+            final Class<?> clazz = loadClass(annotationClassName, defaultPackage);
 
-            ValidatedByType validatedByType = constraintDefinition.getValidatedBy();
-            List<Class<? extends ConstraintValidator<?, ?>>> classes =
-                new ArrayList<Class<? extends ConstraintValidator<?, ?>>>();
-            /*
-             If include-existing-validator is set to false,
-             ConstraintValidator defined on the constraint annotation are ignored.
-              */
-            if (validatedByType.getIncludeExistingValidators() != null
-                && validatedByType.getIncludeExistingValidators()) {
-                /*
-                 If set to true, the list of ConstraintValidators described in XML
-                 are concatenated to the list of ConstraintValidator described on the
-                 annotation to form a new array of ConstraintValidator evaluated.
-                 */
-                classes.addAll(findConstraintValidatorClasses(annotationClass));
-            }
-            for (String validatorClassName : validatedByType.getValue()) {
-                Class<? extends ConstraintValidator<?, ?>> validatorClass;
-                validatorClass = (Class<? extends ConstraintValidator<?, ?>>) loadClass(validatorClassName);
+            Exceptions.raiseUnless(clazz.isAnnotation(), ValidationException::new, "%s is not an annotation",
+                annotationClassName);
 
-                if (!ConstraintValidator.class.isAssignableFrom(validatorClass)) {
-                    throw new ValidationException(validatorClass + " is not a constraint validator class");
-                }
+            final Class<? extends Annotation> annotationClass = clazz.asSubclass(Annotation.class);
 
-                /*
-                Annotation based ConstraintValidator come before XML based
-                ConstraintValidator in the array. The new list is returned
-                by ConstraintDescriptor.getConstraintValidatorClasses().
-                 */
-                if (!classes.contains(validatorClass))
-                    classes.add(validatorClass);
-            }
-            if (factory.getConstraintsCache().containsConstraintValidator(annotationClass)) {
-                throw new ValidationException(
-                    "Constraint validator for " + annotationClass.getName() + " already configured.");
-            } else {
-                factory.getConstraintsCache().putConstraintValidator(annotationClass,
-                    classes.toArray(new Class[classes.size()]));
-            }
-        }
-    }
+            Exceptions.raiseIf(validatorMappings.containsKey(annotationClass), ValidationException::new,
+                "Constraint validator for %s already configured.", annotationClass);
 
-    private List<Class<? extends ConstraintValidator<? extends Annotation, ?>>> findConstraintValidatorClasses(
-        Class<? extends Annotation> annotationType) {
-        List<Class<? extends ConstraintValidator<? extends Annotation, ?>>> classes =
-            new ArrayList<Class<? extends ConstraintValidator<? extends Annotation, ?>>>();
-
-        Class<? extends ConstraintValidator<?, ?>>[] validator =
-            factory.getDefaultConstraints().getValidatorClasses(annotationType);
-        if (validator == null) {
-            /* Collections.addAll() would be more straightforward here, but there is an Oracle compiler bug of some sort
-             * that precludes this:
-             */
-            Class<? extends ConstraintValidator<?, ?>>[] validatedBy =
-                annotationType.getAnnotation(Constraint.class).validatedBy();
-            classes.addAll(Arrays.asList(validatedBy));
-        } else {
-            Collections.addAll(classes, validator);
+            validatorMappings.put(annotationClass, constraintDefinition.getValidatedBy());
         }
-        return classes;
+        factory.getConstraintsCache()
+            .add(new XmlValidationMappingProvider(validatorMappings, cn -> toQualifiedClassName(cn, defaultPackage)));
     }
 
     private Class<?> loadClass(String className, String defaultPackage) {
@@ -751,42 +121,27 @@ public class ValidationMappingParser {
     private String toQualifiedClassName(String className, String defaultPackage) {
         if (!isQualifiedClass(className)) {
             if (className.startsWith("[L") && className.endsWith(";")) {
-                className = "[L" + defaultPackage + "." + className.substring(2);
+                className = "[L" + defaultPackage + '.' + className.substring(2);
             } else {
-                className = defaultPackage + "." + className;
+                className = defaultPackage + '.' + className;
             }
         }
         return className;
     }
 
     private boolean isQualifiedClass(String clazz) {
-        return clazz.contains(".");
-    }
-
-    @Privileged
-    private static Method getGetter(Class<?> clazz, String propertyName) {
-        try {
-            final String p = StringUtils.capitalize(propertyName);
-            try {
-                return clazz.getMethod("get" + p);
-            } catch (NoSuchMethodException e) {
-                return clazz.getMethod("is" + p);
-            }
-        } catch (NoSuchMethodException e) {
-            return null;
-        }
+        return clazz.indexOf('.') >= 0;
     }
 
     private Class<?> loadClass(final String className) {
         ClassLoader loader = Reflection.getClassLoader(ValidationMappingParser.class);
-        if (loader == null)
+        if (loader == null) {
             loader = getClass().getClassLoader();
-
+        }
         try {
-            return Class.forName(className, true, loader);
+            return Reflection.toClass(className, loader);
         } catch (ClassNotFoundException ex) {
-            throw new ValidationException("Unable to load class: " + className, ex);
+            throw Exceptions.create(ValidationException::new, ex, "Unable to load class: %s", className);
         }
     }
-
 }


[08/17] bval git commit: BV2: constraint/validator updates

Posted by mb...@apache.org.
BV2: constraint/validator updates


Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/40fc20f6
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/40fc20f6
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/40fc20f6

Branch: refs/heads/bv2
Commit: 40fc20f6fb1b6585e101ceec66dc44e459d898ae
Parents: a602e41
Author: Matt Benson <mb...@apache.org>
Authored: Wed Feb 21 14:38:00 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Wed Feb 21 14:59:56 2018 -0600

----------------------------------------------------------------------
 .../constraints/checkdigit/ABANumber.java       |   4 +-
 .../extras/constraints/checkdigit/CUSIP.java    |   4 +-
 .../extras/constraints/checkdigit/EAN13.java    |   4 +-
 .../extras/constraints/checkdigit/IBAN.java     |   4 +-
 .../constraints/checkdigit/IBANValidator.java   |   8 +-
 .../extras/constraints/checkdigit/ISBN10.java   |   4 +-
 .../extras/constraints/checkdigit/Luhn.java     |   4 +-
 .../checkdigit/ModulusValidator.java            |   4 +-
 .../extras/constraints/checkdigit/Sedol.java    |   4 +-
 .../extras/constraints/checkdigit/Verhoeff.java |   4 +-
 .../checkdigit/VerhoeffValidator.java           |   4 +-
 .../constraints/creditcard/AmericanExpress.java |   4 +-
 .../extras/constraints/creditcard/Diners.java   |   4 +-
 .../extras/constraints/creditcard/Discover.java |   4 +-
 .../constraints/creditcard/Mastercard.java      |   4 +-
 .../constraints/file/DirectoryValidator.java    |   2 +-
 .../constraints/file/SymlinkValidator.java      |   2 +-
 .../extras/constraints/net/DomainValidator.java |  33 +++--
 .../constraints/net/InetAddressValidator.java   |   4 +-
 .../checkdigit/ABANumberValidatorTest.java      |   2 +-
 .../checkdigit/AbstractCheckDigitTest.java      |   4 +-
 .../checkdigit/CUSIPValidatorTest.java          |   2 +-
 .../checkdigit/EAN13CheckDigitTest.java         |   2 +-
 .../checkdigit/IBANCheckDigitTest.java          |   2 +-
 .../checkdigit/ISBN10CheckDigitTest.java        |   2 +-
 .../checkdigit/LuhnCheckDigitTest.java          |   2 +-
 .../checkdigit/SedolCheckDigitTest.java         |   2 +-
 .../checkdigit/VerhoeffCheckDigitTest.java      |   2 +-
 .../net/InetAddressValidatorTest.java           |   2 +-
 .../bval/constraints/AssertFalseValidator.java  |   6 +-
 .../bval/constraints/AssertTrueValidator.java   |   6 +-
 .../DecimalMaxValidatorForNumber.java           |  12 +-
 .../DecimalMaxValidatorForString.java           |   2 +-
 .../DecimalMinValidatorForNumber.java           |  12 +-
 .../DecimalMinValidatorForString.java           |   2 +-
 .../java/org/apache/bval/constraints/Email.java |  15 +-
 .../apache/bval/constraints/EmailValidator.java |   7 +-
 .../constraints/FutureOrPresentValidator.java   | 138 +++++++++++++++++++
 .../bval/constraints/FutureValidator.java       | 138 +++++++++++++++++++
 .../bval/constraints/MaxValidatorForNumber.java |   4 +-
 .../bval/constraints/MaxValidatorForString.java |   2 +-
 .../bval/constraints/MinValidatorForNumber.java |   4 +-
 .../bval/constraints/MinValidatorForString.java |   2 +-
 .../bval/constraints/NotBlankValidator.java     |  34 +++++
 .../org/apache/bval/constraints/NotEmpty.java   |  28 ++--
 .../bval/constraints/NotEmptyValidator.java     |  19 +--
 .../NotEmptyValidatorForCharSequence.java       |  34 +++++
 .../NotEmptyValidatorForCollection.java         |   7 +-
 .../constraints/NotEmptyValidatorForMap.java    |   6 +-
 .../constraints/NotEmptyValidatorForString.java |  37 -----
 .../bval/constraints/NotNullValidator.java      |   4 -
 .../apache/bval/constraints/NullValidator.java  |   5 -
 .../bval/constraints/NumberSignValidator.java   |  68 +++++++++
 .../constraints/PastOrPresentValidator.java     | 138 +++++++++++++++++++
 .../apache/bval/constraints/PastValidator.java  | 138 +++++++++++++++++++
 .../bval/constraints/PatternValidator.java      |   4 +-
 .../apache/bval/constraints/TimeValidator.java  |  44 ++++++
 .../bval/jsr/DefaultConstraints.properties      |  81 ++++++++++-
 58 files changed, 950 insertions(+), 174 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumber.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumber.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumber.java
index 921ffbc..821b6aa 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumber.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumber.java
@@ -25,7 +25,9 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -40,7 +42,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
  */
 @Documented
 @Constraint(validatedBy = ABANumberValidator.class)
-@Target({ FIELD, ANNOTATION_TYPE, PARAMETER })
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
 @Retention(RUNTIME)
 public @interface ABANumber {
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIP.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIP.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIP.java
index 85e84fd..139ab50 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIP.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIP.java
@@ -25,7 +25,9 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -40,7 +42,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
  */
 @Documented
 @Constraint(validatedBy = CUSIPValidator.class)
-@Target({ FIELD, ANNOTATION_TYPE, PARAMETER })
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
 @Retention(RUNTIME)
 public @interface CUSIP {
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13.java
index 4f835f6..667c786 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13.java
@@ -25,7 +25,9 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -40,7 +42,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
  */
 @Documented
 @Constraint(validatedBy = EAN13Validator.class)
-@Target({ FIELD, ANNOTATION_TYPE, PARAMETER })
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
 @Retention(RUNTIME)
 public @interface EAN13 {
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBAN.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBAN.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBAN.java
index f0685c0..c48173f 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBAN.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBAN.java
@@ -25,7 +25,9 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -40,7 +42,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
  */
 @Documented
 @Constraint(validatedBy = IBANValidator.class)
-@Target({ FIELD, ANNOTATION_TYPE, PARAMETER })
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
 @Retention(RUNTIME)
 public @interface IBAN {
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBANValidator.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBANValidator.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBANValidator.java
index 38eace6..1f392c7 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBANValidator.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBANValidator.java
@@ -26,7 +26,7 @@ import static java.lang.Character.getNumericValue;
 /**
  * <b>IBAN</b> (International Bank Account Number) Check Digit calculation/validation.
  * <p>
- * This rountine is based on the ISO 7064 Mod 97,10 check digit caluclation routine.
+ * This routine is based on the ISO 7064 Mod 97,10 check digit caluclation routine.
  * <p>
  * The two check digit characters in a IBAN number are the third and fourth characters
  * in the code. For <i>check digit</i> calculation/validation the first four characters are moved
@@ -40,7 +40,7 @@ import static java.lang.Character.getNumericValue;
  *  <a href="http://en.wikipedia.org/wiki/International_Bank_Account_Number">Wikipedia -
  *  IBAN number</a>.
  */
-public final class IBANValidator implements ConstraintValidator<IBAN, String> {
+public final class IBANValidator implements ConstraintValidator<IBAN, CharSequence> {
 
     private static final long MAX = 999999999;
 
@@ -50,12 +50,12 @@ public final class IBANValidator implements ConstraintValidator<IBAN, String> {
      * {@inheritDoc}
      */
     @Override
-    public boolean isValid(String code, ConstraintValidatorContext context) {
+    public boolean isValid(CharSequence code, ConstraintValidatorContext context) {
         if (code.length() < 5) {
             return false;
         }
 
-        String reformattedCode = code.substring(4) + code.substring(0, 4);
+        String reformattedCode = code.subSequence(4, code.length()).toString() + code.subSequence(0, 4).toString();
         long total = 0;
         for (int i = 0; i < reformattedCode.length(); i++) {
             int charValue = getNumericValue(reformattedCode.charAt(i));

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10.java
index a3a1bc3..9ce9b19 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10.java
@@ -25,7 +25,9 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -40,7 +42,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
  */
 @Documented
 @Constraint(validatedBy = ISBN10Validator.class)
-@Target({ FIELD, ANNOTATION_TYPE, PARAMETER })
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
 @Retention(RUNTIME)
 public @interface ISBN10 {
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Luhn.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Luhn.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Luhn.java
index 3bc186e..4e7ede5 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Luhn.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Luhn.java
@@ -25,7 +25,9 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -40,7 +42,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
  */
 @Documented
 @Constraint(validatedBy = LuhnValidator.class)
-@Target({ FIELD, ANNOTATION_TYPE, PARAMETER })
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
 @Retention(RUNTIME)
 public @interface Luhn {
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ModulusValidator.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ModulusValidator.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ModulusValidator.java
index f25bea4..57d463d 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ModulusValidator.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ModulusValidator.java
@@ -38,7 +38,7 @@ import static java.lang.Character.isDigit;
  *
  * @param <A>
  */
-abstract class ModulusValidator<A extends Annotation> implements ConstraintValidator<A, String> {
+abstract class ModulusValidator<A extends Annotation> implements ConstraintValidator<A, CharSequence> {
 
     private final int modulus;
 
@@ -58,7 +58,7 @@ abstract class ModulusValidator<A extends Annotation> implements ConstraintValid
      * {@inheritDoc}
      */
     @Override
-    public boolean isValid(String code, ConstraintValidatorContext context) {
+    public boolean isValid(CharSequence code, ConstraintValidatorContext context) {
         if (code.length() == 0) {
             return false;
         }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Sedol.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Sedol.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Sedol.java
index e69947c..67a2c01 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Sedol.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Sedol.java
@@ -25,7 +25,9 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -40,7 +42,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
  */
 @Documented
 @Constraint(validatedBy = SedolValidator.class)
-@Target({ FIELD, ANNOTATION_TYPE, PARAMETER })
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
 @Retention(RUNTIME)
 public @interface Sedol {
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Verhoeff.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Verhoeff.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Verhoeff.java
index e051a39..d3afc67 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Verhoeff.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Verhoeff.java
@@ -25,7 +25,9 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -40,7 +42,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
  */
 @Documented
 @Constraint(validatedBy = VerhoeffValidator.class)
-@Target({ FIELD, ANNOTATION_TYPE, PARAMETER })
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
 @Retention(RUNTIME)
 public @interface Verhoeff {
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffValidator.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffValidator.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffValidator.java
index 5a7d76b..b6c6249 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffValidator.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffValidator.java
@@ -33,7 +33,7 @@ import static java.lang.Character.getNumericValue;
  * See <a href="http://en.wikipedia.org/wiki/Verhoeff_algorithm">Wikipedia
  *  - Verhoeff algorithm</a> for more details.
  */
-public final class VerhoeffValidator implements ConstraintValidator<Verhoeff, String> {
+public final class VerhoeffValidator implements ConstraintValidator<Verhoeff, CharSequence> {
 
     //@formatter:off
     /** D - multiplication table */
@@ -65,7 +65,7 @@ public final class VerhoeffValidator implements ConstraintValidator<Verhoeff, St
      * {@inheritDoc}
      */
     @Override
-    public boolean isValid(String code, ConstraintValidatorContext context) {
+    public boolean isValid(CharSequence code, ConstraintValidatorContext context) {
         if (code.length() == 0) {
             return false;
         }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/AmericanExpress.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/AmericanExpress.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/AmericanExpress.java
index 3d08d19..051d210 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/AmericanExpress.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/AmericanExpress.java
@@ -28,7 +28,9 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -45,7 +47,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
 @Luhn
 @Pattern(regexp = "^(3[47]\\d{13})$")
 @Constraint(validatedBy = {})
-@Target({ FIELD, ANNOTATION_TYPE, PARAMETER })
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
 @Retention(RUNTIME)
 public @interface AmericanExpress {
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Diners.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Diners.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Diners.java
index de6fd35..efb0772 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Diners.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Diners.java
@@ -28,7 +28,9 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -45,7 +47,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
 @Luhn
 @Pattern(regexp = "^(30[0-5]\\d{11}|3095\\d{10}|36\\d{12}|3[8-9]\\d{12})$")
 @Constraint(validatedBy = {})
-@Target({ FIELD, ANNOTATION_TYPE, PARAMETER })
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
 @Retention(RUNTIME)
 public @interface Diners {
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Discover.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Discover.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Discover.java
index c445837..4e5ddd5 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Discover.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Discover.java
@@ -28,7 +28,9 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -45,7 +47,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
 @Luhn
 @Pattern(regexp = "^((6011\\d{12})|(64[4-9]\\d{13})|(65\\d{14}))$")
 @Constraint(validatedBy = {})
-@Target({ FIELD, ANNOTATION_TYPE, PARAMETER })
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
 @Retention(RUNTIME)
 public @interface Discover {
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Mastercard.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Mastercard.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Mastercard.java
index f30482e..1a9b6a5 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Mastercard.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Mastercard.java
@@ -28,7 +28,9 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -45,7 +47,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
 @Luhn
 @Pattern(regexp = "^(5[1-5]\\d{14})$")
 @Constraint(validatedBy = {})
-@Target({ FIELD, ANNOTATION_TYPE, PARAMETER })
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
 @Retention(RUNTIME)
 public @interface Mastercard {
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/DirectoryValidator.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/DirectoryValidator.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/DirectoryValidator.java
index a95ebde..6bca3b7 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/DirectoryValidator.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/DirectoryValidator.java
@@ -32,7 +32,7 @@ public class DirectoryValidator implements ConstraintValidator<Directory, File>
      */
     @Override
     public boolean isValid(File value, ConstraintValidatorContext context) {
-        return value.exists() && value.isDirectory();
+        return value.isDirectory();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/SymlinkValidator.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/SymlinkValidator.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/SymlinkValidator.java
index 6ed85fd..ef85896 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/SymlinkValidator.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/SymlinkValidator.java
@@ -49,7 +49,7 @@ public class SymlinkValidator implements ConstraintValidator<Symlink, File> {
         }
 
         try {
-            File fileInCanonicalDir = null;
+            File fileInCanonicalDir;
             if (value.getParent() == null) {
                 fileInCanonicalDir = value;
             } else {

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/DomainValidator.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/DomainValidator.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/DomainValidator.java
index b989531..c7668d5 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/DomainValidator.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/DomainValidator.java
@@ -20,11 +20,12 @@ package org.apache.bval.extras.constraints.net;
 
 import javax.validation.ConstraintValidator;
 import javax.validation.ConstraintValidatorContext;
-import java.util.List;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import static java.util.Arrays.asList;
+import java.util.Arrays;
+import java.util.HashSet;
 
 /**
  * <p><b>Domain name</b> validation routines.</p>
@@ -63,7 +64,7 @@ import static java.util.Arrays.asList;
  * {@link java.net.InetAddress} for that functionality.)
  * </p>
  */
-public class DomainValidator implements ConstraintValidator<Domain, String> {
+public class DomainValidator implements ConstraintValidator<Domain, CharSequence> {
 
     private boolean allowLocal;
 
@@ -77,15 +78,13 @@ public class DomainValidator implements ConstraintValidator<Domain, String> {
      * {@inheritDoc}
      */
     @Override
-    public boolean isValid(String domain, ConstraintValidatorContext context) {
+    public boolean isValid(CharSequence domain, ConstraintValidatorContext context) {
         Matcher matcher = DOMAIN_NAME_REGEX.matcher(domain);
         if (matcher.matches()) {
             domain = matcher.group(1);
-            return isValidTld(domain);
-        } else if (allowLocal && DOMAIN_LABEL.matcher(domain).matches()) {
-            return true;
+            return isValidTld(domain.toString());
         }
-        return false;
+        return allowLocal && DOMAIN_LABEL.matcher(domain).matches();
     }
 
     /**
@@ -154,7 +153,7 @@ public class DomainValidator implements ConstraintValidator<Domain, String> {
     }
 
     private static String chompLeadingDot(String str) {
-        if (str.startsWith(".")) {
+        if (str.charAt(0) == '.') {
             return str.substring(1);
         }
         return str;
@@ -165,11 +164,11 @@ public class DomainValidator implements ConstraintValidator<Domain, String> {
     // ----- Authoritative and comprehensive list at:
     // ----- http://data.iana.org/TLD/tlds-alpha-by-domain.txt
 
-    private static final List<String> INFRASTRUCTURE_TLDS = asList("arpa", // internet infrastructure
+    private static final Set<String> INFRASTRUCTURE_TLDS = new HashSet<>(Arrays.asList("arpa", // internet infrastructure
         "root" // diagnostic marker for non-truncated root zone
-    );
+    ));
 
-    private static final List<String> GENERIC_TLDS = asList("aero", // air transport industry
+    private static final Set<String> GENERIC_TLDS = new HashSet<>(Arrays.asList("aero", // air transport industry
         "asia", // Pan-Asia/Asia Pacific
         "biz", // businesses
         "cat", // Catalan linguistic/cultural community
@@ -189,9 +188,9 @@ public class DomainValidator implements ConstraintValidator<Domain, String> {
         "edu", // accredited postsecondary US education entities
         "mil", // United States Military
         "int" // organizations established by international treaty
-    );
+    ));
 
-    private static final List<String> COUNTRY_CODE_TLDS = asList("ac", // Ascension Island
+    private static final Set<String> COUNTRY_CODE_TLDS = new HashSet<>(Arrays.asList("ac", // Ascension Island
         "ad", // Andorra
         "ae", // United Arab Emirates
         "af", // Afghanistan
@@ -440,11 +439,11 @@ public class DomainValidator implements ConstraintValidator<Domain, String> {
         "za", // South Africa
         "zm", // Zambia
         "zw" // Zimbabwe
-    );
+    ));
 
-    private static final List<String> LOCAL_TLDS = asList("localhost", // RFC2606 defined
+    private static final Set<String> LOCAL_TLDS = new HashSet<>(Arrays.asList("localhost", // RFC2606 defined
         "localdomain" // Also widely used as localhost.localdomain
-    );
+    ));
 
     /**
      * {@inheritDoc}

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddressValidator.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddressValidator.java b/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddressValidator.java
index 461562c..d482e01 100644
--- a/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddressValidator.java
+++ b/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddressValidator.java
@@ -27,7 +27,7 @@ import java.util.regex.Pattern;
  *
  * <p>This class provides methods to validate a candidate IP address.
  */
-public class InetAddressValidator implements ConstraintValidator<InetAddress, String> {
+public class InetAddressValidator implements ConstraintValidator<InetAddress, CharSequence> {
 
     private static final Pattern IPV4_PATTERN =
         Pattern.compile("^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
@@ -37,7 +37,7 @@ public class InetAddressValidator implements ConstraintValidator<InetAddress, St
      * {@inheritDoc}
      */
     @Override
-    public boolean isValid(String value, ConstraintValidatorContext context) {
+    public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
         if (!IPV4_PATTERN.matcher(value).matches()) {
             return false;
         }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidatorTest.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidatorTest.java b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidatorTest.java
index d186dcb..8cd2399 100644
--- a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidatorTest.java
+++ b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidatorTest.java
@@ -25,7 +25,7 @@ import java.lang.annotation.Annotation;
 public class ABANumberValidatorTest extends AbstractCheckDigitTest {
 
     @Override
-    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+    protected ConstraintValidator<? extends Annotation, ? super String> getConstraint() {
         return new ABANumberValidator();
     }
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/AbstractCheckDigitTest.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/AbstractCheckDigitTest.java b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/AbstractCheckDigitTest.java
index c25278f..190bd5b 100644
--- a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/AbstractCheckDigitTest.java
+++ b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/AbstractCheckDigitTest.java
@@ -37,7 +37,7 @@ public abstract class AbstractCheckDigitTest {
     private int checkDigitLth;
 
     /** Check digit routine being tested */
-    private ConstraintValidator<? extends Annotation, String> routine;
+    private ConstraintValidator<? extends Annotation, ? super String> routine;
 
     /** Array of valid code values */
     private String[] valid;
@@ -55,7 +55,7 @@ public abstract class AbstractCheckDigitTest {
         return 1;
     }
 
-    protected abstract ConstraintValidator<? extends Annotation, String> getConstraint();
+    protected abstract ConstraintValidator<? extends Annotation, ? super String> getConstraint();
 
     protected abstract String[] getValid();
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidatorTest.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidatorTest.java b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidatorTest.java
index 5bc57c3..fe13c4b 100644
--- a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidatorTest.java
+++ b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidatorTest.java
@@ -25,7 +25,7 @@ import java.lang.annotation.Annotation;
 public class CUSIPValidatorTest extends AbstractCheckDigitTest {
 
     @Override
-    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+    protected ConstraintValidator<? extends Annotation, ? super String> getConstraint() {
         return new CUSIPValidator();
     }
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/EAN13CheckDigitTest.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/EAN13CheckDigitTest.java b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/EAN13CheckDigitTest.java
index 83cbf63..052a1c9 100644
--- a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/EAN13CheckDigitTest.java
+++ b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/EAN13CheckDigitTest.java
@@ -25,7 +25,7 @@ import java.lang.annotation.Annotation;
 public class EAN13CheckDigitTest extends AbstractCheckDigitTest {
 
     @Override
-    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+    protected ConstraintValidator<? extends Annotation, ? super String> getConstraint() {
         return new EAN13Validator();
     }
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/IBANCheckDigitTest.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/IBANCheckDigitTest.java b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/IBANCheckDigitTest.java
index c0ea8e6..04cfd73 100644
--- a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/IBANCheckDigitTest.java
+++ b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/IBANCheckDigitTest.java
@@ -35,7 +35,7 @@ public class IBANCheckDigitTest extends AbstractCheckDigitTest {
     }
 
     @Override
-    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+    protected ConstraintValidator<? extends Annotation, ? super String> getConstraint() {
         return new IBANValidator();
     }
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ISBN10CheckDigitTest.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ISBN10CheckDigitTest.java b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ISBN10CheckDigitTest.java
index f03e7a1..4940c8b 100644
--- a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ISBN10CheckDigitTest.java
+++ b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ISBN10CheckDigitTest.java
@@ -25,7 +25,7 @@ import java.lang.annotation.Annotation;
 public class ISBN10CheckDigitTest extends AbstractCheckDigitTest {
 
     @Override
-    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+    protected ConstraintValidator<? extends Annotation, ? super String> getConstraint() {
         return new ISBN10Validator();
     }
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/LuhnCheckDigitTest.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/LuhnCheckDigitTest.java b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/LuhnCheckDigitTest.java
index 06263e7..2b09d50 100644
--- a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/LuhnCheckDigitTest.java
+++ b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/LuhnCheckDigitTest.java
@@ -37,7 +37,7 @@ public class LuhnCheckDigitTest extends AbstractCheckDigitTest {
     private static final String VALID_DINERS     = "30569309025904";
 
     @Override
-    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+    protected ConstraintValidator<? extends Annotation, ? super String> getConstraint() {
         return new LuhnValidator();
     }
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/SedolCheckDigitTest.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/SedolCheckDigitTest.java b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/SedolCheckDigitTest.java
index 73ea523..55d1f51 100644
--- a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/SedolCheckDigitTest.java
+++ b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/SedolCheckDigitTest.java
@@ -25,7 +25,7 @@ import java.lang.annotation.Annotation;
 public class SedolCheckDigitTest extends AbstractCheckDigitTest {
 
     @Override
-    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+    protected ConstraintValidator<? extends Annotation, ? super String> getConstraint() {
         return new SedolValidator();
     }
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffCheckDigitTest.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffCheckDigitTest.java b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffCheckDigitTest.java
index 2c6e982..fbd3c7f 100644
--- a/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffCheckDigitTest.java
+++ b/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffCheckDigitTest.java
@@ -28,7 +28,7 @@ import java.lang.annotation.Annotation;
 public class VerhoeffCheckDigitTest extends AbstractCheckDigitTest {
 
     @Override
-    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+    protected ConstraintValidator<? extends Annotation, ? super String> getConstraint() {
         return new VerhoeffValidator();
     }
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/InetAddressValidatorTest.java
----------------------------------------------------------------------
diff --git a/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/InetAddressValidatorTest.java b/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/InetAddressValidatorTest.java
index 9eba6ec..a79430d 100644
--- a/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/InetAddressValidatorTest.java
+++ b/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/InetAddressValidatorTest.java
@@ -27,7 +27,7 @@ import static org.junit.Assert.assertTrue;
 /**
  * Test cases for InetAddressValidator.
  *
- * @version $Revision$
+ * @version $Revision: 1766546 $
  */
 public class InetAddressValidatorTest {
 

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/AssertFalseValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/AssertFalseValidator.java b/bval-jsr/src/main/java/org/apache/bval/constraints/AssertFalseValidator.java
index 9eb4a14..143b76a 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/AssertFalseValidator.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/AssertFalseValidator.java
@@ -28,12 +28,8 @@ import javax.validation.constraints.AssertFalse;
 public class AssertFalseValidator implements ConstraintValidator<AssertFalse, Boolean> {
 
     @Override
-    public void initialize(AssertFalse annotation) {
-    }
-
-    @Override
     public boolean isValid(Boolean value, ConstraintValidatorContext context) {
-        return value == null || Boolean.FALSE.equals(value);
+        return !Boolean.TRUE.equals(value);
     }
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/AssertTrueValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/AssertTrueValidator.java b/bval-jsr/src/main/java/org/apache/bval/constraints/AssertTrueValidator.java
index 47ee7f6..93ae933 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/AssertTrueValidator.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/AssertTrueValidator.java
@@ -28,12 +28,8 @@ import javax.validation.constraints.AssertTrue;
 public class AssertTrueValidator implements ConstraintValidator<AssertTrue, Boolean> {
 
     @Override
-    public void initialize(AssertTrue annotation) {
-    }
-
-    @Override
     public boolean isValid(Boolean value, ConstraintValidatorContext context) {
-        return value == null || Boolean.TRUE.equals(value);
+        return !Boolean.FALSE.equals(value);
     }
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForNumber.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForNumber.java b/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForNumber.java
index dd0c210..725613c 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForNumber.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForNumber.java
@@ -43,12 +43,14 @@ public class DecimalMaxValidatorForNumber implements ConstraintValidator<Decimal
         if (value == null) {
             return true;
         }
+        BigDecimal bigValue;
         if (value instanceof BigDecimal) {
-            return ((BigDecimal) value).compareTo(maxValue) != 1;
+            bigValue = (BigDecimal) value;
+        } else if (value instanceof BigInteger) {
+            bigValue = new BigDecimal((BigInteger) value);
+        } else {
+            bigValue = new BigDecimal(value.doubleValue());
         }
-        if (value instanceof BigInteger) {
-            return (new BigDecimal((BigInteger) value)).compareTo(maxValue) != 1;
-        }
-        return (new BigDecimal(value.doubleValue()).compareTo(maxValue)) != 1;
+        return bigValue.compareTo(maxValue) < 1;
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForString.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForString.java b/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForString.java
index a247297..cb0e232 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForString.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForString.java
@@ -46,7 +46,7 @@ public class DecimalMaxValidatorForString implements ConstraintValidator<Decimal
             return true;
         }
         try {
-            return new BigDecimal(value).compareTo(maxValue) != 1;
+            return new BigDecimal(value).compareTo(maxValue) < 1;
         } catch (NumberFormatException nfe) {
             return false;
         }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForNumber.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForNumber.java b/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForNumber.java
index 74ed17a..17c6c38 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForNumber.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForNumber.java
@@ -43,12 +43,14 @@ public class DecimalMinValidatorForNumber implements ConstraintValidator<Decimal
         if (value == null) {
             return true;
         }
+        BigDecimal bigValue;
         if (value instanceof BigDecimal) {
-            return ((BigDecimal) value).compareTo(minValue) != -1;
+            bigValue = (BigDecimal) value;
+        } else if (value instanceof BigInteger) {
+            bigValue = new BigDecimal((BigInteger) value);
+        } else {
+            bigValue = new BigDecimal(value.doubleValue());
         }
-        if (value instanceof BigInteger) {
-            return (new BigDecimal((BigInteger) value)).compareTo(minValue) != -1;
-        }
-        return (new BigDecimal(value.doubleValue()).compareTo(minValue)) != -1;
+        return bigValue.compareTo(minValue) >= 0;
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForString.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForString.java b/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForString.java
index 8110f8e..ef62387 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForString.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForString.java
@@ -48,7 +48,7 @@ public class DecimalMinValidatorForString implements ConstraintValidator<Decimal
             return true;
         }
         try {
-            return new BigDecimal(value).compareTo(minValue) != -1;
+            return new BigDecimal(value).compareTo(minValue) >= 0;
         } catch (NumberFormatException nfe) {
             return false;
         }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/Email.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/Email.java b/bval-jsr/src/main/java/org/apache/bval/constraints/Email.java
index 2d66d28..f1f9435 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/Email.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/Email.java
@@ -19,34 +19,41 @@
 package org.apache.bval.constraints;
 
 import javax.validation.Constraint;
+import javax.validation.OverridesAttribute;
 import javax.validation.Payload;
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE_USE;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 /**
  * <p>
  * --
- * TODO - This class is NOT part of the bean_validation spec and might disappear
- * as soon as a final version of the specification contains a similar functionality.
+ * NOTE - This constraint predates the equivalent version from the bean_validation spec.
  * --
  * </p>
  * Description: annotation to validate an email address (by pattern)<br/>
  */
+@Deprecated
 @Documented
-@Constraint(validatedBy = EmailValidator.class)
-@Target({ METHOD, FIELD, ANNOTATION_TYPE, PARAMETER })
+@Constraint(validatedBy = {})
+@javax.validation.constraints.Email
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
 @Retention(RUNTIME)
 public @interface Email {
+    @OverridesAttribute(constraint = javax.validation.constraints.Email.class, name = "groups")
     Class<?>[] groups() default {};
 
+    @OverridesAttribute(constraint = javax.validation.constraints.Email.class, name = "message")
     String message() default "{org.apache.bval.constraints.Email.message}";
 
+    @OverridesAttribute(constraint = javax.validation.constraints.Email.class, name = "payload")
     Class<? extends Payload>[] payload() default {};
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/EmailValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/EmailValidator.java b/bval-jsr/src/main/java/org/apache/bval/constraints/EmailValidator.java
index bcdf0fe..b20806b 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/EmailValidator.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/EmailValidator.java
@@ -26,15 +26,10 @@ import javax.validation.ConstraintValidatorContext;
 /**
  * Description: <br/>
  */
-public class EmailValidator implements ConstraintValidator<Email, CharSequence> {
+public class EmailValidator implements ConstraintValidator<javax.validation.constraints.Email, CharSequence> {
 
     @Override
     public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
         return EMailValidationUtils.isValid(value);
     }
-
-    @Override
-    public void initialize(Email parameters) {
-        // do nothing (as long as Email has no properties)
-    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/FutureOrPresentValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/FutureOrPresentValidator.java b/bval-jsr/src/main/java/org/apache/bval/constraints/FutureOrPresentValidator.java
new file mode 100644
index 0000000..4cc85b1
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/FutureOrPresentValidator.java
@@ -0,0 +1,138 @@
+/*
+ * 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.constraints;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.MonthDay;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.ZonedDateTime;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.ChronoLocalDateTime;
+import java.time.chrono.ChronoZonedDateTime;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.function.Function;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.constraints.FutureOrPresent;
+
+/**
+ * Defines built-in {@link ConstraintValidator} implementations for {@link FutureOrPresent}.
+ *
+ * @param <T>
+ *            validated type
+ */
+public abstract class FutureOrPresentValidator<T extends Comparable<T>> extends TimeValidator<FutureOrPresent, T> {
+
+    public static class ForDate extends FutureOrPresentValidator<Date> {
+
+        public ForDate() {
+            super(clock -> Date.from(clock.instant()));
+        }
+    }
+
+    public static class ForCalendar extends FutureOrPresentValidator<Calendar> {
+
+        public ForCalendar() {
+            super(clock -> GregorianCalendar.from(clock.instant().atZone(clock.getZone())));
+        }
+    }
+
+    public static class ForInstant extends FutureOrPresentValidator<Instant> {
+
+        public ForInstant() {
+            super(Instant::now);
+        }
+    }
+
+    public static class ForChronoLocalDate extends FutureOrPresentValidator<ChronoLocalDate> {
+
+        public ForChronoLocalDate() {
+            super(LocalDate::now);
+        }
+    }
+
+    public static class ForChronoLocalDateTime extends FutureOrPresentValidator<ChronoLocalDateTime<?>> {
+
+        public ForChronoLocalDateTime() {
+            super(LocalDateTime::now);
+        }
+    }
+
+    public static class ForLocalTime extends FutureOrPresentValidator<LocalTime> {
+
+        public ForLocalTime() {
+            super(LocalTime::now);
+        }
+    }
+
+    public static class ForOffsetDateTime extends FutureOrPresentValidator<OffsetDateTime> {
+
+        public ForOffsetDateTime() {
+            super(OffsetDateTime::now);
+        }
+    }
+
+    public static class ForOffsetTime extends FutureOrPresentValidator<OffsetTime> {
+
+        public ForOffsetTime() {
+            super(OffsetTime::now);
+        }
+    }
+
+    public static class ForChronoZonedDateTime extends FutureOrPresentValidator<ChronoZonedDateTime<?>> {
+
+        public ForChronoZonedDateTime() {
+            super(ZonedDateTime::now);
+        }
+    }
+
+    public static class ForMonthDay extends FutureOrPresentValidator<MonthDay> {
+
+        public ForMonthDay() {
+            super(MonthDay::now);
+        }
+    }
+
+    public static class ForYear extends FutureOrPresentValidator<Year> {
+
+        public ForYear() {
+            super(Year::now);
+        }
+    }
+
+    public static class ForYearMonth extends FutureOrPresentValidator<YearMonth> {
+
+        public ForYearMonth() {
+            super(YearMonth::now);
+        }
+    }
+
+    protected FutureOrPresentValidator(Function<Clock, T> now) {
+        super(now, n -> n >= 0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/FutureValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/FutureValidator.java b/bval-jsr/src/main/java/org/apache/bval/constraints/FutureValidator.java
new file mode 100644
index 0000000..dd6116b
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/FutureValidator.java
@@ -0,0 +1,138 @@
+/*
+ * 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.constraints;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.MonthDay;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.ZonedDateTime;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.ChronoLocalDateTime;
+import java.time.chrono.ChronoZonedDateTime;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.function.Function;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.constraints.Future;
+
+/**
+ * Defines built-in {@link ConstraintValidator} implementations for {@link Future}.
+ *
+ * @param <T>
+ *            validated type
+ */
+public abstract class FutureValidator<T extends Comparable<T>> extends TimeValidator<Future, T> {
+
+    public static class ForDate extends FutureValidator<Date> {
+
+        public ForDate() {
+            super(clock -> Date.from(clock.instant()));
+        }
+    }
+
+    public static class ForCalendar extends FutureValidator<Calendar> {
+
+        public ForCalendar() {
+            super(clock -> GregorianCalendar.from(clock.instant().atZone(clock.getZone())));
+        }
+    }
+
+    public static class ForInstant extends FutureValidator<Instant> {
+
+        public ForInstant() {
+            super(Instant::now);
+        }
+    }
+
+    public static class ForChronoLocalDate extends FutureValidator<ChronoLocalDate> {
+
+        public ForChronoLocalDate() {
+            super(LocalDate::now);
+        }
+    }
+
+    public static class ForChronoLocalDateTime extends FutureValidator<ChronoLocalDateTime<?>> {
+
+        public ForChronoLocalDateTime() {
+            super(LocalDateTime::now);
+        }
+    }
+
+    public static class ForLocalTime extends FutureValidator<LocalTime> {
+
+        public ForLocalTime() {
+            super(LocalTime::now);
+        }
+    }
+
+    public static class ForOffsetDateTime extends FutureValidator<OffsetDateTime> {
+
+        public ForOffsetDateTime() {
+            super(OffsetDateTime::now);
+        }
+    }
+
+    public static class ForOffsetTime extends FutureValidator<OffsetTime> {
+
+        public ForOffsetTime() {
+            super(OffsetTime::now);
+        }
+    }
+
+    public static class ForChronoZonedDateTime extends FutureValidator<ChronoZonedDateTime<?>> {
+
+        public ForChronoZonedDateTime() {
+            super(ZonedDateTime::now);
+        }
+    }
+
+    public static class ForMonthDay extends FutureValidator<MonthDay> {
+
+        public ForMonthDay() {
+            super(MonthDay::now);
+        }
+    }
+
+    public static class ForYear extends FutureValidator<Year> {
+
+        public ForYear() {
+            super(Year::now);
+        }
+    }
+
+    public static class ForYearMonth extends FutureValidator<YearMonth> {
+
+        public ForYearMonth() {
+            super(YearMonth::now);
+        }
+    }
+
+    protected FutureValidator(Function<Clock, T> now) {
+        super(now, n -> n > 0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/MaxValidatorForNumber.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/MaxValidatorForNumber.java b/bval-jsr/src/main/java/org/apache/bval/constraints/MaxValidatorForNumber.java
index 78524f9..0ad81a8 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/MaxValidatorForNumber.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/MaxValidatorForNumber.java
@@ -43,10 +43,10 @@ public class MaxValidatorForNumber implements ConstraintValidator<Max, Number> {
             return true;
         }
         if (value instanceof BigDecimal) {
-            return ((BigDecimal) value).compareTo(BigDecimal.valueOf(max)) != 1;
+            return ((BigDecimal) value).compareTo(BigDecimal.valueOf(max)) < 1;
         }
         if (value instanceof BigInteger) {
-            return ((BigInteger) value).compareTo(BigInteger.valueOf(max)) != 1;
+            return ((BigInteger) value).compareTo(BigInteger.valueOf(max)) < 1;
         }
         return value.longValue() <= max;
     }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/MaxValidatorForString.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/MaxValidatorForString.java b/bval-jsr/src/main/java/org/apache/bval/constraints/MaxValidatorForString.java
index 4170a5c..f14f8e7 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/MaxValidatorForString.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/MaxValidatorForString.java
@@ -42,7 +42,7 @@ public class MaxValidatorForString implements ConstraintValidator<Max, String> {
             return true;
         }
         try {
-            return new BigDecimal(value).compareTo(BigDecimal.valueOf(max)) != 1;
+            return new BigDecimal(value).compareTo(BigDecimal.valueOf(max)) < 1;
         } catch (NumberFormatException nfe) {
             return false;
         }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/MinValidatorForNumber.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/MinValidatorForNumber.java b/bval-jsr/src/main/java/org/apache/bval/constraints/MinValidatorForNumber.java
index b5aeaad..c467de2 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/MinValidatorForNumber.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/MinValidatorForNumber.java
@@ -42,10 +42,10 @@ public class MinValidatorForNumber implements ConstraintValidator<Min, Number> {
             return true;
         }
         if (value instanceof BigDecimal) {
-            return ((BigDecimal) value).compareTo(BigDecimal.valueOf(minValue)) != -1;
+            return ((BigDecimal) value).compareTo(BigDecimal.valueOf(minValue)) >= 0;
         }
         if (value instanceof BigInteger) {
-            return ((BigInteger) value).compareTo(BigInteger.valueOf(minValue)) != -1;
+            return ((BigInteger) value).compareTo(BigInteger.valueOf(minValue)) >= 0;
         }
         return value.longValue() >= minValue;
     }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/MinValidatorForString.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/MinValidatorForString.java b/bval-jsr/src/main/java/org/apache/bval/constraints/MinValidatorForString.java
index bd3a4dd..5d45fc2 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/MinValidatorForString.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/MinValidatorForString.java
@@ -42,7 +42,7 @@ public class MinValidatorForString implements ConstraintValidator<Min, String> {
             return true;
         }
         try {
-            return new BigDecimal(value).compareTo(BigDecimal.valueOf(minValue)) != -1;
+            return new BigDecimal(value).compareTo(BigDecimal.valueOf(minValue)) >= 0;
         } catch (NumberFormatException nfe) {
             return false;
         }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/NotBlankValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/NotBlankValidator.java b/bval-jsr/src/main/java/org/apache/bval/constraints/NotBlankValidator.java
new file mode 100644
index 0000000..e201c0d
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/NotBlankValidator.java
@@ -0,0 +1,34 @@
+/*
+ * 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.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.NotBlank;
+
+/**
+ * Validate {@link NotBlank} for {@link CharSequence}.
+ */
+public class NotBlankValidator implements ConstraintValidator<NotBlank, CharSequence> {
+
+    @Override
+    public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
+        return value == null || value.length() > 0 && !value.chars().allMatch(Character::isWhitespace);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmpty.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmpty.java b/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmpty.java
index ffa8cf8..00497ea 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmpty.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmpty.java
@@ -18,18 +18,22 @@
  */
 package org.apache.bval.constraints;
 
-import javax.validation.Constraint;
-import javax.validation.Payload;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE_USE;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.OverridesAttribute;
+import javax.validation.Payload;
+
 /**
  * <pre>
  * This class is NOT part of the bean_validation spec and might disappear
@@ -37,14 +41,20 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
  * </pre>
  */
 @Documented
-@Constraint(validatedBy = { NotEmptyValidatorForCollection.class, NotEmptyValidatorForMap.class,
-    NotEmptyValidatorForString.class, NotEmptyValidator.class })
-@Target({ METHOD, FIELD, ANNOTATION_TYPE, PARAMETER })
+@Constraint(validatedBy = {})
+@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
 @Retention(RUNTIME)
+@javax.validation.constraints.NotEmpty
+@Deprecated
 public @interface NotEmpty {
     Class<?>[] groups() default {};
 
+    @OverridesAttribute(constraint = javax.validation.constraints.NotEmpty.class, name = "message")
     String message() default "{org.apache.bval.constraints.NotEmpty.message}";
 
     Class<? extends Payload>[] payload() default {};
+
+    public @interface List {
+        NotEmpty[] value();
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidator.java b/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidator.java
index 4316b39..92c8739 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidator.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidator.java
@@ -25,14 +25,10 @@ import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
 /**
- * Description:  Check the non emptyness of an
+ * Description:  Check the non emptiness of an
  * any object that has a public isEmpty():boolean or a valid toString() method
  */
-public class NotEmptyValidator implements ConstraintValidator<NotEmpty, Object> {
-    @Override
-    public void initialize(NotEmpty constraintAnnotation) {
-        // do nothing
-    }
+public class NotEmptyValidator implements ConstraintValidator<javax.validation.constraints.NotEmpty, Object> {
 
     @Override
     public boolean isValid(Object value, ConstraintValidatorContext context) {
@@ -45,15 +41,12 @@ public class NotEmptyValidator implements ConstraintValidator<NotEmpty, Object>
         try {
             final Method isEmptyMethod = value.getClass().getMethod("isEmpty");
             if (isEmptyMethod != null) {
-                return !((Boolean) isEmptyMethod.invoke(value)).booleanValue();
+                return !Boolean.TRUE.equals(isEmptyMethod.invoke(value));
             }
-        } catch (IllegalAccessException iae) {
-            // do nothing
-        } catch (NoSuchMethodException nsme) {
-            // do nothing
-        } catch (InvocationTargetException ite) {
+        } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException iae) {
             // do nothing
         }
-        return !value.toString().isEmpty();
+        final String s = value.toString();
+        return s != null && !s.isEmpty();
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCharSequence.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCharSequence.java b/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCharSequence.java
new file mode 100644
index 0000000..320c105
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCharSequence.java
@@ -0,0 +1,34 @@
+/*
+ * 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.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Description: <br/>
+ */
+public class NotEmptyValidatorForCharSequence
+    implements ConstraintValidator<javax.validation.constraints.NotEmpty, CharSequence> {
+
+    @Override
+    public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
+        return value == null || value.length() > 0;
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCollection.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCollection.java b/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCollection.java
index 5faab13..da0c438 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCollection.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCollection.java
@@ -25,11 +25,8 @@ import java.util.Collection;
 /**
  * Description: <br/>
  */
-public class NotEmptyValidatorForCollection implements ConstraintValidator<NotEmpty, Collection<?>> {
-    @Override
-    public void initialize(NotEmpty constraintAnnotation) {
-        // do nothing
-    }
+public class NotEmptyValidatorForCollection
+    implements ConstraintValidator<javax.validation.constraints.NotEmpty, Collection<?>> {
 
     @Override
     public boolean isValid(Collection<?> value, ConstraintValidatorContext context) {

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForMap.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForMap.java b/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForMap.java
index b5a4f49..6a5221e 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForMap.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForMap.java
@@ -25,11 +25,7 @@ import java.util.Map;
 /**
  * Description: <br/>
  */
-public class NotEmptyValidatorForMap implements ConstraintValidator<NotEmpty, Map<?, ?>> {
-    @Override
-    public void initialize(NotEmpty constraintAnnotation) {
-        // do nothing
-    }
+public class NotEmptyValidatorForMap implements ConstraintValidator<javax.validation.constraints.NotEmpty, Map<?, ?>> {
 
     @Override
     public boolean isValid(Map<?, ?> value, ConstraintValidatorContext context) {

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForString.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForString.java b/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForString.java
deleted file mode 100644
index 9fc3023..0000000
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForString.java
+++ /dev/null
@@ -1,37 +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.constraints;
-
-import javax.validation.ConstraintValidator;
-import javax.validation.ConstraintValidatorContext;
-
-/**
- * Description: <br/>
- */
-public class NotEmptyValidatorForString implements ConstraintValidator<NotEmpty, String> {
-    @Override
-    public void initialize(NotEmpty constraintAnnotation) {
-        // do nothing
-    }
-
-    @Override
-    public boolean isValid(String value, ConstraintValidatorContext context) {
-        return value == null || !value.isEmpty();
-    }
-}

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/NotNullValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/NotNullValidator.java b/bval-jsr/src/main/java/org/apache/bval/constraints/NotNullValidator.java
index b39d493..c14cff2 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/NotNullValidator.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/NotNullValidator.java
@@ -24,10 +24,6 @@ import javax.validation.constraints.NotNull;
 
 /** valid when object is NOT null */
 public class NotNullValidator implements ConstraintValidator<NotNull, Object> {
-    @Override
-    public void initialize(NotNull constraintAnnotation) {
-        // do nothing
-    }
 
     @Override
     public boolean isValid(Object value, ConstraintValidatorContext context) {

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/NullValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/NullValidator.java b/bval-jsr/src/main/java/org/apache/bval/constraints/NullValidator.java
index 7c16296..523ef75 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/NullValidator.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/NullValidator.java
@@ -28,11 +28,6 @@ import javax.validation.constraints.Null;
 public class NullValidator implements ConstraintValidator<Null, Object> {
 
     @Override
-    public void initialize(Null annotation) {
-        // do nothing
-    }
-
-    @Override
     public boolean isValid(Object object, ConstraintValidatorContext context) {
         return object == null;
     }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/NumberSignValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/NumberSignValidator.java b/bval-jsr/src/main/java/org/apache/bval/constraints/NumberSignValidator.java
new file mode 100644
index 0000000..fe8f59c
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/NumberSignValidator.java
@@ -0,0 +1,68 @@
+/*
+ * 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.constraints;
+
+import java.util.function.IntPredicate;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Negative;
+
+import org.apache.bval.util.Validate;
+
+/**
+ * Description: validate positive/negative number values.
+ */
+public abstract class NumberSignValidator implements ConstraintValidator<Negative, Number> {
+    public static class ForPositive extends NumberSignValidator {
+        public static class OrZero extends NumberSignValidator {
+            public OrZero() {
+                super(n -> n >= 0);
+            }
+        }
+
+        public ForPositive() {
+            super(n -> n > 0);
+        }
+    }
+
+    public static class ForNegative extends NumberSignValidator {
+        public static class OrZero extends NumberSignValidator {
+            public OrZero() {
+                super(n -> n <= 0);
+            }
+        }
+
+        public ForNegative() {
+            super(n -> n < 0);
+        }
+    }
+    
+    private final IntPredicate comparisonTest;
+    
+    protected NumberSignValidator(IntPredicate comparisonTest) {
+        super();
+        this.comparisonTest = Validate.notNull(comparisonTest);
+    }
+
+    @Override
+    public boolean isValid(Number value, ConstraintValidatorContext context) {
+        return value == null || comparisonTest.test(Double.compare(value.doubleValue(), 0.0));
+    }
+}


[07/17] bval git commit: BV2: constraint/validator updates

Posted by mb...@apache.org.
http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/PastOrPresentValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/PastOrPresentValidator.java b/bval-jsr/src/main/java/org/apache/bval/constraints/PastOrPresentValidator.java
new file mode 100644
index 0000000..d1e3e19
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/PastOrPresentValidator.java
@@ -0,0 +1,138 @@
+/*
+ * 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.constraints;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.MonthDay;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.ZonedDateTime;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.ChronoLocalDateTime;
+import java.time.chrono.ChronoZonedDateTime;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.function.Function;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.constraints.PastOrPresent;
+
+/**
+ * Defines built-in {@link ConstraintValidator} implementations for {@link PastOrPresent}.
+ *
+ * @param <T>
+ *            validated type
+ */
+public abstract class PastOrPresentValidator<T extends Comparable<T>> extends TimeValidator<PastOrPresent, T> {
+
+    public static class ForDate extends PastOrPresentValidator<Date> {
+
+        public ForDate() {
+            super(clock -> Date.from(clock.instant()));
+        }
+    }
+
+    public static class ForCalendar extends PastOrPresentValidator<Calendar> {
+
+        public ForCalendar() {
+            super(clock -> GregorianCalendar.from(clock.instant().atZone(clock.getZone())));
+        }
+    }
+
+    public static class ForInstant extends PastOrPresentValidator<Instant> {
+
+        public ForInstant() {
+            super(Instant::now);
+        }
+    }
+
+    public static class ForChronoLocalDate extends PastOrPresentValidator<ChronoLocalDate> {
+
+        public ForChronoLocalDate() {
+            super(LocalDate::now);
+        }
+    }
+
+    public static class ForChronoLocalDateTime extends PastOrPresentValidator<ChronoLocalDateTime<?>> {
+
+        public ForChronoLocalDateTime() {
+            super(LocalDateTime::now);
+        }
+    }
+
+    public static class ForLocalTime extends PastOrPresentValidator<LocalTime> {
+
+        public ForLocalTime() {
+            super(LocalTime::now);
+        }
+    }
+
+    public static class ForOffsetDateTime extends PastOrPresentValidator<OffsetDateTime> {
+
+        public ForOffsetDateTime() {
+            super(OffsetDateTime::now);
+        }
+    }
+
+    public static class ForOffsetTime extends PastOrPresentValidator<OffsetTime> {
+
+        public ForOffsetTime() {
+            super(OffsetTime::now);
+        }
+    }
+
+    public static class ForChronoZonedDateTime extends PastOrPresentValidator<ChronoZonedDateTime<?>> {
+
+        public ForChronoZonedDateTime() {
+            super(ZonedDateTime::now);
+        }
+    }
+
+    public static class ForMonthDay extends PastOrPresentValidator<MonthDay> {
+
+        public ForMonthDay() {
+            super(MonthDay::now);
+        }
+    }
+
+    public static class ForYear extends PastOrPresentValidator<Year> {
+
+        public ForYear() {
+            super(Year::now);
+        }
+    }
+
+    public static class ForYearMonth extends PastOrPresentValidator<YearMonth> {
+
+        public ForYearMonth() {
+            super(YearMonth::now);
+        }
+    }
+
+    protected PastOrPresentValidator(Function<Clock, T> now) {
+        super(now, n -> n <= 0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/PastValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/PastValidator.java b/bval-jsr/src/main/java/org/apache/bval/constraints/PastValidator.java
new file mode 100644
index 0000000..0136d83
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/PastValidator.java
@@ -0,0 +1,138 @@
+/*
+ * 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.constraints;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.MonthDay;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.ZonedDateTime;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.ChronoLocalDateTime;
+import java.time.chrono.ChronoZonedDateTime;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.function.Function;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.constraints.Past;
+
+/**
+ * Defines built-in {@link ConstraintValidator} implementations for {@link Past}.
+ *
+ * @param <T>
+ *            validated type
+ */
+public abstract class PastValidator<T extends Comparable<T>> extends TimeValidator<Past, T> {
+
+    public static class ForDate extends PastValidator<Date> {
+
+        public ForDate() {
+            super(clock -> Date.from(clock.instant()));
+        }
+    }
+
+    public static class ForCalendar extends PastValidator<Calendar> {
+
+        public ForCalendar() {
+            super(clock -> GregorianCalendar.from(clock.instant().atZone(clock.getZone())));
+        }
+    }
+
+    public static class ForInstant extends PastValidator<Instant> {
+
+        public ForInstant() {
+            super(Instant::now);
+        }
+    }
+
+    public static class ForChronoLocalDate extends PastValidator<ChronoLocalDate> {
+
+        public ForChronoLocalDate() {
+            super(LocalDate::now);
+        }
+    }
+
+    public static class ForChronoLocalDateTime extends PastValidator<ChronoLocalDateTime<?>> {
+
+        public ForChronoLocalDateTime() {
+            super(LocalDateTime::now);
+        }
+    }
+
+    public static class ForLocalTime extends PastValidator<LocalTime> {
+
+        public ForLocalTime() {
+            super(LocalTime::now);
+        }
+    }
+
+    public static class ForOffsetDateTime extends PastValidator<OffsetDateTime> {
+
+        public ForOffsetDateTime() {
+            super(OffsetDateTime::now);
+        }
+    }
+
+    public static class ForOffsetTime extends PastValidator<OffsetTime> {
+
+        public ForOffsetTime() {
+            super(OffsetTime::now);
+        }
+    }
+
+    public static class ForChronoZonedDateTime extends PastValidator<ChronoZonedDateTime<?>> {
+
+        public ForChronoZonedDateTime() {
+            super(ZonedDateTime::now);
+        }
+    }
+
+    public static class ForMonthDay extends PastValidator<MonthDay> {
+
+        public ForMonthDay() {
+            super(MonthDay::now);
+        }
+    }
+
+    public static class ForYear extends PastValidator<Year> {
+
+        public ForYear() {
+            super(Year::now);
+        }
+    }
+
+    public static class ForYearMonth extends PastValidator<YearMonth> {
+
+        public ForYearMonth() {
+            super(YearMonth::now);
+        }
+    }
+
+    protected PastValidator(Function<Clock, T> now) {
+        super(now, n -> n < 0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/PatternValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/PatternValidator.java b/bval-jsr/src/main/java/org/apache/bval/constraints/PatternValidator.java
index 9534e6b..8a53062 100644
--- a/bval-jsr/src/main/java/org/apache/bval/constraints/PatternValidator.java
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/PatternValidator.java
@@ -27,7 +27,7 @@ import java.util.regex.PatternSyntaxException;
  * validator using a regular expression,
  * based on the jsr Pattern constraint annotation.
  */
-public class PatternValidator implements ConstraintValidator<Pattern, String> {
+public class PatternValidator implements ConstraintValidator<Pattern, CharSequence> {
     protected java.util.regex.Pattern pattern;
 
     @Override
@@ -46,7 +46,7 @@ public class PatternValidator implements ConstraintValidator<Pattern, String> {
     }
 
     @Override
-    public boolean isValid(String value, ConstraintValidatorContext context) {
+    public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
         return value == null || pattern.matcher(value).matches();
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/java/org/apache/bval/constraints/TimeValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/constraints/TimeValidator.java b/bval-jsr/src/main/java/org/apache/bval/constraints/TimeValidator.java
new file mode 100644
index 0000000..02e3836
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/constraints/TimeValidator.java
@@ -0,0 +1,44 @@
+/*
+ * 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.constraints;
+
+import java.lang.annotation.Annotation;
+import java.time.Clock;
+import java.util.function.Function;
+import java.util.function.IntPredicate;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+public abstract class TimeValidator<A extends Annotation, T extends Comparable<T>> implements ConstraintValidator<A, T> {
+
+    private final Function<Clock, T> now;
+    private final IntPredicate test;
+    
+    protected TimeValidator(Function<Clock, T> now, IntPredicate test) {
+        super();
+        this.now = now;
+        this.test = test;
+    }
+
+    @Override
+    public final boolean isValid(T value, ConstraintValidatorContext context) {
+        return value == null || test.test(value.compareTo(now.apply(context.getClockProvider().getClock())));
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40fc20f6/bval-jsr/src/main/resources/org/apache/bval/jsr/DefaultConstraints.properties
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/resources/org/apache/bval/jsr/DefaultConstraints.properties b/bval-jsr/src/main/resources/org/apache/bval/jsr/DefaultConstraints.properties
index 7bc433b..cc5cd6c 100644
--- a/bval-jsr/src/main/resources/org/apache/bval/jsr/DefaultConstraints.properties
+++ b/bval-jsr/src/main/resources/org/apache/bval/jsr/DefaultConstraints.properties
@@ -22,22 +22,94 @@
 
 javax.validation.constraints.AssertFalse=org.apache.bval.constraints.AssertFalseValidator
 javax.validation.constraints.AssertTrue=org.apache.bval.constraints.AssertTrueValidator
+
 javax.validation.constraints.DecimalMax=org.apache.bval.constraints.DecimalMaxValidatorForNumber,\
   org.apache.bval.constraints.DecimalMaxValidatorForString
+
 javax.validation.constraints.DecimalMin=org.apache.bval.constraints.DecimalMinValidatorForNumber,\
   org.apache.bval.constraints.DecimalMinValidatorForString
+
 javax.validation.constraints.Digits=org.apache.bval.constraints.DigitsValidatorForNumber,\
   org.apache.bval.constraints.DigitsValidatorForString
-javax.validation.constraints.Future=org.apache.bval.constraints.FutureValidatorForDate,\
-  org.apache.bval.constraints.FutureValidatorForCalendar
+
+javax.validation.constraints.Email=org.apache.bval.constraints.EmailValidator
+
+javax.validation.constraints.Future=\
+  org.apache.bval.constraints.FutureValidator$ForCalendar,\
+  org.apache.bval.constraints.FutureValidator$ForDate,\
+  org.apache.bval.constraints.FutureValidator$ForChronoLocalDate,\
+  org.apache.bval.constraints.FutureValidator$ForChronoLocalDateTime,\
+  org.apache.bval.constraints.FutureValidator$ForChronoZonedDateTime,\
+  org.apache.bval.constraints.FutureValidator$ForInstant,\
+  org.apache.bval.constraints.FutureValidator$ForLocalTime,\
+  org.apache.bval.constraints.FutureValidator$ForMonthDay,\
+  org.apache.bval.constraints.FutureValidator$ForOffsetDateTime,\
+  org.apache.bval.constraints.FutureValidator$ForOffsetTime,\
+  org.apache.bval.constraints.FutureValidator$ForYear,\
+  org.apache.bval.constraints.FutureValidator$ForYearMonth
+
+javax.validation.constraints.FutureOrPresent=\
+  org.apache.bval.constraints.FutureOrPresentValidator$ForCalendar,\
+  org.apache.bval.constraints.FutureOrPresentValidator$ForDate,\
+  org.apache.bval.constraints.FutureOrPresentValidator$ForChronoLocalDate,\
+  org.apache.bval.constraints.FutureOrPresentValidator$ForChronoLocalDateTime,\
+  org.apache.bval.constraints.FutureOrPresentValidator$ForChronoZonedDateTime,\
+  org.apache.bval.constraints.FutureOrPresentValidator$ForInstant,\
+  org.apache.bval.constraints.FutureOrPresentValidator$ForLocalTime,\
+  org.apache.bval.constraints.FutureOrPresentValidator$ForMonthDay,\
+  org.apache.bval.constraints.FutureOrPresentValidator$ForOffsetDateTime,\
+  org.apache.bval.constraints.FutureOrPresentValidator$ForOffsetTime,\
+  org.apache.bval.constraints.FutureOrPresentValidator$ForYear,\
+  org.apache.bval.constraints.FutureOrPresentValidator$ForYearMonth
+
 javax.validation.constraints.Max=org.apache.bval.constraints.MaxValidatorForNumber,\
   org.apache.bval.constraints.MaxValidatorForString
+
 javax.validation.constraints.Min=org.apache.bval.constraints.MinValidatorForNumber,\
   org.apache.bval.constraints.MinValidatorForString
+
+javax.validation.constraints.Negative=org.apache.bval.constraints.NumberSignValidator$ForNegative
+javax.validation.constraints.NegativeOrZero=org.apache.bval.constraints.NumberSignValidator$ForNegative$OrZero
+
+javax.validation.constraints.NotEmpty=org.apache.bval.constraints.NotEmptyValidator,\
+ org.apache.bval.constraints.NotEmptyValidatorForCharSequence,\
+ org.apache.bval.constraints.NotEmptyValidatorForCollection,\
+ org.apache.bval.constraints.NotEmptyValidatorForMap
+
 javax.validation.constraints.NotNull=org.apache.bval.constraints.NotNullValidator
 javax.validation.constraints.Null=org.apache.bval.constraints.NullValidator
-javax.validation.constraints.Past=org.apache.bval.constraints.PastValidatorForDate,\
-  org.apache.bval.constraints.PastValidatorForCalendar
+
+javax.validation.constraints.Past=\
+  org.apache.bval.constraints.PastValidator$ForCalendar,\
+  org.apache.bval.constraints.PastValidator$ForDate,\
+  org.apache.bval.constraints.PastValidator$ForChronoLocalDate,\
+  org.apache.bval.constraints.PastValidator$ForChronoLocalDateTime,\
+  org.apache.bval.constraints.PastValidator$ForChronoZonedDateTime,\
+  org.apache.bval.constraints.PastValidator$ForInstant,\
+  org.apache.bval.constraints.PastValidator$ForLocalTime,\
+  org.apache.bval.constraints.PastValidator$ForMonthDay,\
+  org.apache.bval.constraints.PastValidator$ForOffsetDateTime,\
+  org.apache.bval.constraints.PastValidator$ForOffsetTime,\
+  org.apache.bval.constraints.PastValidator$ForYear,\
+  org.apache.bval.constraints.PastValidator$ForYearMonth
+
+javax.validation.constraints.PastOrPresent=\
+  org.apache.bval.constraints.PastOrPresentValidator$ForCalendar,\
+  org.apache.bval.constraints.PastOrPresentValidator$ForDate,\
+  org.apache.bval.constraints.PastOrPresentValidator$ForChronoLocalDate,\
+  org.apache.bval.constraints.PastOrPresentValidator$ForChronoLocalDateTime,\
+  org.apache.bval.constraints.PastOrPresentValidator$ForChronoZonedDateTime,\
+  org.apache.bval.constraints.PastOrPresentValidator$ForInstant,\
+  org.apache.bval.constraints.PastOrPresentValidator$ForLocalTime,\
+  org.apache.bval.constraints.PastOrPresentValidator$ForMonthDay,\
+  org.apache.bval.constraints.PastOrPresentValidator$ForOffsetDateTime,\
+  org.apache.bval.constraints.PastOrPresentValidator$ForOffsetTime,\
+  org.apache.bval.constraints.PastOrPresentValidator$ForYear,\
+  org.apache.bval.constraints.PastOrPresentValidator$ForYearMonth
+
+javax.validation.constraints.Positive=org.apache.bval.constraints.NumberSignValidator$ForPositive
+javax.validation.constraints.PositiveOrZero=org.apache.bval.constraints.NumberSignValidator$ForPositive$OrZero
+
 javax.validation.constraints.Size=org.apache.bval.constraints.SizeValidatorForCharSequence,\
   org.apache.bval.constraints.SizeValidatorForMap,\
   org.apache.bval.constraints.SizeValidatorForCollection,\
@@ -50,4 +122,5 @@ javax.validation.constraints.Size=org.apache.bval.constraints.SizeValidatorForCh
   org.apache.bval.constraints.SizeValidatorForArrayOfLong,\
   org.apache.bval.constraints.SizeValidatorForArrayOfObject,\
   org.apache.bval.constraints.SizeValidatorForArrayOfShort
+
 javax.validation.constraints.Pattern=org.apache.bval.constraints.PatternValidator


[09/17] bval git commit: BV2: new metadata model

Posted by mb...@apache.org.
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ReflectionBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ReflectionBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ReflectionBuilder.java
new file mode 100644
index 0000000..fc21ea7
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ReflectionBuilder.java
@@ -0,0 +1,272 @@
+/*
+ *  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.metadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedParameterizedType;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TreeMap;
+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.GroupSequence;
+import javax.validation.Valid;
+import javax.validation.constraintvalidation.ValidationTarget;
+import javax.validation.groups.ConvertGroup;
+
+import org.apache.bval.jsr.ApacheValidatorFactory;
+import org.apache.bval.jsr.ConstraintAnnotationAttributes;
+import org.apache.bval.jsr.descriptor.GroupConversion;
+import org.apache.bval.jsr.util.AnnotationsManager;
+import org.apache.bval.jsr.util.Methods;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.ObjectUtils;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+
+@Privilizing(@CallTo(Reflection.class))
+public class ReflectionBuilder {
+
+    private class ForBean implements MetadataBuilder.ForBean {
+        private final Metas<Class<?>> meta;
+
+        ForBean(Metas<Class<?>> meta) {
+            super();
+            this.meta = Validate.notNull(meta, "meta");
+        }
+
+        @Override
+        public MetadataBuilder.ForClass getClass(Metas<Class<?>> ignored) {
+            return new ReflectionBuilder.ForClass(meta);
+        }
+
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Metas<Class<?>> ignored) {
+            final Field[] declaredFields = Reflection.getDeclaredFields(meta.getHost());
+            if (declaredFields.length == 0) {
+                return Collections.emptyMap();
+            }
+            return Stream.of(declaredFields).collect(
+                Collectors.toMap(Field::getName, f -> new ReflectionBuilder.ForContainer<>(new Metas.ForField(f))));
+        }
+
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Metas<Class<?>> ignored) {
+            return Stream.of(Reflection.getDeclaredMethods(meta.getHost())).filter(Methods::isGetter)
+                .collect(ToUnmodifiable.map(Methods::propertyName,
+                    g -> new ReflectionBuilder.ForContainer<>(new Metas.ForMethod(g))));
+        }
+
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Metas<Class<?>> ignored) {
+            final Constructor<?>[] declaredConstructors = Reflection.getDeclaredConstructors(meta.getHost());
+            if (declaredConstructors.length == 0) {
+                return Collections.emptyMap();
+            }
+            return Stream.of(declaredConstructors).collect(
+                Collectors.toMap(Signature::of, c -> new ReflectionBuilder.ForExecutable<>(new Metas.ForConstructor(c),
+                    validatorFactory.getParameterNameProvider()::getParameterNames)));
+        }
+
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Metas<Class<?>> ignored) {
+            final Method[] declaredMethods = Reflection.getDeclaredMethods(meta.getHost());
+            if (declaredMethods.length == 0) {
+                return Collections.emptyMap();
+            }
+            return Stream.of(declaredMethods).filter(((Predicate<Method>) Methods::isGetter).negate()).collect(
+                Collectors.toMap(Signature::of, m -> new ReflectionBuilder.ForExecutable<>(new Metas.ForMethod(m),
+                    validatorFactory.getParameterNameProvider()::getParameterNames)));
+        }
+    }
+
+    private abstract class ForElement<E extends AnnotatedElement> implements MetadataBuilder.ForElement<E> {
+        final Metas<E> meta;
+
+        ForElement(Metas<E> meta) {
+            super();
+            this.meta = Validate.notNull(meta, "meta");
+        }
+
+        @Override
+        public Annotation[] getDeclaredConstraints(Metas<E> ignored) {
+            return AnnotationsManager.getDeclaredConstraints(meta);
+        }
+    }
+
+    private class ForClass extends ForElement<Class<?>> implements MetadataBuilder.ForClass {
+
+        ForClass(Metas<Class<?>> meta) {
+            super(meta);
+        }
+
+        @Override
+        public List<Class<?>> getGroupSequence(Metas<Class<?>> ignored) {
+            final GroupSequence groupSequence = meta.getHost().getAnnotation(GroupSequence.class);
+            return groupSequence == null ? null : Collections.unmodifiableList(Arrays.asList(groupSequence.value()));
+        }
+    }
+
+    private class ForContainer<E extends AnnotatedElement> extends ReflectionBuilder.ForElement<E>
+        implements MetadataBuilder.ForContainer<E> {
+
+        ForContainer(Metas<E> meta) {
+            super(meta);
+        }
+
+        @Override
+        public Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+            Metas<E> ignored) {
+            final AnnotatedType annotatedType = meta.getAnnotatedType();
+            if (annotatedType instanceof AnnotatedParameterizedType) {
+
+                final AnnotatedParameterizedType container = (AnnotatedParameterizedType) annotatedType;
+
+                final Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> result = new TreeMap<>();
+
+                final AnnotatedType[] typeArgs = container.getAnnotatedActualTypeArguments();
+                for (int i = 0; i < typeArgs.length; i++) {
+                    ContainerElementKey key = new ContainerElementKey(container, i);
+                    result.put(key, new ReflectionBuilder.ForContainer<>(new Metas.ForContainerElement(meta, key)));
+                }
+                return result;
+            }
+            return Collections.emptyMap();
+        }
+
+        @Override
+        public boolean isCascade(Metas<E> ignored) {
+            return meta.getHost().isAnnotationPresent(Valid.class);
+        }
+
+        @Override
+        public Set<GroupConversion> getGroupConversions(Metas<E> ignored) {
+            return Stream.of(meta.getHost().getDeclaredAnnotationsByType(ConvertGroup.class))
+                .map(cg -> GroupConversion.from(cg.from()).to(cg.to())).collect(ToUnmodifiable.set());
+        }
+    }
+
+    private class ForExecutable<E extends Executable> implements MetadataBuilder.ForExecutable<E> {
+
+        final Metas<E> meta;
+        final Function<E, List<String>> getParameterNames;
+
+        ForExecutable(Metas<E> meta, Function<E, List<String>> getParameterNames) {
+            super();
+            this.meta = Validate.notNull(meta, "meta");
+            this.getParameterNames = Validate.notNull(getParameterNames, "getParameterNames");
+        }
+
+        @Override
+        public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Metas<E> ignored) {
+            final Parameter[] parameters = meta.getHost().getParameters();
+            if (parameters.length == 0) {
+                return Collections.emptyList();
+            }
+            final List<String> parameterNames = getParameterNames.apply(meta.getHost());
+
+            return IntStream.range(0, parameters.length).mapToObj(
+                n -> new ReflectionBuilder.ForContainer<>(new Metas.ForParameter(parameters[n], parameterNames.get(n))))
+                .collect(ToUnmodifiable.list());
+        }
+
+        @Override
+        public ForContainer<E> getReturnValue(Metas<E> ignored) {
+            return new ReflectionBuilder.ForContainer<E>(meta) {
+
+                @Override
+                public Annotation[] getDeclaredConstraints(Metas<E> meta) {
+                    return getConstraints(meta, ValidationTarget.ANNOTATED_ELEMENT);
+                }
+            };
+        }
+
+        @Override
+        public MetadataBuilder.ForElement<E> getCrossParameter(Metas<E> ignored) {
+            return new ReflectionBuilder.ForElement<E>(meta) {
+                @Override
+                public Annotation[] getDeclaredConstraints(Metas<E> meta) {
+                    return getConstraints(meta, ValidationTarget.PARAMETERS);
+                }
+            };
+        }
+
+        private Annotation[] getConstraints(Metas<E> ignored, ValidationTarget validationTarget) {
+            return Optional.of(getConstraintsByTarget(meta)).map(m -> m.get(validationTarget))
+                .map(l -> l.toArray(new Annotation[l.size()])).orElse(ObjectUtils.EMPTY_ANNOTATION_ARRAY);
+        }
+
+        private Map<ValidationTarget, List<Annotation>> getConstraintsByTarget(Metas<E> ignored) {
+            final Annotation[] declaredConstraints = AnnotationsManager.getDeclaredConstraints(meta);
+            if (ObjectUtils.isEmpty(declaredConstraints)) {
+                return Collections.emptyMap();
+            }
+            final Map<ValidationTarget, List<Annotation>> result = new EnumMap<>(ValidationTarget.class);
+
+            for (Annotation constraint : declaredConstraints) {
+                final Class<? extends Annotation> constraintType = constraint.annotationType();
+                final Optional<ValidationTarget> explicitTarget =
+                    Optional.of(ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO.analyze(constraintType))
+                        .filter(ConstraintAnnotationAttributes.Worker::isValid).map(w -> w.read(constraint));
+
+                final ValidationTarget target = explicitTarget.orElseGet(() -> {
+                    final Set<ValidationTarget> supportedTargets =
+                        validatorFactory.getAnnotationsManager().supportedTargets(constraintType);
+
+                    Validate.validState(supportedTargets.size() == 1,
+                        "Found %d possible %s types for constraint type %s and no explicit assignment via #%s()",
+                        supportedTargets.size(), ValidationTarget.class.getSimpleName(), constraintType.getName(),
+                        ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO.getAttributeName());
+
+                    return supportedTargets.iterator().next();
+                });
+                result.computeIfAbsent(target, k -> new ArrayList<>()).add(constraint);
+            }
+            return result;
+        }
+    }
+
+    private final ApacheValidatorFactory validatorFactory;
+
+    public ReflectionBuilder(ApacheValidatorFactory validatorFactory) {
+        super();
+        this.validatorFactory = Validate.notNull(validatorFactory, "validatorFactory");
+    }
+
+    public <T> MetadataBuilder.ForBean forBean(Class<?> beanClass) {
+        return new ReflectionBuilder.ForBean(new Metas.ForClass(beanClass));
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/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
new file mode 100644
index 0000000..8def7ae
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Signature.java
@@ -0,0 +1,75 @@
+/*
+ *  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.metadata;
+
+import java.lang.reflect.Executable;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.LazyInt;
+import org.apache.bval.util.StringUtils;
+import org.apache.bval.util.Validate;
+
+public final class Signature {
+    public static Signature of(Executable x) {
+        return new Signature(x.getName(), x.getParameterTypes());
+    }
+
+    private final String name;
+    private final Class<?>[] parameterTypes;
+    private final LazyInt hashCode;
+    private final Lazy<String> toString;
+
+    public Signature(String name, Class<?>... parameterTypes) {
+        super();
+        this.name = Validate.notNull(name, "name");
+        Validate.isTrue(StringUtils.isNotBlank(name), "name is blank");
+        this.parameterTypes = Validate.notNull(parameterTypes, "parameterTypes").clone();
+        hashCode = new LazyInt(() -> Arrays.deepHashCode(new Object[] { this.name, this.parameterTypes }));
+        toString = new Lazy<>(() -> String.format("%s: %s(%s)", getClass().getSimpleName(), this.name,
+            Stream.of(this.parameterTypes).map(Class::getName).collect(Collectors.joining(", "))));
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Class<?>[] getParameterTypes() {
+        return parameterTypes.clone();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return obj == this || Optional.ofNullable(obj).filter(Signature.class::isInstance).map(Signature.class::cast)
+            .filter(sig -> Objects.equals(name, sig.name) && Objects.deepEquals(parameterTypes, sig.parameterTypes))
+            .isPresent();
+    }
+
+    @Override
+    public int hashCode() {
+        return hashCode.getAsInt();
+    }
+
+    @Override
+    public String toString() {
+        return toString.get();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ValidatorMapping.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ValidatorMapping.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ValidatorMapping.java
new file mode 100644
index 0000000..bd2ce7f
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ValidatorMapping.java
@@ -0,0 +1,121 @@
+/*
+ *  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.metadata;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import javax.validation.ConstraintValidator;
+
+import org.apache.bval.util.Validate;
+
+public class ValidatorMapping<A extends Annotation> implements HasAnnotationBehavior {
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private static final ValidatorMapping EMPTY = new ValidatorMapping("empty", Collections.emptyList());
+
+    @SuppressWarnings("unchecked")
+    public static <A extends Annotation> ValidatorMapping<A> empty() {
+        return EMPTY;
+    }
+
+    public static <A extends Annotation> ValidatorMapping<A> merge(
+        List<? extends ValidatorMapping<A>> validatorMappings,
+        AnnotationBehaviorMergeStrategy annotationBehaviorMergeStrategy) {
+
+        final AnnotationBehavior behavior = annotationBehaviorMergeStrategy.apply(validatorMappings);
+
+        final List<? extends ValidatorMapping<A>> nonEmpty =
+            validatorMappings.stream().filter(m -> !m.isEmpty()).collect(Collectors.toList());
+
+        if (nonEmpty.size() <= 1) {
+            // avoid creating the composite instance if behavior matches:
+            final ValidatorMapping<A> simpleResult = nonEmpty.isEmpty() ? empty() : nonEmpty.get(0);
+
+            if (simpleResult.hasBehavior(behavior)) {
+                return simpleResult;
+            }
+        }
+        final String source =
+            nonEmpty.stream().map(ValidatorMapping::getSource).collect(Collectors.joining(";", "[", "]"));
+
+        return new ValidatorMapping<>(source, nonEmpty.stream().map(ValidatorMapping::getValidatorTypes)
+            .flatMap(Collection::stream).distinct().collect(Collectors.toList()), behavior);
+    }
+
+    private final String source;
+    private final List<Class<? extends ConstraintValidator<A, ?>>> validatorTypes;
+    private final AnnotationBehavior annotationBehavior;
+
+    public ValidatorMapping(String source, List<Class<? extends ConstraintValidator<A, ?>>> validatorTypes) {
+        this(source, validatorTypes, AnnotationBehavior.ABSTAIN);
+    }
+
+    public ValidatorMapping(String source, List<Class<? extends ConstraintValidator<A, ?>>> validatorTypes,
+        AnnotationBehavior annotationBehavior) {
+        this.source = Objects.toString(source, "unspecified");
+        this.validatorTypes = Collections.unmodifiableList(Validate.notNull(validatorTypes, "validatorTypes"));
+        this.annotationBehavior = Validate.notNull(annotationBehavior, "annotationBehavior");
+    }
+
+    public List<Class<? extends ConstraintValidator<A, ?>>> getValidatorTypes() {
+        return validatorTypes;
+    }
+
+    public AnnotationBehavior getAnnotationBehavior() {
+        return annotationBehavior;
+    }
+
+    public boolean isEmpty() {
+        return validatorTypes.isEmpty();
+    }
+
+    public String getSource() {
+        return source;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!getClass().isInstance(obj)) {
+            return false;
+        }
+        final ValidatorMapping<?> other = (ValidatorMapping<?>) obj;
+        return getSource().equals(other.getSource()) && getAnnotationBehavior() == other.getAnnotationBehavior()
+            && getValidatorTypes().equals(other.getValidatorTypes());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getSource(), getAnnotationBehavior(), getValidatorTypes());
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s[source: %s; annotationBehavior: %s; validatorTypes: %s]",
+            ValidatorMapping.class.getSimpleName(), getSource(), getAnnotationBehavior(), getValidatorTypes());
+    }
+
+    public boolean hasBehavior(AnnotationBehavior annotationBehavior) {
+        return getAnnotationBehavior() == annotationBehavior;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ValidatorMappingProvider.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ValidatorMappingProvider.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ValidatorMappingProvider.java
new file mode 100644
index 0000000..8a8cd3f
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ValidatorMappingProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr.metadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Optional;
+
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.ConstraintValidator;
+
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.reflection.TypeUtils;
+
+public abstract class ValidatorMappingProvider {
+
+    public final <A extends Annotation> ValidatorMapping<A> getValidatorMapping(Class<A> constraintType) {
+        final Optional<ValidatorMapping<A>> result =
+            Optional.ofNullable(this.<A> doGetValidatorMapping(constraintType));
+        if (result.isPresent()) {
+            for (Class<? extends ConstraintValidator<A, ?>> t : result.get().getValidatorTypes()) {
+                final Type constraintParameter = TypeUtils.getTypeArguments(t, ConstraintValidator.class)
+                    .get(ConstraintValidator.class.getTypeParameters()[0]);
+
+                Exceptions.raiseUnless(constraintType.equals(constraintParameter), ConstraintDefinitionException::new,
+                    "%s %s expected first type parameter of %s, %s; source %s", ConstraintValidator.class, t,
+                    constraintType, constraintParameter, result.get().getSource());
+            }
+            return result.get();
+        }
+        return null;
+    }
+
+    protected abstract <A extends Annotation> ValidatorMapping<A> doGetValidatorMapping(Class<A> constraintType);
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/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
new file mode 100644
index 0000000..37082d4
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java
@@ -0,0 +1,694 @@
+/*
+ *  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.metadata;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedParameterizedType;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+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;
+
+import javax.validation.ConstraintDeclarationException;
+import javax.validation.ConstraintTarget;
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+import javax.xml.bind.JAXBElement;
+
+import org.apache.bval.jsr.ConstraintAnnotationAttributes;
+import org.apache.bval.jsr.descriptor.GroupConversion;
+import org.apache.bval.jsr.util.AnnotationsManager;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.jsr.xml.AnnotationProxyBuilder;
+import org.apache.bval.jsr.xml.AnnotationType;
+import org.apache.bval.jsr.xml.BeanType;
+import org.apache.bval.jsr.xml.ClassType;
+import org.apache.bval.jsr.xml.ConstraintMappingsType;
+import org.apache.bval.jsr.xml.ConstraintType;
+import org.apache.bval.jsr.xml.ConstructorType;
+import org.apache.bval.jsr.xml.ContainerElementTypeType;
+import org.apache.bval.jsr.xml.CrossParameterType;
+import org.apache.bval.jsr.xml.ElementType;
+import org.apache.bval.jsr.xml.FieldType;
+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.MethodType;
+import org.apache.bval.jsr.xml.ParameterType;
+import org.apache.bval.jsr.xml.PayloadType;
+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.Validate;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+
+@Privilizing(@CallTo(Reflection.class))
+public class XmlBuilder {
+    //@formatter:off
+    public enum Version {
+        v10("1.0"), v11("1.1"), v20("2.0");
+
+        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 implements MetadataBuilder.ForBean {
+
+        private final BeanType descriptor;
+
+        ForBean(BeanType descriptor) {
+            super();
+            this.descriptor = Validate.notNull(descriptor, "descriptor");
+        }
+
+        Class<?> getBeanClass() {
+            return resolveClass(descriptor.getClazz());
+        }
+
+        @Override
+        public MetadataBuilder.ForClass getClass(Metas<Class<?>> meta) {
+            return new XmlBuilder.ForClass(descriptor.getClassType());
+        }
+
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Metas<Class<?>> meta) {
+            return descriptor.getField().stream()
+                .collect(ToUnmodifiable.map(FieldType::getName, XmlBuilder.ForField::new));
+        }
+
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Metas<Class<?>> meta) {
+            return descriptor.getGetter().stream()
+                .collect(ToUnmodifiable.map(GetterType::getName, XmlBuilder.ForGetter::new));
+        }
+
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Metas<Class<?>> meta) {
+            if (!atLeast(Version.v11)) {
+                return Collections.emptyMap();
+            }
+            final Function<ConstructorType, Class<?>[]> params = ct -> ct.getParameter().stream()
+                .map(ParameterType::getType).map(XmlBuilder.this::resolveClass).toArray(Class[]::new);
+
+            final Function<ConstructorType, Signature> signature =
+                ct -> new Signature(meta.getHost().getSimpleName(), params.apply(ct));
+
+            return descriptor.getConstructor().stream()
+                .collect(Collectors.toMap(signature, XmlBuilder.ForConstructor::new));
+        }
+
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Metas<Class<?>> meta) {
+            if (!atLeast(Version.v11)) {
+                return Collections.emptyMap();
+            }
+            final Function<MethodType, Class<?>[]> params = mt -> mt.getParameter().stream().map(ParameterType::getType)
+                .map(XmlBuilder.this::resolveClass).toArray(Class[]::new);
+
+            final Function<MethodType, Signature> signature = mt -> new Signature(mt.getName(), params.apply(mt));
+
+            return descriptor.getMethod().stream().collect(Collectors.toMap(signature, XmlBuilder.ForMethod::new));
+        }
+
+        @Override
+        public final AnnotationBehavior getAnnotationBehavior() {
+            return descriptor.getIgnoreAnnotations() ? AnnotationBehavior.EXCLUDE : AnnotationBehavior.INCLUDE;
+        }
+    }
+
+    private class NonRootLevel<SELF extends NonRootLevel<SELF, D>, D> implements HasAnnotationBehavior {
+        protected final D descriptor;
+        private Lazy<Boolean> getIgnoreAnnotations;
+
+        public NonRootLevel(D descriptor) {
+            super();
+            this.descriptor = Validate.notNull(descriptor, "descriptor");
+        }
+
+        @Override
+        public final AnnotationBehavior getAnnotationBehavior() {
+            return Optional.ofNullable(getIgnoreAnnotations).map(Lazy::get)
+                .map(b -> b.booleanValue() ? AnnotationBehavior.EXCLUDE : AnnotationBehavior.INCLUDE)
+                .orElse(AnnotationBehavior.ABSTAIN);
+        }
+
+        @SuppressWarnings("unchecked")
+        final SELF withGetIgnoreAnnotations(Function<D, Boolean> getIgnoreAnnotations) {
+            Validate.notNull(getIgnoreAnnotations);
+            this.getIgnoreAnnotations = new Lazy<>(() -> getIgnoreAnnotations.apply(descriptor));
+            return (SELF) this;
+        }
+    }
+
+    private class ForElement<SELF extends XmlBuilder.ForElement<SELF, E, D>, E extends AnnotatedElement, D>
+        extends NonRootLevel<SELF, D> implements MetadataBuilder.ForElement<E> {
+
+        private Lazy<Annotation[]> getDeclaredConstraints;
+
+        ForElement(D descriptor) {
+            super(descriptor);
+        }
+
+        @Override
+        public final Annotation[] getDeclaredConstraints(Metas<E> meta) {
+            return lazy(getDeclaredConstraints, "getDeclaredConstraints");
+        }
+
+        final SELF withGetConstraintTypes(Function<D, List<ConstraintType>> getConstraintTypes) {
+            return withGetDeclaredConstraints(getConstraintTypes
+                .andThen(l -> l.stream().map(XmlBuilder.this::createConstraint).toArray(Annotation[]::new)));
+        }
+
+        @SuppressWarnings("unchecked")
+        final SELF withGetDeclaredConstraints(Function<D, Annotation[]> getDeclaredConstraints) {
+            this.getDeclaredConstraints = new Lazy<>(() -> getDeclaredConstraints.apply(descriptor));
+            return (SELF) this;
+        }
+    }
+
+    private class ForClass extends ForElement<ForClass, Class<?>, ClassType> implements MetadataBuilder.ForClass {
+
+        ForClass(ClassType descriptor) {
+            super(descriptor);
+            this.withGetConstraintTypes(ct -> ct.getConstraint());
+        }
+
+        @Override
+        public List<Class<?>> getGroupSequence(Metas<Class<?>> meta) {
+            final GroupSequenceType groupSequence = descriptor.getGroupSequence();
+            return groupSequence == null ? null
+                : groupSequence.getValue().stream().map(XmlBuilder.this::resolveClass).collect(ToUnmodifiable.list());
+        }
+    }
+
+    private class ForContainer<SELF extends XmlBuilder.ForContainer<SELF, E, D>, E extends AnnotatedElement, D>
+        extends XmlBuilder.ForElement<SELF, E, D> implements MetadataBuilder.ForContainer<E> {
+
+        private Lazy<Boolean> isCascade;
+        private Lazy<Set<GroupConversion>> getGroupConversions;
+        private Lazy<List<ContainerElementTypeType>> getContainerElementTypes;
+
+        ForContainer(D descriptor) {
+            super(descriptor);
+        }
+
+        @Override
+        public boolean isCascade(Metas<E> meta) {
+            return lazy(isCascade, "isCascade").booleanValue();
+        }
+
+        @Override
+        public Set<GroupConversion> getGroupConversions(Metas<E> meta) {
+            return lazy(getGroupConversions, "getGroupConversions");
+        }
+
+        @Override
+        public Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+            Metas<E> meta) {
+            if (!atLeast(Version.v20)) {
+                return Collections.emptyMap();
+            }
+            final List<ContainerElementTypeType> elements = lazy(getContainerElementTypes, "getContainerElementTypes");
+            final AnnotatedType annotatedType = meta.getAnnotatedType();
+            final E host = meta.getHost();
+
+            if (annotatedType instanceof AnnotatedParameterizedType) {
+                final AnnotatedType[] actualTypeArguments =
+                    ((AnnotatedParameterizedType) annotatedType).getAnnotatedActualTypeArguments();
+
+                return elements.stream().collect(ToUnmodifiable.map(cet -> {
+                    Integer typeArgumentIndex = cet.getTypeArgumentIndex();
+                    if (typeArgumentIndex == null) {
+                        Exceptions.raiseIf(actualTypeArguments.length > 1, ValidationException::new,
+                            "Missing required type argument index for %s", host);
+                        typeArgumentIndex = Integer.valueOf(0);
+                    }
+                    return new ContainerElementKey((AnnotatedParameterizedType) annotatedType, typeArgumentIndex);
+                }, XmlBuilder.ForContainerElementType::new));
+            }
+            Exceptions.raiseUnless(elements.isEmpty(), ValidationException::new,
+                "Illegally specified %d container element type(s) for %s", elements.size(), host);
+
+            return Collections.emptyMap();
+        }
+
+        @SuppressWarnings("unchecked")
+        SELF withGetValid(Function<D, String> getValid) {
+            Validate.notNull(getValid);
+            this.isCascade = new Lazy<>(() -> getValid.apply(descriptor) != null);
+            return (SELF) this;
+        }
+
+        @SuppressWarnings("unchecked")
+        SELF withGetGroupConversions(Function<D, List<GroupConversionType>> getGroupConversions) {
+            Validate.notNull(getGroupConversions);
+
+            this.getGroupConversions = new Lazy<>(() -> {
+                return getGroupConversions.apply(descriptor).stream().map(gc -> {
+                    final Class<?> source = resolveClass(gc.getFrom());
+                    final Class<?> target = resolveClass(gc.getTo());
+                    return GroupConversion.from(source).to(target);
+                }).collect(ToUnmodifiable.set());
+            });
+            return (SELF) this;
+        }
+
+        @SuppressWarnings("unchecked")
+        SELF withGetContainerElementTypes(Function<D, List<ContainerElementTypeType>> getContainerElementTypes) {
+            Validate.notNull(getContainerElementTypes);
+            this.getContainerElementTypes = new Lazy<>(() -> getContainerElementTypes.apply(descriptor));
+            return (SELF) this;
+        }
+    }
+
+    private class ForContainerElementType
+        extends ForContainer<ForContainerElementType, AnnotatedType, ContainerElementTypeType> {
+
+        ForContainerElementType(ContainerElementTypeType descriptor) {
+            super(descriptor);
+            this.withGetConstraintTypes(ContainerElementTypeType::getConstraint)
+                .withGetValid(ContainerElementTypeType::getValid)
+                .withGetGroupConversions(ContainerElementTypeType::getConvertGroup)
+                .withGetContainerElementTypes(ContainerElementTypeType::getContainerElementType);
+        }
+    }
+
+    private class ForField extends XmlBuilder.ForContainer<ForField, Field, FieldType> {
+
+        ForField(FieldType descriptor) {
+            super(descriptor);
+            this.withGetIgnoreAnnotations(FieldType::getIgnoreAnnotations)
+                .withGetConstraintTypes(FieldType::getConstraint).withGetValid(FieldType::getValid)
+                .withGetGroupConversions(FieldType::getConvertGroup)
+                .withGetContainerElementTypes(FieldType::getContainerElementType);
+        }
+    }
+
+    private class ForGetter extends XmlBuilder.ForContainer<ForGetter, Method, GetterType> {
+
+        ForGetter(GetterType descriptor) {
+            super(descriptor);
+            this.withGetIgnoreAnnotations(GetterType::getIgnoreAnnotations)
+                .withGetConstraintTypes(GetterType::getConstraint).withGetValid(GetterType::getValid)
+                .withGetGroupConversions(GetterType::getConvertGroup)
+                .withGetContainerElementTypes(GetterType::getContainerElementType);
+        }
+    }
+
+    private abstract class ForExecutable<SELF extends ForExecutable<SELF, E, D>, E extends Executable, D>
+        extends NonRootLevel<SELF, D> implements MetadataBuilder.ForExecutable<E> {
+
+        Lazy<ReturnValueType> getReturnValue;
+        Lazy<CrossParameterType> getCrossParameter;
+        Lazy<List<ParameterType>> getParameters;
+
+        ForExecutable(D descriptor) {
+            super(descriptor);
+        }
+
+        @Override
+        public MetadataBuilder.ForElement<E> getCrossParameter(Metas<E> meta) {
+            return new XmlBuilder.ForCrossParameter<>(lazy(getCrossParameter, "getCrossParameter"));
+        }
+
+        @Override
+        public MetadataBuilder.ForContainer<E> getReturnValue(Metas<E> meta) {
+            return new XmlBuilder.ForReturnValue<>(lazy(getReturnValue, "getReturnValue"));
+        }
+
+        @Override
+        public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Metas<E> meta) {
+            return lazy(getParameters, "getParameters").stream().map(XmlBuilder.ForParameter::new)
+                .collect(Collectors.toList());
+        }
+
+        @SuppressWarnings("unchecked")
+        SELF withGetReturnValue(Function<D, ReturnValueType> getReturnValue) {
+            Validate.notNull(getReturnValue);
+            this.getReturnValue = new Lazy<>(() -> getReturnValue.apply(descriptor));
+            return (SELF) this;
+        }
+
+        @SuppressWarnings("unchecked")
+        SELF withGetCrossParameter(Function<D, CrossParameterType> getCrossParameter) {
+            Validate.notNull(getCrossParameter);
+            this.getCrossParameter = new Lazy<>(() -> getCrossParameter.apply(descriptor));
+            return (SELF) this;
+        }
+
+        @SuppressWarnings("unchecked")
+        SELF withGetParameters(Function<D, List<ParameterType>> getParameters) {
+            Validate.notNull(getParameters);
+            this.getParameters = new Lazy<>(() -> getParameters.apply(descriptor));
+            return (SELF) this;
+        }
+    }
+
+    private class ForConstructor extends ForExecutable<ForConstructor, Constructor<?>, ConstructorType> {
+
+        ForConstructor(ConstructorType descriptor) {
+            super(descriptor);
+            this.withGetIgnoreAnnotations(ConstructorType::getIgnoreAnnotations)
+                .withGetReturnValue(ConstructorType::getReturnValue)
+                .withGetCrossParameter(ConstructorType::getCrossParameter)
+                .withGetParameters(ConstructorType::getParameter);
+        }
+    }
+
+    private class ForMethod extends ForExecutable<ForMethod, Method, MethodType> {
+
+        ForMethod(MethodType descriptor) {
+            super(descriptor);
+            this.withGetIgnoreAnnotations(MethodType::getIgnoreAnnotations)
+                .withGetReturnValue(MethodType::getReturnValue).withGetCrossParameter(MethodType::getCrossParameter)
+                .withGetParameters(MethodType::getParameter);
+        }
+    }
+
+    private class ForParameter extends ForContainer<ForParameter, Parameter, ParameterType> {
+
+        ForParameter(ParameterType descriptor) {
+            super(descriptor);
+            this.withGetIgnoreAnnotations(ParameterType::getIgnoreAnnotations)
+                .withGetConstraintTypes(ParameterType::getConstraint).withGetValid(ParameterType::getValid)
+                .withGetGroupConversions(ParameterType::getConvertGroup)
+                .withGetContainerElementTypes(ParameterType::getContainerElementType);
+        }
+    }
+
+    private class ForCrossParameter<E extends Executable>
+        extends ForElement<ForCrossParameter<E>, E, CrossParameterType> {
+
+        ForCrossParameter(CrossParameterType descriptor) {
+            super(descriptor);
+            this.withGetIgnoreAnnotations(CrossParameterType::getIgnoreAnnotations)
+                .withGetDeclaredConstraints(d -> d.getConstraint().stream()
+                    .map(ct -> createConstraint(ct, ConstraintTarget.PARAMETERS)).toArray(Annotation[]::new));
+        }
+    }
+
+    private class ForReturnValue<E extends Executable> extends ForContainer<ForReturnValue<E>, E, ReturnValueType> {
+
+        ForReturnValue(ReturnValueType descriptor) {
+            super(descriptor);
+            this.withGetDeclaredConstraints(d -> d.getConstraint().stream()
+                .map(ct -> createConstraint(ct, ConstraintTarget.RETURN_VALUE)).toArray(Annotation[]::new))
+                .withGetContainerElementTypes(d -> d.getContainerElementType());
+        }
+    }
+
+    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) {
+        Validate.validState(lazy != null, "%s not set", name);
+        return lazy.get();
+    }
+
+    private final ConstraintMappingsType constraintMappings;
+    private final BigDecimal 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;
+    }
+
+    public Map<Class<?>, MetadataBuilder.ForBean> forBeans() {
+        return constraintMappings.getBean().stream().map(XmlBuilder.ForBean::new)
+            .collect(ToUnmodifiable.map(XmlBuilder.ForBean::getBeanClass, Function.identity()));
+    }
+
+    public String getDefaultPackage() {
+        return constraintMappings.getDefaultPackage();
+    }
+
+    boolean atLeast(Version v) {
+        return version.compareTo(v.number) >= 0;
+    }
+
+    <T> Class<T> resolveClass(String className) {
+        return loadClass(toQualifiedClassName(className));
+    }
+
+    private String toQualifiedClassName(String className) {
+        if (isQualifiedClass(className)) {
+            return className;
+        }
+        if (className.startsWith("[L") && className.endsWith(";")) {
+            return "[L" + getDefaultPackage() + "." + className.substring(2);
+        }
+        return getDefaultPackage() + "." + className;
+    }
+
+    private boolean isQualifiedClass(String clazz) {
+        return clazz.indexOf('.') >= 0;
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> Class<T> loadClass(final String fqn) {
+        ClassLoader loader = Reflection.getClassLoader(XmlBuilder.class);
+        if (loader == null) {
+            loader = getClass().getClassLoader();
+        }
+        try {
+            return (Class<T>) Class.forName(fqn, true, loader);
+        } catch (ClassNotFoundException ex) {
+            throw Exceptions.create(ValidationException::new, ex, "Unable to load class: %d", fqn);
+        }
+    }
+
+    private Class<?>[] loadClasses(Supplier<Stream<String>> classNames) {
+        return streamClasses(classNames).toArray(Class[]::new);
+    }
+
+    private Stream<Class<?>> streamClasses(Supplier<Stream<String>> classNames) {
+        return classNames.get().map(this::loadClass);
+    }
+
+    private <A extends Annotation, T> A createConstraint(final ConstraintType constraint) {
+        return createConstraint(constraint, ConstraintTarget.IMPLICIT);
+    }
+
+    @SuppressWarnings("unchecked")
+    private <A extends Annotation, T> A createConstraint(final ConstraintType constraint, ConstraintTarget target) {
+
+        final Class<A> annotationClass = (Class<A>) loadClass(toQualifiedClassName(constraint.getAnnotation()));
+        final AnnotationProxyBuilder<A> annoBuilder = new AnnotationProxyBuilder<A>(annotationClass);
+
+        if (constraint.getMessage() != null) {
+            annoBuilder.setMessage(constraint.getMessage());
+        }
+        annoBuilder.setGroups(getGroups(constraint.getGroups()));
+        annoBuilder.setPayload(getPayload(constraint.getPayload()));
+
+        if (AnnotationsManager.declaresAttribute(annotationClass,
+            ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO.getAttributeName())) {
+            annoBuilder.setValidationAppliesTo(target);
+        }
+
+        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);
+        }
+        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);
+        Exceptions.raiseIf(m == null, ValidationException::new,
+            "Annotation of type %s does not contain a parameter %s.", annotationClass.getName(), name);
+        return m.getReturnType();
+    }
+
+    private Object getElementValue(ElementType elementType, Class<?> returnType) {
+        removeEmptyContentElements(elementType);
+
+        final List<Serializable> content = elementType.getContent();
+        final int sz = content.size();
+        if (returnType.isArray()) {
+            final Object result = Array.newInstance(returnType.getComponentType(), sz);
+            for (int i = 0; i < sz; i++) {
+                Array.set(result, i, getSingleValue(content.get(i), returnType.getComponentType()));
+            }
+            return result;
+        }
+        Exceptions.raiseIf(sz != 1, ValidationException::new,
+            "Attempt to specify an array where single value is expected.");
+
+        return getSingleValue(content.get(0), returnType);
+    }
+
+    private void removeEmptyContentElements(ElementType elementType) {
+        for (Iterator<Serializable> iter = elementType.getContent().iterator(); iter.hasNext();) {
+            final Serializable content = iter.next();
+            if (content instanceof String && ((String) content).matches("[\\n ].*")) {
+                iter.remove();
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private Object getSingleValue(Serializable serializable, Class<?> returnType) {
+        if (serializable instanceof String) {
+            return convertToResultType(returnType, (String) serializable);
+        }
+        if (serializable instanceof JAXBElement<?>) {
+            final JAXBElement<?> elem = (JAXBElement<?>) serializable;
+            if (String.class.equals(elem.getDeclaredType())) {
+                return convertToResultType(returnType, (String) elem.getValue());
+            }
+            if (AnnotationType.class.equals(elem.getDeclaredType())) {
+                AnnotationType annotationType = (AnnotationType) elem.getValue();
+                try {
+                    return createAnnotation(annotationType, (Class<? extends Annotation>) returnType);
+                } catch (ClassCastException e) {
+                    throw new ValidationException("Unexpected parameter value");
+                }
+            }
+        }
+        throw new ValidationException("Unexpected parameter value");
+    }
+
+    private Object convertToResultType(Class<?> returnType, String value) {
+        /**
+         * Class is represented by the fully qualified class name of the class. spec: Note that if the raw string is
+         * unqualified, default package is taken into account.
+         */
+        if (String.class.equals(returnType)) {
+            return value;
+        }
+        if (Class.class.equals(returnType)) {
+            return resolveClass(value);
+        }
+        if (returnType.isEnum()) {
+            try {
+                @SuppressWarnings({ "rawtypes", "unchecked" })
+                final Enum e = Enum.valueOf(returnType.asSubclass(Enum.class), value);
+                return e;
+            } catch (IllegalArgumentException e) {
+                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);
+        }
+        if (Character.class.equals(returnType) || char.class.equals(returnType)) {
+            Exceptions.raiseIf(value.length() > 1, ConstraintDeclarationException::new,
+                "a char must have a length of 1");
+            return value.charAt(0);
+        }
+        return Exceptions.raise(ValidationException::new, "Unknown annotation value type %s", returnType.getName());
+    }
+
+    private <A extends Annotation> Annotation createAnnotation(AnnotationType annotationType, Class<A> returnType) {
+        final AnnotationProxyBuilder<A> metaAnnotation = new AnnotationProxyBuilder<>(returnType);
+        for (ElementType elementType : annotationType.getElement()) {
+            final String name = elementType.getName();
+            metaAnnotation.setValue(name, getElementValue(elementType, getAnnotationParameterType(returnType, name)));
+        }
+        return metaAnnotation.createAnnotation();
+    }
+
+    private Class<?>[] getGroups(GroupsType groupsType) {
+        if (groupsType == null) {
+            return ObjectUtils.EMPTY_CLASS_ARRAY;
+        }
+        return loadClasses(groupsType.getValue()::stream);
+    }
+
+    @SuppressWarnings("unchecked")
+    private Class<? extends Payload>[] getPayload(PayloadType payloadType) {
+        if (payloadType == null) {
+            return (Class<? extends Payload>[]) ObjectUtils.EMPTY_CLASS_ARRAY;
+        }
+        return streamClasses(payloadType.getValue()::stream).peek(pc -> {
+            Exceptions.raiseUnless(Payload.class.isAssignableFrom(pc), ConstraintDeclarationException::new,
+                "Specified payload class %s does not implement %s", pc.getName(), Payload.class.getName());
+        }).<Class<? extends Payload>> map(pc -> pc.asSubclass(Payload.class)).toArray(Class[]::new);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlValidationMappingProvider.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlValidationMappingProvider.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlValidationMappingProvider.java
new file mode 100644
index 0000000..a47d1c6
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlValidationMappingProvider.java
@@ -0,0 +1,64 @@
+/*
+ *  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.metadata;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import javax.validation.ConstraintValidator;
+
+import org.apache.bval.jsr.xml.ValidatedByType;
+import org.apache.bval.util.Validate;
+
+public class XmlValidationMappingProvider extends ClassLoadingValidatorMappingProvider {
+    private static final Logger log = Logger.getLogger(XmlValidationMappingProvider.class.getName());
+
+    private final Map<Class<? extends Annotation>, ValidatedByType> config;
+    private final Function<String, String> classNameTransformer;
+
+    public XmlValidationMappingProvider(Map<Class<? extends Annotation>, ValidatedByType> validatorMappings,
+        Function<String, String> classNameTransformer) {
+        super();
+        this.config = Validate.notNull(validatorMappings, "validatorMappings");
+        this.classNameTransformer = Validate.notNull(classNameTransformer, "classNameTransformer");
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @Override
+    public <A extends Annotation> ValidatorMapping<A> doGetValidatorMapping(Class<A> constraintType) {
+        final ValidatedByType validatedByType = config.get(constraintType);
+        if (validatedByType == null) {
+            return null;
+        }
+        return new ValidatorMapping<>("XML descriptor",
+            load(validatedByType.getValue().stream().map(String::trim).map(classNameTransformer),
+                (Class<ConstraintValidator<A, ?>>) (Class) ConstraintValidator.class,
+                e -> log.log(Level.SEVERE, "exception loading XML-declared constraint validators", e))
+                    .collect(Collectors.toList()),
+            toAnnotationBehavior(validatedByType));
+    }
+
+    private AnnotationBehavior toAnnotationBehavior(ValidatedByType validatedByType) {
+        final Boolean includeExistingValidators = validatedByType.getIncludeExistingValidators();
+        return includeExistingValidators == null ? AnnotationBehavior.ABSTAIN
+            : includeExistingValidators.booleanValue() ? AnnotationBehavior.INCLUDE : AnnotationBehavior.EXCLUDE;
+    }
+}


[14/17] bval git commit: CDI updates

Posted by mb...@apache.org.
CDI updates


Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/b808381e
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/b808381e
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/b808381e

Branch: refs/heads/bv2
Commit: b808381e13932d4e6dd3d78413877e18f6df0d94
Parents: 05df7ee
Author: Matt Benson <mb...@apache.org>
Authored: Wed Feb 21 14:54:32 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Wed Feb 21 14:59:59 2018 -0600

----------------------------------------------------------------------
 .../java/org/apache/bval/cdi/AnyLiteral.java    |  3 +-
 .../org/apache/bval/cdi/BValAnnotatedType.java  | 33 ++++++--------------
 .../java/org/apache/bval/cdi/BValExtension.java | 18 ++---------
 .../org/apache/bval/cdi/BValInterceptor.java    | 28 +++++++++--------
 .../apache/bval/cdi/BValInterceptorBean.java    |  6 ++--
 .../org/apache/bval/cdi/DefaultLiteral.java     |  3 +-
 .../apache/bval/cdi/EmptyAnnotationLiteral.java | 24 +++++++-------
 .../java/org/apache/bval/cdi/ValidatorBean.java |  6 ++--
 .../apache/bval/cdi/ValidatorFactoryBean.java   |  6 ++--
 9 files changed, 50 insertions(+), 77 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/b808381e/bval-jsr/src/main/java/org/apache/bval/cdi/AnyLiteral.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/cdi/AnyLiteral.java b/bval-jsr/src/main/java/org/apache/bval/cdi/AnyLiteral.java
index 2b62c1e..1e14f1e 100644
--- a/bval-jsr/src/main/java/org/apache/bval/cdi/AnyLiteral.java
+++ b/bval-jsr/src/main/java/org/apache/bval/cdi/AnyLiteral.java
@@ -19,7 +19,6 @@
 package org.apache.bval.cdi;
 
 import javax.enterprise.inject.Any;
-import javax.enterprise.util.AnnotationLiteral;
 
 public class AnyLiteral extends EmptyAnnotationLiteral<Any> implements Any {
     private static final long serialVersionUID = 1L;
@@ -31,7 +30,7 @@ public class AnyLiteral extends EmptyAnnotationLiteral<Any> implements Any {
 
     @Override
     public String toString() {
-        return "@javax.enterprise.inject.Any()";
+        return String.format("@%s()", Any.class.getName());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/b808381e/bval-jsr/src/main/java/org/apache/bval/cdi/BValAnnotatedType.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/cdi/BValAnnotatedType.java b/bval-jsr/src/main/java/org/apache/bval/cdi/BValAnnotatedType.java
index a143a5b..2b08fe6 100644
--- a/bval-jsr/src/main/java/org/apache/bval/cdi/BValAnnotatedType.java
+++ b/bval-jsr/src/main/java/org/apache/bval/cdi/BValAnnotatedType.java
@@ -18,17 +18,16 @@
  */
 package org.apache.bval.cdi;
 
-import javax.enterprise.inject.spi.AnnotatedConstructor;
-import javax.enterprise.inject.spi.AnnotatedField;
-import javax.enterprise.inject.spi.AnnotatedMethod;
-import javax.enterprise.inject.spi.AnnotatedType;
-import javax.enterprise.util.AnnotationLiteral;
-
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.util.HashSet;
 import java.util.Set;
 
+import javax.enterprise.inject.spi.AnnotatedConstructor;
+import javax.enterprise.inject.spi.AnnotatedField;
+import javax.enterprise.inject.spi.AnnotatedMethod;
+import javax.enterprise.inject.spi.AnnotatedType;
+
 public class BValAnnotatedType<A> implements AnnotatedType<A> {
     private final AnnotatedType<A> delegate;
     private final Set<Annotation> annotations;
@@ -36,8 +35,7 @@ public class BValAnnotatedType<A> implements AnnotatedType<A> {
     public BValAnnotatedType(final AnnotatedType<A> annotatedType) {
         delegate = annotatedType;
 
-        annotations = new HashSet<Annotation>(annotatedType.getAnnotations().size());
-        annotations.addAll(annotatedType.getAnnotations());
+        annotations = new HashSet<>(annotatedType.getAnnotations());
         annotations.add(BValBindingLiteral.INSTANCE);
     }
 
@@ -73,14 +71,8 @@ public class BValAnnotatedType<A> implements AnnotatedType<A> {
 
     @Override
     public <T extends Annotation> T getAnnotation(final Class<T> annotationType) {
-        for (final Annotation ann : annotations) {
-            if (ann.annotationType().equals(annotationType)) {
-                @SuppressWarnings("unchecked")
-                final T result = (T) ann;
-                return result;
-            }
-        }
-        return null;
+        return annotations.stream().filter(ann -> ann.annotationType().equals(annotationType)).map(annotationType::cast)
+            .findFirst().orElse(null);
     }
 
     @Override
@@ -90,12 +82,7 @@ public class BValAnnotatedType<A> implements AnnotatedType<A> {
 
     @Override
     public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
-        for (final Annotation ann : annotations) {
-            if (ann.annotationType().equals(annotationType)) {
-                return true;
-            }
-        }
-        return false;
+        return annotations.stream().anyMatch(ann -> ann.annotationType().equals(annotationType));
     }
 
     public static class BValBindingLiteral extends EmptyAnnotationLiteral<BValBinding> implements BValBinding {
@@ -105,7 +92,7 @@ public class BValAnnotatedType<A> implements AnnotatedType<A> {
 
         @Override
         public String toString() {
-            return "@org.apache.bval.cdi.BValBinding()";
+            return String.format("@%s()", BValBinding.class.getName());
         }
 
     }

http://git-wip-us.apache.org/repos/asf/bval/blob/b808381e/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java b/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java
index f84074e..e10a9ba 100644
--- a/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java
+++ b/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java
@@ -59,13 +59,7 @@ import org.apache.bval.util.Validate;
 public class BValExtension implements Extension {
     private static final Logger LOGGER = Logger.getLogger(BValExtension.class.getName());
 
-    private static final AnnotatedTypeFilter DEFAULT_ANNOTATED_TYPE_FILTER = new AnnotatedTypeFilter() {
-
-        @Override
-        public boolean accept(AnnotatedType<?> annotatedType) {
-            return !annotatedType.getJavaClass().getName().startsWith("org.apache.bval.");
-        }
-    };
+    private static final AnnotatedTypeFilter DEFAULT_ANNOTATED_TYPE_FILTER = annotatedType -> !annotatedType.getJavaClass().getName().startsWith("org.apache.bval.");
 
     private static AnnotatedTypeFilter annotatedTypeFilter = DEFAULT_ANNOTATED_TYPE_FILTER;
 
@@ -183,8 +177,7 @@ public class BValExtension implements Extension {
                                 && !classConstraints.getConstrainedMethods(MethodType.NON_GETTER).isEmpty()
                             || validGetterMethods
                                 && !classConstraints.getConstrainedMethods(MethodType.GETTER).isEmpty())) {
-                        final BValAnnotatedType<A> bValAnnotatedType = new BValAnnotatedType<A>(annotatedType);
-                        pat.setAnnotatedType(bValAnnotatedType);
+                        pat.setAnnotatedType(new BValAnnotatedType<A>(annotatedType));
                     }
                 } catch (final NoClassDefFoundError ncdfe) {
                     // skip
@@ -199,12 +192,7 @@ public class BValExtension implements Extension {
 
     private static <A> boolean hasValidationAnnotation(
         final Collection<? extends AnnotatedCallable<? super A>> methods) {
-        for (final AnnotatedCallable<? super A> m : methods) {
-            if (m.isAnnotationPresent(ValidateOnExecution.class)) {
-                return true;
-            }
-        }
-        return false;
+        return methods.stream().anyMatch(m -> m.isAnnotationPresent(ValidateOnExecution.class));
     }
 
     public <A> void processBean(final @Observes ProcessBean<A> processBeanEvent) {

http://git-wip-us.apache.org/repos/asf/bval/blob/b808381e/bval-jsr/src/main/java/org/apache/bval/cdi/BValInterceptor.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/cdi/BValInterceptor.java b/bval-jsr/src/main/java/org/apache/bval/cdi/BValInterceptor.java
index 3a7f000..7bf7709 100644
--- a/bval-jsr/src/main/java/org/apache/bval/cdi/BValInterceptor.java
+++ b/bval-jsr/src/main/java/org/apache/bval/cdi/BValInterceptor.java
@@ -18,8 +18,7 @@
  */
 package org.apache.bval.cdi;
 
-import org.apache.bval.jsr.util.ClassHelper;
-import org.apache.bval.jsr.util.Proxies;
+import static java.util.Arrays.asList;
 
 import java.io.Serializable;
 import java.lang.reflect.Constructor;
@@ -33,6 +32,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+
 import javax.annotation.Priority;
 import javax.enterprise.inject.spi.AnnotatedConstructor;
 import javax.enterprise.inject.spi.AnnotatedMethod;
@@ -53,18 +53,20 @@ import javax.validation.executable.ValidateOnExecution;
 import javax.validation.metadata.ConstructorDescriptor;
 import javax.validation.metadata.MethodDescriptor;
 
-import static java.util.Arrays.asList;
+import org.apache.bval.jsr.util.ClassHelper;
+import org.apache.bval.jsr.util.Proxies;
 
 /**
  * Interceptor class for the {@link BValBinding} {@link InterceptorBinding}.
  */
+@SuppressWarnings("serial")
 @Interceptor
 @BValBinding
 @Priority(4800)
 // TODO: maybe add it through ASM to be compliant with CDI 1.0 containers using simply this class as a template to
 // generate another one for CDI 1.1 impl
 public class BValInterceptor implements Serializable {
-    private transient volatile Map<Method, Boolean> methodConfiguration = new ConcurrentHashMap<Method, Boolean>();
+    private transient volatile Map<Method, Boolean> methodConfiguration = new ConcurrentHashMap<>();
     private transient volatile Set<ExecutableType> classConfiguration;
     private transient volatile Boolean constructorValidated;
 
@@ -204,7 +206,7 @@ public class BValInterceptor implements Serializable {
                 methodConfig = methodConfiguration.get(method);
                 if (methodConfig == null) {
                     final List<Class<?>> classHierarchy =
-                        ClassHelper.fillFullClassHierarchyAsList(new LinkedList<Class<?>>(), targetClass);
+                        ClassHelper.fillFullClassHierarchyAsList(new LinkedList<>(), targetClass);
                     Collections.reverse(classHierarchy);
 
                     // search on method @ValidateOnExecution
@@ -213,19 +215,19 @@ public class BValInterceptor implements Serializable {
                     for (final Class<?> c : classHierarchy) {
                         final AnnotatedType<?> annotatedType = CDI.current().getBeanManager().createAnnotatedType(c);
                         AnnotatedMethod<?> annotatedMethod = null;
+
                         for (final AnnotatedMethod<?> m : annotatedType.getMethods()) {
-                            if (!m.getJavaMember().getName().equals(method.getName())
-                                || !asList(method.getGenericParameterTypes())
+                            if (m.getJavaMember().getName().equals(method.getName())
+                                && asList(method.getGenericParameterTypes())
                                     .equals(asList(m.getJavaMember().getGenericParameterTypes()))) {
-                                continue;
+                                annotatedMethod = m;
+                                break;
                             }
-                            annotatedMethod = m;
-                            break;
+                        }
+                        if (annotatedMethod == null) {
+                            continue;
                         }
                         try {
-                            if (annotatedMethod == null) {
-                                continue;
-                            }
                             if (validateOnExecutionType == null) {
                                 final ValidateOnExecution vat = annotatedType.getAnnotation(ValidateOnExecution.class);
                                 if (vat != null) {

http://git-wip-us.apache.org/repos/asf/bval/blob/b808381e/bval-jsr/src/main/java/org/apache/bval/cdi/BValInterceptorBean.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/cdi/BValInterceptorBean.java b/bval-jsr/src/main/java/org/apache/bval/cdi/BValInterceptorBean.java
index 8a0c95e..e8f7e1d 100644
--- a/bval-jsr/src/main/java/org/apache/bval/cdi/BValInterceptorBean.java
+++ b/bval-jsr/src/main/java/org/apache/bval/cdi/BValInterceptorBean.java
@@ -43,12 +43,12 @@ public class BValInterceptorBean implements Bean<BValInterceptor>, PassivationCa
     private final InjectionTarget<BValInterceptor> injectionTarget;
 
     public BValInterceptorBean(final BeanManager bm) {
-        final Set<Type> t = new HashSet<Type>();
+        final Set<Type> t = new HashSet<>();
         t.add(BValInterceptor.class);
         t.add(Object.class);
         types = Collections.unmodifiableSet(t);
 
-        final Set<Annotation> q = new HashSet<Annotation>();
+        final Set<Annotation> q = new HashSet<>();
         q.add(DefaultLiteral.INSTANCE);
         q.add(AnyLiteral.INSTANCE);
         qualifiers = Collections.unmodifiableSet(q);
@@ -120,7 +120,7 @@ public class BValInterceptorBean implements Bean<BValInterceptor>, PassivationCa
 
     @Override
     public String getId() {
-        return "BValInterceptor-" + hashCode();
+        return String.format("%s-%d",BValInterceptor.class.getSimpleName(), hashCode());
     }
 
     private static class BValInterceptorInjectionPoint implements InjectionPoint {

http://git-wip-us.apache.org/repos/asf/bval/blob/b808381e/bval-jsr/src/main/java/org/apache/bval/cdi/DefaultLiteral.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/cdi/DefaultLiteral.java b/bval-jsr/src/main/java/org/apache/bval/cdi/DefaultLiteral.java
index 07b2246..e86db31 100644
--- a/bval-jsr/src/main/java/org/apache/bval/cdi/DefaultLiteral.java
+++ b/bval-jsr/src/main/java/org/apache/bval/cdi/DefaultLiteral.java
@@ -19,7 +19,6 @@
 package org.apache.bval.cdi;
 
 import javax.enterprise.inject.Default;
-import javax.enterprise.util.AnnotationLiteral;
 
 public class DefaultLiteral extends EmptyAnnotationLiteral<Default> implements Default {
     private static final long serialVersionUID = 1L;
@@ -28,6 +27,6 @@ public class DefaultLiteral extends EmptyAnnotationLiteral<Default> implements D
 
     @Override
     public String toString() {
-        return "@javax.enterprise.inject.Default()";
+        return String.format("@%s()", Default.class.getName());
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/b808381e/bval-jsr/src/main/java/org/apache/bval/cdi/EmptyAnnotationLiteral.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/cdi/EmptyAnnotationLiteral.java b/bval-jsr/src/main/java/org/apache/bval/cdi/EmptyAnnotationLiteral.java
index 6866551..e9e9f1a 100644
--- a/bval-jsr/src/main/java/org/apache/bval/cdi/EmptyAnnotationLiteral.java
+++ b/bval-jsr/src/main/java/org/apache/bval/cdi/EmptyAnnotationLiteral.java
@@ -29,6 +29,7 @@ import javax.enterprise.util.AnnotationLiteral;
  * Taken from Apache OpenWebBeans.
  * @param <T>
  */
+@SuppressWarnings("serial")
 public abstract class EmptyAnnotationLiteral<T extends Annotation> extends AnnotationLiteral<T> {
     private Class<T> annotationType;
 
@@ -77,11 +78,10 @@ public abstract class EmptyAnnotationLiteral<T extends Annotation> extends Annot
     private Class<T> getAnnotationType(Class<?> definedClazz) {
         Type superClazz = definedClazz.getGenericSuperclass();
 
-        Class<T> clazz = null;
-
-        if (superClazz.equals(Object.class)) {
-            throw new RuntimeException("Super class must be parametrized type!");
-        } else if (superClazz instanceof ParameterizedType) {
+        if (Object.class.equals(superClazz)) {
+            throw new RuntimeException("Super class must be parameterized type!");
+        }
+        if (superClazz instanceof ParameterizedType) {
             ParameterizedType paramType = (ParameterizedType) superClazz;
             Type[] actualArgs = paramType.getActualTypeArguments();
 
@@ -89,17 +89,15 @@ public abstract class EmptyAnnotationLiteral<T extends Annotation> extends Annot
                 //Actual annotation type
                 Type type = actualArgs[0];
 
-                if (type instanceof Class) {
-                    clazz = (Class<T>) type;
+                if (type instanceof Class<?>) {
+                    @SuppressWarnings("unchecked")
+                    Class<T> clazz = (Class<T>) type;
                     return clazz;
-                } else {
-                    throw new RuntimeException("Not class type!");
                 }
-            } else {
-                throw new RuntimeException("More than one parametric type!");
+                throw new RuntimeException("Not class type!");
             }
-        } else {
-            return getAnnotationType((Class<?>) superClazz);
+            throw new RuntimeException("More than one parametric type!");
         }
+        return getAnnotationType((Class<?>) superClazz);
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/b808381e/bval-jsr/src/main/java/org/apache/bval/cdi/ValidatorBean.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/cdi/ValidatorBean.java b/bval-jsr/src/main/java/org/apache/bval/cdi/ValidatorBean.java
index c551d72..cd17fae 100644
--- a/bval-jsr/src/main/java/org/apache/bval/cdi/ValidatorBean.java
+++ b/bval-jsr/src/main/java/org/apache/bval/cdi/ValidatorBean.java
@@ -44,12 +44,12 @@ public class ValidatorBean implements Bean<Validator>, PassivationCapable {
         this.factory = factory;
         this.instance = validator;
 
-        final Set<Type> t = new HashSet<Type>();
+        final Set<Type> t = new HashSet<>();
         t.add(Validator.class);
         t.add(Object.class);
         types = Collections.unmodifiableSet(t);
 
-        final Set<Annotation> q = new HashSet<Annotation>();
+        final Set<Annotation> q = new HashSet<>();
         q.add(DefaultLiteral.INSTANCE);
         q.add(AnyLiteral.INSTANCE);
         qualifiers = Collections.unmodifiableSet(q);
@@ -119,6 +119,6 @@ public class ValidatorBean implements Bean<Validator>, PassivationCapable {
 
     @Override
     public String getId() {
-        return "BValValidator-" + hashCode();
+        return String.format("BVal%s-%d", Validator.class.getSimpleName(), hashCode());
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/b808381e/bval-jsr/src/main/java/org/apache/bval/cdi/ValidatorFactoryBean.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/cdi/ValidatorFactoryBean.java b/bval-jsr/src/main/java/org/apache/bval/cdi/ValidatorFactoryBean.java
index aeffd81..0a8c2e5 100644
--- a/bval-jsr/src/main/java/org/apache/bval/cdi/ValidatorFactoryBean.java
+++ b/bval-jsr/src/main/java/org/apache/bval/cdi/ValidatorFactoryBean.java
@@ -41,12 +41,12 @@ public class ValidatorFactoryBean implements Bean<ValidatorFactory>, Passivation
     public ValidatorFactoryBean(final ValidatorFactory validatorFactory) {
         this.instance = validatorFactory;
 
-        final Set<Type> t = new HashSet<Type>();
+        final Set<Type> t = new HashSet<>();
         t.add(ValidatorFactory.class);
         t.add(Object.class);
         types = Collections.unmodifiableSet(t);
 
-        final Set<Annotation> q = new HashSet<Annotation>();
+        final Set<Annotation> q = new HashSet<>();
         q.add(DefaultLiteral.INSTANCE);
         q.add(AnyLiteral.INSTANCE);
         qualifiers = Collections.unmodifiableSet(q);
@@ -109,6 +109,6 @@ public class ValidatorFactoryBean implements Bean<ValidatorFactory>, Passivation
 
     @Override
     public String getId() {
-        return "BValValidatorFactory-" + hashCode();
+        return String.format("BVal%s-%d", ValidatorFactory.class.getSimpleName(), hashCode());
     }
 }


[11/17] bval git commit: BV2: new descriptor model

Posted by mb...@apache.org.
BV2: new descriptor model


Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/40ac09f7
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/40ac09f7
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/40ac09f7

Branch: refs/heads/bv2
Commit: 40ac09f77a673fc61863304a72facc38f2ef2e42
Parents: 59bd964
Author: Matt Benson <mb...@apache.org>
Authored: Wed Feb 21 14:50:59 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Wed Feb 21 14:59:57 2018 -0600

----------------------------------------------------------------------
 .../org/apache/bval/jsr/descriptor/BeanD.java   | 128 ++++++++
 .../jsr/descriptor/CascadableContainerD.java    |  88 ++++++
 .../apache/bval/jsr/descriptor/ComposedD.java   | 123 ++++++++
 .../ComputeConstraintValidatorClass.java        | 183 ++++++++++++
 .../apache/bval/jsr/descriptor/ConstraintD.java | 276 ++++++++++++++++++
 .../bval/jsr/descriptor/ConstructorD.java       |  41 +++
 .../jsr/descriptor/ContainerElementTypeD.java   | 119 ++++++++
 .../bval/jsr/descriptor/CrossParameterD.java    |  18 ++
 .../bval/jsr/descriptor/DescriptorManager.java  |  74 +++++
 .../apache/bval/jsr/descriptor/ElementD.java    | 122 ++++++++
 .../apache/bval/jsr/descriptor/ExecutableD.java |  84 ++++++
 .../org/apache/bval/jsr/descriptor/Finder.java  | 103 +++++++
 .../bval/jsr/descriptor/GroupConversion.java    |  85 ++++++
 .../bval/jsr/descriptor/MetadataReader.java     | 291 +++++++++++++++++++
 .../org/apache/bval/jsr/descriptor/MethodD.java |  49 ++++
 .../apache/bval/jsr/descriptor/ParameterD.java  |  62 ++++
 .../apache/bval/jsr/descriptor/PropertyD.java   | 106 +++++++
 .../bval/jsr/descriptor/ReturnValueD.java       |  36 +++
 18 files changed, 1988 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/BeanD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/BeanD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/BeanD.java
new file mode 100644
index 0000000..7f52c6d
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/BeanD.java
@@ -0,0 +1,128 @@
+/*
+ * 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.reflect.Type;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.ConstructorDescriptor;
+import javax.validation.metadata.MethodDescriptor;
+import javax.validation.metadata.MethodType;
+import javax.validation.metadata.PropertyDescriptor;
+
+import org.apache.bval.jsr.metadata.Signature;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.StringUtils;
+
+public class BeanD extends ElementD<Class<?>, MetadataReader.ForBean> implements BeanDescriptor {
+
+    private static boolean constrainedProperty(PropertyDescriptor pd) {
+        return pd.hasConstraints() || pd.isCascaded();
+    }
+
+    private final Class<?> beanClass;
+
+    private final Lazy<List<Class<?>>> groupSequence;
+    private final Lazy<Map<String, PropertyDescriptor>> propertiesMap;
+    private final Lazy<Set<PropertyDescriptor>> properties;
+    private final Lazy<Map<Signature, ConstructorD>> constructors;
+    private final Lazy<Map<Signature, MethodD>> methods;
+
+    BeanD(MetadataReader.ForBean reader) {
+        super(reader);
+        this.beanClass = reader.meta.getHost();
+
+        groupSequence = new Lazy<>(reader::getGroupSequence);
+        propertiesMap = new Lazy<>(() -> reader.getProperties(this));
+        properties = new Lazy<>(() -> propertiesMap.get().values().stream().filter(BeanD::constrainedProperty)
+            .collect(ToUnmodifiable.set()));
+        constructors = new Lazy<>(() -> reader.getConstructors(this));
+        methods = new Lazy<>(() -> reader.getMethods(this));
+    }
+
+    @Override
+    public Class<?> getElementClass() {
+        return beanClass;
+    }
+
+    @Override
+    public boolean isBeanConstrained() {
+        return hasConstraints() || properties.get().stream().anyMatch(DescriptorManager::isConstrained);
+    }
+
+    @Override
+    public PropertyDescriptor getConstraintsForProperty(String propertyName) {
+        return Optional.ofNullable(getProperty(propertyName)).filter(BeanD::constrainedProperty).orElse(null);
+    }
+
+    @Override
+    public Set<PropertyDescriptor> getConstrainedProperties() {
+        return properties.get();
+    }
+
+    @Override
+    public MethodDescriptor getConstraintsForMethod(String methodName, Class<?>... parameterTypes) {
+        return methods.get().get(new Signature(methodName, parameterTypes));
+    }
+
+    @SuppressWarnings("unlikely-arg-type")
+    @Override
+    public Set<MethodDescriptor> getConstrainedMethods(MethodType methodType, MethodType... methodTypes) {
+        return methods.get().values().stream().filter(EnumSet.of(methodType, methodTypes)::contains)
+            .collect(ToUnmodifiable.set());
+    }
+
+    @Override
+    public ConstructorDescriptor getConstraintsForConstructor(Class<?>... parameterTypes) {
+        return constructors.get().get(new Signature(beanClass.getName(), parameterTypes));
+    }
+
+    @Override
+    public Set<ConstructorDescriptor> getConstrainedConstructors() {
+        return constructors.get().values().stream().collect(ToUnmodifiable.set());
+    }
+
+    public PropertyDescriptor getProperty(String propertyName) {
+        Exceptions.raiseIf(StringUtils.isBlank(propertyName), IllegalArgumentException::new,
+            "propertyName was null/empty/blank");
+
+        return propertiesMap.get().get(propertyName);
+    }
+
+    @Override
+    protected BeanD getBean() {
+        return this;
+    }
+
+    @Override
+    public List<Class<?>> getGroupSequence() {
+        return groupSequence.get();
+    }
+
+    public final Type getGenericType() {
+        return getElementClass();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java
new file mode 100644
index 0000000..8c76fb0
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java
@@ -0,0 +1,88 @@
+/*
+ * 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.reflect.AnnotatedElement;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import javax.validation.ValidationException;
+import javax.validation.metadata.CascadableDescriptor;
+import javax.validation.metadata.ContainerDescriptor;
+import javax.validation.metadata.ContainerElementTypeDescriptor;
+import javax.validation.metadata.GroupConversionDescriptor;
+
+import org.apache.bval.jsr.GraphContext;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.TypeUtils;
+
+public abstract class CascadableContainerD<P extends ElementD<?, ?>, E extends AnnotatedElement> extends
+    ElementD.NonRoot<P, E, MetadataReader.ForContainer<E>> implements CascadableDescriptor, ContainerDescriptor {
+
+    private final boolean cascaded;
+    private final Set<GroupConversion> groupConversions;
+    private final Lazy<Set<ContainerElementTypeD>> containerElementTypes;
+
+    protected CascadableContainerD(MetadataReader.ForContainer<E> reader, P parent) {
+        super(reader, parent);
+        cascaded = reader.isCascaded();
+        groupConversions = reader.getGroupConversions();
+        containerElementTypes = new Lazy<>(() -> reader.getContainerElementTypes(this));
+    }
+
+    @Override
+    public Class<?> getElementClass() {
+        return TypeUtils.getRawType(getGenericType(), parent.getElementClass());
+    }
+
+    @Override
+    public boolean isCascaded() {
+        return cascaded;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @Override
+    public Set<GroupConversionDescriptor> getGroupConversions() {
+        return (Set) groupConversions;
+    }
+
+    @Override
+    public Set<ContainerElementTypeDescriptor> getConstrainedContainerElementTypes() {
+        return containerElementTypes.get().stream().filter(DescriptorManager::isConstrained)
+            .collect(ToUnmodifiable.set());
+    }
+
+    public final Stream<GraphContext> read(GraphContext context) {
+        Validate.notNull(context);
+        if (context.getValue() == null) {
+            return Stream.empty();
+        }
+        try {
+            return readImpl(context);
+        } catch (Exception e) {
+            throw new ValidationException(e);
+        }
+    }
+
+    protected Stream<GraphContext> readImpl(GraphContext context) throws Exception {
+        throw new UnsupportedOperationException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComposedD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComposedD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComposedD.java
new file mode 100644
index 0000000..6d3b004
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComposedD.java
@@ -0,0 +1,123 @@
+/*
+ * 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.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import javax.validation.metadata.CascadableDescriptor;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ContainerDescriptor;
+import javax.validation.metadata.ContainerElementTypeDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+import javax.validation.metadata.GroupConversionDescriptor;
+import javax.validation.metadata.PropertyDescriptor;
+
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Validate;
+
+public abstract class ComposedD<D extends ElementD<?, ?>> implements ElementDescriptor {
+
+    static abstract class ForCascadableContainer<D extends CascadableContainerD<?, ?>> extends ComposedD<D>
+        implements CascadableDescriptor, ContainerDescriptor {
+
+        ForCascadableContainer(List<D> delegates) {
+            super(delegates);
+        }
+
+        @Override
+        public Set<ContainerElementTypeDescriptor> getConstrainedContainerElementTypes() {
+            return delegates.stream().map(ContainerDescriptor::getConstrainedContainerElementTypes)
+                .flatMap(Collection::stream).collect(ToUnmodifiable.set());
+        }
+
+        @Override
+        public boolean isCascaded() {
+            return delegates.stream().anyMatch(CascadableDescriptor::isCascaded);
+        }
+
+        @Override
+        public Set<GroupConversionDescriptor> getGroupConversions() {
+            return delegates.stream().map(CascadableDescriptor::getGroupConversions).flatMap(Collection::stream)
+                .collect(ToUnmodifiable.set());
+        }
+    }
+
+    static class ForProperty extends ComposedD.ForCascadableContainer<PropertyD<?>> implements PropertyDescriptor {
+
+        ForProperty(List<PropertyD<?>> delegates) {
+            super(delegates);
+        }
+
+        @Override
+        public String getPropertyName() {
+            return delegates.stream().map(PropertyDescriptor::getPropertyName).findFirst()
+                .orElseThrow(IllegalStateException::new);
+        }
+    }
+
+    public static <T extends ElementD<?, ?>> Stream<T> unwrap(ElementDescriptor descriptor, Class<T> delegateType) {
+        final Stream<?> s;
+
+        if (descriptor instanceof ComposedD<?>) {
+            s = ((ComposedD<?>) descriptor).delegates.stream()
+                // unwrap recursively:
+                .flatMap(d -> unwrap(d, delegateType));
+        } else {
+            s = Stream.of(descriptor);
+        }
+        return s.map(delegateType::cast);
+    }
+
+    protected final List<D> delegates;
+
+    ComposedD(List<D> delegates) {
+        super();
+        this.delegates = delegates;
+
+        Validate.notNull(delegates, "delegates");
+        Validate.isTrue(!delegates.isEmpty(), "At least one delegate is required");
+        Validate.isTrue(delegates.stream().noneMatch(Objects::isNull), "null delegates not permitted");
+    }
+
+    @Override
+    public boolean hasConstraints() {
+        return delegates.stream().anyMatch(ElementDescriptor::hasConstraints);
+    }
+
+    @Override
+    public Class<?> getElementClass() {
+        return delegates.stream().map(ElementDescriptor::getElementClass).findFirst()
+            .orElseThrow(IllegalStateException::new);
+    }
+
+    @Override
+    public Set<ConstraintDescriptor<?>> getConstraintDescriptors() {
+        return delegates.stream().map(ElementDescriptor::getConstraintDescriptors).flatMap(Collection::stream)
+            .collect(ToUnmodifiable.set());
+    }
+
+    @Override
+    public ConstraintFinder findConstraints() {
+        return new Finder(this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/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
new file mode 100644
index 0000000..7eeefd2
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComputeConstraintValidatorClass.java
@@ -0,0 +1,183 @@
+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.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<?> unwrap(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]);
+        Exceptions.raiseUnless(isSupported(result), 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();
+
+        Exceptions.raiseIf(set.size() > 1 || !composed && set.isEmpty(), UnexpectedTypeException::new,
+            "%d cross-parameter %ss found for constraint type %s", set.size(), CV, constraintType);
+
+        final Class<? extends ConstraintValidator<A, ?>> result = set.iterator().next().getType();
+        Exceptions.raiseUnless(TypeUtils.isAssignable(Object[].class, getValidatedType(result)),
+            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()));
+
+        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::unwrap);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/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
new file mode 100644
index 0000000..0c1be1b
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java
@@ -0,0 +1,276 @@
+/*
+ * 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.annotation.ElementType;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import javax.validation.ConstraintDeclarationException;
+import javax.validation.ConstraintDefinitionException;
+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.Metas;
+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.Lazy;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.TypeUtils;
+
+public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A> {
+    private static <T> Set<T> set(Supplier<T[]> array) {
+        return Stream.of(array.get()).collect(ToUnmodifiable.set());
+    }
+
+    private final A annotation;
+    private final Scope scope;
+    private final Metas<?> meta;
+    private final Class<?> validatedType;
+
+    private final Lazy<Set<Class<?>>> groups = new Lazy<>(this::computeGroups);
+
+    private final Set<Class<? extends Payload>> payload;
+
+    private final Lazy<Boolean> reportAsSingle =
+        new Lazy<>(() -> getAnnotation().annotationType().isAnnotationPresent(ReportAsSingleViolation.class));
+
+    private final Lazy<ValidateUnwrappedValue> valueUnwrapping = new Lazy<>(this::computeValidateUnwrappedValue);
+
+    private final Lazy<Map<String, Object>> attributes;
+    private final Lazy<Set<ConstraintDescriptor<?>>> composingConstraints;
+    private final Lazy<List<Class<? extends ConstraintValidator<A, ?>>>> constraintValidatorClasses;
+    private final Lazy<Class<? extends ConstraintValidator<A, ?>>> constraintValidatorClass;
+
+    public ConstraintD(A annotation, Scope scope, Metas<?> meta, ApacheValidatorFactory validatorFactory) {
+        this.annotation = Validate.notNull(annotation, "annotation");
+        this.scope = Validate.notNull(scope, "scope");
+        this.meta = Validate.notNull(meta, "meta");
+        this.payload = computePayload();
+        this.validatedType = computeValidatedType(validatorFactory);
+
+        attributes = new Lazy<>(() -> AnnotationsManager.readAttributes(annotation));
+
+        // retain no references to the validatorFactory; only wrap it in lazy
+        // suppliers
+        Validate.notNull(validatorFactory, "validatorFactory");
+        composingConstraints = new Lazy<>(computeComposingConstraints(validatorFactory));
+        constraintValidatorClasses = new Lazy<>(computeConstraintValidatorClasses(validatorFactory));
+
+        final Supplier<Class<? extends ConstraintValidator<A, ?>>> computeConstraintValidatorClass =
+            new ComputeConstraintValidatorClass<>(validatorFactory, meta.getValidationTarget(), annotation,
+                validatedType);
+
+        constraintValidatorClass = new Lazy<>(computeConstraintValidatorClass);
+    }
+
+    @Override
+    public A getAnnotation() {
+        return annotation;
+    }
+
+    @Override
+    public Set<Class<?>> getGroups() {
+        return groups.get();
+    }
+
+    @Override
+    public Set<Class<? extends Payload>> getPayload() {
+        return payload;
+    }
+
+    @Override
+    public List<Class<? extends ConstraintValidator<A, ?>>> getConstraintValidatorClasses() {
+        return constraintValidatorClasses.get();
+    }
+
+    @Override
+    public Map<String, Object> getAttributes() {
+        return attributes.get();
+    }
+
+    @Override
+    public Set<ConstraintDescriptor<?>> getComposingConstraints() {
+        return composingConstraints.get();
+    }
+
+    @Override
+    public boolean isReportAsSingleViolation() {
+        return reportAsSingle.get().booleanValue();
+    }
+
+    @Override
+    public String getMessageTemplate() {
+        final boolean required = true;
+        return read(ConstraintAnnotationAttributes.MESSAGE, required);
+    }
+
+    @Override
+    public ConstraintTarget getValidationAppliesTo() {
+        return read(ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO);
+    }
+
+    @Override
+    public ValidateUnwrappedValue getValueUnwrapping() {
+        return valueUnwrapping.get();
+    }
+
+    @Override
+    public <U> U unwrap(Class<U> type) throws ValidationException {
+        try {
+            return type.cast(this);
+        } catch (ClassCastException e) {
+            throw new ValidationException(e);
+        }
+    }
+
+    public Scope getScope() {
+        return scope;
+    }
+
+    public Class<?> getDeclaringClass() {
+        return meta.getDeclaringClass();
+    }
+
+    public ElementType getDeclaredOn() {
+        return meta.getElementType();
+    }
+
+    public Class<?> getValidatedType() {
+        return validatedType;
+    }
+
+    public Class<? extends ConstraintValidator<A, ?>> getConstraintValidatorClass() {
+        return constraintValidatorClass.get();
+    }
+
+    private <T> T read(ConstraintAnnotationAttributes attr) {
+        return read(attr, false);
+    }
+
+    private <T> T read(ConstraintAnnotationAttributes attr, boolean required) {
+        final Class<? extends Annotation> constraintType = annotation.annotationType();
+        final Optional<T> result =
+            Optional.of(constraintType).map(attr::analyze).filter(Worker::isValid).map(w -> w.<T> read(annotation));
+
+        Exceptions.raiseIf(required && !result.isPresent(), ConstraintDefinitionException::new,
+            "Required attribute %s missing from constraint type %s", attr.getAttributeName(), constraintType);
+
+        return result.orElse(null);
+    }
+
+    private Supplier<Set<ConstraintDescriptor<?>>> computeComposingConstraints(
+        ApacheValidatorFactory validatorFactory) {
+        return () -> Stream.of(validatorFactory.getAnnotationsManager().getComposingConstraints(annotation))
+            .map(c -> new ConstraintD<>(c, scope, meta, validatorFactory))
+            .collect(ToUnmodifiable.set(LinkedHashSet::new));
+    }
+
+    @SuppressWarnings("unchecked")
+    private Supplier<List<Class<? extends ConstraintValidator<A, ?>>>> computeConstraintValidatorClasses(
+        ApacheValidatorFactory validatorFactory) {
+        return () -> validatorFactory.getConstraintsCache()
+            .getConstraintValidatorClasses((Class<A>) annotation.annotationType());
+    }
+
+    private ValidateUnwrappedValue computeValidateUnwrappedValue() {
+        final Set<Class<? extends Payload>> p = getPayload();
+        final boolean unwrap = p.contains(Unwrap.class);
+        final boolean skip = p.contains(Skip.class);
+        if (unwrap) {
+            Validate.validState(!skip, "Cannot specify both %s and %s", Unwrap.class.getSimpleName(),
+                Skip.class.getSimpleName());
+            return ValidateUnwrappedValue.UNWRAP;
+        }
+        return skip ? ValidateUnwrappedValue.SKIP : ValidateUnwrappedValue.DEFAULT;
+    }
+
+    private Set<Class<?>> computeGroups() {
+        final boolean required = true;
+        final Class<?>[] groups = read(ConstraintAnnotationAttributes.GROUPS, required);
+        if (groups.length == 0) {
+            return Collections.singleton(Default.class);
+        }
+        return set(() -> groups);
+    }
+
+    private Set<Class<? extends Payload>> computePayload() {
+        final boolean required = true;
+        final Set<Class<? extends Payload>> result = set(() -> read(ConstraintAnnotationAttributes.PAYLOAD, required));
+        Exceptions.raiseIf(result.containsAll(Arrays.asList(Unwrapping.Unwrap.class, Unwrapping.Skip.class)),
+            ConstraintDeclarationException::new,
+            "Constraint %s declared at %s specifies conflicting value unwrapping hints", annotation, meta.getHost());
+        return result;
+    }
+
+    private Class<?> computeValidatedType(ApacheValidatorFactory validatorFactory) {
+        final Class<?> rawType = TypeUtils.getRawType(meta.getType(), null);
+
+        Exceptions.raiseIf(rawType == null, 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) {
+            Exceptions.raiseIf(unwrap, 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/40ac09f7/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstructorD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstructorD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstructorD.java
new file mode 100644
index 0000000..b89d29c
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstructorD.java
@@ -0,0 +1,41 @@
+/*
+ * 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.reflect.Constructor;
+
+import javax.validation.metadata.ConstructorDescriptor;
+
+public class ConstructorD extends ExecutableD<Constructor<?>, MetadataReader.ForConstructor, ConstructorD>
+    implements ConstructorDescriptor {
+
+    ConstructorD(MetadataReader.ForConstructor reader, BeanD parent) {
+        super(reader, parent);
+    }
+
+    @Override
+    public Class<?> getElementClass() {
+        return getParent().getElementClass();
+    }
+
+    @Override
+    protected String nameOf(Constructor<?> e) {
+        return e.getDeclaringClass().getSimpleName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ContainerElementTypeD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ContainerElementTypeD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ContainerElementTypeD.java
new file mode 100644
index 0000000..7cacff3
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ContainerElementTypeD.java
@@ -0,0 +1,119 @@
+/*
+ * 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.reflect.AnnotatedType;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import javax.validation.ValidationException;
+import javax.validation.metadata.ContainerElementTypeDescriptor;
+import javax.validation.valueextraction.ValueExtractor;
+
+import org.apache.bval.jsr.GraphContext;
+import org.apache.bval.jsr.metadata.ContainerElementKey;
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.Validate;
+
+public class ContainerElementTypeD extends CascadableContainerD<CascadableContainerD<?, ?>, AnnotatedType>
+    implements ContainerElementTypeDescriptor {
+
+    private static class Receiver implements ValueExtractor.ValueReceiver {
+        private final GraphContext context;
+        private Lazy<List<GraphContext>> result = new Lazy<>(ArrayList::new);
+
+        Receiver(GraphContext context) {
+            super();
+            this.context = context;
+        }
+
+        @Override
+        public void value(String nodeName, Object object) {
+            addChild(new NodeImpl.PropertyNodeImpl(nodeName), object);
+        }
+
+        @Override
+        public void iterableValue(String nodeName, Object object) {
+            final NodeImpl.PropertyNodeImpl node = new NodeImpl.PropertyNodeImpl(nodeName);
+            node.setInIterable(true);
+            addChild(node, object);
+        }
+
+        @Override
+        public void indexedValue(String nodeName, int i, Object object) {
+            final NodeImpl.PropertyNodeImpl node = new NodeImpl.PropertyNodeImpl(nodeName);
+            node.setIndex(Integer.valueOf(i));
+            addChild(node, object);
+        }
+
+        @Override
+        public void keyedValue(String nodeName, Object key, Object object) {
+            final NodeImpl.PropertyNodeImpl node = new NodeImpl.PropertyNodeImpl(nodeName);
+            node.setKey(key);
+            addChild(node, object);
+        }
+
+        private void addChild(NodeImpl node, Object value) {
+            result.get().add(context.child(node, value));
+        }
+    }
+
+    private final ContainerElementKey key;
+
+    ContainerElementTypeD(ContainerElementKey key, MetadataReader.ForContainer<AnnotatedType> reader,
+        CascadableContainerD<?, ?> parent) {
+        super(reader, parent);
+        this.key = Validate.notNull(key, "key");
+    }
+
+    @Override
+    public Class<?> getContainerClass() {
+        return key.getContainerClass();
+    }
+
+    @Override
+    public Integer getTypeArgumentIndex() {
+        return Integer.valueOf(key.getTypeArgumentIndex());
+    }
+
+    public ContainerElementKey getKey() {
+        return key;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Override
+    protected Stream<GraphContext> readImpl(GraphContext context) throws Exception {
+        final ValueExtractor valueExtractor = context.getValidatorContext().getValueExtractors().find(key);
+        Exceptions.raiseIf(valueExtractor == null, ValidationException::new, "No %s found for %s",
+            ValueExtractor.class.getSimpleName(), key);
+
+        final Receiver receiver = new Receiver(context);
+        try {
+            valueExtractor.extractValues(context.getValue(), receiver);
+        } catch (ValidationException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ValidationException(e);
+        }
+        return receiver.result.get().stream();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CrossParameterD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CrossParameterD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CrossParameterD.java
new file mode 100644
index 0000000..0d51800
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CrossParameterD.java
@@ -0,0 +1,18 @@
+package org.apache.bval.jsr.descriptor;
+
+import java.lang.reflect.Executable;
+
+import javax.validation.metadata.CrossParameterDescriptor;
+
+public class CrossParameterD<P extends ExecutableD<?, ?, P>, E extends Executable>
+    extends ElementD.NonRoot<P, E, MetadataReader.ForElement<E, ?>> implements CrossParameterDescriptor {
+
+    protected CrossParameterD(MetadataReader.ForElement<E, ?> reader, P parent) {
+        super(reader, parent);
+    }
+
+    @Override
+    public Class<?> getElementClass() {
+        return Object[].class;
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/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
new file mode 100644
index 0000000..21816d7
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java
@@ -0,0 +1,74 @@
+/*
+ *  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.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.CascadableDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+
+import org.apache.bval.jsr.ApacheValidatorFactory;
+import org.apache.bval.jsr.metadata.AnnotationBehaviorMergeStrategy;
+import org.apache.bval.jsr.metadata.CompositeBuilder;
+import org.apache.bval.jsr.metadata.HierarchyBuilder;
+import org.apache.bval.jsr.metadata.MetadataBuilder;
+import org.apache.bval.jsr.metadata.DualBuilder;
+import org.apache.bval.jsr.metadata.ReflectionBuilder;
+import org.apache.bval.util.Validate;
+
+public class DescriptorManager {
+    public static <D extends ElementDescriptor & CascadableDescriptor> boolean isConstrained(D descriptor) {
+        return descriptor.hasConstraints() || descriptor.isCascaded();
+    }
+
+    private final ApacheValidatorFactory validatorFactory;
+    private final ConcurrentMap<Class<?>, BeanD> beanDescriptors = new ConcurrentHashMap<>();
+    private final ReflectionBuilder reflectionBuilder;
+    private final MetadataReader metadataReader;
+
+    public DescriptorManager(ApacheValidatorFactory validatorFactory) {
+        super();
+        this.validatorFactory = Validate.notNull(validatorFactory, "validatorFactory");
+        this.reflectionBuilder = new ReflectionBuilder(validatorFactory);
+        this.metadataReader = new MetadataReader(validatorFactory);
+    }
+
+    public BeanDescriptor getBeanDescriptor(Class<?> beanClass) {
+        Validate.notNull(beanClass, IllegalArgumentException::new, "beanClass");
+        return beanDescriptors.computeIfAbsent(beanClass, k -> new BeanD(metadataReader.forBean(k, builder(k))));
+    }
+
+    private MetadataBuilder.ForBean builder(Class<?> beanClass) {
+        final MetadataBuilder.ForBean primaryBuilder =
+            new HierarchyBuilder(reflectionBuilder::forBean).forBean(beanClass);
+
+        final MetadataBuilder.ForBean customBuilder = new HierarchyBuilder(this::customBuilder).forBean(beanClass);
+
+        return customBuilder.isEmpty() ? primaryBuilder : DualBuilder.forBean(primaryBuilder, customBuilder);
+    }
+
+    private MetadataBuilder.ForBean customBuilder(Class<?> beanClass) {
+        final List<MetadataBuilder.ForBean> customBuilders =
+            validatorFactory.getMetadataBuilders().getCustomBuilders(beanClass);
+
+        return customBuilders.isEmpty() ? null : customBuilders.stream()
+            .collect(CompositeBuilder.with(AnnotationBehaviorMergeStrategy.consensus()).compose());
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ElementD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ElementD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ElementD.java
new file mode 100644
index 0000000..c139773
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ElementD.java
@@ -0,0 +1,122 @@
+/*
+ * 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.ElementType;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.TypeUtils;
+
+public abstract class ElementD<E extends AnnotatedElement, R extends MetadataReader.ForElement<E, ?>>
+    implements ElementDescriptor {
+
+    public static abstract class NonRoot<P extends ElementD<?, ?>, E extends AnnotatedElement, R extends MetadataReader.ForElement<E, ?>>
+        extends ElementD<E, R> {
+
+        protected final P parent;
+
+        protected NonRoot(R reader, P parent) {
+            super(reader);
+            this.parent = Validate.notNull(parent, "parent");
+        }
+
+        public P getParent() {
+            return parent;
+        }
+
+        @Override
+        public final Type getGenericType() {
+            if (TypeUtils.containsTypeVariables(genericType)) {
+                final Map<TypeVariable<?>, Type> args =
+                    TypeUtils.getTypeArguments(parent.getGenericType(), Object.class);
+                return TypeUtils.unrollVariables(args, genericType);
+            }
+            return genericType;
+        }
+
+        @Override
+        final protected BeanD getBean() {
+            return parent.getBean();
+        }
+
+        @Override
+        public final List<Class<?>> getGroupSequence() {
+            return getBean().getGroupSequence();
+        }
+    }
+
+    protected final Type genericType;
+
+    private final E target;
+    private final ElementType elementType;
+    private final Set<ConstraintD<?>> constraints;
+
+    protected ElementD(R reader) {
+        super();
+        Validate.notNull(reader, "reader");
+        this.genericType = reader.meta.getType();
+        this.target = reader.meta.getHost();
+        this.elementType = reader.meta.getElementType();
+        this.constraints = reader.getConstraints();
+    }
+
+    @Override
+    public final boolean hasConstraints() {
+        return !constraints.isEmpty();
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @Override
+    public final Set<ConstraintDescriptor<?>> getConstraintDescriptors() {
+        return (Set) constraints;
+    }
+
+    @Override
+    public final ConstraintFinder findConstraints() {
+        return new Finder(this);
+    }
+
+    public final ElementType getElementType() {
+        return elementType;
+    }
+
+    public final E getTarget() {
+        return target;
+    }
+
+    public abstract Type getGenericType();
+
+    public abstract List<Class<?>> getGroupSequence();
+
+    protected abstract BeanD getBean();
+
+    @Override
+    public String toString() {
+        return String.format("%s: %s", getClass().getSimpleName(), target);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ExecutableD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ExecutableD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ExecutableD.java
new file mode 100644
index 0000000..db3df6c
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ExecutableD.java
@@ -0,0 +1,84 @@
+/*
+ * 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.reflect.Executable;
+import java.util.List;
+
+import javax.validation.metadata.CrossParameterDescriptor;
+import javax.validation.metadata.ExecutableDescriptor;
+import javax.validation.metadata.ParameterDescriptor;
+import javax.validation.metadata.ReturnValueDescriptor;
+
+public abstract class ExecutableD<E extends Executable, R extends MetadataReader.ForExecutable<E, R>, SELF extends ExecutableD<E, R, SELF>>
+    extends ElementD.NonRoot<BeanD, E, R> implements ExecutableDescriptor {
+
+    private final String name;
+    private final ReturnValueD<SELF, E> returnValue;
+    private final List<ParameterD<SELF>> parameters;
+    private final CrossParameterD<SELF, E> crossParameter;
+
+    @SuppressWarnings("unchecked")
+    protected ExecutableD(R reader, BeanD parent) {
+        super(reader, parent);
+
+        name = reader.meta.getName();
+
+        returnValue = reader.getReturnValueDescriptor((SELF) this);
+        parameters = reader.getParameterDescriptors((SELF) this);
+        crossParameter = reader.getCrossParameterDescriptor((SELF) this);
+    }
+
+    @Override
+    public final String getName() {
+        return name;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @Override
+    public final List<ParameterDescriptor> getParameterDescriptors() {
+        return (List) parameters;
+    }
+
+    @Override
+    public final CrossParameterDescriptor getCrossParameterDescriptor() {
+        return crossParameter;
+    }
+
+    @Override
+    public final ReturnValueDescriptor getReturnValueDescriptor() {
+        return returnValue;
+    }
+
+    @Override
+    public final boolean hasConstrainedParameters() {
+        return parameters.stream().anyMatch(this::isConstrained);
+    }
+
+    @Override
+    public final boolean hasConstrainedReturnValue() {
+        return isConstrained(returnValue);
+    }
+
+    protected abstract String nameOf(E e);
+
+    private boolean isConstrained(CascadableContainerD<?, ?> child) {
+        return child.isCascaded() || child.hasConstraints();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/Finder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/Finder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/Finder.java
new file mode 100644
index 0000000..c01e069
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/Finder.java
@@ -0,0 +1,103 @@
+/*
+ * 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.ElementType;
+import java.util.EnumSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.groups.Default;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+import javax.validation.metadata.ElementDescriptor.ConstraintFinder;
+import javax.validation.metadata.Scope;
+
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Validate;
+
+class Finder implements ConstraintFinder, Supplier<Stream<ConstraintD<?>>> {
+    private Predicate<ConstraintD<?>> groups = c -> true;
+    private Predicate<ConstraintD<?>> scope;
+    private Predicate<ConstraintD<?>> elements;
+
+    private final ElementDescriptor owner;
+
+    Finder(ElementDescriptor owner) {
+        this.owner = Validate.notNull(owner, "owner");
+    }
+
+    @Override
+    public ConstraintFinder unorderedAndMatchingGroups(Class<?>... groups) {
+        this.groups = c -> Stream.of(groups).anyMatch(t -> {
+            final Set<Class<?>> constraintGroups = c.getGroups();
+            return constraintGroups.contains(t)
+                || constraintGroups.contains(Default.class) && c.getDeclaringClass().isAssignableFrom(t);
+        });
+        return this;
+    }
+
+    @Override
+    public ConstraintFinder lookingAt(Scope scope) {
+        this.scope = scope == Scope.HIERARCHY ? null : c -> c.getScope() == scope;
+        return this;
+    }
+
+    @Override
+    public ConstraintFinder declaredOn(ElementType... types) {
+        this.elements = c -> Stream.of(types).filter(Objects::nonNull)
+            .collect(Collectors.toCollection(() -> EnumSet.noneOf(ElementType.class))).contains(c.getDeclaredOn());
+
+        return this;
+    }
+
+    @Override
+    public Set<ConstraintDescriptor<?>> getConstraintDescriptors() {
+        return get().collect(ToUnmodifiable.set());
+    }
+
+    @Override
+    public Stream<ConstraintD<?>> get() {
+        return getConstraints().filter(filter());
+    }
+
+    @Override
+    public boolean hasConstraints() {
+        return getConstraints().anyMatch(filter());
+    }
+
+    private Stream<ConstraintD<?>> getConstraints() {
+        return owner.getConstraintDescriptors().stream().<ConstraintD<?>> map(c -> c.unwrap(ConstraintD.class));
+    }
+
+    private Predicate<ConstraintD<?>> filter() {
+        Predicate<ConstraintD<?>> result = groups;
+        if (scope != null) {
+            result = result.and(scope);
+        }
+        if (elements != null) {
+            result = result.and(elements);
+        }
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/GroupConversion.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/GroupConversion.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/GroupConversion.java
new file mode 100644
index 0000000..9ef724e
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/GroupConversion.java
@@ -0,0 +1,85 @@
+/*
+ * 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.util.Objects;
+import java.util.Optional;
+
+import javax.validation.metadata.GroupConversionDescriptor;
+
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.LazyInt;
+import org.apache.bval.util.Validate;
+
+public class GroupConversion implements GroupConversionDescriptor {
+    public static class Builder {
+        private final Class<?> from;
+
+        private Builder(Class<?> from) {
+            this.from = from;
+        }
+
+        public GroupConversion to(Class<?> to) {
+            return new GroupConversion(from, to);
+        }
+    }
+
+    public static Builder from(Class<?> from) {
+        return new Builder(from);
+    }
+
+    private final Class<?> from;
+    private final Class<?> to;
+    private final LazyInt hashCode;
+    private final Lazy<String> toString;
+
+    private GroupConversion(Class<?> from, Class<?> to) {
+        super();
+        this.from = Validate.notNull(from, "from");
+        this.to = Validate.notNull(to, "to");
+        this.hashCode = new LazyInt(() -> Objects.hash(this.from, this.to));
+        this.toString = new Lazy<>(
+            () -> String.format("%s from %s to %s", GroupConversion.class.getSimpleName(), this.from, this.to));
+    }
+
+    @Override
+    public Class<?> getFrom() {
+        return from;
+    }
+
+    @Override
+    public Class<?> getTo() {
+        return to;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return obj == this
+            || Optional.ofNullable(obj).filter(GroupConversion.class::isInstance).map(GroupConversion.class::cast)
+                .filter(gc -> Objects.equals(from, gc.from) && Objects.equals(to, gc.to)).isPresent();
+    }
+
+    @Override
+    public int hashCode() {
+        return hashCode.getAsInt();
+    }
+
+    @Override
+    public String toString() {
+        return toString.get();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/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
new file mode 100644
index 0000000..c2f9f0c
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
@@ -0,0 +1,291 @@
+/*
+ * 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.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import javax.validation.GroupDefinitionException;
+import javax.validation.GroupSequence;
+import javax.validation.ParameterNameProvider;
+import javax.validation.groups.Default;
+import javax.validation.metadata.PropertyDescriptor;
+import javax.validation.metadata.Scope;
+
+import org.apache.bval.jsr.ApacheValidatorFactory;
+import org.apache.bval.jsr.metadata.ContainerElementKey;
+import org.apache.bval.jsr.metadata.EmptyBuilder;
+import org.apache.bval.jsr.metadata.MetadataBuilder;
+import org.apache.bval.jsr.metadata.Metas;
+import org.apache.bval.jsr.metadata.Signature;
+import org.apache.bval.jsr.util.Methods;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+
+class MetadataReader {
+
+    class ForElement<E extends AnnotatedElement, B extends MetadataBuilder.ForElement<E>> {
+        final Metas<E> meta;
+        protected final B builder;
+
+        ForElement(Metas<E> meta, B builder) {
+            super();
+            this.meta = Validate.notNull(meta, "meta");
+            this.builder = Validate.notNull(builder, "builder");
+        }
+
+        Set<ConstraintD<?>> getConstraints() {
+            return builder.getConstraintsByScope(meta).entrySet().stream()
+                .flatMap(e -> describe(e.getValue(), e.getKey(), meta)).collect(ToUnmodifiable.set());
+        }
+
+        private Stream<ConstraintD<?>> describe(Annotation[] constraints, Scope scope, Metas<?> meta) {
+            return Stream.of(constraints).map(c -> new ConstraintD<>(c, scope, meta, validatorFactory));
+        }
+    }
+
+    class ForBean extends MetadataReader.ForElement<Class<?>, MetadataBuilder.ForClass> {
+        private final MetadataBuilder.ForBean beanBuilder;
+
+        ForBean(Metas<Class<?>> meta, MetadataBuilder.ForBean builder) {
+            super(meta, Validate.notNull(builder, "builder").getClass(meta));
+            this.beanBuilder = builder;
+        }
+
+        Map<String, PropertyDescriptor> getProperties(BeanD parent) {
+            final Map<String, List<PropertyD<?>>> properties = new LinkedHashMap<>();
+            final Function<? super String, ? extends List<PropertyD<?>>> descriptorList = k -> new ArrayList<>();
+
+            beanBuilder.getFields(meta).forEach((f, builder) -> {
+                final Field fld = Reflection.find(meta.getHost(), t -> Reflection.getDeclaredField(t, f));
+                properties.computeIfAbsent(f, descriptorList).add(new PropertyD.ForField(
+                    new MetadataReader.ForContainer<>(new Metas.ForField(fld), builder), parent));
+            });
+
+            beanBuilder.getGetters(meta).forEach((g, builder) -> {
+                final Method getter = Reflection.find(meta.getHost(), t -> {
+                    return Stream.of(Reflection.getDeclaredMethods(t)).filter(Methods::isGetter)
+                        .filter(m -> g.equals(Methods.propertyName(m))).findFirst().orElse(null);
+                });
+                Exceptions.raiseIf(getter == null, IllegalStateException::new,
+                    "Getter method for property %s not found", g);
+
+                properties.computeIfAbsent(g, descriptorList).add(new PropertyD.ForMethod(
+                    new MetadataReader.ForContainer<>(new Metas.ForMethod(getter), builder), parent));
+            });
+            return properties.entrySet().stream().collect(ToUnmodifiable.map(Map.Entry::getKey, e -> {
+                final List<PropertyD<?>> delegates = e.getValue();
+
+                if (delegates.size() == 1) {
+                    return delegates.get(0);
+                }
+                final Set<PropertyD<?>> constrained =
+                    delegates.stream().filter(DescriptorManager::isConstrained).collect(Collectors.toSet());
+                if (constrained.isEmpty()) {
+                    return delegates.get(0);
+                }
+                if (constrained.size() == 1) {
+                    return constrained.iterator().next();
+                }
+                return new ComposedD.ForProperty(delegates);
+            }));
+        }
+
+        Map<Signature, MethodD> getMethods(BeanD parent) {
+            final Map<Signature, MetadataBuilder.ForExecutable<Method>> methodBuilders = beanBuilder.getMethods(meta);
+            if (methodBuilders.isEmpty()) {
+                return Collections.emptyMap();
+            }
+            final Map<Signature, MethodD> result = new LinkedHashMap<>();
+
+            methodBuilders.forEach((sig, builder) -> {
+                final Method m = Reflection.find(meta.getHost(),
+                    t -> Reflection.getDeclaredMethod(t, sig.getName(), sig.getParameterTypes()));
+
+                result.put(sig, new MethodD(new MetadataReader.ForMethod(new Metas.ForMethod(m), builder), parent));
+            });
+            return Collections.unmodifiableMap(result);
+        }
+
+        Map<Signature, ConstructorD> getConstructors(BeanD parent) {
+            final Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> ctorBuilders =
+                beanBuilder.getConstructors(meta);
+
+            if (ctorBuilders.isEmpty()) {
+                return Collections.emptyMap();
+            }
+            final Map<Signature, ConstructorD> result = new LinkedHashMap<>();
+
+            ctorBuilders.forEach((sig, builder) -> {
+                final Constructor<?> c = Reflection.getDeclaredConstructor(meta.getHost(), sig.getParameterTypes());
+                result.put(sig,
+                    new ConstructorD(new MetadataReader.ForConstructor(new Metas.ForConstructor(c), builder), parent));
+            });
+            return Collections.unmodifiableMap(result);
+        }
+
+        List<Class<?>> getGroupSequence() {
+            List<Class<?>> result = builder.getGroupSequence(meta);
+            if (result == null) {
+                // resolve group sequence/Default redefinition up class hierarchy:
+                final Class<?> superclass = meta.getHost().getSuperclass();
+                if (superclass != null) {
+                    // attempt to mock parent sequence intent by appending this type immediately after supertype:
+                    result = ((ElementD<?, ?>) validatorFactory.getDescriptorManager().getBeanDescriptor(superclass))
+                        .getGroupSequence();
+                    if (result != null) {
+                        result = new ArrayList<>(result);
+                        result.add(result.indexOf(superclass) + 1, meta.getHost());
+                    }
+                }
+            }
+            if (result != null) {
+                Exceptions.raiseUnless(result.contains(meta.getHost()), GroupDefinitionException::new,
+                    "@%s for %s must contain %<s", GroupSequence.class.getSimpleName(), meta.getHost());
+                Exceptions.raiseIf(result.contains(Default.class), GroupDefinitionException::new,
+                    "@%s for %s must not contain %s", GroupSequence.class.getSimpleName(), meta.getHost(),
+                    Default.class.getName());
+            }
+            return result;
+        }
+    }
+
+    class ForContainer<E extends AnnotatedElement> extends ForElement<E, MetadataBuilder.ForContainer<E>> {
+
+        ForContainer(Metas<E> meta, MetadataBuilder.ForContainer<E> builder) {
+            super(meta, builder);
+        }
+
+        boolean isCascaded() {
+            return builder.isCascade(meta);
+        }
+
+        Set<GroupConversion> getGroupConversions() {
+            return builder.getGroupConversions(meta);
+        }
+
+        Set<ContainerElementTypeD> getContainerElementTypes(CascadableContainerD<?, ?> parent) {
+            final Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> containerElementTypes =
+                builder.getContainerElementTypes(meta);
+
+            if (containerElementTypes.isEmpty()) {
+                return Collections.emptySet();
+            }
+            final Set<ContainerElementTypeD> result =
+                new TreeSet<>(Comparator.comparing(ContainerElementTypeD::getKey));
+
+            containerElementTypes.forEach((k, builder) -> {
+                result.add(new ContainerElementTypeD(k,
+                    new MetadataReader.ForContainer<>(new Metas.ForContainerElement(meta, k), builder), parent));
+            });
+            return Collections.unmodifiableSet(result);
+        }
+    }
+
+    abstract class ForExecutable<E extends Executable, SELF extends ForExecutable<E, SELF>>
+        extends ForElement<E, MetadataBuilder.ForElement<E>> {
+        private final MetadataBuilder.ForExecutable<E> executableBuilder;
+
+        ForExecutable(Metas<E> meta, MetadataBuilder.ForExecutable<E> executableBuilder) {
+            super(meta, EmptyBuilder.instance().forElement());
+            this.executableBuilder = Validate.notNull(executableBuilder, "executableBuilder");
+        }
+
+        <X extends ExecutableD<E, SELF, X>> List<ParameterD<X>> getParameterDescriptors(X parent) {
+            final Parameter[] parameters = meta.getHost().getParameters();
+
+            final List<String> parameterNames =
+                getParameterNames(validatorFactory.getParameterNameProvider(), meta.getHost());
+
+            final List<MetadataBuilder.ForContainer<Parameter>> builders = executableBuilder.getParameters(meta);
+
+            return IntStream.range(0, parameters.length).mapToObj(i -> {
+                final Metas.ForParameter param = new Metas.ForParameter(parameters[i], parameterNames.get(i));
+                return new ParameterD<>(param, i, new MetadataReader.ForContainer<>(param, builders.get(i)), parent);
+            }).collect(ToUnmodifiable.list());
+        }
+
+        <X extends ExecutableD<E, SELF, X>> CrossParameterD<X, E> getCrossParameterDescriptor(X parent) {
+            final Metas.ForCrossParameter<E> cp = new Metas.ForCrossParameter<>(meta);
+            return new CrossParameterD<>(new MetadataReader.ForElement<>(cp, executableBuilder.getCrossParameter(cp)),
+                parent);
+        }
+
+        <X extends ExecutableD<E, SELF, X>> ReturnValueD<X, E> getReturnValueDescriptor(X parent) {
+            return new ReturnValueD<>(new MetadataReader.ForContainer<>(meta, executableBuilder.getReturnValue(meta)),
+                parent);
+        }
+
+        abstract List<String> getParameterNames(ParameterNameProvider parameterNameProvider, E host);
+    }
+
+    class ForMethod extends ForExecutable<Method, ForMethod> {
+        ForMethod(Metas<Method> meta, MetadataBuilder.ForExecutable<Method> builder) {
+            super(meta, builder);
+        }
+
+        @Override
+        List<String> getParameterNames(ParameterNameProvider parameterNameProvider, Method host) {
+            return parameterNameProvider.getParameterNames(host);
+        }
+    }
+
+    class ForConstructor extends ForExecutable<Constructor<?>, ForConstructor> {
+
+        ForConstructor(Metas<Constructor<?>> meta, MetadataBuilder.ForExecutable<Constructor<?>> builder) {
+            super(meta, builder);
+        }
+
+        @Override
+        List<String> getParameterNames(ParameterNameProvider parameterNameProvider, Constructor<?> host) {
+            return parameterNameProvider.getParameterNames(host);
+        }
+    }
+
+    private final ApacheValidatorFactory validatorFactory;
+
+    MetadataReader(ApacheValidatorFactory validatorFactory) {
+        super();
+        this.validatorFactory = Validate.notNull(validatorFactory, "validatorFactory");
+    }
+
+    MetadataReader.ForBean forBean(Class<?> beanClass, MetadataBuilder.ForBean builder) {
+        return new MetadataReader.ForBean(new Metas.ForClass(beanClass), builder);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MethodD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MethodD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MethodD.java
new file mode 100644
index 0000000..647569d
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MethodD.java
@@ -0,0 +1,49 @@
+/*
+ * 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.reflect.Method;
+
+import javax.validation.metadata.MethodDescriptor;
+import javax.validation.metadata.MethodType;
+
+import org.apache.bval.jsr.util.Methods;
+
+class MethodD extends ExecutableD<Method, MetadataReader.ForMethod, MethodD> implements MethodDescriptor {
+    private final MethodType methodType;
+
+    MethodD(MetadataReader.ForMethod reader, BeanD parent) {
+        super(reader, parent);
+        methodType = Methods.isGetter(reader.meta.getHost()) ? MethodType.GETTER : MethodType.NON_GETTER;
+    }
+
+    @Override
+    public Class<?> getElementClass() {
+        return getTarget().getReturnType();
+    }
+
+    MethodType getMethodType() {
+        return methodType;
+    }
+
+    @Override
+    protected String nameOf(Method e) {
+        return e.getName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/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
new file mode 100644
index 0000000..951eccb
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ParameterD.java
@@ -0,0 +1,62 @@
+/*
+ * 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.reflect.Parameter;
+
+import javax.validation.metadata.ParameterDescriptor;
+
+import org.apache.bval.jsr.metadata.Metas;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.TypeUtils;
+
+public class ParameterD<P extends ExecutableD<?, ?, P>> extends CascadableContainerD<P, Parameter>
+    implements ParameterDescriptor {
+
+    private final int index;
+    private final String name;
+    private final Class<?> type;
+
+    protected ParameterD(Metas.ForParameter meta, int index, MetadataReader.ForContainer<Parameter> reader, P parent) {
+        super(reader, parent);
+
+        Validate.isTrue(index >= 0 && index < meta.getHost().getDeclaringExecutable().getParameterCount(),
+            "Invalid parameter index %d", index);
+
+        this.index = index;
+
+        name = reader.meta.getName();
+        type = TypeUtils.getRawType(reader.meta.getType(), parent.getElementClass());
+    }
+
+    @Override
+    public Class<?> getElementClass() {
+        return type;
+    }
+
+    @Override
+    public int getIndex() {
+        return index;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/PropertyD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/PropertyD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/PropertyD.java
new file mode 100644
index 0000000..818e7e0
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/PropertyD.java
@@ -0,0 +1,106 @@
+/*
+ *  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.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import javax.validation.metadata.PropertyDescriptor;
+
+import org.apache.bval.jsr.GraphContext;
+import org.apache.bval.jsr.util.Methods;
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+
+@Privilizing(@CallTo(Reflection.class))
+public abstract class PropertyD<E extends AnnotatedElement> extends CascadableContainerD<BeanD, E>
+    implements PropertyDescriptor {
+
+    static class ForField extends PropertyD<Field> {
+
+        ForField(MetadataReader.ForContainer<Field> reader, BeanD parent) {
+            super(reader, parent);
+        }
+
+        @Override
+        public String getPropertyName() {
+            return host.getName();
+        }
+
+        @Override
+        public Object getValue(Object parent) throws Exception {
+            final boolean mustUnset = Reflection.setAccessible(host, true);
+            try {
+                return host.get(parent);
+            } catch (IllegalAccessException e) {
+                throw new IllegalArgumentException(e);
+            } finally {
+                if (mustUnset) {
+                    Reflection.setAccessible(host, false);
+                }
+            }
+        }
+    }
+
+    static class ForMethod extends PropertyD<Method> {
+
+        ForMethod(MetadataReader.ForContainer<Method> reader, BeanD parent) {
+            super(reader, parent);
+        }
+
+        @Override
+        public String getPropertyName() {
+            return Methods.propertyName(host);
+        }
+
+        @Override
+        public Object getValue(Object parent) throws Exception {
+            final boolean mustUnset = Reflection.setAccessible(host, true);
+            try {
+                return host.invoke(parent);
+            } catch (IllegalAccessException | InvocationTargetException e) {
+                throw new IllegalArgumentException(e);
+            } finally {
+                if (mustUnset) {
+                    Reflection.setAccessible(host, false);
+                }
+            }
+        }
+    }
+
+    protected final E host;
+
+    protected PropertyD(MetadataReader.ForContainer<E> reader, BeanD parent) {
+        super(reader, parent);
+        this.host = reader.meta.getHost();
+    }
+
+    @Override
+    protected Stream<GraphContext> readImpl(GraphContext context) throws Exception {
+        final Supplier<NodeImpl> propertyNode = () -> new NodeImpl.PropertyNodeImpl(getPropertyName());
+        final Object value = getValue(context.getValue());
+        return Stream.of(context.child(propertyNode.get(), value));
+    }
+
+    public abstract Object getValue(Object parent) throws Exception;
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/40ac09f7/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ReturnValueD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ReturnValueD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ReturnValueD.java
new file mode 100644
index 0000000..a2204fc
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ReturnValueD.java
@@ -0,0 +1,36 @@
+/*
+ * 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.reflect.Executable;
+
+import javax.validation.metadata.ReturnValueDescriptor;
+
+public class ReturnValueD<P extends ExecutableD<?, ?, P>, E extends Executable> extends CascadableContainerD<P, E>
+    implements ReturnValueDescriptor {
+
+    ReturnValueD(MetadataReader.ForContainer<E> reader, P parent) {
+        super(reader, parent);
+    }
+
+    @Override
+    public Class<?> getElementClass() {
+        return parent.getElementClass();
+    }
+}


[17/17] bval git commit: test updates

Posted by mb...@apache.org.
test updates


Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/1eb57573
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/1eb57573
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/1eb57573

Branch: refs/heads/bv2
Commit: 1eb57573d70fbe251762535e322176e51c626128
Parents: f87be82
Author: Matt Benson <mb...@apache.org>
Authored: Wed Feb 21 15:00:56 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Wed Feb 21 15:00:56 2018 -0600

----------------------------------------------------------------------
 .../org/apache/bval/jsr/BeanDescriptorTest.java |  3 +
 .../bval/jsr/CustomValidatorFactoryTest.java    |  6 ++
 .../java/org/apache/bval/jsr/Jsr303Test.java    | 24 ++++--
 .../org/apache/bval/jsr/ValidationTest.java     |  2 +
 .../org/apache/bval/jsr/example/Engine.java     |  8 +-
 .../jsr/extensions/MethodValidatorImplTest.java |  4 +-
 .../jsr/groups/GroupSequenceIsolationTest.java  | 10 +--
 .../bval/jsr/groups/GroupsComputerTest.java     | 88 ++++++--------------
 8 files changed, 65 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/1eb57573/bval-jsr/src/test/java/org/apache/bval/jsr/BeanDescriptorTest.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/BeanDescriptorTest.java b/bval-jsr/src/test/java/org/apache/bval/jsr/BeanDescriptorTest.java
index d81f90a..5c949c1 100644
--- a/bval-jsr/src/test/java/org/apache/bval/jsr/BeanDescriptorTest.java
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/BeanDescriptorTest.java
@@ -42,6 +42,7 @@ import javax.validation.metadata.PropertyDescriptor;
 import javax.validation.metadata.Scope;
 
 import org.apache.bval.jsr.util.TestUtils;
+import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -120,6 +121,8 @@ public class BeanDescriptorTest extends ValidationTestBase {
      * interface group when querying the interface directly.
      */
     @Test
+    // spec does not dictate this
+    @Ignore
     public void testNoImplicitGroupWhenQueryingInterfaceDirectly() {
         Set<ConstraintDescriptor<?>> nameDescriptors =
             validator.getConstraintsForClass(Person.class).getConstraintsForProperty("name").getConstraintDescriptors();

http://git-wip-us.apache.org/repos/asf/bval/blob/1eb57573/bval-jsr/src/test/java/org/apache/bval/jsr/CustomValidatorFactoryTest.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/CustomValidatorFactoryTest.java b/bval-jsr/src/test/java/org/apache/bval/jsr/CustomValidatorFactoryTest.java
index 355d676..fca45d1 100644
--- a/bval-jsr/src/test/java/org/apache/bval/jsr/CustomValidatorFactoryTest.java
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/CustomValidatorFactoryTest.java
@@ -20,6 +20,7 @@ package org.apache.bval.jsr;
 
 import static org.hamcrest.CoreMatchers.isA;
 
+import javax.validation.ClockProvider;
 import javax.validation.ConstraintValidatorFactory;
 import javax.validation.MessageInterpolator;
 import javax.validation.ParameterNameProvider;
@@ -95,6 +96,11 @@ public class CustomValidatorFactoryTest {
             return null;
         }
 
+        @Override
+        public ClockProvider getClockProvider() {
+            return null;
+        }
+
     }
 
     public static class NotAValidatorFactory {

http://git-wip-us.apache.org/repos/asf/bval/blob/1eb57573/bval-jsr/src/test/java/org/apache/bval/jsr/Jsr303Test.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/Jsr303Test.java b/bval-jsr/src/test/java/org/apache/bval/jsr/Jsr303Test.java
index 83f2427..2dfe493 100644
--- a/bval-jsr/src/test/java/org/apache/bval/jsr/Jsr303Test.java
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/Jsr303Test.java
@@ -31,8 +31,10 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Set;
 
+import javax.validation.ConstraintValidator;
 import javax.validation.ConstraintViolation;
 import javax.validation.UnexpectedTypeException;
+import javax.validation.constraints.Max;
 import javax.validation.metadata.BeanDescriptor;
 import javax.validation.metadata.ConstraintDescriptor;
 import javax.validation.metadata.ElementDescriptor;
@@ -48,6 +50,7 @@ import org.apache.bval.jsr.example.NoValidatorTestEntity;
 import org.apache.bval.jsr.example.Second;
 import org.apache.bval.jsr.example.SizeTestEntity;
 import org.apache.bval.jsr.util.TestUtils;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -65,8 +68,10 @@ public class Jsr303Test extends ValidationTestBase {
         assertTrue(cons.getConstraintsForProperty("author").hasConstraints());
         assertTrue(cons.getConstraintsForProperty("title").hasConstraints());
         assertTrue(cons.getConstraintsForProperty("uselessField").hasConstraints());
-        // cons.getConstraintsForProperty("unconstraintField") == null without Introspector
-        // cons.getConstraintsForProperty("unconstraintField") != null with Introspector
+        // cons.getConstraintsForProperty("unconstraintField") == null without
+        // Introspector
+        // cons.getConstraintsForProperty("unconstraintField") != null with
+        // Introspector
         assertTrue(cons.getConstraintsForProperty("unconstraintField") == null
             || !cons.getConstraintsForProperty("unconstraintField").hasConstraints());
         assertNull(cons.getConstraintsForProperty("unknownField"));
@@ -83,11 +88,13 @@ public class Jsr303Test extends ValidationTestBase {
 
     @Test(expected = IllegalArgumentException.class)
     public void testUnknownProperty() {
-        // tests for issue 22: validation of unknown field cause ValidationException
+        // tests for issue 22: validation of unknown field cause
+        // ValidationException
         validator.validateValue(Book.class, "unknownProperty", 4);
     }
 
     @Test(expected = IllegalArgumentException.class)
+    @Ignore
     public void testValidateNonCascadedRealNestedProperty() {
         validator.validateValue(IllustratedBook.class, "illustrator.firstName", "Edgar");
     }
@@ -95,7 +102,8 @@ public class Jsr303Test extends ValidationTestBase {
     @Test
     public void testMetadataAPI_Book() {
         assertNotNull(validator.getConstraintsForClass(Book.class));
-        // not necessary for implementation correctness, but we'll test nevertheless:
+        // not necessary for implementation correctness, but we'll test
+        // nevertheless:
         assertSame(validator.getConstraintsForClass(Book.class), validator.getConstraintsForClass(Book.class));
         BeanDescriptor bc = validator.getConstraintsForClass(Book.class);
         assertEquals(Book.class, bc.getElementClass());
@@ -135,8 +143,9 @@ public class Jsr303Test extends ValidationTestBase {
             validator.getConstraintsForClass(Address.class).getConstraintsForProperty("addressline1");
         assertNotNull(desc);
         boolean found = false;
+
         for (ConstraintDescriptor<?> each : desc.getConstraintDescriptors()) {
-            if (SizeValidatorForCharSequence.class.equals(each.getConstraintValidatorClasses().get(0))) {
+            if (each.getConstraintValidatorClasses().contains(SizeValidatorForCharSequence.class)) {
                 assertTrue(each.getAttributes().containsKey("max"));
                 assertEquals(30, each.getAttributes().get("max"));
                 found = true;
@@ -175,9 +184,8 @@ public class Jsr303Test extends ValidationTestBase {
     @Test
     public void testConstraintValidatorResolutionAlgorithm2() {
         thrown.expect(UnexpectedTypeException.class);
-        thrown.expectMessage("No validator could be found for type java.lang.Object. "
-            + "See: @Max at private java.lang.Object org.apache.bval.jsr.example." + "NoValidatorTestEntity.anything");
-
+        thrown.expectMessage(String.format("No compliant %s %s found for annotated element of type %s",
+            Max.class.getName(), ConstraintValidator.class.getSimpleName(), Object.class.getName()));
         NoValidatorTestEntity entity2 = new NoValidatorTestEntity();
         validator.validate(entity2);
     }

http://git-wip-us.apache.org/repos/asf/bval/blob/1eb57573/bval-jsr/src/test/java/org/apache/bval/jsr/ValidationTest.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/ValidationTest.java b/bval-jsr/src/test/java/org/apache/bval/jsr/ValidationTest.java
index f6c7a4f..b696627 100644
--- a/bval-jsr/src/test/java/org/apache/bval/jsr/ValidationTest.java
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/ValidationTest.java
@@ -59,6 +59,7 @@ import org.apache.bval.jsr.example.Last;
 import org.apache.bval.jsr.example.RecursiveFoo;
 import org.apache.bval.jsr.util.TestUtils;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -73,6 +74,7 @@ public class ValidationTest extends ValidationTestBase {
     }
 
     @Test
+    @Ignore // per spec, fields should after all be validated on subclasses
     public void testAccessStrategies_field_method() {
         AccessTestBusinessObject o1 = new AccessTestBusinessObject("1");
         assertTrue(validator.validate(o1).isEmpty());

http://git-wip-us.apache.org/repos/asf/bval/blob/1eb57573/bval-jsr/src/test/java/org/apache/bval/jsr/example/Engine.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/example/Engine.java b/bval-jsr/src/test/java/org/apache/bval/jsr/example/Engine.java
index 4ee8101..b65d091 100644
--- a/bval-jsr/src/test/java/org/apache/bval/jsr/example/Engine.java
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/example/Engine.java
@@ -21,9 +21,11 @@ package org.apache.bval.jsr.example;
 import javax.validation.constraints.Pattern;
 
 public class Engine {
-    @Pattern.List({
-        @Pattern(regexp = "^[A-Z0-9-]+$", flags = Pattern.Flag.CASE_INSENSITIVE, message = "must contain alphabetical characters only"),
-        @Pattern(regexp = "^....-....-....$", message = "must match ....-....-....") })
+    // TODO See about Windows bug with container @ Field#getAnnotatedType()
+//    @Pattern.List({
+        @Pattern(regexp = "^[A-Z0-9-]+$", flags = Pattern.Flag.CASE_INSENSITIVE, message = "must contain alphabetical characters only")//,
+        @Pattern(regexp = "^....-....-....$", message = "must match ....-....-....")
+//        })
     public String serialNumber;
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/bval/blob/1eb57573/bval-jsr/src/test/java/org/apache/bval/jsr/extensions/MethodValidatorImplTest.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/extensions/MethodValidatorImplTest.java b/bval-jsr/src/test/java/org/apache/bval/jsr/extensions/MethodValidatorImplTest.java
index 4740f5b..d05b47d 100644
--- a/bval-jsr/src/test/java/org/apache/bval/jsr/extensions/MethodValidatorImplTest.java
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/extensions/MethodValidatorImplTest.java
@@ -26,7 +26,7 @@ import javax.validation.constraints.Size;
 import javax.validation.executable.ExecutableValidator;
 
 import org.apache.bval.jsr.ApacheValidationProvider;
-import org.apache.bval.jsr.ClassValidator;
+import org.apache.bval.jsr.ValidatorImpl;
 import org.apache.bval.jsr.extensions.ExampleMethodService.Person;
 import org.junit.Ignore;
 
@@ -53,7 +53,7 @@ public class MethodValidatorImplTest extends TestCase {
 
     public void testUnwrap() {
         Validator v = getValidator();
-        ClassValidator cv = v.unwrap(ClassValidator.class);
+        ValidatorImpl cv = v.unwrap(ValidatorImpl.class);
         assertSame(v, cv);
         assertSame(v, v.unwrap(Validator.class));
         assertNotNull(v.forExecutables());

http://git-wip-us.apache.org/repos/asf/bval/blob/1eb57573/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupSequenceIsolationTest.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupSequenceIsolationTest.java b/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupSequenceIsolationTest.java
index 89489fb..17d6dba 100644
--- a/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupSequenceIsolationTest.java
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupSequenceIsolationTest.java
@@ -52,16 +52,13 @@ public class GroupSequenceIsolationTest extends ValidationTestBase {
     public void testGroupSequencesInHierarchyClasses() {
         HolderWithNoGS h = new HolderWithNoGS();
 
-        assertEquals(set("a1", "b2"), violationPaths(validator.validate(h)));
-
-        h.a1 = "good";
         assertEquals(set("a2", "b2"), violationPaths(validator.validate(h)));
 
         h.b2 = "good";
-        assertEquals(set("a2", "b1"), violationPaths(validator.validate(h)));
-
-        h.b1 = "good";
         assertEquals(set("a2"), violationPaths(validator.validate(h)));
+
+        h.a2 = "good";
+        assertEquals(set("b1"), violationPaths(validator.validate(h)));
     }
 
     /**
@@ -82,6 +79,7 @@ public class GroupSequenceIsolationTest extends ValidationTestBase {
         assertEquals(Collections.singleton("b1"), violationPaths(validator.validate(h)));
     }
 
+    @SafeVarargs
     private static <T> Set<T> set(T... elements) {
         return new HashSet<T>(Arrays.asList(elements));
     }

http://git-wip-us.apache.org/repos/asf/bval/blob/1eb57573/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupsComputerTest.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupsComputerTest.java b/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupsComputerTest.java
index 13a49e1..4222339 100644
--- a/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupsComputerTest.java
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupsComputerTest.java
@@ -18,22 +18,25 @@
  */
 package org.apache.bval.jsr.groups;
 
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-import org.apache.bval.jsr.example.Address;
-import org.apache.bval.jsr.example.First;
-import org.apache.bval.jsr.example.Last;
-import org.apache.bval.jsr.example.Second;
+import static org.junit.Assert.assertEquals;
 
-import javax.validation.GroupDefinitionException;
-import javax.validation.ValidationException;
-import javax.validation.groups.Default;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
+import javax.validation.GroupDefinitionException;
+import javax.validation.ValidationException;
+import javax.validation.groups.Default;
+
+import org.apache.bval.jsr.example.Address;
+import org.apache.bval.jsr.example.First;
+import org.apache.bval.jsr.example.Last;
+import org.apache.bval.jsr.example.Second;
+import org.junit.Before;
+import org.junit.Test;
+
 /**
  * GroupListComputer Tester.
  *
@@ -41,79 +44,41 @@ import java.util.Set;
  * @version 1.0
  * @since <pre>04/09/2009</pre>
  */
-public class GroupsComputerTest extends TestCase {
+public class GroupsComputerTest {
     GroupsComputer groupsComputer;
 
-    public GroupsComputerTest(String name) {
-        super(name);
-    }
-
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
         groupsComputer = new GroupsComputer();
     }
 
-    @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
-    }
-
-    public static Test suite() {
-        return new TestSuite(GroupsComputerTest.class);
-    }
-
+    @Test(expected = ValidationException.class)
     public void testComputeGroupsNotAnInterface() {
-        Set<Class<?>> groups = new HashSet<Class<?>>();
-        groups.add(String.class);
-        try {
-            groupsComputer.computeGroups(groups);
-            fail();
-        } catch (ValidationException ex) {
-
-        }
+        groupsComputer.computeGroups(Collections.singleton(String.class));
     }
 
+    @Test(expected = IllegalArgumentException.class)
     public void testGroupChainForNull() {
-        try {
             groupsComputer.computeGroups((Class<?>[]) null);
-            fail();
-        } catch (IllegalArgumentException ex) {
-
-        }
     }
 
+    @Test
     public void testGroupChainForEmptySet() {
-        try {
-            groupsComputer.computeGroups(new HashSet<Class<?>>());
-            fail();
-        } catch (IllegalArgumentException ex) {
-
-        }
+        assertEquals(Collections.singletonList(Group.DEFAULT),
+            groupsComputer.computeGroups(new HashSet<Class<?>>()).getGroups());
     }
 
+    @Test(expected = GroupDefinitionException.class)
     public void testCyclicGroupSequences() {
-        try {
-            Set<Class<?>> groups = new HashSet<Class<?>>();
-            groups.add(CyclicGroupSequence1.class);
-            groupsComputer.computeGroups(groups);
-            fail();
-        } catch (GroupDefinitionException ex) {
-
-        }
+        groupsComputer.computeGroups(Collections.singleton(CyclicGroupSequence1.class));
     }
 
+    @Test(expected = GroupDefinitionException.class)
     public void testCyclicGroupSequence() {
-        try {
-            Set<Class<?>> groups = new HashSet<Class<?>>();
-            groups.add(CyclicGroupSequence.class);
-            groupsComputer.computeGroups(groups);
-            fail();
-        } catch (GroupDefinitionException ex) {
-
-        }
+        groupsComputer.computeGroups(Collections.singleton(CyclicGroupSequence.class));
     }
 
+    @Test
     public void testGroupDuplicates() {
         Set<Class<?>> groups = new HashSet<Class<?>>();
         groups.add(First.class);
@@ -136,6 +101,7 @@ public class GroupsComputerTest extends TestCase {
         assertEquals(2, chain.groups.size());
     }
 
+    @Test
     public void testSequenceResolution() {
         Set<Class<?>> groups = new HashSet<Class<?>>();
         groups.add(Address.Complete.class);


[04/17] bval git commit: BV2: xml processing changes

Posted by mb...@apache.org.
http://git-wip-us.apache.org/repos/asf/bval/blob/5c09f0dd/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java
index 7b9b14f..bed2e10 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java
@@ -22,12 +22,14 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
 import java.util.Collection;
-import java.util.Enumeration;
+import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
+import java.util.Optional;
+import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.logging.Level;
@@ -40,59 +42,34 @@ import javax.validation.TraversableResolver;
 import javax.validation.ValidationException;
 import javax.validation.executable.ExecutableType;
 import javax.validation.spi.ValidationProvider;
-import javax.xml.XMLConstants;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.transform.stream.StreamSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
 
 import org.apache.bval.jsr.BootstrapConfigurationImpl;
 import org.apache.bval.jsr.ConfigurationImpl;
-import org.apache.bval.jsr.util.IOs;
+import org.apache.bval.jsr.metadata.XmlBuilder;
+import org.apache.bval.util.Exceptions;
 import org.apache.bval.util.reflection.Reflection;
 import org.apache.commons.weaver.privilizer.Privileged;
 import org.apache.commons.weaver.privilizer.Privilizing;
 import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
-import org.xml.sax.SAXException;
+import org.xml.sax.InputSource;
 
 /**
  * Description: uses jaxb to parse validation.xml<br/>
  */
 @Privilizing(@CallTo(Reflection.class))
 public class ValidationParser {
+
     private static final String DEFAULT_VALIDATION_XML_FILE = "META-INF/validation.xml";
-    private static final String VALIDATION_CONFIGURATION_XSD = "META-INF/validation-configuration-1.1.xsd";
     private static final Logger log = Logger.getLogger(ValidationParser.class.getName());
-    private static final ConcurrentMap<String, Schema> SCHEMA_CACHE = new ConcurrentHashMap<String, Schema>(1);
-
-    private ValidationConfigType xmlConfig;
-    private BootstrapConfigurationImpl bootstrap;
-    private Collection<ValidationException> exceptions = new CopyOnWriteArrayList<ValidationException>();
-
-    private ValidationParser() {
-        // no-op
-    }
 
-    public void applyConfigWithInstantiation(ConfigurationImpl targetConfig) {
-        if (xmlConfig == null) {
-            return;
-        }
-
-        applyProviderClass(xmlConfig, targetConfig);
-        applyMessageInterpolator(xmlConfig, targetConfig);
-        applyTraversableResolver(xmlConfig, targetConfig);
-        applyConstraintFactory(xmlConfig, targetConfig);
-        applyParameterNameProvider(xmlConfig, targetConfig);
-        applyMappingStreams(xmlConfig, targetConfig);
-        applyProperties(xmlConfig, targetConfig);
-    }
-
-    public BootstrapConfigurationImpl getBootstrap() {
-        return bootstrap;
-    }
+    private static final SchemaManager SCHEMA_MANAGER = new SchemaManager.Builder()
+        .add(null, "http://jboss.org/xml/ns/javax/validation/configuration",
+            "META-INF/validation-configuration-1.0.xsd")
+        .add(XmlBuilder.Version.v11.getId(), "http://jboss.org/xml/ns/javax/validation/configuration",
+            "META-INF/validation-configuration-1.1.xsd")
+        .add(XmlBuilder.Version.v20.getId(), "http://xmlns.jcp.org/xml/ns/javax/validation/configuration",
+            "META-INF/validation-configuration-2.0.xsd")
+        .build();
 
     public static String getValidationXmlFile(String file) {
         if (file == null) {
@@ -101,19 +78,30 @@ public class ValidationParser {
         return file;
     }
 
-    public static ValidationParser processValidationConfig(final String file, final ConfigurationImpl targetConfig, final boolean ignoreXml) {
+    public static ValidationParser processValidationConfig(final String file, final ConfigurationImpl targetConfig,
+        final boolean ignoreXml) {
         final ValidationParser parser = new ValidationParser();
 
         if (!ignoreXml) {
             parser.xmlConfig = parseXmlConfig(file);
         }
 
-        if (parser.xmlConfig != null) {
+        if (parser.xmlConfig == null) { // default config
+            final CopyOnWriteArraySet<ExecutableType> executableTypes = new CopyOnWriteArraySet<>();
+            executableTypes.add(ExecutableType.CONSTRUCTORS);
+            executableTypes.add(ExecutableType.NON_GETTER_METHODS);
+
+            parser.bootstrap = new BootstrapConfigurationImpl(null, null, null, null, null, Collections.emptySet(),
+                true, executableTypes, Collections.emptyMap(), null, Collections.emptySet());
+
+            targetConfig.setExecutableValidation(executableTypes);
+        } else {
             if (parser.xmlConfig.getExecutableValidation() == null) {
                 final ExecutableValidationType value = new ExecutableValidationType();
                 value.setEnabled(true);
 
-                final DefaultValidatedExecutableTypesType defaultValidatedExecutableTypes = new DefaultValidatedExecutableTypesType();
+                final DefaultValidatedExecutableTypesType defaultValidatedExecutableTypes =
+                    new DefaultValidatedExecutableTypesType();
                 value.setDefaultValidatedExecutableTypes(defaultValidatedExecutableTypes);
                 defaultValidatedExecutableTypes.getExecutableType().add(ExecutableType.CONSTRUCTORS);
                 defaultValidatedExecutableTypes.getExecutableType().add(ExecutableType.NON_GETTER_METHODS);
@@ -123,35 +111,19 @@ public class ValidationParser {
 
             applySimpleConfig(parser.xmlConfig, targetConfig);
 
-            parser.bootstrap = new BootstrapConfigurationImpl(
-                    parser.xmlConfig.getDefaultProvider(),
-                    parser.xmlConfig.getConstraintValidatorFactory(),
-                    parser.xmlConfig.getMessageInterpolator(),
-                    parser.xmlConfig.getTraversableResolver(),
-                    parser.xmlConfig.getParameterNameProvider(),
-                    new CopyOnWriteArraySet<String>(parser.xmlConfig.getConstraintMapping()),
-                    parser.xmlConfig.getExecutableValidation().getEnabled(),
-                    new CopyOnWriteArraySet<ExecutableType>(targetConfig.getExecutableValidation()),
-                    toMap(parser.xmlConfig.getProperty()));
-        } else { // default config
-            final CopyOnWriteArraySet<ExecutableType> executableTypes = new CopyOnWriteArraySet<ExecutableType>();
-            executableTypes.add(ExecutableType.CONSTRUCTORS);
-            executableTypes.add(ExecutableType.NON_GETTER_METHODS);
-
-            parser.bootstrap = new BootstrapConfigurationImpl(
-                    null, null, null, null, null,
-                    new CopyOnWriteArraySet<String>(),
-                    true,
-                    executableTypes,
-                    new HashMap<String, String>());
-
-            targetConfig.setExecutableValidation(executableTypes);
+            parser.bootstrap = new BootstrapConfigurationImpl(parser.xmlConfig.getDefaultProvider(),
+                parser.xmlConfig.getConstraintValidatorFactory(), parser.xmlConfig.getMessageInterpolator(),
+                parser.xmlConfig.getTraversableResolver(), parser.xmlConfig.getParameterNameProvider(),
+                new HashSet<>(parser.xmlConfig.getConstraintMapping()),
+                parser.xmlConfig.getExecutableValidation().getEnabled(),
+                new HashSet<>(targetConfig.getExecutableValidation()), toMap(parser.xmlConfig.getProperty()),
+                parser.xmlConfig.getClockProvider(), new HashSet<>(parser.xmlConfig.getValueExtractor()));
         }
         return parser;
     }
 
     private static Map<String, String> toMap(final List<PropertyType> property) {
-        final Map<String, String> map = new HashMap<String, String>();
+        final Map<String, String> map = new HashMap<>();
         if (property != null) {
             for (final PropertyType p : property) {
                 map.put(p.getName(), p.getValue());
@@ -162,78 +134,26 @@ public class ValidationParser {
 
     @Privileged
     private static ValidationConfigType parseXmlConfig(final String validationXmlFile) {
-        InputStream inputStream = null;
-        try {
-            inputStream = getInputStream(getValidationXmlFile(validationXmlFile));
+        try (InputStream inputStream = getInputStream(getValidationXmlFile(validationXmlFile))) {
             if (inputStream == null) {
-            	log.log(Level.FINEST, String.format("No %s found. Using annotation based configuration only.", validationXmlFile));
+                log.log(Level.FINEST,
+                    String.format("No %s found. Using annotation based configuration only.", validationXmlFile));
                 return null;
             }
-
             log.log(Level.FINEST, String.format("%s found.", validationXmlFile));
 
-            Schema schema = getSchema();
-            JAXBContext jc = JAXBContext.newInstance(ValidationConfigType.class);
-            Unmarshaller unmarshaller = jc.createUnmarshaller();
-            unmarshaller.setSchema(schema);
-            StreamSource stream = new StreamSource(inputStream);
-            JAXBElement<ValidationConfigType> root =
-                    unmarshaller.unmarshal(stream, ValidationConfigType.class);
-            return root.getValue();
-        } catch (JAXBException e) {
-            throw new ValidationException("Unable to parse " + validationXmlFile, e);
-        } catch (IOException e) {
+            return SCHEMA_MANAGER.unmarshal(new InputSource(inputStream), ValidationConfigType.class);
+        } catch (Exception e) {
             throw new ValidationException("Unable to parse " + validationXmlFile, e);
-        } finally {
-            IOs.closeQuietly(inputStream);
         }
     }
 
     protected static InputStream getInputStream(final String path) throws IOException {
         final ClassLoader loader = Reflection.getClassLoader(ValidationParser.class);
-        final InputStream inputStream = loader.getResourceAsStream(path);
-
-        if (inputStream != null) {
-            // spec says: If more than one META-INF/validation.xml file
-            // is found in the classpath, a ValidationException is raised.
-            final Enumeration<URL> urls = loader.getResources(path);
-            if (urls.hasMoreElements()) {
-                final String url = urls.nextElement().toString();
-                while (urls.hasMoreElements()) {
-                    if (!url.equals(urls.nextElement().toString())) { // complain when first duplicate found
-                        throw new ValidationException("More than one " + path + " is found in the classpath");
-                    }
-                }
-            }
-        }
-
-        return IOs.convertToMarkableInputStream(inputStream);
-    }
-
-    private static Schema getSchema() {
-        return getSchema(VALIDATION_CONFIGURATION_XSD);
-    }
-
-    static Schema getSchema(final String xsd) {
-        final Schema schema = SCHEMA_CACHE.get(xsd);
-        if (schema != null) {
-            return schema;
-        }
-
-        final ClassLoader loader = Reflection.getClassLoader(ValidationParser.class);
-        final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-        final URL schemaUrl = loader.getResource(xsd);
-        try {
-            Schema s = sf.newSchema(schemaUrl);
-            final Schema old = SCHEMA_CACHE.putIfAbsent(xsd, s);
-            if (old != null) {
-                s = old;
-            }
-            return s;
-        } catch (SAXException e) {
-            log.log(Level.WARNING, String.format("Unable to parse schema: %s", xsd), e);
-            return null;
-        }
+        final List<URL> urls = Collections.list(loader.getResources(path));
+        Exceptions.raiseIf(urls.stream().distinct().count() > 1, ValidationException::new,
+            "More than one %s is found in the classpath", path);
+        return urls.isEmpty() ? null : urls.get(0).openStream();
     }
 
     public static void applySimpleConfig(ValidationConfigType xmlConfig, ConfigurationImpl targetConfig) {
@@ -246,37 +166,84 @@ public class ValidationParser {
         }
     }
 
-    private static void applyExecutableValidation(final ValidationConfigType xmlConfig, final ConfigurationImpl targetConfig) {
-        final CopyOnWriteArrayList<ExecutableType> executableTypes = new CopyOnWriteArrayList<ExecutableType>();
-        if (xmlConfig.getExecutableValidation() != null && xmlConfig.getExecutableValidation().getEnabled()
-                && xmlConfig.getExecutableValidation().getDefaultValidatedExecutableTypes() != null) {
-            executableTypes.addAll(xmlConfig.getExecutableValidation().getDefaultValidatedExecutableTypes().getExecutableType());
-        }
+    private static void applyExecutableValidation(final ValidationConfigType xmlConfig,
+        final ConfigurationImpl targetConfig) {
+
+        final Set<ExecutableType> executableTypes = Optional.of(xmlConfig)
+            .map(ValidationConfigType::getExecutableValidation).filter(vc -> Boolean.TRUE.equals(vc.getEnabled()))
+            .map(ExecutableValidationType::getDefaultValidatedExecutableTypes)
+            .map(DefaultValidatedExecutableTypesType::getExecutableType).map(EnumSet::copyOf)
+            .orElseGet(() -> EnumSet.noneOf(ExecutableType.class));
 
         if (executableTypes.contains(ExecutableType.ALL)) {
             executableTypes.clear();
             executableTypes.add(ExecutableType.CONSTRUCTORS);
             executableTypes.add(ExecutableType.NON_GETTER_METHODS);
             executableTypes.add(ExecutableType.GETTER_METHODS);
-        } else if (executableTypes.contains(ExecutableType.NONE)) { // if both are present ALL gains
+        } else if (executableTypes.contains(ExecutableType.NONE)) { // if both are present ALL trumps NONE
             executableTypes.clear();
         }
+        targetConfig.setExecutableValidation(Collections.unmodifiableSet(executableTypes));
+    }
+
+    private static void applyMappingStreams(ValidationConfigType xmlConfig, ConfigurationImpl target) {
+        for (String rawMappingFileName : xmlConfig.getConstraintMapping()) {
+            String mappingFileName = rawMappingFileName;
+            if (mappingFileName.charAt(0) == '/') {
+                // Classloader needs a path without a starting /
+                mappingFileName = mappingFileName.substring(1);
+            }
+            log.log(Level.FINEST, String.format("Trying to open input stream for %s", mappingFileName));
+            try {
+                final InputStream in = getInputStream(mappingFileName);
+                Exceptions.raiseIf(in == null, ValidationException::new,
+                    "Unable to open input stream for mapping file %s", mappingFileName);
+                target.addMapping(in);
+            } catch (IOException e) {
+                Exceptions.raise(ValidationException::new, e, "Unable to open input stream for mapping file %s",
+                    mappingFileName);
+            }
+        }
+    }
+
+    private ValidationConfigType xmlConfig;
+    private BootstrapConfigurationImpl bootstrap;
+    private Collection<ValidationException> exceptions = new CopyOnWriteArrayList<ValidationException>();
+
+    private ValidationParser() {
+        // no-op
+    }
+
+    public void applyConfigWithInstantiation(ConfigurationImpl targetConfig) {
+        if (xmlConfig == null) {
+            return;
+        }
 
-        targetConfig.setExecutableValidation(executableTypes);
+        applyProviderClass(xmlConfig, targetConfig);
+        applyMessageInterpolator(xmlConfig, targetConfig);
+        applyTraversableResolver(xmlConfig, targetConfig);
+        applyConstraintFactory(xmlConfig, targetConfig);
+        applyParameterNameProvider(xmlConfig, targetConfig);
+        applyMappingStreams(xmlConfig, targetConfig);
+        applyProperties(xmlConfig, targetConfig);
+    }
+
+    public BootstrapConfigurationImpl getBootstrap() {
+        return bootstrap;
     }
 
-    private void applyParameterNameProvider(final ValidationConfigType xmlConfig, final ConfigurationImpl targetConfig) {
+    private void applyParameterNameProvider(final ValidationConfigType xmlConfig,
+        final ConfigurationImpl targetConfig) {
         final String parameterNameProvider = xmlConfig.getParameterNameProvider();
-        if (targetConfig.getParameterNameProvider() == targetConfig.getDefaultParameterNameProvider()) { // ref ==
-            if (parameterNameProvider != null) {
-                final Class<?> loaded = loadClass(parameterNameProvider);
-                if (loaded == null) {
-                    log.log(Level.SEVERE, "Can't load " + parameterNameProvider);
-                } else {
-                    final Class<? extends ParameterNameProvider> clazz = loaded.asSubclass(ParameterNameProvider.class);
-                    targetConfig.parameterNameProviderClass(clazz);
-                    log.log(Level.INFO, String.format("Using %s as validation provider.", parameterNameProvider));
-                }
+        if (targetConfig.getParameterNameProvider() == targetConfig.getDefaultParameterNameProvider()
+            && parameterNameProvider != null) {
+            final Class<?> loaded = loadClass(parameterNameProvider);
+            if (loaded == null) {
+                log.log(Level.SEVERE, "Can't load " + parameterNameProvider);
+            } else {
+                final Class<? extends ParameterNameProvider> clazz = loaded.asSubclass(ParameterNameProvider.class);
+                targetConfig.parameterNameProviderClass(clazz);
+                log.log(Level.INFO, String.format("Using %s as validation provider.", parameterNameProvider));
             }
         }
     }
@@ -293,65 +260,36 @@ public class ValidationParser {
     }
 
     @SuppressWarnings("unchecked")
-    private void applyMessageInterpolator(ValidationConfigType xmlConfig,
-                                          ConfigurationImpl target) {
+    private void applyMessageInterpolator(ValidationConfigType xmlConfig, ConfigurationImpl target) {
         String messageInterpolatorClass = xmlConfig.getMessageInterpolator();
-        if (target.getMessageInterpolator() == target.getDefaultMessageInterpolator()) { // ref ==
-            if (messageInterpolatorClass != null) {
-                Class<MessageInterpolator> clazz = (Class<MessageInterpolator>)
-                        loadClass(messageInterpolatorClass);
-                target.messageInterpolatorClass(clazz);
-                log.log(Level.INFO, String.format("Using %s as message interpolator.", messageInterpolatorClass));
-            }
+        if (target.getMessageInterpolator() == target.getDefaultMessageInterpolator()
+            && messageInterpolatorClass != null) {
+            Class<MessageInterpolator> clazz = (Class<MessageInterpolator>) loadClass(messageInterpolatorClass);
+            target.messageInterpolatorClass(clazz);
+            log.log(Level.INFO, String.format("Using %s as message interpolator.", messageInterpolatorClass));
         }
     }
 
     @SuppressWarnings("unchecked")
-    private void applyTraversableResolver(ValidationConfigType xmlConfig,
-                                          ConfigurationImpl target) {
+    private void applyTraversableResolver(ValidationConfigType xmlConfig, ConfigurationImpl target) {
         String traversableResolverClass = xmlConfig.getTraversableResolver();
-        if (target.getTraversableResolver() == target.getDefaultTraversableResolver() && traversableResolverClass != null) {
-		    Class<TraversableResolver> clazz = (Class<TraversableResolver>)
-		            loadClass(traversableResolverClass);
-		    target.traversableResolverClass(clazz);
-		    log.log(Level.INFO, String.format("Using %s as traversable resolver.", traversableResolverClass));
-		}
+        if (target.getTraversableResolver() == target.getDefaultTraversableResolver()
+            && traversableResolverClass != null) {
+            Class<TraversableResolver> clazz = (Class<TraversableResolver>) loadClass(traversableResolverClass);
+            target.traversableResolverClass(clazz);
+            log.log(Level.INFO, String.format("Using %s as traversable resolver.", traversableResolverClass));
+        }
     }
 
     @SuppressWarnings("unchecked")
-    private void applyConstraintFactory(ValidationConfigType xmlConfig,
-                                        ConfigurationImpl target) {
+    private void applyConstraintFactory(ValidationConfigType xmlConfig, ConfigurationImpl target) {
         String constraintFactoryClass = xmlConfig.getConstraintValidatorFactory();
-        if (target.getConstraintValidatorFactory() == target.getDefaultConstraintValidatorFactory() && constraintFactoryClass != null) {
-		    Class<ConstraintValidatorFactory> clazz = (Class<ConstraintValidatorFactory>)
-		            loadClass(constraintFactoryClass);
-		    target.constraintValidatorFactoryClass(clazz);
-		    log.log(Level.INFO, String.format("Using %s as constraint factory.", constraintFactoryClass));
-		}
-    }
-
-    private static void applyMappingStreams(ValidationConfigType xmlConfig,
-                                     ConfigurationImpl target) {
-        for (String rawMappingFileName : xmlConfig.getConstraintMapping()) {
-            String mappingFileName = rawMappingFileName;
-            if (mappingFileName.startsWith("/")) {
-                // Classloader needs a path without a starting /
-                mappingFileName = mappingFileName.substring(1);
-            }
-            log.log(Level.FINEST, String.format("Trying to open input stream for %s", mappingFileName));
-            InputStream in;
-            try {
-                in = getInputStream(mappingFileName);
-                if (in == null) {
-                    throw new ValidationException(
-                            "Unable to open input stream for mapping file " +
-                                    mappingFileName);
-                }
-            } catch (IOException e) {
-                throw new ValidationException("Unable to open input stream for mapping file " +
-                        mappingFileName, e);
-            }
-            target.addMapping(in);
+        if (target.getConstraintValidatorFactory() == target.getDefaultConstraintValidatorFactory()
+            && constraintFactoryClass != null) {
+            Class<ConstraintValidatorFactory> clazz =
+                (Class<ConstraintValidatorFactory>) loadClass(constraintFactoryClass);
+            target.constraintValidatorFactoryClass(clazz);
+            log.log(Level.INFO, String.format("Using %s as constraint factory.", constraintFactoryClass));
         }
     }
 

http://git-wip-us.apache.org/repos/asf/bval/blob/5c09f0dd/bval-jsr/src/main/java/org/apache/bval/jsr/xml/XmlUtils.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/XmlUtils.java b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/XmlUtils.java
new file mode 100644
index 0000000..2826174
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/XmlUtils.java
@@ -0,0 +1,67 @@
+/*
+ *  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.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.helpers.DefaultValidationEventHandler;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+
+import org.apache.bval.util.reflection.Reflection;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+class XmlUtils {
+    private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
+    private static final Logger log = Logger.getLogger(XmlUtils.class.getName());
+    private static final SchemaFactory SCHEMA_FACTORY = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+
+    static Schema loadSchema(String resource) {
+        final URL schemaUrl = Reflection.getClassLoader(XmlUtils.class).getResource(resource);
+        try {
+            return SCHEMA_FACTORY.newSchema(schemaUrl);
+        } catch (SAXException e) {
+            log.log(Level.WARNING, String.format("Unable to parse schema: %s", resource), e);
+            return null;
+        }
+    }
+
+    static Document parse(InputStream in) throws SAXException, IOException, ParserConfigurationException {
+        return DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().parse(in);
+    }
+
+    static <T> T unmarshal(Document document, Schema schema, Class<T> type) throws JAXBException {
+        final JAXBContext jc = JAXBContext.newInstance(type);
+        final Unmarshaller unmarshaller = jc.createUnmarshaller();
+        unmarshaller.setSchema(schema);
+        unmarshaller.setEventHandler(new DefaultValidationEventHandler());
+        final JAXBElement<T> root = unmarshaller.unmarshal(document, type);
+        return root.getValue();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/bval/blob/5c09f0dd/bval-jsr/src/main/xjb/binding-customization.xjb
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/xjb/binding-customization.xjb b/bval-jsr/src/main/xjb/binding-customization.xjb
index 598bd3c..762a058 100644
--- a/bval-jsr/src/main/xjb/binding-customization.xjb
+++ b/bval-jsr/src/main/xjb/binding-customization.xjb
@@ -19,7 +19,7 @@
 -->
 <jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
               xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc">
-  <jxb:bindings schemaLocation="../xsd/validation-mapping-1.1.xsd" node="/xs:schema">
+  <jxb:bindings schemaLocation="../xsd/validation-mapping-2.0.xsd" node="/xs:schema">
     <jxb:globalBindings>
       <xjc:javaType name="java.lang.String" xmlType="xs:string"
                     adapter="javax.xml.bind.annotation.adapters.CollapsedStringAdapter"/>
@@ -29,7 +29,7 @@
       <jxb:property name="classType"/>
     </jxb:bindings>
   </jxb:bindings>
-  <jxb:bindings schemaLocation="../xsd/validation-configuration-1.1.xsd" node="/xs:schema">
+  <jxb:bindings schemaLocation="../xsd/validation-configuration-2.0.xsd" node="/xs:schema">
     <jxb:bindings node="//xs:element[@name='executable-type']">
       <jxb:javaType name="javax.validation.executable.ExecutableType"
                     parseMethod="javax.validation.executable.ExecutableType.valueOf"/>

http://git-wip-us.apache.org/repos/asf/bval/blob/5c09f0dd/bval-jsr/src/main/xsd/validation-configuration-2.0.xsd
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/xsd/validation-configuration-2.0.xsd b/bval-jsr/src/main/xsd/validation-configuration-2.0.xsd
new file mode 100644
index 0000000..6fd7874
--- /dev/null
+++ b/bval-jsr/src/main/xsd/validation-configuration-2.0.xsd
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<xs:schema attributeFormDefault="unqualified"
+           elementFormDefault="qualified"
+           targetNamespace="http://xmlns.jcp.org/xml/ns/javax/validation/configuration"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           xmlns:config="http://xmlns.jcp.org/xml/ns/javax/validation/configuration"
+           version="2.0">
+  <xs:element name="validation-config" type="config:validation-configType" />
+  <xs:complexType name="validation-configType">
+    <xs:sequence>
+      <xs:element type="xs:string" name="default-provider" minOccurs="0" />
+      <xs:element type="xs:string" name="message-interpolator" minOccurs="0" />
+      <xs:element type="xs:string" name="traversable-resolver" minOccurs="0" />
+      <xs:element type="xs:string" name="constraint-validator-factory" minOccurs="0" />
+      <xs:element type="xs:string" name="parameter-name-provider" minOccurs="0" />
+      <xs:element type="xs:string" name="clock-provider" minOccurs="0" />
+      <xs:element type="xs:string" name="value-extractor" maxOccurs="unbounded" minOccurs="0" />
+      <xs:element type="config:executable-validationType" name="executable-validation" minOccurs="0" />
+      <xs:element type="xs:string" name="constraint-mapping" maxOccurs="unbounded" minOccurs="0" />
+      <xs:element type="config:propertyType" name="property" maxOccurs="unbounded" minOccurs="0" />
+    </xs:sequence>
+    <xs:attribute name="version" type="config:versionType" fixed="2.0" /> <!-- use="required" -->
+  </xs:complexType>
+  <xs:complexType name="executable-validationType">
+    <xs:sequence>
+      <xs:element type="config:default-validated-executable-typesType" name="default-validated-executable-types" minOccurs="0" />
+    </xs:sequence>
+    <xs:attribute name="enabled" use="optional" type="xs:boolean" default="true" />
+  </xs:complexType>
+  <xs:complexType name="default-validated-executable-typesType">
+    <xs:sequence>
+      <xs:element name="executable-type" maxOccurs="unbounded" minOccurs="1">
+        <xs:simpleType>
+          <xs:restriction base="xs:string">
+            <xs:enumeration value="NONE" />
+            <xs:enumeration value="CONSTRUCTORS" />
+            <xs:enumeration value="NON_GETTER_METHODS" />
+            <xs:enumeration value="GETTER_METHODS" />
+            <xs:enumeration value="ALL" />
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:element>
+    </xs:sequence>
+  </xs:complexType>
+  <xs:complexType name="propertyType">
+    <xs:simpleContent>
+      <xs:extension base="xs:string">
+        <xs:attribute name="name" use="required" type="xs:string" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+  <xs:simpleType name="versionType">
+    <xs:restriction base="xs:token">
+      <xs:pattern value="[0-9]+(\.[0-9]+)*" />
+    </xs:restriction>
+  </xs:simpleType>
+</xs:schema>

http://git-wip-us.apache.org/repos/asf/bval/blob/5c09f0dd/bval-jsr/src/main/xsd/validation-mapping-2.0.xsd
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/xsd/validation-mapping-2.0.xsd b/bval-jsr/src/main/xsd/validation-mapping-2.0.xsd
new file mode 100644
index 0000000..9d16bf3
--- /dev/null
+++ b/bval-jsr/src/main/xsd/validation-mapping-2.0.xsd
@@ -0,0 +1,297 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<xs:schema attributeFormDefault="unqualified"
+           elementFormDefault="qualified"
+           targetNamespace="http://xmlns.jcp.org/xml/ns/javax/validation/mapping"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           xmlns:map="http://xmlns.jcp.org/xml/ns/javax/validation/mapping"
+           version="2.0">
+  <xs:element name="constraint-mappings" type="map:constraint-mappingsType" />
+
+  <xs:complexType name="payloadType">
+    <xs:sequence>
+      <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0" />
+    </xs:sequence>
+  </xs:complexType>
+  <xs:complexType name="groupsType">
+    <xs:sequence>
+      <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0" />
+    </xs:sequence>
+  </xs:complexType>
+  <xs:complexType name="groupSequenceType">
+    <xs:sequence>
+      <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0" />
+    </xs:sequence>
+  </xs:complexType>
+  <xs:complexType name="groupConversionType">
+    <xs:attribute type="xs:string" name="from" use="required" />
+    <xs:attribute type="xs:string" name="to" use="required" />
+  </xs:complexType>
+  <xs:complexType name="constraint-mappingsType">
+    <xs:sequence>
+      <xs:element type="xs:string" name="default-package" minOccurs="0" />
+      <xs:element type="map:beanType"
+                  name="bean"
+                  maxOccurs="unbounded"
+                  minOccurs="0" />
+      <xs:element type="map:constraint-definitionType"
+                  name="constraint-definition"
+                  maxOccurs="unbounded"
+                  minOccurs="0" />
+    </xs:sequence>
+    <xs:attribute name="version" type="map:versionType" fixed="2.0" /> <!-- use="required" -->
+  </xs:complexType>
+  <xs:simpleType name="versionType">
+    <xs:restriction base="xs:token">
+      <xs:pattern value="[0-9]+(\.[0-9]+)*" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="validated-byType">
+    <xs:sequence>
+      <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0" />
+    </xs:sequence>
+    <xs:attribute type="xs:boolean" name="include-existing-validators" use="optional" />
+  </xs:complexType>
+  <xs:complexType name="constraintType">
+    <xs:sequence>
+      <xs:element type="xs:string" name="message" minOccurs="0" />
+      <xs:element type="map:groupsType"
+                  name="groups"
+                  minOccurs="0" />
+      <xs:element type="map:payloadType"
+                  name="payload"
+                  minOccurs="0" />
+      <xs:element type="map:elementType"
+                  name="element"
+                  maxOccurs="unbounded"
+                  minOccurs="0" />
+    </xs:sequence>
+    <xs:attribute type="xs:string" name="annotation" use="required" />
+  </xs:complexType>
+  <xs:complexType name="elementType" mixed="true">
+    <xs:sequence>
+      <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0" />
+      <xs:element type="map:annotationType"
+                  name="annotation"
+                  maxOccurs="unbounded"
+                  minOccurs="0" />
+    </xs:sequence>
+    <xs:attribute type="xs:string" name="name" use="required" />
+  </xs:complexType>
+  <xs:complexType name="containerElementTypeType">
+    <xs:sequence>
+      <xs:element type="xs:string"
+                  name="valid"
+                  minOccurs="0"
+                  fixed="" />
+      <xs:element type="map:groupConversionType"
+                  name="convert-group"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:containerElementTypeType"
+                  name="container-element-type"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:constraintType"
+                  name="constraint"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+    </xs:sequence>
+    <xs:attribute name="type-argument-index" use="optional">
+      <xs:simpleType>
+        <xs:restriction base="xs:int">
+          <xs:minInclusive value="0" />
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="classType">
+    <xs:sequence>
+      <xs:element type="map:groupSequenceType"
+                  name="group-sequence"
+                  minOccurs="0" />
+      <xs:element type="map:constraintType"
+                  name="constraint"
+                  maxOccurs="unbounded"
+                  minOccurs="0" />
+    </xs:sequence>
+    <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional" />
+  </xs:complexType>
+  <xs:complexType name="beanType">
+    <xs:sequence>
+      <xs:element type="map:classType"
+                  name="class"
+                  minOccurs="0">
+      </xs:element>
+      <xs:element type="map:fieldType"
+                  name="field"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:getterType"
+                  name="getter"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:constructorType"
+                  name="constructor"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:methodType"
+                  name="method"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+    </xs:sequence>
+    <xs:attribute type="xs:string" name="class" use="required" />
+    <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional" default="true" />
+  </xs:complexType>
+  <xs:complexType name="annotationType">
+    <xs:sequence>
+      <xs:element type="map:elementType"
+                  name="element"
+                  maxOccurs="unbounded"
+                  minOccurs="0" />
+    </xs:sequence>
+  </xs:complexType>
+  <xs:complexType name="getterType">
+    <xs:sequence>
+      <xs:element type="xs:string" name="valid" minOccurs="0" fixed="" />
+      <xs:element type="map:groupConversionType"
+                  name="convert-group"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:containerElementTypeType"
+                  name="container-element-type"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:constraintType"
+                  name="constraint"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+    </xs:sequence>
+    <xs:attribute type="xs:string" name="name" use="required" />
+    <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional" />
+  </xs:complexType>
+  <xs:complexType name="methodType">
+    <xs:sequence>
+      <xs:element type="map:parameterType"
+                  name="parameter"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:crossParameterType"
+                  name="cross-parameter"
+                  minOccurs="0"
+                  maxOccurs="1" />
+      <xs:element type="map:returnValueType"
+                  name="return-value"
+                  minOccurs="0"
+                  maxOccurs="1" />
+    </xs:sequence>
+    <xs:attribute type="xs:string" name="name" use="required" />
+    <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional" />
+  </xs:complexType>
+  <xs:complexType name="constructorType">
+    <xs:sequence>
+      <xs:element type="map:parameterType"
+                  name="parameter"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:crossParameterType"
+                  name="cross-parameter"
+                  minOccurs="0"
+                  maxOccurs="1" />
+      <xs:element type="map:returnValueType"
+                  name="return-value"
+                  minOccurs="0"
+                  maxOccurs="1" />
+    </xs:sequence>
+    <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional" />
+  </xs:complexType>
+  <xs:complexType name="parameterType">
+    <xs:sequence>
+      <xs:element type="xs:string" name="valid" minOccurs="0" fixed="" />
+      <xs:element type="map:groupConversionType"
+                  name="convert-group"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:containerElementTypeType"
+                  name="container-element-type"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:constraintType"
+                  name="constraint"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+    </xs:sequence>
+    <xs:attribute type="xs:string" name="type" use="required" />
+    <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional" />
+  </xs:complexType>
+  <xs:complexType name="returnValueType">
+    <xs:sequence>
+      <xs:element type="xs:string" name="valid" minOccurs="0" fixed="" />
+      <xs:element type="map:groupConversionType"
+                  name="convert-group"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:containerElementTypeType"
+                  name="container-element-type"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:constraintType"
+                  name="constraint"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+    </xs:sequence>
+    <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional" />
+  </xs:complexType>
+  <xs:complexType name="crossParameterType">
+    <xs:sequence>
+      <xs:element type="map:constraintType"
+                  name="constraint"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+    </xs:sequence>
+    <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional" />
+  </xs:complexType>
+  <xs:complexType name="constraint-definitionType">
+    <xs:sequence>
+      <xs:element type="map:validated-byType"
+                  name="validated-by" />
+    </xs:sequence>
+    <xs:attribute type="xs:string" name="annotation" use="required" />
+  </xs:complexType>
+  <xs:complexType name="fieldType">
+    <xs:sequence>
+      <xs:element type="xs:string" name="valid" minOccurs="0" fixed="" />
+      <xs:element type="map:groupConversionType"
+                  name="convert-group"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:containerElementTypeType"
+                  name="container-element-type"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+      <xs:element type="map:constraintType"
+                  name="constraint"
+                  minOccurs="0"
+                  maxOccurs="unbounded" />
+    </xs:sequence>
+    <xs:attribute type="xs:string" name="name" use="required" />
+    <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional" />
+  </xs:complexType>
+</xs:schema>

http://git-wip-us.apache.org/repos/asf/bval/blob/5c09f0dd/bval-jsr/src/test/java/org/apache/bval/jsr/xml/Demo.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/xml/Demo.java b/bval-jsr/src/test/java/org/apache/bval/jsr/xml/Demo.java
new file mode 100644
index 0000000..02a07a8
--- /dev/null
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/xml/Demo.java
@@ -0,0 +1,40 @@
+package org.apache.bval.jsr.xml;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.UnmarshallerHandler;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.junit.Test;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+
+public class Demo {
+
+    @Test
+    public void test1() throws Exception {
+        JAXBContext jc = JAXBContext.newInstance(ObjectFactory.class);
+
+        // Set the parent XMLReader on the XMLFilter
+        SAXParserFactory spf = SAXParserFactory.newInstance();
+        spf.setNamespaceAware(true);
+        SAXParser sp = spf.newSAXParser();
+        XMLReader xr = sp.getXMLReader();
+
+        // Set UnmarshallerHandler as ContentHandler on XMLFilter
+        
+        Unmarshaller unmarshaller = jc.createUnmarshaller();
+        
+        UnmarshallerHandler unmarshallerHandler = unmarshaller.getUnmarshallerHandler();
+        xr.setContentHandler(unmarshallerHandler);
+
+        // Parse the XML
+        InputSource xml = new InputSource(getClass().getResourceAsStream("/sample-validation2.xml"));
+        xr.parse(xml);
+        JAXBElement<ValidationConfigType> result = (JAXBElement<ValidationConfigType>) unmarshallerHandler.getResult();
+        System.out.println(ToStringBuilder.reflectionToString(result.getValue()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/5c09f0dd/bval-jsr/src/test/java/org/apache/bval/jsr/xml/ValidationParserTest.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/xml/ValidationParserTest.java b/bval-jsr/src/test/java/org/apache/bval/jsr/xml/ValidationParserTest.java
index 827abae..41d4f13 100644
--- a/bval-jsr/src/test/java/org/apache/bval/jsr/xml/ValidationParserTest.java
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/xml/ValidationParserTest.java
@@ -19,6 +19,8 @@
 package org.apache.bval.jsr.xml;
 
 import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
@@ -84,6 +86,18 @@ public class ValidationParserTest implements ApacheValidatorConfiguration.Proper
     }
 
     @Test
+    public void testParseV11() {
+        ConfigurationImpl config = new ConfigurationImpl(null, new ApacheValidationProvider());
+        ValidationParser.processValidationConfig("sample-validation11.xml", config, false);
+    }
+
+    @Test
+    public void testParseV20() {
+        ConfigurationImpl config = new ConfigurationImpl(null, new ApacheValidationProvider());
+        ValidationParser.processValidationConfig("sample-validation2.xml", config, false);
+    }
+
+    @Test
     public void testConfigureFromXml() {
         ValidatorFactory factory = getFactory();
         assertThat(factory.getMessageInterpolator(), instanceOf(TestMessageInterpolator.class));
@@ -105,8 +119,8 @@ public class ValidationParserTest implements ApacheValidatorConfiguration.Proper
         bean.setValueCode("illegal");
         Validator validator = getFactory().getValidator();
         Set<ConstraintViolation<XmlEntitySampleBean>> results = validator.validate(bean);
-        assertTrue(!results.isEmpty());
-        assertTrue(results.size() == 3);
+        assertFalse(results.isEmpty());
+        assertEquals(3, results.size());
 
         bean.setZipCode("123");
         bean.setValueCode("20");

http://git-wip-us.apache.org/repos/asf/bval/blob/5c09f0dd/bval-jsr/src/test/resources/sample-validation11.xml
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/resources/sample-validation11.xml b/bval-jsr/src/test/resources/sample-validation11.xml
new file mode 100644
index 0000000..648ba1c
--- /dev/null
+++ b/bval-jsr/src/test/resources/sample-validation11.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<validation-config
+    xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation=
+        "http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.0.xsd"
+    version="1.1">
+  <default-provider>org.apache.bval.jsr.ApacheValidationProvider</default-provider>
+  <message-interpolator>org.apache.bval.jsr.xml.TestMessageInterpolator</message-interpolator>
+  <traversable-resolver>org.apache.bval.jsr.resolver.SimpleTraversableResolver</traversable-resolver>
+  <constraint-validator-factory>org.apache.bval.jsr.xml.TestConstraintValidatorFactory</constraint-validator-factory>
+  <constraint-mapping>sample-constraints.xml</constraint-mapping>
+  <property name="test-prop">test-prop-value</property>
+</validation-config>

http://git-wip-us.apache.org/repos/asf/bval/blob/5c09f0dd/bval-jsr/src/test/resources/sample-validation2.xml
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/resources/sample-validation2.xml b/bval-jsr/src/test/resources/sample-validation2.xml
new file mode 100644
index 0000000..4758454
--- /dev/null
+++ b/bval-jsr/src/test/resources/sample-validation2.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<validation-config
+    xmlns="http://xmlns.jcp.org/xml/ns/javax/validation/configuration"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation=
+        "http://xmlns.jcp.org/xml/ns/javax/validation/configuration validation-configuration-2.0.xsd"
+    version="2.0">
+  <default-provider>org.apache.bval.jsr.ApacheValidationProvider</default-provider>
+  <message-interpolator>org.apache.bval.jsr.xml.TestMessageInterpolator</message-interpolator>
+  <traversable-resolver>org.apache.bval.jsr.resolver.SimpleTraversableResolver</traversable-resolver>
+  <constraint-validator-factory>org.apache.bval.jsr.xml.TestConstraintValidatorFactory</constraint-validator-factory>
+  <constraint-mapping>sample-constraints.xml</constraint-mapping>
+  <property name="test-prop">test-prop-value</property>
+</validation-config>


[03/17] bval git commit: BV2: utility classes

Posted by mb...@apache.org.
BV2: utility classes


Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/fad43eb8
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/fad43eb8
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/fad43eb8

Branch: refs/heads/bv2
Commit: fad43eb892b66592606fa76d70f67f8d016db373
Parents: 5c09f0d
Author: Matt Benson <mb...@apache.org>
Authored: Wed Feb 21 14:31:22 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Wed Feb 21 14:44:27 2018 -0600

----------------------------------------------------------------------
 .../java/org/apache/bval/util/BValVersion.java  |  12 +-
 .../java/org/apache/bval/util/Exceptions.java   | 125 +++++++
 .../main/java/org/apache/bval/util/Lazy.java    |  62 ++++
 .../main/java/org/apache/bval/util/LazyInt.java |  49 +++
 .../java/org/apache/bval/util/ObjectUtils.java  |  20 +-
 .../org/apache/bval/util/ObjectWrapper.java     |  50 +++
 .../java/org/apache/bval/util/StringUtils.java  |  67 ++--
 .../java/org/apache/bval/util/Validate.java     |  29 +-
 .../apache/bval/util/reflection/Reflection.java | 173 +++++++--
 .../apache/bval/util/reflection/TypeUtils.java  |  65 ++--
 .../bval/jsr/util/AnnotationsManager.java       | 359 +++++++++++++++++++
 .../org/apache/bval/jsr/util/ClassHelper.java   |  10 +-
 ...ementNodeBuilderCustomizableContextImpl.java |  77 ++++
 ...nerElementNodeBuilderDefinedContextImpl.java |  65 ++++
 .../ContainerElementNodeContextBuilderImpl.java |  85 +++++
 .../main/java/org/apache/bval/jsr/util/IOs.java |  11 +-
 .../java/org/apache/bval/jsr/util/LRUCache.java |  41 +++
 .../LeafNodeBuilderCustomizableContextImpl.java |  23 +-
 .../java/org/apache/bval/jsr/util/Methods.java  |  45 +++
 .../NodeBuilderCustomizableContextImpl.java     |  55 ++-
 .../jsr/util/NodeBuilderDefinedContextImpl.java |  32 +-
 .../bval/jsr/util/NodeContextBuilderImpl.java   |  52 +--
 .../java/org/apache/bval/jsr/util/NodeImpl.java | 118 +++---
 .../java/org/apache/bval/jsr/util/PathImpl.java |  91 +++--
 .../apache/bval/jsr/util/PathNavigation.java    | 102 ++++--
 .../java/org/apache/bval/jsr/util/Proxies.java  |   2 +-
 .../apache/bval/jsr/util/ToUnmodifiable.java    |  34 ++
 27 files changed, 1503 insertions(+), 351 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/BValVersion.java
----------------------------------------------------------------------
diff --git a/bval-core/src/main/java/org/apache/bval/util/BValVersion.java b/bval-core/src/main/java/org/apache/bval/util/BValVersion.java
index b0c451f..13d1fa3 100644
--- a/bval-core/src/main/java/org/apache/bval/util/BValVersion.java
+++ b/bval-core/src/main/java/org/apache/bval/util/BValVersion.java
@@ -54,21 +54,17 @@ public class BValVersion {
 
     static {
         Properties revisionProps = new Properties();
-        try {
-            InputStream in = BValVersion.class.getResourceAsStream("/META-INF/org.apache.bval.revision.properties");
+        try (InputStream in = BValVersion.class.getResourceAsStream("/META-INF/org.apache.bval.revision.properties")) {
             if (in != null) {
-                try {
-                    revisionProps.load(in);
-                } finally {
-                    in.close();
-                }
+                revisionProps.load(in);
             }
         } catch (IOException ioe) {
         }
 
         String vers = revisionProps.getProperty("project.version");
-        if (vers == null || "".equals(vers.trim()))
+        if (vers == null || "".equals(vers.trim())) {
             vers = "0.0.0";
+        }
         VERSION_NUMBER = vers;
 
         StringTokenizer tok = new StringTokenizer(VERSION_NUMBER, ".-");

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/Exceptions.java
----------------------------------------------------------------------
diff --git a/bval-core/src/main/java/org/apache/bval/util/Exceptions.java b/bval-core/src/main/java/org/apache/bval/util/Exceptions.java
new file mode 100644
index 0000000..9487cde
--- /dev/null
+++ b/bval-core/src/main/java/org/apache/bval/util/Exceptions.java
@@ -0,0 +1,125 @@
+/*
+ *  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.util;
+
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+/**
+ * Utility class for the creation and throwing of Exceptions.
+ */
+public class Exceptions {
+
+    public static <E extends Exception> E create(Function<? super String, ? extends E> fn, String format,
+        Object... args) {
+        return create(fn, () -> String.format(format, args));
+    }
+
+    public static <E extends Exception, C extends Throwable> E create(
+        BiFunction<? super String, ? super C, ? extends E> fn, C cause, String format, Object... args) {
+        return create(fn, cause, () -> String.format(format, args));
+    }
+
+    public static <E extends Exception> E create(Function<? super String, ? extends E> fn, Supplier<String> message) {
+        return elideStackTrace(fn.apply(message.get()));
+    }
+
+    public static <E extends Exception, C extends Throwable> E create(
+        BiFunction<? super String, ? super C, ? extends E> fn, C cause, Supplier<String> message) {
+        return elideStackTrace(fn.apply(message.get(), cause));
+    }
+
+    public static <E extends Exception, R> R raise(Function<? super String, ? extends E> fn, String format,
+        Object... args) throws E {
+        throw create(fn, format, args);
+    }
+
+    public static <E extends Exception> void raiseIf(boolean condition, Function<? super String, ? extends E> fn,
+        String format, Object... args) throws E {
+        if (condition) {
+            raise(fn, format, args);
+        }
+    }
+
+    public static <E extends Exception> void raiseUnless(boolean condition, Function<? super String, ? extends E> fn,
+        String format, Object... args) throws E {
+        raiseIf(!condition, fn, format, args);
+    }
+
+    public static <E extends Exception, R> R raise(Function<? super String, ? extends E> fn, Supplier<String> message)
+        throws E {
+        throw create(fn, message);
+    }
+
+    public static <E extends Exception> void raiseIf(boolean condition, Function<? super String, ? extends E> fn,
+        Supplier<String> message) throws E {
+        if (condition) {
+            raise(fn, message);
+        }
+    }
+
+    public static <E extends Exception> void raiseUnless(boolean condition, Function<? super String, ? extends E> fn,
+        Supplier<String> message) throws E {
+        raiseIf(!condition, fn, message);
+    }
+
+    public static <E extends Exception, C extends Throwable, R> R raise(
+        BiFunction<? super String, ? super C, ? extends E> fn, C cause, String format, Object... args) throws E {
+        throw create(fn, cause, format, args);
+    }
+
+    public static <E extends Exception, C extends Throwable> void raiseIf(boolean condition,
+        BiFunction<? super String, ? super C, ? extends E> fn, C cause, String format, Object... args) throws E {
+        if (condition) {
+            raise(fn, cause, format, args);
+        }
+    }
+
+    public static <E extends Exception, C extends Throwable> void raiseUnless(boolean condition,
+        BiFunction<? super String, ? super C, ? extends E> fn, C cause, String format, Object... args) throws E {
+        raiseIf(!condition, fn, cause, format, args);
+    }
+
+    public static <E extends Exception, C extends Throwable, R> R raise(
+        BiFunction<? super String, ? super C, ? extends E> fn, C cause, Supplier<String> message) throws E {
+        throw create(fn, cause, message);
+    }
+
+    public static <E extends Exception, C extends Throwable> void raiseIf(boolean condition,
+        BiFunction<? super String, ? super C, ? extends E> fn, C cause, Supplier<String> message) throws E {
+        if (condition) {
+            raise(fn, cause, message);
+        }
+    }
+
+    public static <E extends Exception, C extends Throwable> void raiseUnless(boolean condition,
+        BiFunction<? super String, ? super C, ? extends E> fn, C cause, Supplier<String> message) throws E {
+        raiseIf(!condition, fn, cause, message);
+    }
+
+    private static <T extends Throwable> T elideStackTrace(T t) {
+        final StackTraceElement[] stackTrace = t.fillInStackTrace().getStackTrace();
+        t.setStackTrace(Stream.of(stackTrace).filter(e -> !Exceptions.class.getName().equals(e.getClassName()))
+            .toArray(StackTraceElement[]::new));
+        return t;
+    }
+
+    private Exceptions() {
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/Lazy.java
----------------------------------------------------------------------
diff --git a/bval-core/src/main/java/org/apache/bval/util/Lazy.java b/bval-core/src/main/java/org/apache/bval/util/Lazy.java
new file mode 100644
index 0000000..4796de3
--- /dev/null
+++ b/bval-core/src/main/java/org/apache/bval/util/Lazy.java
@@ -0,0 +1,62 @@
+/*
+ *  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.util;
+
+import java.util.Optional;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * @since 2.0
+ *
+ * @param <T>
+ */
+public class Lazy<T> implements Supplier<T> {
+    private T value;
+    private volatile Supplier<T> init;
+
+    public Lazy(Supplier<T> init) {
+        reset(init);
+    }
+
+    public Lazy<T> reset(Supplier<T> init) {
+        this.init = Validate.notNull(init);
+        return this;
+    }
+
+    @Override
+    public T get() {
+        if (init != null) {
+            synchronized (this) {
+                if (init != null) {
+                    value = init.get();
+                    init = null;
+                }
+            }
+        }
+        return value;
+    }
+
+    public Optional<T> optional() {
+        return Optional.ofNullable(value);
+    }
+
+    public <U> Consumer<U> consumer(BiConsumer<? super T, ? super U> delegate) {
+        return u -> delegate.accept(get(), u);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/LazyInt.java
----------------------------------------------------------------------
diff --git a/bval-core/src/main/java/org/apache/bval/util/LazyInt.java b/bval-core/src/main/java/org/apache/bval/util/LazyInt.java
new file mode 100644
index 0000000..44e09dd
--- /dev/null
+++ b/bval-core/src/main/java/org/apache/bval/util/LazyInt.java
@@ -0,0 +1,49 @@
+/*
+ *  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.util;
+
+import java.util.OptionalInt;
+import java.util.function.IntSupplier;
+
+/**
+ * @since 2.0
+ */
+public class LazyInt implements IntSupplier {
+    private int value;
+    private IntSupplier init;
+
+    public LazyInt(IntSupplier init) {
+        this.init = Validate.notNull(init);
+    }
+
+    @Override
+    public int getAsInt() {
+        if (init != null) {
+            synchronized (this) {
+                if (init != null) {
+                    value = init.getAsInt();
+                    init = null;
+                }
+            }
+        }
+        return value;
+    }
+
+    public synchronized OptionalInt optional() {
+        return init == null ? OptionalInt.of(value) : OptionalInt.empty();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/ObjectUtils.java
----------------------------------------------------------------------
diff --git a/bval-core/src/main/java/org/apache/bval/util/ObjectUtils.java b/bval-core/src/main/java/org/apache/bval/util/ObjectUtils.java
index 0464eeb..b7f2fac 100644
--- a/bval-core/src/main/java/org/apache/bval/util/ObjectUtils.java
+++ b/bval-core/src/main/java/org/apache/bval/util/ObjectUtils.java
@@ -18,6 +18,8 @@ package org.apache.bval.util;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Array;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
 
 public final class ObjectUtils {
     public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0];
@@ -44,7 +46,7 @@ public final class ObjectUtils {
      * @return {@code object} if it is not {@code null}, defaultValue otherwise
      */
     public static <T> T defaultIfNull(final T object, final T defaultValue) {
-        return object != null ? object : defaultValue;
+        return object == null ? defaultValue : object;
     }
 
     public static <T> boolean isNotEmpty(final T[] array) {
@@ -68,29 +70,19 @@ public final class ObjectUtils {
         if (array == null) {
             return false;
         }
-        for (Object o : array) {
-            if (o.equals(objectToFind)) {
-                return true;
-            }
-        }
-        return false;
+        return Stream.of(array).anyMatch(Predicate.isEqual(objectToFind));
     }
 
     public static <T> T[] arrayAdd(T[] array, T objectToAdd) {
-        Class<?> type;
-        if (array != null) {
-            type = array.getClass().getComponentType();
-        } else if (objectToAdd != null) {
-            type = objectToAdd.getClass();
-        } else {
+        if (array == null && objectToAdd == null) {
             throw new IllegalArgumentException("Arguments cannot both be null");
         }
         final int arrayLength = Array.getLength(array);
+        @SuppressWarnings("unchecked")
         T[] newArray = (T[]) Array.newInstance(array.getClass().getComponentType(), arrayLength + 1);
         System.arraycopy(array, 0, newArray, 0, arrayLength);
         newArray[newArray.length - 1] = objectToAdd;
 
         return newArray;
-
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/ObjectWrapper.java
----------------------------------------------------------------------
diff --git a/bval-core/src/main/java/org/apache/bval/util/ObjectWrapper.java b/bval-core/src/main/java/org/apache/bval/util/ObjectWrapper.java
new file mode 100644
index 0000000..8483745
--- /dev/null
+++ b/bval-core/src/main/java/org/apache/bval/util/ObjectWrapper.java
@@ -0,0 +1,50 @@
+/*
+ * 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.util;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class ObjectWrapper<T> implements Consumer<T>, Supplier<T> {
+    private T value;
+
+    public ObjectWrapper() {
+        this(null);
+    }
+
+    public ObjectWrapper(T value) {
+        super();
+        this.value = value;
+    }
+
+    @Override
+    public void accept(T value) {
+        this.value = value;
+    }
+
+    @Override
+    public T get() {
+        return value;
+    }
+
+    public Optional<T> optional() {
+        return Optional.ofNullable(value);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/StringUtils.java
----------------------------------------------------------------------
diff --git a/bval-core/src/main/java/org/apache/bval/util/StringUtils.java b/bval-core/src/main/java/org/apache/bval/util/StringUtils.java
index f7add27..6b9c25d 100644
--- a/bval-core/src/main/java/org/apache/bval/util/StringUtils.java
+++ b/bval-core/src/main/java/org/apache/bval/util/StringUtils.java
@@ -17,8 +17,6 @@
 package org.apache.bval.util;
 
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
 import java.util.List;
 
 public final class StringUtils {
@@ -93,49 +91,24 @@ public final class StringUtils {
         return true;
     }
 
-    public static String join(Collection<?> values, String joinToken) {
-        if (values == null) {
-            return null;
-        }
-        if (values.size() == 0) {
-            return "";
-        }
-        if (values.size() == 1) {
-            return values.iterator().next().toString();
-        }
-        if (joinToken == null) {
-            joinToken = "null"; // backward compat with commons-lang StringUtils...
-        }
-
-        StringBuilder sb = new StringBuilder(values.size() * (16 + joinToken.length()));
-        Iterator<?> it = values.iterator();
-        sb.append(it.next());
-        while (it.hasNext()) {
-            sb.append(joinToken).append(it.next());
-        }
-        return sb.toString();
-    }
-
-    public static String joinArray(Object[] values, String joinToken) {
-        if (values == null) {
-            return null;
-        }
-        if (values.length == 0) {
-            return "";
-        }
-        if (values.length == 1) {
-            return values[0].toString();
-        }
-        if (joinToken == null) {
-            joinToken = "null"; // backward compat with commons-lang StringUtils...
-        }
-
-        StringBuilder sb = new StringBuilder(values.length * (16 + joinToken.length()));
-        sb.append(values[0]);
-        for (int i = 1; i < values.length; i++) {
-            sb.append(joinToken).append(values[i]);
-        }
-        return sb.toString();
+    /**
+     * Taken from commons-lang3.
+     * <p>Checks if a CharSequence is not empty (""), not null and not whitespace only.</p>
+     *
+     * <pre>
+     * StringUtils.isNotBlank(null)      = false
+     * StringUtils.isNotBlank("")        = false
+     * StringUtils.isNotBlank(" ")       = false
+     * StringUtils.isNotBlank("bob")     = true
+     * StringUtils.isNotBlank("  bob  ") = true
+     * </pre>
+     *
+     * @param cs  the CharSequence to check, may be null
+     * @return {@code true} if the CharSequence is
+     *  not empty and not null and not whitespace
+     */
+    public static boolean isNotBlank(final CharSequence cs) {
+        return !isBlank(cs);
     }
 
     /**
@@ -149,12 +122,12 @@ public final class StringUtils {
      * <p>Splits the provided text into an array, separator is whitespace.
      */
     public static String[] split(String str, Character token) {
-        if (str == null || str.length() == 0) {
+        if (str == null || str.isEmpty()) {
             return ObjectUtils.EMPTY_STRING_ARRAY;
         }
 
         // split on token
-        List<String> ret = new ArrayList<String>();
+        List<String> ret = new ArrayList<>();
         StringBuilder sb = new StringBuilder(str.length());
         for (int pos = 0; pos < str.length(); pos++) {
             char c = str.charAt(pos);

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/Validate.java
----------------------------------------------------------------------
diff --git a/bval-core/src/main/java/org/apache/bval/util/Validate.java b/bval-core/src/main/java/org/apache/bval/util/Validate.java
index f0e0611..042dc1b 100644
--- a/bval-core/src/main/java/org/apache/bval/util/Validate.java
+++ b/bval-core/src/main/java/org/apache/bval/util/Validate.java
@@ -16,9 +16,10 @@
  */
 package org.apache.bval.util;
 
+import java.util.function.Function;
+
 /**
- * Some used Validate from commons.
- *
+ * Some used validations from commons.
  */
 public final class Validate {
     private Validate() {
@@ -29,16 +30,30 @@ public final class Validate {
     }
 
     public static <T> T notNull(final T object, final String message, final Object... values) {
-        if (object == null) {
-            throw new NullPointerException(String.format(message, values));
-        }
+        return notNull(object, NullPointerException::new, message, values);
+    }
+
+    public static <E extends Exception, T> T notNull(final T object, Function<? super String, ? extends E> fn,
+        final String message, final Object... values) throws E {
+        Exceptions.raiseIf(object == null, fn, message, values);
         return object;
     }
 
     public static void isTrue(final boolean expression, final String message, final Object... values) {
-        if (expression == false) {
-            throw new IllegalArgumentException(String.format(message, values));
+        Exceptions.raiseUnless(expression, IllegalArgumentException::new, message, values);
+    }
+
+    public static <T> T[] noNullElements(final T[] array, final String message, final Object... values) {
+        Validate.notNull(array);
+
+        for (int i = 0; i < array.length; i++) {
+            Exceptions.raiseIf(array[i] == null, IllegalArgumentException::new, message,
+                ObjectUtils.arrayAdd(values, Integer.valueOf(i)));
         }
+        return array;
     }
 
+    public static void validState(final boolean expression, final String message, final Object... values) {
+        Exceptions.raiseUnless(expression, IllegalStateException::new, message, values);
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/reflection/Reflection.java
----------------------------------------------------------------------
diff --git a/bval-core/src/main/java/org/apache/bval/util/reflection/Reflection.java b/bval-core/src/main/java/org/apache/bval/util/reflection/Reflection.java
index 674cf94..221a277 100644
--- a/bval-core/src/main/java/org/apache/bval/util/reflection/Reflection.java
+++ b/bval-core/src/main/java/org/apache/bval/util/reflection/Reflection.java
@@ -24,8 +24,16 @@ import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
 
 import org.apache.commons.weaver.privilizer.Privilizing;
 
@@ -33,36 +41,44 @@ import org.apache.commons.weaver.privilizer.Privilizing;
  * Security-agnostic "blueprint" class for reflection-related operations. Intended for use by Apache BVal code.
  */
 public class Reflection {
+    /**
+     * Inclusivity literals for {@link #hierarchy(Class, Interfaces)}.
+     * Taken from commons-lang3.
+     */
+    public enum Interfaces {
+        INCLUDE, EXCLUDE
+    }
+
     private static final Object[][] NATIVE_CODES = new Object[][]{
-            {byte.class, "byte", "B"},
-            {char.class, "char", "C"},
-            {double.class, "double", "D"},
-            {float.class, "float", "F"},
-            {int.class, "int", "I"},
-            {long.class, "long", "J"},
-            {short.class, "short", "S"},
-            {boolean.class, "boolean", "Z"},
-            {void.class, "void", "V"}
+            { byte.class, "byte", "B" },
+            { char.class, "char", "C" },
+            { double.class, "double", "D" },
+            { float.class, "float", "F" },
+            { int.class, "int", "I" },
+            { long.class, "long", "J" },
+            { short.class, "short", "S" },
+            { boolean.class, "boolean", "Z" },
+            { void.class, "void", "V" }
     };
 
     /**
      * Maps primitive {@code Class}es to their corresponding wrapper {@code Class}.
      */
-    private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_MAP = new HashMap<Class<?>, Class<?>>();
+    private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_MAP;
     static {
-        PRIMITIVE_WRAPPER_MAP.put(Boolean.TYPE, Boolean.class);
-        PRIMITIVE_WRAPPER_MAP.put(Byte.TYPE, Byte.class);
-        PRIMITIVE_WRAPPER_MAP.put(Character.TYPE, Character.class);
-        PRIMITIVE_WRAPPER_MAP.put(Short.TYPE, Short.class);
-        PRIMITIVE_WRAPPER_MAP.put(Integer.TYPE, Integer.class);
-        PRIMITIVE_WRAPPER_MAP.put(Long.TYPE, Long.class);
-        PRIMITIVE_WRAPPER_MAP.put(Double.TYPE, Double.class);
-        PRIMITIVE_WRAPPER_MAP.put(Float.TYPE, Float.class);
-        PRIMITIVE_WRAPPER_MAP.put(Void.TYPE, Void.TYPE);
+        final Map<Class<?>, Class<?>> m = new HashMap<>();
+        m.put(Boolean.TYPE, Boolean.class);
+        m.put(Byte.TYPE, Byte.class);
+        m.put(Character.TYPE, Character.class);
+        m.put(Short.TYPE, Short.class);
+        m.put(Integer.TYPE, Integer.class);
+        m.put(Long.TYPE, Long.class);
+        m.put(Double.TYPE, Double.class);
+        m.put(Float.TYPE, Float.class);
+        m.put(Void.TYPE, Void.TYPE);
+        PRIMITIVE_WRAPPER_MAP = Collections.unmodifiableMap(m);
     }
 
-
-
     /**
      * <p>Converts the specified primitive Class object to its corresponding
      * wrapper Class object.</p>
@@ -129,8 +145,7 @@ public class Reflection {
         return cl == null ? clazz.getClassLoader() : cl;
     }
 
-    public static Class<?> toClass(String className) throws ClassNotFoundException
-    {
+    public static Class<?> toClass(String className) throws ClassNotFoundException {
         ClassLoader cl = getClassLoader(Reflection.class);
         return toClass(className, cl);
     }
@@ -142,7 +157,7 @@ public class Reflection {
      *
      * @throws RuntimeException on load error
      */
-    public static Class toClass(String className, ClassLoader loader) throws ClassNotFoundException {
+    public static Class<?> toClass(String className, ClassLoader loader) throws ClassNotFoundException {
         return toClass(className, false, loader);
     }
 
@@ -153,7 +168,7 @@ public class Reflection {
      *
      * @throws RuntimeException on load error
      */
-    public static Class toClass(String className, boolean resolve, ClassLoader loader) throws ClassNotFoundException {
+    public static Class<?> toClass(String className, boolean resolve, ClassLoader loader) throws ClassNotFoundException {
         if (className == null) {
             throw new NullPointerException("className == null");
         }
@@ -171,7 +186,7 @@ public class Reflection {
             for (int i = 0; !primitive && (i < NATIVE_CODES.length); i++) {
                 if (NATIVE_CODES[i][1].equals(className)) {
                     if (dims == 0) {
-                        return (Class) NATIVE_CODES[i][0];
+                        return (Class<?>) NATIVE_CODES[i][0];
                     }
                     className = (String) NATIVE_CODES[i][2];
                     primitive = true;
@@ -296,6 +311,22 @@ public class Reflection {
     }
 
     /**
+     * Perform a search against the class hierarchy.
+     * @param clazz
+     * @param search
+     * @return T or {@code null}
+     */
+    public static <T> T find(final Class<?> clazz, Function<Class<?>, T> search) {
+        for (Class<?> t : hierarchy(clazz, Interfaces.INCLUDE)) {
+            final T value = search.apply(t);
+            if (value != null) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Construct a new instance of {@code cls} using its default constructor.
      * @param cls
      * @return T
@@ -333,4 +364,94 @@ public class Reflection {
         return true;
     }
 
+    /**
+     * Get an {@link Iterable} that can iterate over a class hierarchy in ascending (subclass to superclass) order.
+     * Taken from commons-lang3.
+     *
+     * @param type the type to get the class hierarchy from
+     * @param interfacesBehavior switch indicating whether to include or exclude interfaces
+     * @return Iterable an Iterable over the class hierarchy of the given class
+     */
+    public static Iterable<Class<?>> hierarchy(final Class<?> type, final Interfaces interfacesBehavior) {
+        if (type == null) {
+            return Collections.emptySet();
+        }
+        final Iterable<Class<?>> classes = new Iterable<Class<?>>() {
+
+            @Override
+            public Iterator<Class<?>> iterator() {
+                return new Iterator<Class<?>>() {
+                    Optional<Class<?>> next;
+                    {
+                        next = Optional.of(type);
+                    }
+
+                    @Override
+                    public boolean hasNext() {
+                        return next.isPresent();
+                    }
+
+                    @Override
+                    public Class<?> next() {
+                        final Class<?> result = next.orElseThrow(NoSuchElementException::new);
+                        next = Optional.ofNullable(result.getSuperclass());
+                        return result;
+                    }
+
+                    @Override
+                    public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+        };
+        if (interfacesBehavior != Interfaces.INCLUDE) {
+            return classes;
+        }
+        return new Iterable<Class<?>>() {
+
+            @Override
+            public Iterator<Class<?>> iterator() {
+                final Set<Class<?>> seenInterfaces = new HashSet<Class<?>>();
+                final Iterator<Class<?>> wrapped = classes.iterator();
+    
+                return new Iterator<Class<?>>() {
+                    Iterator<Class<?>> interfaces = Collections.emptyIterator();
+
+                    @Override
+                    public boolean hasNext() {
+                        return interfaces.hasNext() || wrapped.hasNext();
+                    }
+
+                    @Override
+                    public Class<?> next() {
+                        if (interfaces.hasNext()) {
+                            final Class<?> nextInterface = interfaces.next();
+                            seenInterfaces.add(nextInterface);
+                            return nextInterface;
+                        }
+                        final Class<?> nextSuperclass = wrapped.next();
+                        final Set<Class<?>> currentInterfaces = new LinkedHashSet<>();
+                        walkInterfaces(currentInterfaces, nextSuperclass);
+                        interfaces = currentInterfaces.iterator();
+                        return nextSuperclass;
+                    }
+
+                    private void walkInterfaces(final Set<Class<?>> addTo, final Class<?> c) {
+                        for (final Class<?> iface : c.getInterfaces()) {
+                            if (!seenInterfaces.contains(iface)) {
+                                addTo.add(iface);
+                            }
+                            walkInterfaces(addTo, iface);
+                        }
+                    }
+
+                    @Override
+                    public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+        };
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java
----------------------------------------------------------------------
diff --git a/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java b/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java
index 4734906..b8b044d 100644
--- a/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java
+++ b/bval-core/src/main/java/org/apache/bval/util/reflection/TypeUtils.java
@@ -28,8 +28,11 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Stream;
 
+import org.apache.bval.util.ObjectUtils;
 import org.apache.bval.util.Validate;
 
 /**
@@ -148,13 +151,7 @@ public class TypeUtils {
          */
         @Override
         public int hashCode() {
-            int result = 71 << 4;
-            result |= raw.hashCode();
-            result <<= 4;
-            result |= useOwner == null ? 0 : useOwner.hashCode();
-            result <<= 8;
-            result |= Arrays.hashCode(typeArguments);
-            return result;
+            return Objects.hash(raw, useOwner, typeArguments);
         }
     }
 
@@ -162,7 +159,8 @@ public class TypeUtils {
      * WildcardType implementation class.
      */
     private static final class WildcardTypeImpl implements WildcardType {
-        private static final Type[] EMPTY_BOUNDS = new Type[0];
+        private static final Type[] EMPTY_UPPER_BOUNDS = { Object.class };
+        private static final Type[] EMPTY_LOWER_BOUNDS = new Type[0];
 
         private final Type[] upperBounds;
         private final Type[] lowerBounds;
@@ -173,8 +171,8 @@ public class TypeUtils {
          * @param lowerBounds of this type
          */
         private WildcardTypeImpl(final Type[] upperBounds, final Type[] lowerBounds) {
-            this.upperBounds = upperBounds != null ? upperBounds : EMPTY_BOUNDS;
-            this.lowerBounds = lowerBounds != null ? lowerBounds : EMPTY_BOUNDS;
+            this.upperBounds = ObjectUtils.isEmpty(upperBounds) ? EMPTY_UPPER_BOUNDS : upperBounds;
+            this.lowerBounds = lowerBounds == null ? EMPTY_LOWER_BOUNDS : lowerBounds;
         }
 
         /**
@@ -214,11 +212,7 @@ public class TypeUtils {
          */
         @Override
         public int hashCode() {
-            int result = 73 << 8;
-            result |= Arrays.hashCode(upperBounds);
-            result <<= 8;
-            result |= Arrays.hashCode(lowerBounds);
-            return result;
+            return Objects.hash(upperBounds, lowerBounds);
         }
     }
 
@@ -320,19 +314,13 @@ public class TypeUtils {
         if (type instanceof TypeVariable<?>) {
             // if any of the bounds are assignable to the class, then the
             // type is assignable to the class.
-            for (final Type bound : ((TypeVariable<?>) type).getBounds()) {
-                if (isAssignable(bound, toClass)) {
-                    return true;
-                }
-            }
-
-            return false;
+            return Stream.of(((TypeVariable<?>) type).getBounds()).anyMatch(bound -> isAssignable(bound, toClass));
         }
 
         // the only classes to which a generic array type can be assigned
         // are class Object and array classes
         if (type instanceof GenericArrayType) {
-            return toClass.equals(Object.class)
+            return Object.class.equals(toClass)
                     || toClass.isArray()
                     && isAssignable(((GenericArrayType) type).getGenericComponentType(), toClass
                             .getComponentType());
@@ -554,25 +542,15 @@ public class TypeUtils {
 
         if (type instanceof WildcardType) {
             // so long as one of the upper bounds is assignable, it's good
-            for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
-                if (isAssignable(bound, toGenericArrayType)) {
-                    return true;
-                }
-            }
-
-            return false;
+            return Stream.of(getImplicitUpperBounds((WildcardType) type))
+                .anyMatch(bound -> isAssignable(bound, toGenericArrayType));
         }
 
         if (type instanceof TypeVariable<?>) {
             // probably should remove the following logic and just return false.
             // type variables cannot specify arrays as bounds.
-            for (final Type bound : getImplicitBounds((TypeVariable<?>) type)) {
-                if (isAssignable(bound, toGenericArrayType)) {
-                    return true;
-                }
-            }
-
-            return false;
+            return Stream.of(getImplicitBounds((TypeVariable<?>) type))
+                .anyMatch(bound -> isAssignable(bound, toGenericArrayType));
         }
 
         if (type instanceof ParameterizedType) {
@@ -871,8 +849,7 @@ public class TypeUtils {
                     getRawType(parameterizedOwnerType), subtypeVarAssigns);
         } else {
             // no owner, prep the type variable assignments map
-            typeVarAssigns = subtypeVarAssigns == null ? new HashMap<TypeVariable<?>, Type>()
-                    : new HashMap<TypeVariable<?>, Type>(subtypeVarAssigns);
+            typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>() : new HashMap<>(subtypeVarAssigns);
         }
 
         // get the subject parameterized type's arguments
@@ -917,7 +894,7 @@ public class TypeUtils {
             if (toClass.isPrimitive()) {
                 // dealing with widening here. No type arguments to be
                 // harvested with these two types.
-                return new HashMap<TypeVariable<?>, Type>();
+                return new HashMap<>();
             }
 
             // work with wrapper the wrapper class instead of the primitive
@@ -925,8 +902,8 @@ public class TypeUtils {
         }
 
         // create a copy of the incoming map, or an empty one if it's null
-        final HashMap<TypeVariable<?>, Type> typeVarAssigns = subtypeVarAssigns == null ? new HashMap<TypeVariable<?>, Type>()
-                : new HashMap<TypeVariable<?>, Type>(subtypeVarAssigns);
+        final Map<TypeVariable<?>, Type> typeVarAssigns =
+            subtypeVarAssigns == null ? new HashMap<>() : new HashMap<>(subtypeVarAssigns);
 
         // has target class been reached?
         if (toClass.equals(cls)) {
@@ -1030,7 +1007,7 @@ public class TypeUtils {
             return bounds;
         }
 
-        final Set<Type> types = new HashSet<Type>(bounds.length);
+        final Set<Type> types = new HashSet<>(bounds.length);
 
         for (final Type type1 : bounds) {
             boolean subtypeFound = false;
@@ -1243,7 +1220,7 @@ public class TypeUtils {
                 if (p.getOwnerType() == null) {
                     parameterizedTypeArguments = typeArguments;
                 } else {
-                    parameterizedTypeArguments = new HashMap<TypeVariable<?>, Type>(typeArguments);
+                    parameterizedTypeArguments = new HashMap<>(typeArguments);
                     parameterizedTypeArguments.putAll(TypeUtils.getTypeArguments(p));
                 }
                 final Type[] args = p.getActualTypeArguments();

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java
new file mode 100644
index 0000000..b53b513
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java
@@ -0,0 +1,359 @@
+/*
+ * 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.util;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Repeatable;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.ConstraintTarget;
+import javax.validation.OverridesAttribute;
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+import javax.validation.constraintvalidation.ValidationTarget;
+
+import org.apache.bval.jsr.ApacheValidatorFactory;
+import org.apache.bval.jsr.ConfigurationImpl;
+import org.apache.bval.jsr.ConstraintAnnotationAttributes;
+import org.apache.bval.jsr.ConstraintCached.ConstraintValidatorInfo;
+import org.apache.bval.jsr.groups.Group;
+import org.apache.bval.jsr.groups.Groups;
+import org.apache.bval.jsr.groups.GroupsComputer;
+import org.apache.bval.jsr.metadata.Metas;
+import org.apache.bval.jsr.xml.AnnotationProxyBuilder;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Lazy;
+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;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+
+/**
+ * Manages (constraint) annotations according to the BV spec.
+ * 
+ * @since 2.0
+ */
+@Privilizing(@CallTo(Reflection.class))
+public class AnnotationsManager {
+    private static final class OverriddenAnnotationSpecifier {
+        final Class<? extends Annotation> annotationType;
+        final boolean impliesSingleComposingConstraint;
+        final int constraintIndex;
+
+        OverriddenAnnotationSpecifier(OverridesAttribute annotation) {
+            this(annotation.constraint(), annotation.constraintIndex());
+        }
+
+        OverriddenAnnotationSpecifier(Class<? extends Annotation> annotationType, int constraintIndex) {
+            super();
+            this.annotationType = annotationType;
+            this.impliesSingleComposingConstraint = constraintIndex < 0;
+            this.constraintIndex = Math.max(constraintIndex, 0);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            if (obj == null || !obj.getClass().equals(getClass())) {
+                return false;
+            }
+            final OverriddenAnnotationSpecifier other = (OverriddenAnnotationSpecifier) obj;
+            return Objects.equals(annotationType, other.annotationType) && constraintIndex == other.constraintIndex;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(annotationType, constraintIndex);
+        }
+    }
+
+    private static class Composition {
+        final Lazy<Map<OverriddenAnnotationSpecifier, Map<String, String>>> overrides = new Lazy<>(HashMap::new);
+        final Annotation[] components;
+
+        Composition(Class<? extends Annotation> annotationType) {
+            // TODO detect recursion
+            components = getDeclaredConstraints(annotationType);
+
+            if (!isComposed()) {
+                return;
+            }
+            final Map<Class<? extends Annotation>, AtomicInteger> constraintCounts = new HashMap<>();
+            for (Annotation a : components) {
+                constraintCounts.computeIfAbsent(a.annotationType(), k -> new AtomicInteger()).incrementAndGet();
+            }
+            // create a map of overridden constraints to overridden attributes:
+            for (Method m : Reflection.getDeclaredMethods(annotationType)) {
+                final String from = m.getName();
+                for (OverridesAttribute overridesAttribute : m.getDeclaredAnnotationsByType(OverridesAttribute.class)) {
+                    final String to =
+                        Optional.of(overridesAttribute.name()).filter(StringUtils::isNotBlank).orElse(from);
+
+                    final OverriddenAnnotationSpecifier spec = new OverriddenAnnotationSpecifier(overridesAttribute);
+                    final int count = constraintCounts.get(spec.annotationType).get();
+
+                    if (spec.impliesSingleComposingConstraint) {
+                        Exceptions.raiseUnless(count == 1, ConstraintDefinitionException::new,
+                            "Expected a single composing %s constraint", spec.annotationType);
+                    } else {
+                        Exceptions.raiseUnless(count > spec.constraintIndex, ConstraintDefinitionException::new,
+                            "Expected at least %s composing %s constraints", spec.constraintIndex + 1,
+                            spec.annotationType);
+                    }
+                    final Map<String, String> attributeMapping =
+                        overrides.get().computeIfAbsent(spec, k -> new HashMap<>());
+
+                    Exceptions.raiseIf(attributeMapping.containsKey(to), ConstraintDefinitionException::new,
+                        "Attempt to override %s#%s() index %d from multiple sources", overridesAttribute.constraint(),
+                        to, overridesAttribute.constraintIndex());
+
+                    attributeMapping.put(to, from);
+                }
+            }
+        }
+
+        boolean isComposed() {
+            return components.length > 0;
+        }
+
+        Annotation[] getComponents(Annotation source) {
+            final Class<?>[] groups =
+                ConstraintAnnotationAttributes.GROUPS.analyze(source.annotationType()).read(source);
+
+            final Class<? extends Payload>[] payload =
+                ConstraintAnnotationAttributes.PAYLOAD.analyze(source.annotationType()).read(source);
+
+            final Optional<ConstraintTarget> constraintTarget =
+                Optional.of(source.annotationType()).map(ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO::analyze)
+                    .filter(ConstraintAnnotationAttributes.Worker::isValid).map(w -> w.read(source));
+
+            final Map<Class<? extends Annotation>, AtomicInteger> constraintCounts = new HashMap<>();
+
+            return Stream.of(components).map(c -> {
+                final int index =
+                    constraintCounts.computeIfAbsent(c.annotationType(), k -> new AtomicInteger()).getAndIncrement();
+
+                final AnnotationProxyBuilder<Annotation> proxyBuilder = new AnnotationProxyBuilder<>(c);
+
+                proxyBuilder.setGroups(groups);
+                proxyBuilder.setPayload(payload);
+                constraintTarget.ifPresent(proxyBuilder::setValidationAppliesTo);
+
+                overrides.optional().map(o -> o.get(new OverriddenAnnotationSpecifier(c.annotationType(), index)))
+                    .ifPresent(m -> {
+                        final Map<String, Object> sourceAttributes = readAttributes(source);
+                        m.forEach((k, v) -> proxyBuilder.setValue(k, sourceAttributes.get(v)));
+                    });
+                return proxyBuilder.isChanged() ? proxyBuilder.createAnnotation() : c;
+            }).toArray(Annotation[]::new);
+        }
+    }
+
+    public static Map<String, Object> readAttributes(Annotation a) {
+        final Lazy<Map<String, Object>> result = new Lazy<>(LinkedHashMap::new);
+
+        Stream.of(Reflection.getDeclaredMethods(a.annotationType())).filter(m -> m.getParameterCount() == 0)
+            .forEach(m -> {
+                final boolean mustUnset = Reflection.setAccessible(m, true);
+                try {
+                    result.get().put(m.getName(), m.invoke(a));
+                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+                    Exceptions.raise(ValidationException::new, e, "Caught exception reading attributes of %s", a);
+                } finally {
+                    if (mustUnset) {
+                        Reflection.setAccessible(m, false);
+                    }
+                }
+            });
+        return result.optional().map(Collections::unmodifiableMap).orElseGet(Collections::emptyMap);
+    }
+
+    /**
+     * Meta-annotation aware.
+     * 
+     * @param e
+     * @param t
+     * @return {@code boolean}
+     * @see AnnotatedElement#isAnnotationPresent(Class)
+     */
+    public static boolean isAnnotationPresent(AnnotatedElement e, Class<? extends Annotation> t) {
+        if (e.isAnnotationPresent(t)) {
+            return true;
+        }
+        return Stream.of(e.getAnnotations()).map(Annotation::annotationType).anyMatch(a -> isAnnotationPresent(a, t));
+    }
+
+    /**
+     * Get declared annotations with a particular meta-annotation.
+     * 
+     * @param e
+     * @param meta
+     * @return {@link Annotation}[]
+     */
+    public static Annotation[] getDeclared(AnnotatedElement e, Class<? extends Annotation> meta) {
+        return Stream.of(e.getDeclaredAnnotations()).filter(ann -> isAnnotationPresent(ann.annotationType(), meta))
+            .toArray(Annotation[]::new);
+    }
+
+    /**
+     * Accounts for {@link Constraint} meta-annotation AND {@link Repeatable}
+     * constraint annotations.
+     * 
+     * @param meta
+     * @return Annotation[]
+     */
+    public static Annotation[] getDeclaredConstraints(Metas<?> meta) {
+        final Annotation[] result = getDeclaredConstraints(meta.getHost());
+        final Class<?> dc = meta.getDeclaringClass();
+        if (dc.isInterface()) {
+            final GroupsComputer groupsComputer = new GroupsComputer();
+            // ensure interface group is implied by Default group:
+            Stream.of(result).map(c -> {
+                final Groups groups = groupsComputer
+                    .computeGroups(ConstraintAnnotationAttributes.GROUPS.analyze(c.annotationType()).read(c));
+                if (groups.getGroups().stream().anyMatch(Group::isDefault)) {
+                    final Set<Class<?>> groupClasses = groups.getGroups().stream().map(Group::getGroup)
+                        .collect(Collectors.toCollection(LinkedHashSet::new));
+                    if (groupClasses.add(dc)) {
+                        final AnnotationProxyBuilder<?> proxyBuilder = new AnnotationProxyBuilder<>(c);
+                        proxyBuilder.setGroups(groupClasses.toArray(new Class[groupClasses.size()]));
+                        return proxyBuilder.createAnnotation();
+                    }
+                }
+                return c;
+            }).toArray(n -> result);
+        }
+        return result;
+    }
+
+    private static Annotation[] getDeclaredConstraints(AnnotatedElement e) {
+        return Stream.of(e.getDeclaredAnnotations()).flatMap((Function<Annotation, Stream<Annotation>>) a -> {
+            final ConstraintAnnotationAttributes.Worker<? extends Annotation> analyzer =
+                ConstraintAnnotationAttributes.VALUE.analyze(a.annotationType());
+            if (analyzer.isValid()) {
+                return Stream.of(analyzer.<Annotation[]> read(a));
+            }
+            return Stream.of(a);
+        }).filter(a -> a.annotationType().isAnnotationPresent(Constraint.class)).toArray(Annotation[]::new);
+    }
+
+    public static boolean declaresAttribute(Class<? extends Annotation> annotationType, String name) {
+        try {
+            annotationType.getDeclaredMethod(name);
+            return true;
+        } catch (NoSuchMethodException | SecurityException e) {
+            return false;
+        }
+    }
+
+    private final ApacheValidatorFactory validatorFactory;
+    private final LRUCache<Class<? extends Annotation>, Composition> compositions;
+
+    public AnnotationsManager(ApacheValidatorFactory validatorFactory) {
+        super();
+        this.validatorFactory = Validate.notNull(validatorFactory);
+        final String cacheSize =
+            validatorFactory.getProperties().get(ConfigurationImpl.Properties.CONSTRAINTS_CACHE_SIZE);
+        try {
+            compositions = new LRUCache<>(Integer.parseInt(cacheSize));
+        } catch (NumberFormatException e) {
+            throw Exceptions.create(IllegalStateException::new, e,
+                "Cannot parse value %s for configuration property %s", cacheSize,
+                ConfigurationImpl.Properties.CONSTRAINTS_CACHE_SIZE);
+        }
+    }
+
+    /**
+     * Retrieve the composing constraints for the specified constraint
+     * {@link Annotation}.
+     * 
+     * @param a
+     * @return {@link Annotation}[]
+     */
+    public Annotation[] getComposingConstraints(Annotation a) {
+        return getComposition(a.annotationType()).getComponents(a);
+    }
+
+    /**
+     * Learn whether {@code a} is composed.
+     * 
+     * @param a
+     * @return {@code boolean}
+     */
+    public boolean isComposed(Annotation a) {
+        return getComposition(a.annotationType()).isComposed();
+    }
+
+    /**
+     * Get the supported targets for {@code constraintType}.
+     * 
+     * @param constraintType
+     * @return {@link Set} of {@link ValidationTarget}
+     */
+    public <A extends Annotation> Set<ValidationTarget> supportedTargets(Class<A> constraintType) {
+        final Set<ConstraintValidatorInfo<A>> constraintValidatorInfo =
+            validatorFactory.getConstraintsCache().getConstraintValidatorInfo(constraintType);
+        final Stream<Set<ValidationTarget>> s;
+        if (constraintValidatorInfo.isEmpty()) {
+            // must be for composition:
+            s = Stream.of(new Composition(constraintType).components).map(Annotation::annotationType)
+                .map(this::supportedTargets);
+        } else {
+            s = constraintValidatorInfo.stream().map(ConstraintValidatorInfo::getSupportedTargets);
+        }
+        return s.flatMap(Collection::stream)
+            .collect(Collectors.toCollection(() -> EnumSet.noneOf(ValidationTarget.class)));
+    }
+
+    private Composition getComposition(Class<? extends Annotation> annotationType) {
+        return compositions.computeIfAbsent(annotationType, ct -> {
+            final Set<ValidationTarget> composedTargets = supportedTargets(annotationType);
+            final Composition result = new Composition(annotationType);
+            Stream.of(result.components).map(Annotation::annotationType).forEach(at -> {
+                final Set<ValidationTarget> composingTargets = supportedTargets(at);
+                Exceptions.raiseIf(Collections.disjoint(composingTargets, composedTargets),
+                    ConstraintDefinitionException::new,
+                    "Attempt to compose %s of %s but validator types are incompatible", annotationType.getName(),
+                    at.getName());
+            });
+            return result;
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java
index 9d3bd85..73c82a6 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java
@@ -20,7 +20,11 @@ package org.apache.bval.jsr.util;
 
 import java.io.Serializable;
 import java.security.AccessController;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Common operations on classes that do not require an {@link AccessController}.
@@ -28,6 +32,7 @@ import java.util.List;
  * @author Carlos Vara
  */
 public class ClassHelper {
+    private static final Set<Class<?>> IGNORED_TYPES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(null,Object.class,Serializable.class,Cloneable.class)));
 
     private ClassHelper() {
         // No instances please
@@ -42,10 +47,7 @@ public class ClassHelper {
      * @param clazz
      */
     public static List<Class<?>> fillFullClassHierarchyAsList(List<Class<?>> allClasses, Class<?> clazz) {
-        if (clazz == null || clazz == Object.class || clazz == Serializable.class || clazz == Cloneable.class) {
-            return allClasses;
-        }
-        if (allClasses.contains(clazz)) {
+        if (IGNORED_TYPES.contains(clazz) || allClasses.contains(clazz)) {
             return allClasses;
         }
         allClasses.add(clazz);

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderCustomizableContextImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderCustomizableContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderCustomizableContextImpl.java
new file mode 100644
index 0000000..c0cff10
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderCustomizableContextImpl.java
@@ -0,0 +1,77 @@
+/*
+ * 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.util;
+
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeContextBuilder;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext;
+
+import org.apache.bval.jsr.job.ConstraintValidatorContextImpl;
+
+public class ContainerElementNodeBuilderCustomizableContextImpl
+    implements ContainerElementNodeBuilderCustomizableContext {
+    private final ConstraintValidatorContextImpl<?> context;
+    private final String template;
+    private final PathImpl path;
+    private NodeImpl node;
+
+    public ContainerElementNodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl<?> context, String template,
+        PathImpl path, String name, Class<?> containerType, Integer typeArgumentIndex) {
+        super();
+        this.context = context;
+        this.path = path;
+        this.template = template;
+        this.node = new NodeImpl.ContainerElementNodeImpl(name, containerType, typeArgumentIndex);
+    }
+
+    @Override
+    public ContainerElementNodeContextBuilder inIterable() {
+        node.setInIterable(true);
+        return new ContainerElementNodeContextBuilderImpl(context, template, path, node);
+    }
+
+    @Override
+    public NodeBuilderCustomizableContext addPropertyNode(String name) {
+        path.addNode(node);
+        return new NodeBuilderCustomizableContextImpl(context, template, path, name);
+    }
+
+    @Override
+    public LeafNodeBuilderCustomizableContext addBeanNode() {
+        path.addNode(node);
+        return new LeafNodeBuilderCustomizableContextImpl(context, template, path);
+    }
+
+    @Override
+    public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType,
+        Integer typeArgumentIndex) {
+        path.addNode(node);
+        node = new NodeImpl.ContainerElementNodeImpl(name, containerType, typeArgumentIndex);
+        return this;
+    }
+
+    @Override
+    public ConstraintValidatorContext addConstraintViolation() {
+        context.addError(template, path);
+        return context;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderDefinedContextImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderDefinedContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderDefinedContextImpl.java
new file mode 100644
index 0000000..6077d87
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderDefinedContextImpl.java
@@ -0,0 +1,65 @@
+/*
+ * 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.util;
+
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderDefinedContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext;
+
+import org.apache.bval.jsr.job.ConstraintValidatorContextImpl;
+
+public class ContainerElementNodeBuilderDefinedContextImpl implements ContainerElementNodeBuilderDefinedContext {
+    private final ConstraintValidatorContextImpl<?> context;
+    private final String template;
+    private final PathImpl path;
+
+    ContainerElementNodeBuilderDefinedContextImpl(ConstraintValidatorContextImpl<?> context, String template,
+        PathImpl path) {
+        super();
+        this.context = context;
+        this.template = template;
+        this.path = path;
+    }
+
+    @Override
+    public NodeBuilderCustomizableContext addPropertyNode(String name) {
+        return new NodeBuilderCustomizableContextImpl(context, template, path, name);
+    }
+
+    @Override
+    public LeafNodeBuilderCustomizableContext addBeanNode() {
+        return new LeafNodeBuilderCustomizableContextImpl(context, template, path);
+    }
+
+    @Override
+    public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType,
+        Integer typeArgumentIndex) {
+        return new ContainerElementNodeBuilderCustomizableContextImpl(context, name, path, name, containerType,
+            typeArgumentIndex);
+    }
+
+    @Override
+    public ConstraintValidatorContext addConstraintViolation() {
+        context.addError(template, path);
+        return context;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeContextBuilderImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeContextBuilderImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeContextBuilderImpl.java
new file mode 100644
index 0000000..f05ef76
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeContextBuilderImpl.java
@@ -0,0 +1,85 @@
+/*
+ * 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.util;
+
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderDefinedContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeContextBuilder;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext;
+
+import org.apache.bval.jsr.job.ConstraintValidatorContextImpl;
+
+public class ContainerElementNodeContextBuilderImpl implements ContainerElementNodeContextBuilder {
+    private final ConstraintValidatorContextImpl<?> context;
+    private final String template;
+    private final PathImpl path;
+    private final NodeImpl node;
+
+    ContainerElementNodeContextBuilderImpl(ConstraintValidatorContextImpl<?> context, String template,
+        PathImpl path, NodeImpl node) {
+        super();
+        this.context = context;
+        this.template = template;
+        this.path = path;
+        this.node = node;
+    }
+
+    @Override
+    public ContainerElementNodeBuilderDefinedContext atKey(Object key) {
+        node.setKey(key);
+        path.addNode(node);
+        return new ContainerElementNodeBuilderDefinedContextImpl(context, template, path);
+    }
+
+    @Override
+    public ContainerElementNodeBuilderDefinedContext atIndex(Integer index) {
+        node.setIndex(index);
+        path.addNode(node);
+        return new ContainerElementNodeBuilderDefinedContextImpl(context, template, path);
+    }
+
+    @Override
+    public NodeBuilderCustomizableContext addPropertyNode(String name) {
+        path.addNode(node);
+        return new NodeBuilderCustomizableContextImpl(context, name, path, name);
+    }
+
+    @Override
+    public LeafNodeBuilderCustomizableContext addBeanNode() {
+        path.addNode(node);
+        return new LeafNodeBuilderCustomizableContextImpl(context, template, path);
+    }
+
+    @Override
+    public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType,
+        Integer typeArgumentIndex) {
+        path.addNode(node);
+        return new ContainerElementNodeBuilderCustomizableContextImpl(context, template, path, name, containerType,
+            typeArgumentIndex);
+    }
+
+    @Override
+    public ConstraintValidatorContext addConstraintViolation() {
+        context.addError(template, path);
+        return context;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java
index 611a9d6..57f7cf4 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java
@@ -33,25 +33,20 @@ public class IOs {
         if (stream == null) {
             return null;
         }
-
-        // force ByteArrayOutputStream since we close the stream ATM
-        /*if (stream.markSupported()) {
-            return stream;
-        } else {*/
-        try {
+        try (InputStream in = stream) {
             final ByteArrayOutputStream baos = new ByteArrayOutputStream();
             final byte[] buffer = new byte[1024];
             int length;
-            while ((length = stream.read(buffer)) != -1) {
+            while ((length = in.read(buffer)) != -1) {
                 baos.write(buffer, 0, length);
             }
             return new ByteArrayInputStream(baos.toByteArray());
         } catch (final IOException e) {
             throw new RuntimeException(e);
         }
-        /*}*/
     }
 
+    //TODO see if needed
     public static void closeQuietly(Closeable closeable) {
         if (closeable != null) {
             try {

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java
new file mode 100644
index 0000000..48fcd7d
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java
@@ -0,0 +1,41 @@
+/*
+ * 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.util;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class LRUCache<K, V> extends LinkedHashMap<K, V> {
+    private static final long serialVersionUID = 1L;
+
+    private final int maximumCapacity;
+
+    public LRUCache(int maximumCapacity) {
+        super(16, 0.75f, true);
+        if (maximumCapacity < 1) {
+            throw new IllegalArgumentException("maximumCapacity must be > 0");
+        }
+        this.maximumCapacity = maximumCapacity;
+    }
+
+    @Override
+    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+        return super.removeEldestEntry(eldest) || size() >= maximumCapacity;
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java
index efa9aeb..99305be 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java
@@ -18,9 +18,10 @@
  */
 package org.apache.bval.jsr.util;
 
-import org.apache.bval.jsr.ConstraintValidatorContextImpl;
+import org.apache.bval.jsr.job.ConstraintValidatorContextImpl;
 
 import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext;
 import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderDefinedContext;
 import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeContextBuilder;
 
@@ -43,8 +44,7 @@ public class LeafNodeBuilderCustomizableContextImpl
         }
 
         @Override
-        public LeafNodeBuilderDefinedContext atIndex(
-            Integer index) {
+        public LeafNodeBuilderDefinedContext atIndex(Integer index) {
             node.setIndex(index);
             return definedContext;
         }
@@ -55,16 +55,16 @@ public class LeafNodeBuilderCustomizableContextImpl
         }
     }
 
-    private final ConstraintValidatorContextImpl context;
+    private final ConstraintValidatorContextImpl<?> context;
     private final PathImpl path;
     private final String template;
     private final NodeImpl node;
 
-    public LeafNodeBuilderCustomizableContextImpl(final ConstraintValidatorContextImpl parent, String messageTemplate,
-        PathImpl propertyPath) {
-        context = parent;
-        template = messageTemplate;
-        path = propertyPath;
+    public LeafNodeBuilderCustomizableContextImpl(final ConstraintValidatorContextImpl<?> context, String template,
+        PathImpl path) {
+        this.context = context;
+        this.template = template;
+        this.path = path;
         node = new NodeImpl.BeanNodeImpl();
     }
 
@@ -81,4 +81,9 @@ public class LeafNodeBuilderCustomizableContextImpl
         return context;
     }
 
+    @Override
+    public LeafNodeBuilderCustomizableContext inContainer(Class<?> containerType, Integer typeArgumentIndex) {
+        node.inContainer(containerType, typeArgumentIndex);
+        return this;
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java
new file mode 100644
index 0000000..9f98311
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java
@@ -0,0 +1,45 @@
+/*
+ *  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.util;
+
+import java.beans.Introspector;
+import java.lang.reflect.Method;
+
+import org.apache.bval.util.Validate;
+
+public final class Methods {
+    public static boolean isGetter(Method m) {
+        if (m.getParameterCount() > 0) {
+            return false;
+        }
+        // TODO look for capital letter after verb?
+        if (Boolean.TYPE.equals(m.getReturnType()) && m.getName().startsWith("is")) {
+            return true;
+        }
+        return !Void.TYPE.equals(m.getReturnType()) && m.getName().startsWith("get");
+    }
+
+    public static String propertyName(Method getter) {
+        Validate.isTrue(isGetter(getter), "%s is not a getter", getter);
+        final String name = getter.getName();
+        final String suffix = name.startsWith("is") ? name.substring(2) : name.substring(3);
+        return Introspector.decapitalize(suffix);
+    }
+
+    private Methods() {
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java
index ca058fc..6ec977c 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java
@@ -18,38 +18,40 @@
  */
 package org.apache.bval.jsr.util;
 
-import org.apache.bval.jsr.ConstraintValidatorContextImpl;
-
 import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext;
 import javax.validation.ElementKind;
 
+import org.apache.bval.jsr.job.ConstraintValidatorContextImpl;
+
 /**
  * Description: implementation of {@link javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext}.<br/>
  */
 public final class NodeBuilderCustomizableContextImpl
     implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext {
-    private final ConstraintValidatorContextImpl parent;
-    private final String messageTemplate;
-    private final PathImpl propertyPath;
+    private final ConstraintValidatorContextImpl<?> context;
+    private final String template;
+    private final PathImpl path;
     private NodeImpl node;
 
     /**
      * Create a new NodeBuilderCustomizableContextImpl instance.
-     * @param contextImpl
+     * @param context
      * @param template
      * @param path
      * @param name
      */
-    public NodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl contextImpl, String template, PathImpl path,
+    public NodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl<?> context, String template, PathImpl path,
         String name) {
-        parent = contextImpl;
-        messageTemplate = template;
-        propertyPath = path;
+        this.context = context;
+        this.template = template;
+        this.path = path;
 
-        if (propertyPath.isRootPath() || propertyPath.getLeafNode().getKind() != null) {
+        if (path.isRootPath() || path.getLeafNode().getKind() != null) {
             node = new NodeImpl.PropertyNodeImpl(name);
         } else {
-            node = propertyPath.removeLeafNode();
+            node = path.removeLeafNode();
             node.setName(name);
             node.setKind(ElementKind.PROPERTY); // enforce it
         }
@@ -61,7 +63,7 @@ public final class NodeBuilderCustomizableContextImpl
     @Override
     public ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder inIterable() {
         node.setInIterable(true);
-        return new NodeContextBuilderImpl(parent, messageTemplate, propertyPath, node);
+        return new NodeContextBuilderImpl(context, template, path, node);
     }
 
     /**
@@ -75,15 +77,15 @@ public final class NodeBuilderCustomizableContextImpl
     @Override
     public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addPropertyNode(
         String name) {
-        propertyPath.addNode(node);
+        path.addNode(node);
         node = new NodeImpl.PropertyNodeImpl(name);
         return this;
     }
 
     @Override
     public ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext addBeanNode() {
-        propertyPath.addNode(node);
-        return new LeafNodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath);
+        path.addNode(node);
+        return new LeafNodeBuilderCustomizableContextImpl(context, template, path);
     }
 
     /**
@@ -91,10 +93,25 @@ public final class NodeBuilderCustomizableContextImpl
      */
     @Override
     public ConstraintValidatorContext addConstraintViolation() {
-        propertyPath.addNode(node);
+        path.addNode(node);
         node = null;
-        parent.addError(messageTemplate, propertyPath);
-        return parent;
+        context.addError(template, path);
+        return context;
+    }
+
+    @Override
+    public NodeBuilderCustomizableContext inContainer(Class<?> containerClass, Integer typeArgumentIndex) {
+        path.getLeafNode().inContainer(containerClass, typeArgumentIndex);
+        return this;
+    }
+
+    @Override
+    public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType,
+        Integer typeArgumentIndex) {
+        path.addNode(node);
+        node = new NodeImpl.ContainerElementNodeImpl(name, containerType, typeArgumentIndex);
+        return new ContainerElementNodeBuilderCustomizableContextImpl(context, template, path, name, containerType,
+            typeArgumentIndex);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/fad43eb8/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java
index 5ce20b5..f695e84 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java
@@ -18,18 +18,19 @@
  */
 package org.apache.bval.jsr.util;
 
-import org.apache.bval.jsr.ConstraintValidatorContextImpl;
-
 import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext;
+
+import org.apache.bval.jsr.job.ConstraintValidatorContextImpl;
 
 /**
  * Description: Implementation of {@link NodeBuilderDefinedContext}.<br/>
  */
 public final class NodeBuilderDefinedContextImpl
     implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext {
-    private final ConstraintValidatorContextImpl parent;
-    private final String messageTemplate;
-    private final PathImpl propertyPath;
+    private final ConstraintValidatorContextImpl context;
+    private final String template;
+    private final PathImpl path;
 
     /**
      * Create a new NodeBuilderDefinedContextImpl instance.
@@ -38,9 +39,9 @@ public final class NodeBuilderDefinedContextImpl
      * @param path
      */
     public NodeBuilderDefinedContextImpl(ConstraintValidatorContextImpl contextImpl, String template, PathImpl path) {
-        parent = contextImpl;
-        messageTemplate = template;
-        propertyPath = path;
+        this.context = contextImpl;
+        this.template = template;
+        this.path = path;
     }
 
     /**
@@ -54,12 +55,12 @@ public final class NodeBuilderDefinedContextImpl
     @Override
     public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addPropertyNode(
         String name) {
-        return new NodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath, name);
+        return new NodeBuilderCustomizableContextImpl(context, template, path, name);
     }
 
     @Override
     public ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext addBeanNode() {
-        return new LeafNodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath);
+        return new LeafNodeBuilderCustomizableContextImpl(context, template, path);
     }
 
     /**
@@ -67,7 +68,14 @@ public final class NodeBuilderDefinedContextImpl
      */
     @Override
     public ConstraintValidatorContext addConstraintViolation() {
-        parent.addError(messageTemplate, propertyPath);
-        return parent;
+        context.addError(template, path);
+        return context;
+    }
+
+    @Override
+    public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType,
+        Integer typeArgumentIndex) {
+        return new ContainerElementNodeBuilderCustomizableContextImpl(context, template, path, name, containerType,
+            typeArgumentIndex);
     }
 }


[10/17] bval git commit: BV2: new metadata model

Posted by mb...@apache.org.
BV2: new metadata model


Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/59bd964b
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/59bd964b
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/59bd964b

Branch: refs/heads/bv2
Commit: 59bd964bb20dc465c42123315c8d0f6e760bb728
Parents: 40fc20f
Author: Matt Benson <mb...@apache.org>
Authored: Wed Feb 21 14:50:31 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Wed Feb 21 14:59:57 2018 -0600

----------------------------------------------------------------------
 .../bval/jsr/metadata/AnnotationBehavior.java   |  35 +
 .../AnnotationBehaviorMergeStrategy.java        |  54 ++
 ...otationDeclaredValidatorMappingProvider.java |  42 ++
 .../ClassLoadingValidatorMappingProvider.java   |  48 ++
 .../bval/jsr/metadata/CompositeBuilder.java     | 227 ++++++
 .../CompositeValidatorMappingProvider.java      |  42 ++
 .../bval/jsr/metadata/ContainerElementKey.java  | 175 +++++
 .../apache/bval/jsr/metadata/DualBuilder.java   | 243 +++++++
 .../metadata/DualValidationMappingProvider.java |  50 ++
 .../apache/bval/jsr/metadata/EmptyBuilder.java  | 183 +++++
 .../jsr/metadata/HasAnnotationBehavior.java     |  24 +
 .../bval/jsr/metadata/HierarchyBuilder.java     | 235 +++++++
 .../bval/jsr/metadata/MetadataBuilder.java      |  98 +++
 .../bval/jsr/metadata/MetadataBuilders.java     |  41 ++
 .../org/apache/bval/jsr/metadata/Metas.java     | 324 +++++++++
 .../bval/jsr/metadata/ReflectionBuilder.java    | 272 ++++++++
 .../org/apache/bval/jsr/metadata/Signature.java |  75 ++
 .../bval/jsr/metadata/ValidatorMapping.java     | 121 ++++
 .../jsr/metadata/ValidatorMappingProvider.java  |  51 ++
 .../apache/bval/jsr/metadata/XmlBuilder.java    | 694 +++++++++++++++++++
 .../metadata/XmlValidationMappingProvider.java  |  64 ++
 21 files changed, 3098 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehavior.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehavior.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehavior.java
new file mode 100644
index 0000000..56ed4f0
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehavior.java
@@ -0,0 +1,35 @@
+/*
+ *  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.metadata;
+
+import org.apache.bval.jsr.metadata.MetadataBuilder;
+
+/**
+ * Models the behavior of a {@link MetadataBuilder} with regard to bean validation annotations.
+ * 
+ * @see DualBuilder
+ */
+public enum AnnotationBehavior implements AnnotationBehaviorMergeStrategy {
+    //@formatter:off
+    INCLUDE, EXCLUDE, ABSTAIN;
+    //@formatter:on
+
+    @Override
+    public AnnotationBehavior apply(Iterable<? extends HasAnnotationBehavior> t) {
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehaviorMergeStrategy.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehaviorMergeStrategy.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehaviorMergeStrategy.java
new file mode 100644
index 0000000..bfd16c5
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehaviorMergeStrategy.java
@@ -0,0 +1,54 @@
+/*
+ *  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.metadata;
+
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.bval.util.Validate;
+
+@FunctionalInterface
+public interface AnnotationBehaviorMergeStrategy
+    extends Function<Iterable<? extends HasAnnotationBehavior>, AnnotationBehavior> {
+
+    public static AnnotationBehaviorMergeStrategy first() {
+        return coll -> {
+            final Iterator<? extends HasAnnotationBehavior> iterator = coll.iterator();
+            return iterator.hasNext() ? iterator.next().getAnnotationBehavior() : AnnotationBehavior.ABSTAIN;
+        };
+    }
+
+    public static AnnotationBehaviorMergeStrategy consensus() {
+        return coll -> {
+            final Stream.Builder<HasAnnotationBehavior> b = Stream.builder();
+            coll.forEach(b);
+            final Set<AnnotationBehavior> annotationBehaviors =
+                b.build().map(HasAnnotationBehavior::getAnnotationBehavior).filter(Objects::nonNull)
+                    .filter(Predicate.isEqual(AnnotationBehavior.ABSTAIN).negate())
+                    .collect(Collectors.toCollection(() -> EnumSet.noneOf(AnnotationBehavior.class)));
+            Validate.validState(annotationBehaviors.size() <= 1,
+                "Conflicting annotation inclusion behaviors found among %s", coll);
+            return annotationBehaviors.isEmpty() ? AnnotationBehavior.ABSTAIN : annotationBehaviors.iterator().next();
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationDeclaredValidatorMappingProvider.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationDeclaredValidatorMappingProvider.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationDeclaredValidatorMappingProvider.java
new file mode 100644
index 0000000..b2126ac
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationDeclaredValidatorMappingProvider.java
@@ -0,0 +1,42 @@
+/*
+ *  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.metadata;
+
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintValidator;
+
+import org.apache.bval.util.Validate;
+
+public class AnnotationDeclaredValidatorMappingProvider extends ValidatorMappingProvider {
+    public static final AnnotationDeclaredValidatorMappingProvider INSTANCE =
+        new AnnotationDeclaredValidatorMappingProvider();
+
+    @Override
+    protected <A extends Annotation> ValidatorMapping<A> doGetValidatorMapping(Class<A> constraintType) {
+        Validate.notNull(constraintType);
+        Validate.isTrue(constraintType.isAnnotationPresent(Constraint.class),
+            "%s does not represent a validation constraint", constraintType);
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        final List<Class<? extends ConstraintValidator<A, ?>>> validatorTypes =
+            (List) Arrays.asList(constraintType.getAnnotation(Constraint.class).validatedBy());
+        return new ValidatorMapping<>("@Constraint.validatedBy()", validatorTypes);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ClassLoadingValidatorMappingProvider.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ClassLoadingValidatorMappingProvider.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ClassLoadingValidatorMappingProvider.java
new file mode 100644
index 0000000..e636a8a
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ClassLoadingValidatorMappingProvider.java
@@ -0,0 +1,48 @@
+/*
+ * 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.metadata;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+
+@Privilizing(@CallTo(Reflection.class))
+public abstract class ClassLoadingValidatorMappingProvider extends ValidatorMappingProvider {
+
+    protected final <T> Stream<Class<? extends T>> load(Stream<String> classNames, Class<T> assignableTo,
+        Consumer<? super ClassNotFoundException> handleException) {
+        return classNames.map(className -> {
+            try {
+                return Reflection.toClass(className, getClassLoader());
+            } catch (ClassNotFoundException e) {
+                handleException.accept(e);
+                return (Class<?>) null;
+            }
+        }).filter(Objects::nonNull).map(c -> (Class<? extends T>) c.asSubclass(assignableTo));
+    }
+
+    protected ClassLoader getClassLoader() {
+        final ClassLoader classloader = Thread.currentThread().getContextClassLoader();
+        return classloader == null ? getClass().getClassLoader() : classloader;
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/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
new file mode 100644
index 0000000..52a7407
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
@@ -0,0 +1,227 @@
+/*
+ *  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.metadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+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.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import javax.validation.metadata.Scope;
+
+import org.apache.bval.jsr.descriptor.GroupConversion;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Validate;
+
+public class CompositeBuilder {
+
+    class Delegator<DELEGATE extends HasAnnotationBehavior> implements HasAnnotationBehavior {
+
+        protected final List<DELEGATE> delegates;
+
+        Delegator(List<DELEGATE> delegates) {
+            this.delegates = Validate.notNull(delegates, "delegates");
+            Validate.isTrue(!delegates.isEmpty(), "no delegates specified");
+            Validate.isTrue(delegates.stream().noneMatch(Objects::isNull), "One or more supplied delegates was null");
+        }
+
+        @Override
+        public AnnotationBehavior getAnnotationBehavior() {
+            return annotationBehaviorStrategy.apply(delegates);
+        }
+
+        <K, D> Map<K, D> merge(Function<DELEGATE, Map<K, D>> toMap, Function<List<D>, D> merge) {
+            final List<Map<K, D>> maps = delegates.stream().map(toMap).collect(Collectors.toList());
+
+            final Function<? super K, ? extends D> valueMapper = k -> {
+                final List<D> mappedDelegates =
+                    maps.stream().map(m -> m.get(k)).filter(Objects::nonNull).collect(Collectors.toList());
+                return mappedDelegates.size() == 1 ? mappedDelegates.get(0) : merge.apply(mappedDelegates);
+            };
+
+            return maps.stream().map(Map::keySet).flatMap(Collection::stream).distinct()
+                .collect(Collectors.toMap(Function.identity(), valueMapper));
+        }
+    }
+
+    private class ForBean extends CompositeBuilder.Delegator<MetadataBuilder.ForBean>
+        implements MetadataBuilder.ForBean {
+
+        ForBean(List<MetadataBuilder.ForBean> delegates) {
+            super(delegates);
+        }
+
+        @Override
+        public MetadataBuilder.ForClass getClass(Metas<Class<?>> meta) {
+            return new CompositeBuilder.ForClass(
+                delegates.stream().map(d -> d.getClass(meta)).collect(Collectors.toList()));
+        }
+
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Metas<Class<?>> meta) {
+            return merge(b -> b.getFields(meta), CompositeBuilder.ForContainer::new);
+        }
+
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Metas<Class<?>> meta) {
+            return merge(b -> b.getGetters(meta), CompositeBuilder.ForContainer::new);
+        }
+
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Metas<Class<?>> meta) {
+            return merge(b -> b.getConstructors(meta), CompositeBuilder.ForExecutable::new);
+        }
+
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Metas<Class<?>> meta) {
+            return merge(b -> b.getMethods(meta), CompositeBuilder.ForExecutable::new);
+        }
+    }
+
+    class ForElement<DELEGATE extends MetadataBuilder.ForElement<E>, E extends AnnotatedElement>
+        extends Delegator<DELEGATE> implements MetadataBuilder.ForElement<E> {
+
+        ForElement(List<DELEGATE> delegates) {
+            super(delegates);
+        }
+
+        @Override
+        public Map<Scope, Annotation[]> getConstraintsByScope(Metas<E> meta) {
+            return CompositeBuilder.this.getConstraintsByScope(this, meta);
+        }
+
+        @Override
+        public final Annotation[] getDeclaredConstraints(Metas<E> meta) {
+            return delegates.stream().map(d -> d.getDeclaredConstraints(meta)).flatMap(Stream::of)
+                .toArray(Annotation[]::new);
+        }
+    }
+
+    class ForClass extends ForElement<MetadataBuilder.ForClass, Class<?>> implements MetadataBuilder.ForClass {
+
+        ForClass(List<MetadataBuilder.ForClass> delegates) {
+            super(delegates);
+        }
+
+        @Override
+        public List<Class<?>> getGroupSequence(Metas<Class<?>> meta) {
+            return CompositeBuilder.this.getGroupSequence(this, meta);
+        }
+    }
+
+    private class ForContainer<DELEGATE extends MetadataBuilder.ForContainer<E>, E extends AnnotatedElement>
+        extends CompositeBuilder.ForElement<DELEGATE, E> implements MetadataBuilder.ForContainer<E> {
+
+        ForContainer(List<DELEGATE> delegates) {
+            super(delegates);
+        }
+
+        @Override
+        public final boolean isCascade(Metas<E> meta) {
+            return delegates.stream().anyMatch(d -> d.isCascade(meta));
+        }
+
+        @Override
+        public final Set<GroupConversion> getGroupConversions(Metas<E> meta) {
+            return delegates.stream().map(d -> d.getGroupConversions(meta)).flatMap(Collection::stream)
+                .collect(ToUnmodifiable.set());
+        }
+
+        @Override
+        public final Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+            Metas<E> meta) {
+            return merge(b -> b.getContainerElementTypes(meta), CompositeBuilder.ForContainer::new);
+        }
+    }
+
+    private class ForExecutable<DELEGATE extends MetadataBuilder.ForExecutable<E>, E extends Executable>
+        extends Delegator<DELEGATE> implements MetadataBuilder.ForExecutable<E> {
+
+        ForExecutable(List<DELEGATE> delegates) {
+            super(delegates);
+        }
+
+        @Override
+        public MetadataBuilder.ForContainer<E> getReturnValue(Metas<E> meta) {
+            return new CompositeBuilder.ForContainer<>(
+                delegates.stream().map(d -> d.getReturnValue(meta)).collect(Collectors.toList()));
+        }
+
+        @Override
+        public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Metas<E> meta) {
+            final List<List<MetadataBuilder.ForContainer<Parameter>>> parameterLists =
+                delegates.stream().map(d -> d.getParameters(meta)).collect(Collectors.toList());
+
+            final Set<Integer> parameterCounts = parameterLists.stream().map(List::size).collect(Collectors.toSet());
+            Validate.validState(parameterCounts.size() == 1, "Mismatched parameter counts: %s", parameterCounts);
+
+            return IntStream.range(0, parameterCounts.iterator().next().intValue())
+                .mapToObj(n -> new CompositeBuilder.ForContainer<>(parameterLists.get(n)))
+                .collect(ToUnmodifiable.list());
+        }
+
+        @Override
+        public MetadataBuilder.ForElement<E> getCrossParameter(Metas<E> meta) {
+            return new CompositeBuilder.ForElement<MetadataBuilder.ForElement<E>, E>(
+                delegates.stream().map(d -> d.getCrossParameter(meta)).collect(Collectors.toList()));
+        }
+    }
+
+    public static CompositeBuilder with(AnnotationBehaviorMergeStrategy annotationBehaviorStrategy) {
+        return new CompositeBuilder(annotationBehaviorStrategy);
+    }
+
+    private final AnnotationBehaviorMergeStrategy annotationBehaviorStrategy;
+
+    CompositeBuilder(AnnotationBehaviorMergeStrategy annotationBehaviorMergeStrategy) {
+        super();
+        this.annotationBehaviorStrategy =
+            Validate.notNull(annotationBehaviorMergeStrategy, "annotationBehaviorMergeStrategy");
+    }
+
+    public Collector<MetadataBuilder.ForBean, ?, MetadataBuilder.ForBean> compose() {
+        return Collectors.collectingAndThen(Collectors.toList(), CompositeBuilder.ForBean::new);
+    }
+
+    protected <E extends AnnotatedElement> Map<Scope, Annotation[]> getConstraintsByScope(
+        CompositeBuilder.ForElement<? extends MetadataBuilder.ForElement<E>, E> composite, Metas<E> meta) {
+        return Collections.singletonMap(Scope.LOCAL_ELEMENT, composite.getDeclaredConstraints(meta));
+    }
+
+    protected List<Class<?>> getGroupSequence(CompositeBuilder.ForClass composite, Metas<Class<?>> meta) {
+        final List<List<Class<?>>> groupSequence =
+            composite.delegates.stream().map(d -> d.getGroupSequence(meta)).collect(Collectors.toList());
+        Validate.validState(groupSequence.size() <= 1,
+            "group sequence returned from multiple composite class metadata builders");
+        return groupSequence.isEmpty() ? null : groupSequence.get(0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeValidatorMappingProvider.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeValidatorMappingProvider.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeValidatorMappingProvider.java
new file mode 100644
index 0000000..9808f89
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeValidatorMappingProvider.java
@@ -0,0 +1,42 @@
+/*
+ *  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.metadata;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.bval.util.Validate;
+
+public class CompositeValidatorMappingProvider extends ValidatorMappingProvider {
+
+    private final List<ValidatorMappingProvider> delegates;
+
+    public CompositeValidatorMappingProvider(List<ValidatorMappingProvider> delegates) {
+        super();
+        this.delegates = Validate.notNull(delegates, "delegates");
+        Validate.isTrue(!delegates.isEmpty(), "no delegates specified");
+        Validate.isTrue(delegates.stream().noneMatch(Objects::isNull), "One or more supplied delegates was null");
+    }
+
+    @Override
+    protected <A extends Annotation> ValidatorMapping<A> doGetValidatorMapping(Class<A> constraintType) {
+        return ValidatorMapping.merge(delegates.stream().map(d -> d.doGetValidatorMapping(constraintType))
+            .filter(Objects::nonNull).collect(Collectors.toList()), AnnotationBehaviorMergeStrategy.consensus());
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ContainerElementKey.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ContainerElementKey.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ContainerElementKey.java
new file mode 100644
index 0000000..322a4ef
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ContainerElementKey.java
@@ -0,0 +1,175 @@
+/*
+ *  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.metadata;
+
+import java.lang.reflect.AnnotatedParameterizedType;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.TypeVariable;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.logging.Logger;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import javax.validation.valueextraction.ExtractedValue;
+import javax.validation.valueextraction.ValueExtractor;
+import javax.validation.valueextraction.ValueExtractorDefinitionException;
+
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.LazyInt;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.TypeUtils;
+
+public class ContainerElementKey implements Comparable<ContainerElementKey> {
+    private static Logger log = Logger.getLogger(ContainerElementKey.class.getName());
+
+    public static ContainerElementKey forValueExtractor(ValueExtractor<?> extractor) {
+        @SuppressWarnings("rawtypes")
+        final Class<? extends ValueExtractor> extractorType = extractor.getClass();
+        final Lazy<Set<ContainerElementKey>> result = new Lazy<>(HashSet::new);
+
+        Stream.of(extractorType.getAnnotatedInterfaces()).filter(AnnotatedParameterizedType.class::isInstance)
+            .map(AnnotatedParameterizedType.class::cast)
+            .filter(apt -> ValueExtractor.class.equals(((ParameterizedType) apt.getType()).getRawType()))
+            .forEach(decl -> {
+                final AnnotatedType containerType = decl.getAnnotatedActualTypeArguments()[0];
+
+                if (containerType.isAnnotationPresent(ExtractedValue.class)) {
+                    Exceptions.raiseIf(void.class.equals(containerType.getAnnotation(ExtractedValue.class).type()),
+                        ValueExtractorDefinitionException::new, "%s does not specify %s type for %s", extractorType,
+                        ExtractedValue.class.getSimpleName(), containerType);
+                    result.get().add(new ContainerElementKey(containerType, null));
+                }
+                Optional.of(containerType).filter(AnnotatedParameterizedType.class::isInstance)
+                    .map(AnnotatedParameterizedType.class::cast)
+                    .map(AnnotatedParameterizedType::getAnnotatedActualTypeArguments).ifPresent(args -> {
+                        IntStream.range(0, args.length).forEach(n -> {
+                            if (args[n].isAnnotationPresent(ExtractedValue.class)) {
+                                if (void.class.equals(args[n].getAnnotation(ExtractedValue.class).type())) {
+                                    log.warning(String.format("Ignoring non-default %s type specified for %s by %s",
+                                        ExtractedValue.class.getSimpleName(), containerType, extractorType));
+                                }
+                                result.get().add(new ContainerElementKey(containerType, Integer.valueOf(n)));
+                            }
+                        });
+                    });
+            });
+        return result.optional().filter(s -> s.size() == 1)
+            .orElseThrow(() -> new ValueExtractorDefinitionException(extractorType.getName())).iterator().next();
+    }
+
+    private static Integer validTypeArgumentIndex(Integer typeArgumentIndex, Class<?> containerClass) {
+        if (typeArgumentIndex != null) {
+            final int i = typeArgumentIndex.intValue();
+            Validate.isTrue(i >= 0 && i < containerClass.getTypeParameters().length,
+                "type argument index %d is invalid for container type %s", typeArgumentIndex, containerClass);
+        }
+        return typeArgumentIndex;
+    }
+
+    private final Integer typeArgumentIndex;
+    private final Class<?> containerClass;
+    private final LazyInt hashCode = new LazyInt(() -> Objects.hash(getContainerClass(), getTypeArgumentIndex()));
+    private final Lazy<String> toString = new Lazy<>(() -> String.format("%s: %s<[%d]>",
+        ContainerElementKey.class.getSimpleName(), getContainerClass().getName(), getTypeArgumentIndex()));
+    private final AnnotatedType annotatedType;
+
+    public ContainerElementKey(AnnotatedType containerType, Integer typeArgumentIndex) {
+        super();
+        Validate.notNull(containerType, "containerType");
+        this.containerClass = TypeUtils.getRawType(containerType.getType(), null);
+        this.typeArgumentIndex = validTypeArgumentIndex(typeArgumentIndex, containerClass);
+        this.annotatedType = typeArgumentIndex == null ? containerType : ((AnnotatedParameterizedType) containerType)
+            .getAnnotatedActualTypeArguments()[typeArgumentIndex.intValue()];
+    }
+
+    public Class<?> getContainerClass() {
+        return containerClass;
+    }
+
+    public Integer getTypeArgumentIndex() {
+        return typeArgumentIndex;
+    }
+
+    public AnnotatedType getAnnotatedType() {
+        return annotatedType;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return obj == this || Optional.ofNullable(obj).filter(ContainerElementKey.class::isInstance)
+            .map(ContainerElementKey.class::cast)
+            .filter(
+                cek -> Objects.equals(containerClass, cek.containerClass) && typeArgumentIndex == cek.typeArgumentIndex)
+            .isPresent();
+    }
+
+    @Override
+    public int hashCode() {
+        return hashCode.getAsInt();
+    }
+
+    @Override
+    public String toString() {
+        return toString.get();
+    }
+
+    @Override
+    public int compareTo(ContainerElementKey o) {
+        return Comparator.comparing(ContainerElementKey::containerClassName)
+            .thenComparing(Comparator.comparing(ContainerElementKey::getTypeArgumentIndex)).compare(this, o);
+    }
+
+    public Set<ContainerElementKey> getAssignableKeys() {
+        final Lazy<Set<ContainerElementKey>> result = new Lazy<>(LinkedHashSet::new);
+
+        if (typeArgumentIndex != null) {
+            final TypeVariable<?> var = containerClass.getTypeParameters()[typeArgumentIndex.intValue()];
+
+            Stream
+                .concat(Stream.of(containerClass.getAnnotatedSuperclass()),
+                    Stream.of(containerClass.getAnnotatedInterfaces()))
+                .filter(AnnotatedParameterizedType.class::isInstance).map(AnnotatedParameterizedType.class::cast)
+                .forEach(t -> {
+                    final AnnotatedType[] args = t.getAnnotatedActualTypeArguments();
+
+                    for (int i = 0; i < args.length; i++) {
+                        if (args[i].getType().equals(var)) {
+                            result.get().add(new ContainerElementKey(t, Integer.valueOf(i)));
+                        }
+                    }
+                });
+        }
+        return result.optional().map(Collections::unmodifiableSet).orElseGet(Collections::emptySet);
+    }
+
+    public boolean represents(TypeVariable<?> var) {
+        return Optional.ofNullable(typeArgumentIndex)
+            .map(index -> getContainerClass().getTypeParameters()[index.intValue()]).filter(var::equals).isPresent();
+    }
+
+    private String containerClassName() {
+        return getContainerClass().getName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/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
new file mode 100644
index 0000000..269d953
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualBuilder.java
@@ -0,0 +1,243 @@
+/*
+ *  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.metadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import org.apache.bval.jsr.descriptor.GroupConversion;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Validate;
+
+/**
+ * Maintains two metadata builds in parallel. The "primary" build is assumed to be the reflection/annotation-based build
+ * and is subject to the {@link AnnotationBehavior} prescribed by the "custom" build.
+ */
+public class DualBuilder {
+
+    private static class Delegator<DELEGATE extends HasAnnotationBehavior> implements HasAnnotationBehavior {
+
+        private final Delegator<?> parent;
+        protected final DELEGATE primaryDelegate;
+        protected final DELEGATE customDelegate;
+
+        Delegator(Delegator<?> parent, DELEGATE primaryDelegate, DELEGATE customDelegate) {
+            this.parent = parent;
+            this.primaryDelegate = Validate.notNull(primaryDelegate, "primaryDelegate");
+            this.customDelegate = Validate.notNull(customDelegate, "customDelegate");
+        }
+
+        AnnotationBehavior getCustomAnnotationBehavior() {
+            final AnnotationBehavior annotationBehavior = customDelegate.getAnnotationBehavior();
+            Validate.validState(annotationBehavior != null, "null %s returned from %s",
+                AnnotationBehavior.class.getSimpleName(), customDelegate);
+            if (annotationBehavior == AnnotationBehavior.ABSTAIN && parent != null) {
+                return parent.getCustomAnnotationBehavior();
+            }
+            return annotationBehavior;
+        }
+
+        protected Stream<DELEGATE> activeDelegates() {
+            return getCustomAnnotationBehavior() == AnnotationBehavior.EXCLUDE ? Stream.of(customDelegate)
+                : Stream.of(primaryDelegate, customDelegate);
+        }
+
+        <K, D> Map<K, D> merge(Function<DELEGATE, Map<K, D>> toMap, BiFunction<D, D, D> parallel,
+            Supplier<D> emptyBuilder) {
+
+            final Map<K, D> primaries = toMap.apply(primaryDelegate);
+            final Map<K, D> customs = toMap.apply(customDelegate);
+
+            if (primaries.isEmpty() && customs.isEmpty()) {
+                return Collections.emptyMap();
+            }
+
+            final Function<? super K, ? extends D> valueMapper = k -> {
+                final D primary = primaries.get(k);
+                final D custom = customs.get(k);
+
+                if (custom == null) {
+                    if (primary != null) {
+                        switch (getCustomAnnotationBehavior()) {
+                        case INCLUDE:
+                        case ABSTAIN:
+                            return primary;
+                        default:
+                            break;
+                        }
+                    }
+                    return emptyBuilder.get();
+                }
+                return parallel.apply(primary, custom);
+            };
+            return Stream.of(primaries, customs).map(Map::keySet).flatMap(Collection::stream).distinct()
+                .collect(Collectors.toMap(Function.identity(), valueMapper));
+        }
+    }
+
+    private static class ForBean extends DualBuilder.Delegator<MetadataBuilder.ForBean>
+        implements MetadataBuilder.ForBean {
+
+        ForBean(MetadataBuilder.ForBean primaryDelegate, MetadataBuilder.ForBean customDelegate) {
+            super(null, primaryDelegate, customDelegate);
+        }
+
+        @Override
+        public MetadataBuilder.ForClass getClass(Metas<Class<?>> meta) {
+            return new DualBuilder.ForClass(this, primaryDelegate.getClass(meta), customDelegate.getClass(meta));
+        }
+
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Metas<Class<?>> meta) {
+            return merge(b -> b.getFields(meta), (t, u) -> new DualBuilder.ForContainer<>(this, t, u),
+                EmptyBuilder.instance()::forContainer);
+        }
+
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Metas<Class<?>> meta) {
+            return merge(b -> b.getGetters(meta), (t, u) -> new DualBuilder.ForContainer<>(this, t, u),
+                EmptyBuilder.instance()::forContainer);
+        }
+
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Metas<Class<?>> meta) {
+            return merge(b -> b.getConstructors(meta), (t, u) -> new DualBuilder.ForExecutable<>(this, t, u),
+                EmptyBuilder.instance()::forExecutable);
+        }
+
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Metas<Class<?>> meta) {
+            return merge(b -> b.getMethods(meta), (t, u) -> new DualBuilder.ForExecutable<>(this, t, u),
+                EmptyBuilder.instance()::forExecutable);
+        }
+    }
+
+    private static class ForElement<DELEGATE extends MetadataBuilder.ForElement<E>, E extends AnnotatedElement>
+        extends Delegator<DELEGATE> implements MetadataBuilder.ForElement<E> {
+
+        ForElement(Delegator<?> parent, DELEGATE primaryDelegate, DELEGATE customDelegate) {
+            super(parent, primaryDelegate, customDelegate);
+        }
+
+        @Override
+        public final Annotation[] getDeclaredConstraints(Metas<E> meta) {
+            return activeDelegates().map(d -> d.getDeclaredConstraints(meta)).flatMap(Stream::of)
+                .toArray(Annotation[]::new);
+        }
+    }
+
+    private static class ForClass extends ForElement<MetadataBuilder.ForClass, Class<?>>
+        implements MetadataBuilder.ForClass {
+
+        ForClass(Delegator<?> parent, MetadataBuilder.ForClass primaryDelegate,
+            MetadataBuilder.ForClass customDelegate) {
+            super(parent, primaryDelegate, customDelegate);
+        }
+
+        @Override
+        public List<Class<?>> getGroupSequence(Metas<Class<?>> meta) {
+            final List<Class<?>> customGroupSequence = customDelegate.getGroupSequence(meta);
+            if (customGroupSequence != null) {
+                return customGroupSequence;
+            }
+            return customDelegate.getAnnotationBehavior() == AnnotationBehavior.EXCLUDE ? null
+                : primaryDelegate.getGroupSequence(meta);
+        }
+    }
+
+    private static class ForContainer<DELEGATE extends MetadataBuilder.ForContainer<E>, E extends AnnotatedElement>
+        extends DualBuilder.ForElement<DELEGATE, E> implements MetadataBuilder.ForContainer<E> {
+
+        ForContainer(Delegator<?> parent, DELEGATE primaryDelegate, DELEGATE customDelegate) {
+            super(parent, primaryDelegate, customDelegate);
+        }
+
+        @Override
+        public final boolean isCascade(Metas<E> meta) {
+            return activeDelegates().anyMatch(d -> d.isCascade(meta));
+        }
+
+        @Override
+        public final Set<GroupConversion> getGroupConversions(Metas<E> meta) {
+            return activeDelegates().map(d -> d.getGroupConversions(meta)).flatMap(Collection::stream)
+                .collect(ToUnmodifiable.set());
+        }
+
+        @Override
+        public final Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+            Metas<E> meta) {
+            return merge(b -> b.getContainerElementTypes(meta), (t, u) -> new DualBuilder.ForContainer<>(this, t, u),
+                EmptyBuilder.instance()::forContainer);
+        }
+    }
+
+    private static class ForExecutable<DELEGATE extends MetadataBuilder.ForExecutable<E>, E extends Executable>
+        extends Delegator<DELEGATE> implements MetadataBuilder.ForExecutable<E> {
+
+        ForExecutable(Delegator<?> parent, DELEGATE primaryDelegate, DELEGATE customDelegate) {
+            super(parent, primaryDelegate, customDelegate);
+        }
+
+        @Override
+        public MetadataBuilder.ForContainer<E> getReturnValue(Metas<E> meta) {
+            return new DualBuilder.ForContainer<>(this, primaryDelegate.getReturnValue(meta),
+                customDelegate.getReturnValue(meta));
+        }
+
+        @Override
+        public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Metas<E> meta) {
+
+            final List<MetadataBuilder.ForContainer<Parameter>> primaries = primaryDelegate.getParameters(meta);
+            final List<MetadataBuilder.ForContainer<Parameter>> customs = customDelegate.getParameters(meta);
+
+            Validate.validState(primaries.size() == customs.size(), "Mismatched parameter counts: %d vs. %d",
+                primaries.size(), customs.size());
+
+            return IntStream.range(0, primaries.size())
+                .mapToObj(n -> new DualBuilder.ForContainer<>(this, primaries.get(n), customs.get(n)))
+                .collect(ToUnmodifiable.list());
+        }
+
+        @Override
+        public MetadataBuilder.ForElement<E> getCrossParameter(Metas<E> meta) {
+            return new DualBuilder.ForElement<MetadataBuilder.ForElement<E>, E>(this,
+                primaryDelegate.getCrossParameter(meta), customDelegate.getCrossParameter(meta));
+        }
+    }
+
+    public static MetadataBuilder.ForBean forBean(MetadataBuilder.ForBean primaryDelegate,
+        MetadataBuilder.ForBean customDelegate) {
+        return new DualBuilder.ForBean(primaryDelegate, customDelegate);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualValidationMappingProvider.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualValidationMappingProvider.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualValidationMappingProvider.java
new file mode 100644
index 0000000..e5b5038
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualValidationMappingProvider.java
@@ -0,0 +1,50 @@
+/*
+ *  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.metadata;
+
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+
+import org.apache.bval.util.Validate;
+
+public class DualValidationMappingProvider extends ValidatorMappingProvider {
+    private final ValidatorMappingProvider primaryDelegate;
+    private final ValidatorMappingProvider secondaryDelegate;
+
+    public DualValidationMappingProvider(ValidatorMappingProvider primary, ValidatorMappingProvider secondary) {
+        super();
+        this.primaryDelegate = Validate.notNull(primary, "primary delegate");
+        this.secondaryDelegate = Validate.notNull(secondary, "secondary delegate");
+    }
+
+    @Override
+    protected <A extends Annotation> ValidatorMapping<A> doGetValidatorMapping(Class<A> constraintType) {
+
+        final ValidatorMapping<A> secondaryMapping = secondaryDelegate.doGetValidatorMapping(constraintType);
+        if (secondaryMapping == null) {
+            return primaryDelegate.doGetValidatorMapping(constraintType);
+        }
+        final AnnotationBehavior annotationBehavior = secondaryMapping.getAnnotationBehavior();
+
+        if (annotationBehavior == AnnotationBehavior.EXCLUDE) {
+            return secondaryMapping;
+        }
+        return ValidatorMapping.merge(
+            Arrays.asList(primaryDelegate.doGetValidatorMapping(constraintType), secondaryMapping),
+            AnnotationBehaviorMergeStrategy.consensus());
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/EmptyBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/EmptyBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/EmptyBuilder.java
new file mode 100644
index 0000000..c95f6d7
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/EmptyBuilder.java
@@ -0,0 +1,183 @@
+/*
+ *  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.metadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.bval.jsr.descriptor.GroupConversion;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.ObjectUtils;
+import org.apache.bval.util.Validate;
+
+public class EmptyBuilder {
+    private static final Map<AnnotationBehavior, EmptyBuilder> INSTANCES = new EnumMap<>(AnnotationBehavior.class);
+
+    public static EmptyBuilder instance() {
+        return instance(AnnotationBehavior.ABSTAIN);
+    }
+
+    public static EmptyBuilder instance(AnnotationBehavior annotationBehavior) {
+        return INSTANCES.computeIfAbsent(annotationBehavior, EmptyBuilder::new);
+    }
+
+    private class Level implements HasAnnotationBehavior {
+
+        @Override
+        public final AnnotationBehavior getAnnotationBehavior() {
+            return annotationBehavior;
+        }
+    }
+
+    private class ForBean extends Level implements MetadataBuilder.ForBean {
+        private final Lazy<EmptyBuilder.ForClass> forClass = new Lazy<>(EmptyBuilder.ForClass::new);
+
+        @Override
+        public MetadataBuilder.ForClass getClass(Metas<Class<?>> meta) {
+            return forClass.get();
+        }
+
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Metas<Class<?>> meta) {
+            return Collections.emptyMap();
+        }
+
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Metas<Class<?>> meta) {
+            return Collections.emptyMap();
+        }
+
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Metas<Class<?>> meta) {
+            return Collections.emptyMap();
+        }
+
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Metas<Class<?>> meta) {
+            return Collections.emptyMap();
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return true;
+        }
+    }
+
+    private class ForElement<E extends AnnotatedElement> extends Level implements MetadataBuilder.ForElement<E> {
+
+        @Override
+        public final Annotation[] getDeclaredConstraints(Metas<E> meta) {
+            return ObjectUtils.EMPTY_ANNOTATION_ARRAY;
+        }
+    }
+
+    private class ForClass extends ForElement<Class<?>> implements MetadataBuilder.ForClass {
+
+        @Override
+        public List<Class<?>> getGroupSequence(Metas<Class<?>> meta) {
+            return null;
+        }
+    }
+
+    private class ForContainer<E extends AnnotatedElement> extends ForElement<E>
+        implements MetadataBuilder.ForContainer<E> {
+
+        @Override
+        public boolean isCascade(Metas<E> meta) {
+            return false;
+        }
+
+        @Override
+        public Set<GroupConversion> getGroupConversions(Metas<E> meta) {
+            return Collections.emptySet();
+        }
+
+        @Override
+        public Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+            Metas<E> meta) {
+            return Collections.emptyMap();
+        }
+    }
+
+    private class ForExecutable<E extends Executable> extends Level implements MetadataBuilder.ForExecutable<E> {
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public MetadataBuilder.ForElement<E> getCrossParameter(Metas<E> meta) {
+            return forElement.get();
+        }
+
+        @Override
+        public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Metas<E> meta) {
+            return Collections.emptyList();
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public MetadataBuilder.ForContainer<E> getReturnValue(Metas<E> meta) {
+            return forContainer.get();
+        }
+    }
+
+    private final AnnotationBehavior annotationBehavior;
+    private final Lazy<EmptyBuilder.ForBean> forBean;
+    @SuppressWarnings("rawtypes")
+    private final Lazy<EmptyBuilder.ForContainer> forContainer;
+    @SuppressWarnings("rawtypes")
+    private final Lazy<EmptyBuilder.ForExecutable> forExecutable;
+    @SuppressWarnings("rawtypes")
+    private final Lazy<EmptyBuilder.ForElement> forElement;
+
+    private EmptyBuilder(AnnotationBehavior annotationBehavior) {
+        super();
+        this.annotationBehavior = Validate.notNull(annotationBehavior, "annotationBehavior");
+        forBean = new Lazy<>(EmptyBuilder.ForBean::new);
+        forContainer = new Lazy<>(EmptyBuilder.ForContainer::new);
+        forExecutable = new Lazy<>(EmptyBuilder.ForExecutable::new);
+        forElement = new Lazy<>(EmptyBuilder.ForElement::new);
+    }
+
+    public MetadataBuilder.ForBean forBean() {
+        return forBean.get();
+    }
+
+    @SuppressWarnings("unchecked")
+    public <E extends AnnotatedElement> MetadataBuilder.ForContainer<E> forContainer() {
+        return forContainer.get();
+    }
+
+    @SuppressWarnings("unchecked")
+    public <E extends Executable> MetadataBuilder.ForExecutable<E> forExecutable() {
+        return forExecutable.get();
+    }
+
+    @SuppressWarnings("unchecked")
+    public <E extends AnnotatedElement> MetadataBuilder.ForElement<E> forElement() {
+        return forElement.get();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HasAnnotationBehavior.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HasAnnotationBehavior.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HasAnnotationBehavior.java
new file mode 100644
index 0000000..2060954
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HasAnnotationBehavior.java
@@ -0,0 +1,24 @@
+/*
+ *  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.metadata;
+
+public interface HasAnnotationBehavior {
+
+    default AnnotationBehavior getAnnotationBehavior() {
+        return AnnotationBehavior.ABSTAIN;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
new file mode 100644
index 0000000..35276ea
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
@@ -0,0 +1,235 @@
+/*
+ *  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.metadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumMap;
+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 javax.validation.metadata.Scope;
+
+import org.apache.bval.jsr.descriptor.GroupConversion;
+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))
+public class HierarchyBuilder extends CompositeBuilder {
+    private static abstract class HierarchyDelegate<T> {
+        final T delegate;
+
+        HierarchyDelegate(T delegate) {
+            super();
+            this.delegate = Validate.notNull(delegate, "delegate");
+        }
+
+        static class ForBean extends HierarchyDelegate<MetadataBuilder.ForBean> implements MetadataBuilder.ForBean {
+            final Metas<Class<?>> hierarchyType;
+
+            ForBean(MetadataBuilder.ForBean delegate, Class<?> hierarchyType) {
+                super(delegate);
+                this.hierarchyType = new Metas.ForClass(hierarchyType);
+            }
+
+            @Override
+            public MetadataBuilder.ForClass getClass(Metas<Class<?>> meta) {
+                return new HierarchyDelegate.ForClass(delegate.getClass(hierarchyType), hierarchyType);
+            }
+
+            @Override
+            public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Metas<Class<?>> meta) {
+                return delegate.getFields(hierarchyType);
+            }
+
+            @Override
+            public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Metas<Class<?>> meta) {
+                return delegate.getGetters(hierarchyType);
+            }
+
+            @Override
+            public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Metas<Class<?>> meta) {
+                return delegate.getConstructors(hierarchyType);
+            }
+
+            @Override
+            public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Metas<Class<?>> meta) {
+                final Map<Signature, MetadataBuilder.ForExecutable<Method>> m = delegate.getMethods(hierarchyType);
+
+                return m;
+            }
+        }
+
+        static class ForClass extends HierarchyDelegate<MetadataBuilder.ForClass> implements MetadataBuilder.ForClass {
+
+            final Metas<Class<?>> hierarchyType;
+
+            ForClass(MetadataBuilder.ForClass delegate, Metas<Class<?>> hierarchyType) {
+                super(delegate);
+                this.hierarchyType = hierarchyType;
+            }
+
+            @Override
+            public Annotation[] getDeclaredConstraints(Metas<Class<?>> meta) {
+                return delegate.getDeclaredConstraints(hierarchyType);
+            }
+
+            @Override
+            public List<Class<?>> getGroupSequence(Metas<Class<?>> meta) {
+                return delegate.getGroupSequence(hierarchyType);
+            }
+        }
+
+        static class ForGetter extends HierarchyDelegate<MetadataBuilder.ForContainer<Method>>
+            implements MetadataBuilder.ForContainer<Method> {
+            final Metas.ForMethod meta;
+
+            ForGetter(MetadataBuilder.ForContainer<Method> delegate,
+                org.apache.bval.jsr.metadata.Metas.ForMethod meta) {
+                super(delegate);
+                this.meta = Validate.notNull(meta, "meta");
+            }
+
+            @Override
+            public Annotation[] getDeclaredConstraints(Metas<Method> meta) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+            @Override
+            public boolean isCascade(Metas<Method> meta) {
+                // TODO Auto-generated method stub
+                return false;
+            }
+
+            @Override
+            public Set<GroupConversion> getGroupConversions(Metas<Method> meta) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+            @Override
+            public Map<ContainerElementKey, org.apache.bval.jsr.metadata.MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+                Metas<Method> meta) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+        }
+
+        static class ForMethod extends HierarchyDelegate<MetadataBuilder.ForExecutable<Method>>
+            implements MetadataBuilder.ForExecutable<Method> {
+            final Metas.ForMethod meta;
+
+            public ForMethod(MetadataBuilder.ForExecutable<Method> delegate, Metas.ForMethod meta) {
+                super(delegate);
+                this.meta = Validate.notNull(meta, "meta");
+            }
+
+            @Override
+            public MetadataBuilder.ForContainer<Method> getReturnValue(Metas<Method> meta) {
+
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+            @Override
+            public MetadataBuilder.ForElement<Method> getCrossParameter(Metas<Method> meta) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+            @Override
+            public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Metas<Method> meta) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+        }
+    }
+
+    private final Function<Class<?>, MetadataBuilder.ForBean> getBeanBuilder;
+
+    public HierarchyBuilder(Function<Class<?>, MetadataBuilder.ForBean> getBeanBuilder) {
+        super(AnnotationBehaviorMergeStrategy.first());
+        this.getBeanBuilder = Validate.notNull(getBeanBuilder, "getBeanBuilder function was null");
+    }
+
+    public MetadataBuilder.ForBean forBean(Class<?> beanClass) {
+        final List<MetadataBuilder.ForBean> delegates = new ArrayList<>();
+
+        /*
+         * First add the delegate for the requested bean class, forcing to empty if absent. This is important for the
+         * same reason that we use the #first() AnnotationBehaviorMergeStrategy: namely, that custom metadata overrides
+         * only from the immediately available mapping per the BV spec.
+         */
+        delegates.add(Optional.of(beanClass).map(getBeanBuilder).orElseGet(() -> EmptyBuilder.instance().forBean()));
+
+        // iterate the hierarchy, skipping the first (i.e. beanClass handled
+        // above)
+        final Iterator<Class<?>> hierarchy = Reflection.hierarchy(beanClass, Interfaces.INCLUDE).iterator();
+        hierarchy.next();
+
+        // skip Object.class; skip null/empty hierarchy builders, mapping others
+        // to HierarchyDelegate
+        hierarchy
+            .forEachRemaining(t -> Optional.of(t).filter(Predicate.isEqual(Object.class).negate()).map(getBeanBuilder)
+                .filter(b -> !b.isEmpty()).map(b -> new HierarchyDelegate.ForBean(b, t)).ifPresent(delegates::add));
+
+        // if we have nothing but empty builders (which should only happen for
+        // absent custom metadata), return empty:
+        if (delegates.stream().allMatch(MetadataBuilder.ForBean::isEmpty)) {
+            return EmptyBuilder.instance().forBean();
+        }
+        return delegates.stream().collect(compose());
+    }
+
+    @Override
+    protected <E extends AnnotatedElement> Map<Scope, Annotation[]> getConstraintsByScope(
+        CompositeBuilder.ForElement<? extends MetadataBuilder.ForElement<E>, E> composite, Metas<E> meta) {
+
+        final Iterator<? extends MetadataBuilder.ForElement<E>> iter = composite.delegates.iterator();
+
+        final Map<Scope, Annotation[]> result = new EnumMap<>(Scope.class);
+        result.put(Scope.LOCAL_ELEMENT, iter.next().getDeclaredConstraints(meta));
+
+        if (iter.hasNext()) {
+            final List<Annotation> hierarchyConstraints = new ArrayList<>();
+            iter.forEachRemaining(d -> Collections.addAll(hierarchyConstraints, d.getDeclaredConstraints(meta)));
+            result.put(Scope.HIERARCHY, hierarchyConstraints.toArray(new Annotation[hierarchyConstraints.size()]));
+        }
+        return result;
+    }
+
+    @Override
+    protected List<Class<?>> getGroupSequence(CompositeBuilder.ForClass composite, Metas<Class<?>> meta) {
+        return composite.delegates.get(0).getGroupSequence(meta);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilder.java
new file mode 100644
index 0000000..7dbdcbc
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilder.java
@@ -0,0 +1,98 @@
+/*
+ *  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.metadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.validation.metadata.Scope;
+
+import org.apache.bval.jsr.descriptor.GroupConversion;
+
+/**
+ * Common interface for populating the Bean Validation descriptors from various sources. Most implementations should
+ * concern themselves with a single level of an inheritance hierarchy.
+ */
+public final class MetadataBuilder {
+
+    public interface ForBean extends HasAnnotationBehavior {
+        MetadataBuilder.ForClass getClass(Metas<Class<?>> meta);
+
+        Map<String, ForContainer<Field>> getFields(Metas<Class<?>> meta);
+
+        /**
+         * Returned keys are property names per XML mapping spec.
+         * 
+         * @param meta
+         * @return {@link Map}
+         */
+        Map<String, ForContainer<Method>> getGetters(Metas<Class<?>> meta);
+
+        Map<Signature, ForExecutable<Constructor<?>>> getConstructors(Metas<Class<?>> meta);
+
+        Map<Signature, ForExecutable<Method>> getMethods(Metas<Class<?>> meta);
+
+        default boolean isEmpty() {
+            return false;
+        }
+    }
+
+    public interface ForElement<E extends AnnotatedElement> extends HasAnnotationBehavior {
+
+        Annotation[] getDeclaredConstraints(Metas<E> meta);
+
+        default Map<Scope, Annotation[]> getConstraintsByScope(Metas<E> meta) {
+            return Collections.singletonMap(Scope.LOCAL_ELEMENT, getDeclaredConstraints(meta));
+        }
+    }
+
+    public interface ForClass extends ForElement<Class<?>> {
+
+        List<Class<?>> getGroupSequence(Metas<Class<?>> meta);
+    }
+
+    public interface ForContainer<E extends AnnotatedElement> extends MetadataBuilder.ForElement<E> {
+
+        boolean isCascade(Metas<E> meta);
+
+        Set<GroupConversion> getGroupConversions(Metas<E> meta);
+
+        Map<ContainerElementKey, ForContainer<AnnotatedType>> getContainerElementTypes(Metas<E> meta);
+    }
+
+    public interface ForExecutable<E extends Executable> extends HasAnnotationBehavior {
+
+        MetadataBuilder.ForContainer<E> getReturnValue(Metas<E> meta);
+
+        MetadataBuilder.ForElement<E> getCrossParameter(Metas<E> meta);
+
+        List<ForContainer<Parameter>> getParameters(Metas<E> meta);
+    }
+
+    private MetadataBuilder() {
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilders.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilders.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilders.java
new file mode 100644
index 0000000..aa301a4
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilders.java
@@ -0,0 +1,41 @@
+/*
+ *  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.metadata;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.bval.util.Validate;
+
+public class MetadataBuilders {
+
+    private final Map<Class<?>, List<MetadataBuilder.ForBean>> beanBuilders = new ConcurrentHashMap<>();
+
+    public <T> void registerCustomBuilder(Class<?> bean, MetadataBuilder.ForBean builder) {
+        Validate.notNull(bean, "bean");
+        Validate.notNull(builder, "builder");
+        beanBuilders.computeIfAbsent(bean, c -> new ArrayList<>()).add(builder);
+    }
+
+    public List<MetadataBuilder.ForBean> getCustomBuilders(Class<?> bean) {
+        final List<MetadataBuilder.ForBean> list = beanBuilders.get(bean);
+        return list == null ? Collections.emptyList() : Collections.unmodifiableList(list);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Metas.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Metas.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Metas.java
new file mode 100644
index 0000000..667c404
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Metas.java
@@ -0,0 +1,324 @@
+/*
+ * 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.metadata;
+
+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;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.lang.reflect.Type;
+import java.util.Objects;
+
+import javax.validation.constraintvalidation.ValidationTarget;
+
+import org.apache.bval.util.Validate;
+
+/**
+ * Validation class model.
+ *
+ * @param <E>
+ */
+// TODO rename to Meta; delete old type of that name
+public abstract class Metas<E extends AnnotatedElement> {
+
+    public static class ForClass extends Metas<Class<?>> {
+
+        public ForClass(Class<?> host) {
+            super(host, ElementType.TYPE);
+        }
+
+        @Override
+        public final Class<?> getDeclaringClass() {
+            return getHost();
+        }
+
+        @Override
+        public Type getType() {
+            return getHost();
+        }
+
+        @Override
+        public AnnotatedType getAnnotatedType() {
+            return new AnnotatedType() {
+
+                @Override
+                public Annotation[] getDeclaredAnnotations() {
+                    return getHost().getDeclaredAnnotations();
+                }
+
+                @Override
+                public Annotation[] getAnnotations() {
+                    return getHost().getAnnotations();
+                }
+
+                @Override
+                public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+                    return getHost().getAnnotation(annotationClass);
+                }
+
+                @Override
+                public Type getType() {
+                    return getHost();
+                }
+            };
+        }
+
+        @Override
+        public String getName() {
+            return getHost().getName();
+        }
+    }
+
+    public static abstract class ForMember<M extends Member & AnnotatedElement> extends Metas<M> {
+
+        protected ForMember(M host, ElementType elementType) {
+            super(host, elementType);
+        }
+
+        @Override
+        public Class<?> getDeclaringClass() {
+            return getHost().getDeclaringClass();
+        }
+    }
+
+    public static class ForField extends ForMember<Field> {
+
+        public ForField(Field host) {
+            super(host, ElementType.FIELD);
+        }
+
+        @Override
+        public Type getType() {
+            return getHost().getGenericType();
+        }
+
+        @Override
+        public AnnotatedType getAnnotatedType() {
+            return getHost().getAnnotatedType();
+        }
+
+        @Override
+        public String getName() {
+            return getHost().getName();
+        }
+    }
+
+    public static abstract class ForExecutable<E extends Executable> extends ForMember<E> {
+
+        protected ForExecutable(E host, ElementType elementType) {
+            super(host, elementType);
+        }
+
+        @Override
+        public AnnotatedType getAnnotatedType() {
+            return getHost().getAnnotatedReturnType();
+        }
+    }
+
+    public static class ForConstructor extends ForExecutable<Constructor<?>> {
+
+        public ForConstructor(Constructor<?> host) {
+            super(host, ElementType.CONSTRUCTOR);
+        }
+
+        @Override
+        public Type getType() {
+            return getHost().getDeclaringClass();
+        }
+
+        @Override
+        public String getName() {
+            return getHost().getDeclaringClass().getSimpleName();
+        }
+    }
+
+    public static class ForMethod extends ForExecutable<Method> {
+
+        public ForMethod(Method host) {
+            super(host, ElementType.METHOD);
+        }
+
+        @Override
+        public Type getType() {
+            return getHost().getGenericReturnType();
+        }
+
+        @Override
+        public String getName() {
+            return getHost().getName();
+        }
+    }
+
+    public static class ForCrossParameter<E extends Executable> extends Metas.ForExecutable<E> {
+
+        public ForCrossParameter(Metas<E> parent) {
+            super(parent.getHost(), parent.getElementType());
+        }
+
+        @Override
+        public Type getType() {
+            return Object[].class;
+        }
+
+        @Override
+        public String getName() {
+            return "<cross parameter>";
+        }
+
+        @Override
+        public ValidationTarget getValidationTarget() {
+            return ValidationTarget.PARAMETERS;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s(%s of %s)", getStringPrefix(), getName(), getHost());
+        }
+    }
+
+    public static class ForParameter extends Metas<Parameter> {
+
+        private final String name;
+
+        public ForParameter(Parameter host, String name) {
+            super(host, ElementType.PARAMETER);
+            this.name = Validate.notNull(name, "name");
+        }
+
+        @Override
+        public Type getType() {
+            return getHost().getType();
+        }
+
+        @Override
+        public Class<?> getDeclaringClass() {
+            return getHost().getDeclaringExecutable().getDeclaringClass();
+        }
+
+        @Override
+        public AnnotatedType getAnnotatedType() {
+            return getHost().getAnnotatedType();
+        }
+
+        public String getName() {
+            return name;
+        }
+    }
+
+    public static class ForContainerElement extends Metas<AnnotatedType> {
+
+        private final Metas<?> parent;
+        private final ContainerElementKey key;
+
+        public ForContainerElement(Metas<?> parent, ContainerElementKey key) {
+            super(key.getAnnotatedType(), ElementType.TYPE_USE);
+            this.parent = Validate.notNull(parent, "parent");
+            this.key = Validate.notNull(key, "key");
+        }
+
+        @Override
+        public Type getType() {
+            return getHost().getType();
+        }
+
+        @Override
+        public Class<?> getDeclaringClass() {
+            return parent.getDeclaringClass();
+        }
+
+        @Override
+        public AnnotatedType getAnnotatedType() {
+            return key.getAnnotatedType();
+        }
+
+        public Integer getTypeArgumentIndex() {
+            return Integer.valueOf(key.getTypeArgumentIndex());
+        }
+
+        @Override
+        public String getName() {
+            return key.toString();
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s(%s of %s)", getStringPrefix(), key, getHost());
+        }
+    }
+
+    private final E host;
+    private final ElementType elementType;
+
+    protected Metas(E host, ElementType elementType) {
+        super();
+        this.host = Validate.notNull(host, "host");
+        this.elementType = Validate.notNull(elementType, "elementType");
+    }
+
+    public E getHost() {
+        return host;
+    }
+
+    public ElementType getElementType() {
+        return elementType;
+    }
+
+    public abstract Type getType();
+
+    public abstract Class<?> getDeclaringClass();
+
+    public abstract AnnotatedType getAnnotatedType();
+
+    public abstract String getName();
+
+    public ValidationTarget getValidationTarget() {
+        return ValidationTarget.ANNOTATED_ELEMENT;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s(%s)", getStringPrefix(), host);
+    }
+
+    protected String getStringPrefix() {
+        return Metas.class.getSimpleName() + '.' + getClass().getSimpleName();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!obj.getClass().equals(getClass())) {
+            return false;
+        }
+        return Objects.equals(((Metas<?>) obj).getHost(), getHost());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getHost());
+    }
+}


[15/17] bval git commit: code updates

Posted by mb...@apache.org.
http://git-wip-us.apache.org/repos/asf/bval/blob/f87be824/bval-jsr/src/main/java/org/apache/bval/jsr/resolver/CachingTraversableResolver.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/resolver/CachingTraversableResolver.java b/bval-jsr/src/main/java/org/apache/bval/jsr/resolver/CachingTraversableResolver.java
index 05639c7..2f212de 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/resolver/CachingTraversableResolver.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/resolver/CachingTraversableResolver.java
@@ -21,6 +21,7 @@ import javax.validation.TraversableResolver;
 import java.lang.annotation.ElementType;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * Cache results of a delegated traversable resovler to optimize calls
@@ -34,7 +35,7 @@ import java.util.Map;
  */
 public class CachingTraversableResolver implements TraversableResolver, CachingRelevant {
     private TraversableResolver delegate;
-    private Map<CacheEntry, CacheEntry> cache = new HashMap<CacheEntry, CacheEntry>();
+    private Map<CacheEntry, CacheEntry> cache = new HashMap<>();
 
     /**
      * Convenience method to check whether caching is necessary on a given {@link TraversableResolver}.
@@ -61,11 +62,8 @@ public class CachingTraversableResolver implements TraversableResolver, CachingR
      * @see #needsCaching(TraversableResolver)
      */
     public static TraversableResolver cacheFor(TraversableResolver traversableResolver) {
-        if (needsCaching(traversableResolver)) {
-            return new CachingTraversableResolver(traversableResolver);
-        } else {
-            return traversableResolver;
-        }
+        return needsCaching(traversableResolver) ? new CachingTraversableResolver(traversableResolver)
+            : traversableResolver;
     }
 
     /**
@@ -158,15 +156,14 @@ public class CachingTraversableResolver implements TraversableResolver, CachingR
             if (this == o) {
                 return true;
             }
-            if (o == null || getClass() != o.getClass()) {
+            if (o == null || !getClass().equals(o.getClass())) {
                 return false;
             }
 
             CacheEntry that = (CacheEntry) o;
 
-            return elementType == that.elementType && path.equals(that.path) && type.equals(that.type)
-                && !(object != null ? !object.equals(that.object) : that.object != null) && node.equals(that.node);
-
+            return elementType == that.elementType && Objects.equals(path, that.path) && Objects.equals(type, that.type)
+                && Objects.equals(object, that.object) && Objects.equals(node, that.node);
         }
 
         /**
@@ -178,12 +175,7 @@ public class CachingTraversableResolver implements TraversableResolver, CachingR
         }
 
         private int buildHashCode() {
-            int result = object != null ? object.hashCode() : 0;
-            result = 31 * result + node.hashCode();
-            result = 31 * result + type.hashCode();
-            result = 31 * result + path.hashCode();
-            result = 31 * result + elementType.hashCode();
-            return result;
+            return Objects.hash(object, node, type, path, elementType);
         }
     }
-}
\ No newline at end of file
+}