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:53 UTC

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

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