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 20:25:04 UTC

[08/11] bval git commit: implement BV 2.0 against existing BVal unit tests

http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/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/3f287a7a/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/3f287a7a/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/3f287a7a/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/3f287a7a/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/3f287a7a/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/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/NodeBuilderCustomizableContextImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/NodeBuilderCustomizableContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/NodeBuilderCustomizableContextImpl.java
new file mode 100644
index 0000000..8200bfe
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/NodeBuilderCustomizableContextImpl.java
@@ -0,0 +1,89 @@
+/*
+ * 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 org.apache.bval.jsr.util.LeafNodeBuilderCustomizableContextImpl;
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.jsr.util.PathImpl;
+
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext;
+import javax.validation.ElementKind;
+
+public class NodeBuilderCustomizableContextImpl
+    implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext {
+    private final PathImpl path;
+    private final ConstraintValidatorContextImpl context;
+    private final String template;
+
+    public NodeBuilderCustomizableContextImpl(final ConstraintValidatorContextImpl parent, final String messageTemplate,
+        final PathImpl propertyPath) {
+        context = parent;
+        template = messageTemplate;
+        path = propertyPath;
+    }
+
+    @Override
+    public ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder inIterable() {
+        path.getLeafNode().setInIterable(true);
+        return new NodeContextBuilderImpl(context, template, path);
+    }
+
+    @Override
+    public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(String name) {
+        path.addNode(new NodeImpl(name));
+        return this;
+    }
+
+    @Override
+    public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addPropertyNode(
+        String name) {
+        final NodeImpl node = new NodeImpl.PropertyNodeImpl(name);
+        path.addNode(node);
+        return this;
+    }
+
+    @Override
+    public ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext addBeanNode() {
+        final NodeImpl node = new NodeImpl.BeanNodeImpl();
+        path.addNode(node);
+        return null;
+//        return new LeafNodeBuilderCustomizableContextImpl(context, template, path);
+    }
+
+    @Override
+    public ConstraintValidatorContext addConstraintViolation() {
+        context.addError(template, path);
+        return context;
+    }
+
+    @Override
+    public NodeBuilderCustomizableContext inContainer(Class<?> containerClass, Integer typeArgumentIndex) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType,
+        Integer typeArgumentIndex) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/NodeContextBuilderImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/NodeContextBuilderImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/NodeContextBuilderImpl.java
new file mode 100644
index 0000000..d1f191e
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/NodeContextBuilderImpl.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;
+
+import org.apache.bval.jsr.util.LeafNodeBuilderCustomizableContextImpl;
+import org.apache.bval.jsr.util.NodeBuilderDefinedContextImpl;
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.jsr.util.PathImpl;
+
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext;
+import javax.validation.ElementKind;
+
+public class NodeContextBuilderImpl
+    implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder {
+    private final PathImpl path;
+    private final String template;
+    private final ConstraintValidatorContextImpl context;
+
+    public NodeContextBuilderImpl(final ConstraintValidatorContextImpl context, final String template,
+        final PathImpl path) {
+        this.context = context;
+        this.template = template;
+        this.path = path;
+    }
+
+    @Override
+    public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext atKey(Object key) {
+        path.getLeafNode().setKey(key);
+        return null;
+//        return new NodeBuilderDefinedContextImpl(context, template, path);
+    }
+
+    @Override
+    public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext atIndex(Integer index) {
+        path.getLeafNode().setIndex(index);
+        return null;
+//        return new NodeBuilderDefinedContextImpl(context, template, path);
+    }
+
+    @Override
+    public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(String name) {
+        return new NodeBuilderCustomizableContextImpl(context, template, path).addNode(name);
+    }
+
+    @Override
+    public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addPropertyNode(
+        String name) {
+        return new NodeBuilderCustomizableContextImpl(context, template, path).addPropertyNode(name);
+    }
+
+    @Override
+    public ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext addBeanNode() {
+        final NodeImpl node = new NodeImpl.BeanNodeImpl();
+        path.addNode(node);
+        return null;
+//        return new LeafNodeBuilderCustomizableContextImpl(context, template, path);
+    }
+
+    @Override
+    public ConstraintValidatorContext addConstraintViolation() {
+        context.addError(template, path);
+        return context;
+    }
+
+    @Override
+    public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType,
+        Integer typeArgumentIndex) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/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/3f287a7a/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/3f287a7a/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/3f287a7a/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);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/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/3f287a7a/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/3f287a7a/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/3f287a7a/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/3f287a7a/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/3f287a7a/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/3f287a7a/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/3f287a7a/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;
+    }
+}