bval-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mben...@apache.org
Subject [13/15] bval git commit: wip
Date Wed, 15 Nov 2017 22:54:18 GMT
http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/Finder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/Finder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/Finder.java
new file mode 100644
index 0000000..8a416a7
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/Finder.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr.descriptor;
+
+import java.lang.annotation.ElementType;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+import javax.validation.metadata.ElementDescriptor.ConstraintFinder;
+import javax.validation.metadata.Scope;
+
+import org.apache.bval.jsr.groups.GroupsComputer;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Validate;
+
+class Finder implements ConstraintFinder {
+    private static final Predicate<ConstraintD<?>> ALWAYS = c -> true;
+
+    private Predicate<ConstraintD<?>> groups = ALWAYS;
+    private Predicate<ConstraintD<?>> scope = ALWAYS;
+    private Predicate<ConstraintD<?>> elements = ALWAYS;
+
+    private final ElementDescriptor owner;
+    private final List<Class<?>> groupSequence;
+
+    Finder(ElementDescriptor owner, List<Class<?>> groupSequence) {
+        this.owner = Validate.notNull(owner, "owner");
+        this.groupSequence = groupSequence;
+    }
+
+    @Override
+    public ConstraintFinder unorderedAndMatchingGroups(Class<?>... groups) {
+        this.groups = c -> new GroupsComputer().computeGroups(groups).getGroups().stream().flatMap(g -> {
+            if (g.isDefault()) {
+                final List<Class<?>> seq = groupSequence;
+                if (seq != null) {
+                    return seq.stream();
+                }
+            }
+            return Stream.of(g.getGroup());
+        }).anyMatch(c.getGroups()::contains);
+
+        return this;
+    }
+
+    @Override
+    public ConstraintFinder lookingAt(Scope scope) {
+        this.scope = scope == Scope.HIERARCHY ? ALWAYS : c -> c.getScope() == scope;
+        return this;
+    }
+
+    @Override
+    public ConstraintFinder declaredOn(ElementType... types) {
+        this.elements = c -> Stream.of(types).filter(Objects::nonNull)
+            .collect(Collectors.toCollection(() -> EnumSet.noneOf(ElementType.class))).contains(c.getDeclaredOn());
+
+        return this;
+    }
+
+    @Override
+    public Set<ConstraintDescriptor<?>> getConstraintDescriptors() {
+        return getConstraints().filter(filter()).collect(ToUnmodifiable.set());
+    }
+
+    @Override
+    public boolean hasConstraints() {
+        return getConstraints().anyMatch(filter());
+    }
+
+    private Stream<ConstraintD<?>> getConstraints() {
+        return owner.getConstraintDescriptors().stream().<ConstraintD<?>> map(c -> c.unwrap(ConstraintD.class));
+    }
+    
+    private Predicate<ConstraintD<?>> filter() {
+        return groups.and(scope).and(elements);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/GroupConversion.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/GroupConversion.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/GroupConversion.java
new file mode 100644
index 0000000..9ef724e
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/GroupConversion.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.bval.jsr.descriptor;
+
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.validation.metadata.GroupConversionDescriptor;
+
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.LazyInt;
+import org.apache.bval.util.Validate;
+
+public class GroupConversion implements GroupConversionDescriptor {
+    public static class Builder {
+        private final Class<?> from;
+
+        private Builder(Class<?> from) {
+            this.from = from;
+        }
+
+        public GroupConversion to(Class<?> to) {
+            return new GroupConversion(from, to);
+        }
+    }
+
+    public static Builder from(Class<?> from) {
+        return new Builder(from);
+    }
+
+    private final Class<?> from;
+    private final Class<?> to;
+    private final LazyInt hashCode;
+    private final Lazy<String> toString;
+
+    private GroupConversion(Class<?> from, Class<?> to) {
+        super();
+        this.from = Validate.notNull(from, "from");
+        this.to = Validate.notNull(to, "to");
+        this.hashCode = new LazyInt(() -> Objects.hash(this.from, this.to));
+        this.toString = new Lazy<>(
+            () -> String.format("%s from %s to %s", GroupConversion.class.getSimpleName(), this.from, this.to));
+    }
+
+    @Override
+    public Class<?> getFrom() {
+        return from;
+    }
+
+    @Override
+    public Class<?> getTo() {
+        return to;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return obj == this
+            || Optional.ofNullable(obj).filter(GroupConversion.class::isInstance).map(GroupConversion.class::cast)
+                .filter(gc -> Objects.equals(from, gc.from) && Objects.equals(to, gc.to)).isPresent();
+    }
+
+    @Override
+    public int hashCode() {
+        return hashCode.getAsInt();
+    }
+
+    @Override
+    public String toString() {
+        return toString.get();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
new file mode 100644
index 0000000..8f08674
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
@@ -0,0 +1,266 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+package org.apache.bval.jsr.descriptor;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.function.Function;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import javax.validation.ParameterNameProvider;
+import javax.validation.metadata.PropertyDescriptor;
+import javax.validation.metadata.Scope;
+
+import org.apache.bval.jsr.ApacheValidatorFactory;
+import org.apache.bval.jsr.metadata.ContainerElementKey;
+import org.apache.bval.jsr.metadata.EmptyBuilder;
+import org.apache.bval.jsr.metadata.MetadataBuilder;
+import org.apache.bval.jsr.metadata.Metas;
+import org.apache.bval.jsr.metadata.Signature;
+import org.apache.bval.jsr.util.Exceptions;
+import org.apache.bval.jsr.util.Methods;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+
+class MetadataReader {
+
+    class ForElement<E extends AnnotatedElement, B extends MetadataBuilder.ForElement<E>> {
+        final Metas<E> meta;
+        protected final B builder;
+
+        ForElement(Metas<E> meta, B builder) {
+            super();
+            this.meta = Validate.notNull(meta, "meta");
+            this.builder = Validate.notNull(builder, "builder");
+        }
+
+        Set<ConstraintD<?>> getConstraints() {
+            return builder.getConstraintsByScope(meta).entrySet().stream()
+                .flatMap(e -> describe(e.getValue(), e.getKey(), meta)).collect(ToUnmodifiable.set());
+        }
+
+        private Stream<ConstraintD<?>> describe(Annotation[] constraints, Scope scope, Metas<?> meta) {
+            return Stream.of(constraints).map(c -> new ConstraintD<>(c, scope, meta, validatorFactory));
+        }
+    }
+
+    class ForBean extends MetadataReader.ForElement<Class<?>, MetadataBuilder.ForClass> {
+        private final MetadataBuilder.ForBean beanBuilder;
+
+        ForBean(Metas<Class<?>> meta, MetadataBuilder.ForBean builder) {
+            super(meta, Validate.notNull(builder, "builder").getClass(meta));
+            this.beanBuilder = builder;
+        }
+
+        Set<PropertyDescriptor> getProperties(BeanD parent) {
+            final Map<String, List<PropertyD<?>>> properties = new TreeMap<>();
+            final Function<? super String, ? extends List<PropertyD<?>>> descriptorList = k -> new ArrayList<>();
+
+            beanBuilder.getFields(meta).forEach((f, builder) -> {
+                final Field fld = Reflection.find(meta.getHost(), t -> Reflection.getDeclaredField(t, f));
+                properties.computeIfAbsent(f, descriptorList).add(new PropertyD.ForField(
+                    new MetadataReader.ForContainer<>(new Metas.ForField(fld), builder), parent));
+            });
+
+            beanBuilder.getGetters(meta).forEach((g, builder) -> {
+                final Method getter = Reflection.find(meta.getHost(), t -> {
+                    return Stream.of(Reflection.getDeclaredMethods(t)).filter(Methods::isGetter)
+                        .filter(m -> g.equals(Methods.propertyName(m))).findFirst().orElse(null);
+                });
+                Exceptions.raiseIf(getter == null, IllegalStateException::new,
+                    "Getter method for property %s not found", g);
+
+                properties.computeIfAbsent(g, descriptorList).add(new PropertyD.ForMethod(
+                    new MetadataReader.ForContainer<>(new Metas.ForMethod(getter), builder), parent));
+            });
+
+            if (properties.isEmpty()) {
+                return Collections.emptySet();
+            }
+
+            // merge field/getter into composed PropertyDescriptors:
+            return properties.values().stream()
+                .map(delegates -> delegates.size() == 1 ? delegates.get(0) : new ComposedD.ForProperty(delegates))
+                .collect(ToUnmodifiable.set());
+        }
+
+        Map<Signature, MethodD> getMethods(BeanD parent) {
+            final Map<Signature, MetadataBuilder.ForExecutable<Method>> methodBuilders = beanBuilder.getMethods(meta);
+            if (methodBuilders.isEmpty()) {
+                return Collections.emptyMap();
+            }
+
+            final Map<Signature, MethodD> result = new LinkedHashMap<>();
+
+            methodBuilders.forEach((sig, builder) -> {
+                final Method m = Reflection.find(meta.getHost(),
+                    t -> Reflection.getDeclaredMethod(t, sig.getName(), sig.getParameterTypes()));
+
+                result.put(sig, new MethodD(new MetadataReader.ForMethod(new Metas.ForMethod(m), builder), parent));
+            });
+
+            return Collections.unmodifiableMap(result);
+        }
+
+        Map<Signature, ConstructorD> getConstructors(BeanD parent) {
+            final Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> ctorBuilders =
+                beanBuilder.getConstructors(meta);
+
+            if (ctorBuilders.isEmpty()) {
+                return Collections.emptyMap();
+            }
+
+            final Map<Signature, ConstructorD> result = new LinkedHashMap<>();
+
+            ctorBuilders.forEach((sig, builder) -> {
+                final Constructor<?> c = Reflection.getDeclaredConstructor(meta.getHost(), sig.getParameterTypes());
+                result.put(sig,
+                    new ConstructorD(new MetadataReader.ForConstructor(new Metas.ForConstructor(c), builder), parent));
+            });
+
+            return Collections.unmodifiableMap(result);
+        }
+
+        List<Class<?>> getGroupSequence() {
+            return builder.getGroupSequence(meta);
+        }
+    }
+
+    class ForContainer<E extends AnnotatedElement> extends ForElement<E, MetadataBuilder.ForContainer<E>> {
+
+        ForContainer(Metas<E> meta, MetadataBuilder.ForContainer<E> builder) {
+            super(meta, builder);
+        }
+
+        boolean isCascaded() {
+            return builder.isCascade(meta);
+        }
+
+        Set<GroupConversion> getGroupConversions() {
+            return builder.getGroupConversions(meta);
+        }
+
+        Set<ContainerElementTypeD> getContainerElementTypes(CascadableContainerD<?, ?> parent) {
+            final Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> containerElementTypes =
+                builder.getContainerElementTypes(meta);
+
+            if (containerElementTypes.isEmpty()) {
+                return Collections.emptySet();
+            }
+
+            final Set<ContainerElementTypeD> result =
+                new TreeSet<>(Comparator.comparing(ContainerElementTypeD::getKey));
+
+            containerElementTypes.forEach((k, builder) -> {
+                result.add(new ContainerElementTypeD(k,
+                    new MetadataReader.ForContainer<>(new Metas.ForContainerElement(k), builder), parent));
+            });
+
+            return Collections.unmodifiableSet(result);
+        }
+    }
+
+    abstract class ForExecutable<E extends Executable, SELF extends ForExecutable<E, SELF>>
+        extends ForElement<E, MetadataBuilder.ForElement<E>> {
+        private final MetadataBuilder.ForExecutable<E> executableBuilder;
+
+        ForExecutable(Metas<E> meta, MetadataBuilder.ForExecutable<E> executableBuilder) {
+            super(meta, EmptyBuilder.instance().forElement());
+            this.executableBuilder = Validate.notNull(executableBuilder, "executableBuilder");
+        }
+
+        <X extends ExecutableD<E, SELF, X>> List<ParameterD<X>> getParameterDescriptors(X parent) {
+            final Parameter[] parameters = meta.getHost().getParameters();
+
+            final List<String> parameterNames =
+                getParameterNames(validatorFactory.getParameterNameProvider(), meta.getHost());
+
+            final List<MetadataBuilder.ForContainer<Parameter>> builders = executableBuilder.getParameters(meta);
+
+            return IntStream.range(0, parameters.length).mapToObj(i -> {
+                final Metas.ForParameter param = new Metas.ForParameter(parameters[i], parameterNames.get(i));
+                return new ParameterD<X>(param, i, new MetadataReader.ForContainer<>(param, builders.get(i)), parent);
+            }).collect(ToUnmodifiable.list());
+        }
+
+        <X extends ExecutableD<E, SELF, X>> CrossParameterD<X, E> getCrossParameterDescriptor(X parent) {
+            final Metas.ForCrossParameter<E> cp = new Metas.ForCrossParameter<>(meta);
+            return new CrossParameterD<>(new MetadataReader.ForElement<>(cp, executableBuilder.getCrossParameter(cp)),
+                parent);
+        }
+
+        <X extends ExecutableD<E, SELF, X>> ReturnValueD<X, E> getReturnValueDescriptor(X parent) {
+            return new ReturnValueD<>(new MetadataReader.ForContainer<>(meta, executableBuilder.getReturnValue(meta)),
+                parent);
+        }
+
+        abstract List<String> getParameterNames(ParameterNameProvider parameterNameProvider, E host);
+    }
+
+    class ForMethod extends ForExecutable<Method, ForMethod> {
+        ForMethod(Metas<Method> meta, MetadataBuilder.ForExecutable<Method> builder) {
+            super(meta, builder);
+        }
+
+        @Override
+        List<String> getParameterNames(ParameterNameProvider parameterNameProvider, Method host) {
+            return parameterNameProvider.getParameterNames(host);
+        }
+    }
+
+    class ForConstructor extends ForExecutable<Constructor<?>, ForConstructor> {
+
+        ForConstructor(Metas<Constructor<?>> meta, MetadataBuilder.ForExecutable<Constructor<?>> builder) {
+            super(meta, builder);
+        }
+
+        @Override
+        List<String> getParameterNames(ParameterNameProvider parameterNameProvider, Constructor<?> host) {
+            return parameterNameProvider.getParameterNames(host);
+        }
+    }
+
+    private final ApacheValidatorFactory validatorFactory;
+
+    MetadataReader(ApacheValidatorFactory validatorFactory) {
+        super();
+        this.validatorFactory = Validate.notNull(validatorFactory, "validatorFactory");
+    }
+
+    MetadataReader.ForBean forBean(Class<?> beanClass, MetadataBuilder.ForBean builder) {
+        return new MetadataReader.ForBean(new Metas.ForClass(beanClass), builder);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MethodD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MethodD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MethodD.java
new file mode 100644
index 0000000..7f718e0
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MethodD.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+package org.apache.bval.jsr.descriptor;
+
+import java.lang.reflect.Method;
+
+import javax.validation.metadata.MethodDescriptor;
+import javax.validation.metadata.MethodType;
+
+import org.apache.bval.jsr.util.Methods;
+
+class MethodD extends ExecutableD<Method, MetadataReader.ForMethod, MethodD> implements MethodDescriptor {
+    private final MethodType methodType;
+
+    MethodD(MetadataReader.ForMethod reader, BeanD parent) {
+        super(reader, parent);
+        methodType = Methods.isGetter(reader.meta.getHost()) ? MethodType.GETTER : MethodType.NON_GETTER;
+    }
+
+    MethodType getMethodType() {
+        return methodType;
+    }
+
+    @Override
+    protected String nameOf(Method e) {
+        return e.getName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ParameterD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ParameterD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ParameterD.java
new file mode 100644
index 0000000..951eccb
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ParameterD.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+package org.apache.bval.jsr.descriptor;
+
+import java.lang.reflect.Parameter;
+
+import javax.validation.metadata.ParameterDescriptor;
+
+import org.apache.bval.jsr.metadata.Metas;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.TypeUtils;
+
+public class ParameterD<P extends ExecutableD<?, ?, P>> extends CascadableContainerD<P, Parameter>
+    implements ParameterDescriptor {
+
+    private final int index;
+    private final String name;
+    private final Class<?> type;
+
+    protected ParameterD(Metas.ForParameter meta, int index, MetadataReader.ForContainer<Parameter> reader, P parent) {
+        super(reader, parent);
+
+        Validate.isTrue(index >= 0 && index < meta.getHost().getDeclaringExecutable().getParameterCount(),
+            "Invalid parameter index %d", index);
+
+        this.index = index;
+
+        name = reader.meta.getName();
+        type = TypeUtils.getRawType(reader.meta.getType(), parent.getElementClass());
+    }
+
+    @Override
+    public Class<?> getElementClass() {
+        return type;
+    }
+
+    @Override
+    public int getIndex() {
+        return index;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/PropertyD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/PropertyD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/PropertyD.java
new file mode 100644
index 0000000..dbd9875
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/PropertyD.java
@@ -0,0 +1,142 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.bval.jsr.descriptor;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import javax.validation.metadata.PropertyDescriptor;
+
+import org.apache.bval.jsr.GraphContext;
+import org.apache.bval.jsr.util.Methods;
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+
+@Privilizing(@CallTo(Reflection.class))
+public abstract class PropertyD<E extends AnnotatedElement> extends CascadableContainerD<BeanD, E>
+    implements PropertyDescriptor {
+
+    static class ForField extends PropertyD<Field> {
+
+        ForField(MetadataReader.ForContainer<Field> reader, BeanD parent) {
+            super(reader, parent);
+        }
+
+        @Override
+        public String getPropertyName() {
+            return host.getName();
+        }
+
+        @Override
+        public Object getValue(Object parent) throws Exception {
+            final boolean mustUnset = Reflection.setAccessible(host, true);
+            try {
+                return host.get(parent);
+            } catch (IllegalAccessException e) {
+                throw new IllegalArgumentException(e);
+            } finally {
+                if (mustUnset) {
+                    Reflection.setAccessible(host, false);
+                }
+            }
+        }
+    }
+
+    static class ForMethod extends PropertyD<Method> {
+
+        ForMethod(MetadataReader.ForContainer<Method> reader, BeanD parent) {
+            super(reader, parent);
+        }
+
+        @Override
+        public String getPropertyName() {
+            return Methods.propertyName(host);
+        }
+
+        @Override
+        public Object getValue(Object parent) throws Exception {
+            final boolean mustUnset = Reflection.setAccessible(host, true);
+            try {
+                return host.invoke(parent);
+            } catch (IllegalAccessException | InvocationTargetException e) {
+                throw new IllegalArgumentException(e);
+            } finally {
+                if (mustUnset) {
+                    Reflection.setAccessible(host, false);
+                }
+            }
+        }
+    }
+
+    protected final E host;
+
+    protected PropertyD(MetadataReader.ForContainer<E> reader, BeanD parent) {
+        super(reader, parent);
+        this.host = reader.meta.getHost();
+    }
+
+    @Override
+    protected Stream<GraphContext> readImpl(GraphContext context) throws Exception {
+        final Supplier<NodeImpl> propertyNode = () -> new NodeImpl.PropertyNodeImpl(getPropertyName());
+        final Object value = getValue(context.getValue());
+
+        if (Map.class.isInstance(value)) {
+
+            return ((Map<?, ?>) value).entrySet().stream().map(e -> {
+                final NodeImpl node = propertyNode.get();
+                node.setKey(e.getKey());
+                return context.child(node, e.getValue());
+            });
+        }
+        if (value != null && value.getClass().isArray()) {
+            IntStream.range(0, Array.getLength(value)).mapToObj(i -> {
+                final NodeImpl node = propertyNode.get();
+                node.setIndex(i);
+                return context.child(node, Array.get(value, i));
+            });
+        }
+        if (List.class.isInstance(value)) {
+            final List<?> l = (List<?>) value;
+
+            return IntStream.range(0, l.size()).mapToObj(i -> {
+                final NodeImpl node = propertyNode.get();
+                node.setIndex(Integer.valueOf(i));
+                return context.child(node, l.get(i));
+            });
+        }
+        if (Iterable.class.isInstance(value)) {
+            final NodeImpl node = propertyNode.get();
+            node.setInIterable(true);
+            final Stream.Builder<Object> b = Stream.builder();
+            ((Iterable<?>) value).forEach(b);
+            return b.build().map(o -> context.child(node, o));
+        }
+        return Stream.of(context.child(propertyNode.get(), value));
+    }
+
+    public abstract Object getValue(Object parent) throws Exception;
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ReturnValueD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ReturnValueD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ReturnValueD.java
new file mode 100644
index 0000000..a2204fc
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ReturnValueD.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+package org.apache.bval.jsr.descriptor;
+
+import java.lang.reflect.Executable;
+
+import javax.validation.metadata.ReturnValueDescriptor;
+
+public class ReturnValueD<P extends ExecutableD<?, ?, P>, E extends Executable> extends CascadableContainerD<P, E>
+    implements ReturnValueDescriptor {
+
+    ReturnValueD(MetadataReader.ForContainer<E> reader, P parent) {
+        super(reader, parent);
+    }
+
+    @Override
+    public Class<?> getElementClass() {
+        return parent.getElementClass();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/job/ConstraintValidatorContextImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ConstraintValidatorContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ConstraintValidatorContextImpl.java
new file mode 100644
index 0000000..5ee9ee1
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ConstraintValidatorContextImpl.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr.job;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.validation.ClockProvider;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintViolation;
+import javax.validation.MessageInterpolator;
+import javax.validation.Path;
+import javax.validation.ValidationException;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.CrossParameterDescriptor;
+
+import org.apache.bval.jsr.descriptor.ComposedD;
+import org.apache.bval.jsr.descriptor.ConstraintD;
+import org.apache.bval.jsr.descriptor.CrossParameterD;
+import org.apache.bval.jsr.util.ContainerElementNodeBuilderCustomizableContextImpl;
+import org.apache.bval.jsr.util.Exceptions;
+import org.apache.bval.jsr.util.LeafNodeBuilderCustomizableContextImpl;
+import org.apache.bval.jsr.util.NodeBuilderCustomizableContextImpl;
+import org.apache.bval.jsr.util.NodeBuilderDefinedContextImpl;
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.jsr.util.PathImpl;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.Validate;
+
+public class ConstraintValidatorContextImpl<T> implements ConstraintValidatorContext, MessageInterpolator.Context {
+    private class ConstraintViolationBuilderImpl implements ConstraintValidatorContext.ConstraintViolationBuilder {
+        private final String template;
+        private final PathImpl path;
+
+        ConstraintViolationBuilderImpl(String template, PathImpl path) {
+            this.template = template;
+            this.path = path;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public NodeBuilderDefinedContext addNode(String name) {
+            PathImpl p;
+            if (path.isRootPath()) {
+                p = PathImpl.create();
+                p.getLeafNode().setName(name);
+            } else {
+                p = PathImpl.copy(path);
+                p.addNode(new NodeImpl(name));
+            }
+            return new NodeBuilderDefinedContextImpl(ConstraintValidatorContextImpl.this, template, p);
+        }
+
+        @Override
+        public NodeBuilderCustomizableContext addPropertyNode(String name) {
+            return new NodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl.this, template, path, name);
+        }
+
+        @Override
+        public LeafNodeBuilderCustomizableContext addBeanNode() {
+            return new LeafNodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl.this, template, path);
+        }
+
+        @Override
+        public NodeBuilderDefinedContext addParameterNode(int index) {
+            Exceptions.raiseUnless(frame.descriptor instanceof CrossParameterDescriptor, ValidationException::new,
+                "Cannot add parameter node for %s", frame.descriptor.getClass().getName());
+
+            final CrossParameterD<?, ?> crossParameter =
+                ComposedD.unwrap(frame.descriptor, CrossParameterD.class).findFirst().get();
+
+            final String parameterName = crossParameter.getParent().getParameterDescriptors().get(index).getName();
+
+            final NodeImpl node = new NodeImpl.ParameterNodeImpl(parameterName, index);
+            if (!path.isRootPath()) {
+                path.removeLeafNode();
+            }
+            path.addNode(node);
+            return new NodeBuilderDefinedContextImpl(ConstraintValidatorContextImpl.this, template, path);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public ConstraintValidatorContext addConstraintViolation() {
+            addError(template, path);
+            return ConstraintValidatorContextImpl.this;
+        }
+
+        @Override
+        public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name,
+            Class<?> containerType, Integer typeArgumentIndex) {
+            return new ContainerElementNodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl.this, template,
+                path, name, containerType, typeArgumentIndex);
+        }
+    }
+
+    private final ValidationJob<T,?>.Frame<?> frame;
+    private final ConstraintD<?> constraint;
+    private final Lazy<Set<ConstraintViolation<T>>> violations = new Lazy<>(HashSet::new);
+    private boolean defaultConstraintViolationDisabled;
+
+    /**
+     * Temporary for code migration
+     */
+    @Deprecated
+    protected ConstraintValidatorContextImpl() {
+        this.frame = null;
+        this.constraint = null;
+    }
+
+    ConstraintValidatorContextImpl(ValidationJob<T,?>.Frame<?> frame, ConstraintD<?> constraint) {
+        super();
+        this.frame = Validate.notNull(frame, "frame");
+        this.constraint = Validate.notNull(constraint, "constraint");
+    }
+
+    @Override
+    public void disableDefaultConstraintViolation() {
+        this.defaultConstraintViolationDisabled = true;
+    }
+
+    @Override
+    public String getDefaultConstraintMessageTemplate() {
+        return constraint.getMessageTemplate();
+    }
+
+    @Override
+    public ConstraintViolationBuilder buildConstraintViolationWithTemplate(String messageTemplate) {
+        return new ConstraintViolationBuilderImpl(messageTemplate, frame.context.getPath());
+    }
+
+    @Override
+    public ClockProvider getClockProvider() {
+        return frame.getJob().validatorContext.getClockProvider();
+    }
+
+    @Override
+    public <U> U unwrap(Class<U> type) {
+        try {
+            return type.cast(this);
+        } catch (ClassCastException e) {
+            throw new ValidationException(e);
+        }
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void addError(String messageTemplate, Path propertyPath) {
+        violations.get().add(((ValidationJob) frame.getJob()).createViolation(messageTemplate, this, null));
+    }
+
+    ValidationJob<T,?>.Frame<?> getFrame() {
+        return frame;
+    }
+
+    Set<ConstraintViolation<T>> getRequiredViolations() {
+        if (!violations.optional().isPresent()) {
+            Exceptions.raiseIf(defaultConstraintViolationDisabled, ValidationException::new,
+                "Expected custom constraint violation(s)");
+
+            addError(getDefaultConstraintMessageTemplate(), frame.context.getPath());
+        }
+        return violations.get();
+    }
+
+    @Override
+    public ConstraintDescriptor<?> getConstraintDescriptor() {
+        return constraint;
+    }
+
+    @Override
+    public Object getValidatedValue() {
+        return frame.context.getValue();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateBean.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateBean.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateBean.java
new file mode 100644
index 0000000..74b2efe
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateBean.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr.job;
+
+import org.apache.bval.jsr.ApacheFactoryContext;
+import org.apache.bval.jsr.ConstraintViolationImpl;
+import org.apache.bval.jsr.GraphContext;
+import org.apache.bval.jsr.descriptor.ConstraintD;
+import org.apache.bval.jsr.util.PathImpl;
+import org.apache.bval.util.Validate;
+
+public final class ValidateBean<T> extends ValidationJob<T, ValidateBean<T>> {
+
+    private final T bean;
+
+    ValidateBean(ApacheFactoryContext validatorContext, T bean, Class<?>[] groups) {
+        super(validatorContext, groups);
+        this.bean = Validate.notNull(bean, "bean");
+    }
+
+    @Override
+    protected BeanFrame initBaseFrame() {
+        return new BeanFrame(new GraphContext(validatorContext, PathImpl.create(), bean));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected Class<T> getRootBeanClass() {
+        return (Class<T>) bean.getClass();
+    }
+
+    @Override
+    ConstraintViolationImpl<T> createViolation(String messageTemplate, ConstraintValidatorContextImpl<T> context,
+        Object leafBean) {
+        final String message = validatorContext.getMessageInterpolator().interpolate(messageTemplate, context);
+
+        return new ConstraintViolationImpl<>(messageTemplate, message, bean, leafBean,
+            context.getFrame().context.getPath(), context.getFrame().context.getValue(),
+            context.getConstraintDescriptor(), getRootBeanClass(),
+            context.getConstraintDescriptor().unwrap(ConstraintD.class).getDeclaredOn(), null, null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java
new file mode 100644
index 0000000..0e02639
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateParameters.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr.job;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import javax.validation.ParameterNameProvider;
+import javax.validation.metadata.CrossParameterDescriptor;
+import javax.validation.metadata.ExecutableDescriptor;
+import javax.validation.metadata.ParameterDescriptor;
+
+import org.apache.bval.jsr.ApacheFactoryContext;
+import org.apache.bval.jsr.ConstraintViolationImpl;
+import org.apache.bval.jsr.GraphContext;
+import org.apache.bval.jsr.descriptor.ConstraintD;
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.jsr.util.PathImpl;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.Validate;
+
+public abstract class ValidateParameters<E extends Executable, T, J extends ValidateParameters<E, T, J>>
+    extends ValidationJob<T, J> {
+
+    public static class ForMethod<T> extends ValidateParameters<Method, T, ForMethod<T>> {
+
+        private final T object;
+
+        ForMethod(ApacheFactoryContext validatorContext, T object, Method executable, Object[] parameterValues,
+            Class<?>[] groups) {
+            super(validatorContext, object, executable, parameterValues, groups);
+            this.object = Validate.notNull(object, "object");
+        }
+
+        @Override
+        protected ExecutableDescriptor describe() {
+            return validatorContext.getDescriptorManager().getBeanDescriptor(object.getClass())
+                .getConstraintsForMethod(executable.getName(), executable.getParameterTypes());
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        protected Class<T> getRootBeanClass() {
+            return (Class<T>) object.getClass();
+        }
+
+        @Override
+        protected List<String> getParameterNames(ParameterNameProvider parameterNameProvider) {
+            return parameterNameProvider.getParameterNames(executable);
+        }
+
+        @Override
+        protected T getRootBean() {
+            return object;
+        }
+    }
+
+    public static class ForConstructor<T> extends ValidateParameters<Constructor<? extends T>, T, ForConstructor<T>> {
+
+        ForConstructor(ApacheFactoryContext validatorContext, Constructor<? extends T> executable,
+            Object[] parameterValues, Class<?>[] groups) {
+            super(validatorContext, null, executable, parameterValues, groups);
+        }
+
+        @Override
+        protected ExecutableDescriptor describe() {
+            return validatorContext.getDescriptorManager().getBeanDescriptor(executable.getDeclaringClass())
+                .getConstraintsForConstructor(executable.getParameterTypes());
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        protected Class<T> getRootBeanClass() {
+            return (Class<T>) executable.getDeclaringClass();
+        }
+
+        @Override
+        protected List<String> getParameterNames(ParameterNameProvider parameterNameProvider) {
+            return parameterNameProvider.getParameterNames(executable);
+        }
+
+        @Override
+        protected T getRootBean() {
+            return null;
+        }
+    }
+
+    class ParametersFrame extends Frame<CrossParameterDescriptor> {
+        private final ExecutableDescriptor executableDescriptor;
+
+        protected ParametersFrame(ExecutableDescriptor executableDescriptor, GraphContext context) {
+            super(executableDescriptor.getCrossParameterDescriptor(), context);
+            this.executableDescriptor = executableDescriptor;
+        }
+
+        @Override
+        void recurse(Class<?> group) {
+            executableDescriptor.getParameterDescriptors().stream()
+                .map(pd -> new SproutFrame<ParameterDescriptor>(pd, parameter(pd.getIndex())))
+                .forEach(f -> f.visit(group));
+        }
+    }
+
+    protected final E executable;
+    protected final Lazy<List<String>> parameterNames =
+        new Lazy<>(() -> getParameterNames(validatorContext.getParameterNameProvider()));
+
+    private final Object[] parameterValues;
+
+    ValidateParameters(ApacheFactoryContext validatorContext, T object, E executable, Object[] parameterValues,
+        Class<?>[] groups) {
+        super(validatorContext, groups);
+        this.executable = Validate.notNull(executable, "executable");
+        this.parameterValues = Validate.notNull(parameterValues, "parameterValues").clone();
+    }
+
+    @Override
+    protected J.Frame<?> initBaseFrame() {
+        final PathImpl cp = PathImpl.create();
+        cp.addNode(new NodeImpl.CrossParameterNodeImpl());
+        return new ParametersFrame(describe(), new GraphContext(validatorContext, cp, parameterValues));
+    }
+
+    protected abstract ExecutableDescriptor describe();
+
+    protected abstract List<String> getParameterNames(ParameterNameProvider parameterNameProvider);
+
+    protected abstract T getRootBean();
+
+    @Override
+    ConstraintViolationImpl<T> createViolation(String messageTemplate, ConstraintValidatorContextImpl<T> context,
+        Object leafBean) {
+
+        final String message = validatorContext.getMessageInterpolator().interpolate(messageTemplate, context);
+
+        return new ConstraintViolationImpl<T>(messageTemplate, message, getRootBean(), leafBean,
+            context.getFrame().context.getPath(), context.getFrame().context.getValue(),
+            context.getConstraintDescriptor(), getRootBeanClass(),
+            context.getConstraintDescriptor().unwrap(ConstraintD.class).getDeclaredOn(), null, parameterValues);
+    }
+
+    private GraphContext parameter(int i) {
+        final PathImpl path = PathImpl.create();
+        path.addNode(new NodeImpl.ParameterNodeImpl(parameterNames.get().get(i), i));
+        return new GraphContext(validatorContext, path, parameterValues[i]);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateProperty.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateProperty.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateProperty.java
new file mode 100644
index 0000000..f8aa79e
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateProperty.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.job;
+
+import javax.validation.metadata.PropertyDescriptor;
+
+import org.apache.bval.jsr.ApacheFactoryContext;
+import org.apache.bval.jsr.ConstraintViolationImpl;
+import org.apache.bval.jsr.GraphContext;
+import org.apache.bval.jsr.descriptor.ConstraintD;
+import org.apache.bval.jsr.descriptor.PropertyD;
+import org.apache.bval.jsr.util.PathImpl;
+import org.apache.bval.util.StringUtils;
+import org.apache.bval.util.Validate;
+
+public final class ValidateProperty<T> extends ValidationJob<T, ValidateProperty<T>> {
+    private final Class<T> rootBeanClass;
+    private final PropertyD<?> descriptor;
+    private GraphContext baseContext;
+    private boolean cascade;
+
+    private ValidateProperty(ApacheFactoryContext validatorContext, Class<T> rootBeanClass, String property,
+        Class<?>[] groups) {
+        super(validatorContext, groups);
+        this.rootBeanClass = Validate.notNull(rootBeanClass, "rootBeanClass");
+        Validate.isTrue(StringUtils.isNotBlank(property), "Null/empty/blank property");
+        this.descriptor = (PropertyD<?>) validatorContext.getDescriptorManager().getBeanDescriptor(rootBeanClass)
+            .getConstraintsForProperty(property);
+    }
+
+    ValidateProperty(ApacheFactoryContext validatorContext, Class<T> rootBeanClass, String property, Object value,
+        Class<?>[] groups) {
+        this(validatorContext, rootBeanClass, property, groups);
+
+        baseContext = new GraphContext(validatorContext, PathImpl.createPathFromString(property), value);
+    }
+
+    @SuppressWarnings("unchecked")
+    ValidateProperty(ApacheFactoryContext validatorContext, T bean, String property, Class<?>[] groups)
+        throws Exception {
+        this(validatorContext, (Class<T>) Validate.notNull(bean, "bean").getClass(), property, groups);
+
+        baseContext = new GraphContext(validatorContext, PathImpl.create(), bean)
+            .child(PathImpl.createPathFromString(property).getLeafNode(), descriptor.getValue(bean));
+    }
+
+    public ValidateProperty<T> cascade(boolean cascade) {
+        this.cascade = cascade;
+        return this;
+    }
+
+    @Override
+    protected SproutFrame<PropertyDescriptor> initBaseFrame() {
+        return new SproutFrame<PropertyDescriptor>(descriptor, baseContext) {
+            @Override
+            void recurse(Class<?> group) {
+                if (cascade) {
+                    super.recurse(group);
+                }
+            }
+        };
+    }
+
+    @Override
+    protected Class<T> getRootBeanClass() {
+        return rootBeanClass;
+    }
+
+    @Override
+    ConstraintViolationImpl<T> createViolation(String messageTemplate, ConstraintValidatorContextImpl<T> context,
+        Object leafBean) {
+        final String message = validatorContext.getMessageInterpolator().interpolate(messageTemplate, context);
+
+        return new ConstraintViolationImpl<>(messageTemplate, message, null, leafBean,
+            context.getFrame().context.getPath(), context.getFrame().context.getValue(),
+            context.getConstraintDescriptor(), rootBeanClass,
+            context.getConstraintDescriptor().unwrap(ConstraintD.class).getDeclaredOn(), null, null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateReturnValue.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateReturnValue.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateReturnValue.java
new file mode 100644
index 0000000..d90f932
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidateReturnValue.java
@@ -0,0 +1,104 @@
+package org.apache.bval.jsr.job;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+
+import javax.validation.metadata.ExecutableDescriptor;
+
+import org.apache.bval.jsr.ApacheFactoryContext;
+import org.apache.bval.jsr.ConstraintViolationImpl;
+import org.apache.bval.jsr.GraphContext;
+import org.apache.bval.jsr.descriptor.ConstraintD;
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.jsr.util.PathImpl;
+import org.apache.bval.util.Validate;
+
+public abstract class ValidateReturnValue<E extends Executable, T, J extends ValidateReturnValue<E, T, J>>
+    extends ValidationJob<T, J> {
+    public static class ForMethod<T> extends ValidateReturnValue<Method, T, ForMethod<T>> {
+        private final T object;
+
+        ForMethod(ApacheFactoryContext validatorContext, T object, Method method, Object returnValue,
+            Class<?>[] groups) {
+            super(validatorContext, method, returnValue, groups);
+            this.object = Validate.notNull(object, "object");
+        }
+
+        @Override
+        protected T getRootBean() {
+            return object;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        protected Class<T> getRootBeanClass() {
+            return (Class<T>) object.getClass();
+        }
+
+        @Override
+        protected ExecutableDescriptor describe() {
+            return validatorContext.getDescriptorManager().getBeanDescriptor(object.getClass())
+                .getConstraintsForMethod(executable.getName(), executable.getParameterTypes());
+        }
+    }
+
+    public static class ForConstructor<T> extends ValidateReturnValue<Constructor<? extends T>, T, ForConstructor<T>> {
+
+        ForConstructor(ApacheFactoryContext validatorContext, Constructor<? extends T> ctor, Object returnValue,
+            Class<?>[] groups) {
+            super(validatorContext, ctor, returnValue, groups);
+        }
+
+        @Override
+        protected T getRootBean() {
+            return null;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        protected Class<T> getRootBeanClass() {
+            return (Class<T>) executable.getDeclaringClass();
+        }
+
+        @Override
+        protected ExecutableDescriptor describe() {
+            return validatorContext.getDescriptorManager().getBeanDescriptor(executable.getDeclaringClass())
+                .getConstraintsForConstructor(executable.getParameterTypes());
+        }
+    }
+
+    protected final E executable;
+    private final Object returnValue;
+
+    ValidateReturnValue(ApacheFactoryContext validatorContext, E executable, Object returnValue, Class<?>[] groups) {
+        super(validatorContext, groups);
+        this.executable = Validate.notNull(executable, "executable");
+        this.returnValue = returnValue;
+    }
+
+    @Override
+    protected J.Frame<?> initBaseFrame() {
+        final PathImpl path = PathImpl.create();
+        path.addNode(new NodeImpl.ReturnValueNodeImpl());
+
+        return new SproutFrame<>(describe().getReturnValueDescriptor(),
+            new GraphContext(validatorContext, path, returnValue));
+    }
+
+    @Override
+    ConstraintViolationImpl<T> createViolation(String messageTemplate, ConstraintValidatorContextImpl<T> context,
+        Object leafBean) {
+
+        final String message = validatorContext.getMessageInterpolator().interpolate(messageTemplate, context);
+
+        return new ConstraintViolationImpl<>(messageTemplate, message, getRootBean(), leafBean,
+            context.getFrame().context.getPath(), context.getFrame().context.getValue(),
+            context.getConstraintDescriptor(), getRootBeanClass(),
+            context.getConstraintDescriptor().unwrap(ConstraintD.class).getDeclaredOn(), returnValue, null);
+    }
+
+    protected abstract ExecutableDescriptor describe();
+
+    protected abstract T getRootBean();
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java
new file mode 100644
index 0000000..96e1d73
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJob.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr.job;
+
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintViolation;
+import javax.validation.TraversableResolver;
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.CascadableDescriptor;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ContainerDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+import javax.validation.metadata.PropertyDescriptor;
+
+import org.apache.bval.jsr.ApacheFactoryContext;
+import org.apache.bval.jsr.ConstraintViolationImpl;
+import org.apache.bval.jsr.GraphContext;
+import org.apache.bval.jsr.descriptor.CascadableContainerD;
+import org.apache.bval.jsr.descriptor.ComposedD;
+import org.apache.bval.jsr.descriptor.ConstraintD;
+import org.apache.bval.jsr.descriptor.PropertyD;
+import org.apache.bval.jsr.groups.Group;
+import org.apache.bval.jsr.groups.Groups;
+import org.apache.bval.jsr.util.NodeImpl;
+import org.apache.bval.jsr.util.PathImpl;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.Validate;
+
+public abstract class ValidationJob<T, J extends ValidationJob<T, J>> {
+
+    public abstract class Frame<D extends ElementDescriptor> {
+        protected final D descriptor;
+        protected final GraphContext context;
+
+        protected Frame(D descriptor, GraphContext context) {
+            super();
+            this.descriptor = Validate.notNull(descriptor, "descriptor");
+            this.context = Validate.notNull(context, "context");
+        }
+
+        @SuppressWarnings("unchecked")
+        final J getJob() {
+            return (J) ValidationJob.this;
+        }
+
+        final void visit(Class<?> group) {
+            if (skip()) {
+                return;
+            }
+            descriptor.findConstraints().unorderedAndMatchingGroups(group).getConstraintDescriptors().stream()
+                .map(ConstraintD.class::cast).forEach(this::validate);
+
+            recurse(group);
+        }
+
+        boolean skip() {
+            return false;
+        }
+
+        abstract void recurse(Class<?> group);
+
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        private boolean validate(ConstraintD<?> constraint) {
+            final Class<? extends ConstraintValidator> constraintValidatorClass =
+                constraint.getConstraintValidatorClass();
+
+            final ConstraintValidator constraintValidator =
+                validatorContext.getConstraintValidatorFactory().getInstance(constraintValidatorClass);
+
+            constraintValidator.initialize(constraint.getAnnotation());
+
+            final ConstraintValidatorContextImpl<T> constraintValidatorContext =
+                new ConstraintValidatorContextImpl<>(this, constraint);
+
+            boolean valid = constraintValidator.isValid(context.getValue(), constraintValidatorContext);
+            if (!valid) {
+                results.get().addAll(constraintValidatorContext.getRequiredViolations());
+            }
+
+            final Iterator<ConstraintDescriptor<?>> components = constraint.getComposingConstraints().iterator();
+
+            while (valid || !constraint.isReportAsSingleViolation()) {
+                if (components.hasNext()) {
+                    valid = validate((ConstraintD<?>) components.next());
+                    continue;
+                }
+                break;
+            }
+            return valid;
+        }
+    }
+
+    public class BeanFrame extends Frame<BeanDescriptor> {
+
+        BeanFrame(GraphContext context) {
+            super(getBeanDescriptor(context.getValue()), context);
+        }
+
+        @Override
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        void recurse(Class<?> group) {
+            // TODO experiment with parallel streaming, perhaps with an option. In this case would probably need each
+            // frame to record local violations before adding to the job
+
+            final Stream<PropertyD<?>> properties =
+                descriptor.getConstrainedProperties().stream().flatMap(d -> ComposedD.unwrap(d, PropertyD.class));
+
+            final TraversableResolver traversableResolver = validatorContext.getTraversableResolver();
+
+            final Stream<PropertyD<?>> reachableProperties =
+                properties.filter(d -> traversableResolver.isReachable(context.getValue(),
+                    new NodeImpl.PropertyNodeImpl(d.getPropertyName()), getRootBeanClass(), context.getPath(),
+                    d.getElementType()));
+
+            reachableProperties.forEach(d -> d.read(context).filter(Objects::nonNull)
+                .map(child -> new SproutFrame(d, child)).forEach(f -> f.visit(group)));
+        }
+
+        @Override
+        boolean skip() {
+            return seenBeans.put(context.getValue(), Boolean.TRUE) != null;
+        }
+    }
+
+    public class SproutFrame<D extends ElementDescriptor & CascadableDescriptor & ContainerDescriptor>
+        extends Frame<D> {
+
+        public SproutFrame(D descriptor, GraphContext context) {
+            super(descriptor, context);
+        }
+
+        @Override
+        void recurse(Class<?> group) {
+            final Stream<CascadableContainerD<?, ?>> containerElements =
+                descriptor.getConstrainedContainerElementTypes().stream()
+                    .flatMap(d -> ComposedD.unwrap(d, CascadableContainerD.class));
+
+            containerElements.flatMap(d -> d.read(context).map(child -> new SproutFrame<>(d, child)))
+                .forEach(f -> f.visit(group));
+
+            // Stream<CascadableContainerD<?, ?>> descriptors = ComposedD.unwrap(descriptor,
+            // CascadableContainerD.class);
+
+            if (!descriptor.isCascaded()) {
+                return;
+            }
+            if (descriptor instanceof PropertyDescriptor) {
+                final TraversableResolver traversableResolver = validatorContext.getTraversableResolver();
+
+                final PathImpl pathToTraversableObject = PathImpl.copy(context.getPath());
+                final NodeImpl traversableProperty = pathToTraversableObject.removeLeafNode();
+
+                if (!traversableResolver.isCascadable(context.getValue(), traversableProperty, getRootBeanClass(),
+                    pathToTraversableObject, ((PropertyD<?>) descriptor).getElementType())) {
+                    return;
+                }
+                // descriptors = descriptors.filter(d -> traversableResolver.isCascadable(context.getValue(),
+                // traversableProperty, getRootBeanClass(), pathToTraversableObject, d.getElementType()));
+            }
+            // descriptors.flatMap(d -> d.read(context)).map(BeanFrame::new).forEach(f -> f.visit(group));
+            new BeanFrame(context).visit(group);
+        }
+    }
+
+    protected final ApacheFactoryContext validatorContext;
+
+    private final Groups groups;
+    private final Lazy<Set<ConstraintViolation<T>>> results = new Lazy<>(LinkedHashSet::new);
+    private final IdentityHashMap<Object, Boolean> seenBeans = new IdentityHashMap<>();
+    private Frame<?> baseFrame;
+
+    ValidationJob(ApacheFactoryContext validatorContext, Class<?>[] groups) {
+        super();
+        this.validatorContext = Validate.notNull(validatorContext, "validatorContext");
+        this.groups = validatorContext.getGroupsComputer().computeGroups(groups);
+    }
+
+    public Set<ConstraintViolation<T>> getResults() {
+        Validate.validState(!results.optional().isPresent(), "#getResults() already called");
+
+        groups.getGroups().stream().map(Group::getGroup).forEach(baseFrame::visit);
+
+        sequences: for (List<Group> seq : groups.getSequences()) {
+            for (Group g : seq) {
+                baseFrame.visit(g.getGroup());
+                if (violationsFound()) {
+                    break sequences;
+                }
+            }
+        }
+        return results.optional().orElse(Collections.emptySet());
+    }
+
+    BeanDescriptor getBeanDescriptor(Object bean) {
+        Validate.notNull(bean, "bean");
+        return validatorContext.getFactory().getDescriptorManager().getBeanDescriptor(bean.getClass());
+    }
+
+    boolean isCascading() {
+        return true;
+    }
+
+    @SuppressWarnings("unchecked")
+    J init() {
+        this.baseFrame = initBaseFrame();
+        Validate.validState(baseFrame != null, "%s calculated null baseFrame", getClass().getName());
+        return (J) this;
+    }
+
+    abstract ConstraintViolationImpl<T> createViolation(String messageTemplate,
+        ConstraintValidatorContextImpl<T> context, Object leafBean);
+
+    protected abstract Frame<?> initBaseFrame();
+
+    protected abstract Class<T> getRootBeanClass();
+
+    private boolean violationsFound() {
+        return results.optional().filter(s -> !s.isEmpty()).isPresent();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJobFactory.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJobFactory.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJobFactory.java
new file mode 100644
index 0000000..7ccc03e
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ValidationJobFactory.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr.job;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+import javax.validation.ValidationException;
+import javax.validation.Validator;
+import javax.validation.executable.ExecutableValidator;
+
+import org.apache.bval.jsr.ApacheFactoryContext;
+import org.apache.bval.util.Validate;
+
+/**
+ * Creates {@link ValidationJob} instances.
+ */
+public class ValidationJobFactory {
+
+    private final ApacheFactoryContext validatorContext;
+
+    /**
+     * Create a new {@link ValidationJobFactory}.
+     * 
+     * @param validatorContext
+     */
+    public ValidationJobFactory(ApacheFactoryContext validatorContext) {
+        super();
+        this.validatorContext = Validate.notNull(validatorContext, "validatorContext");
+    }
+
+    /**
+     * @see Validator#validate(Object, Class...)
+     */
+    public <T> ValidateBean<T> validateBean(T bean, Class<?>... groups) {
+        return new ValidateBean<>(validatorContext, bean, groups).init();
+    }
+
+    /**
+     * @see Validator#validateProperty(Object, String, Class...)
+     */
+    public <T> ValidateProperty<T> validateProperty(T bean, String property, Class<?>... groups) {
+        try {
+            return new ValidateProperty<>(validatorContext, bean, property, groups).init();
+        } catch (Exception e) {
+            throw new ValidationException(e);
+        }
+    }
+
+    /**
+     * @see Validator#validateValue(Class, String, Object, Class...)
+     */
+    public <T> ValidateProperty<T> validateValue(Class<T> rootBeanClass, String property, Object value,
+        Class<?>... groups) {
+        return new ValidateProperty<T>(validatorContext, rootBeanClass, property, value, groups).init();
+    }
+
+    /**
+     * @see ExecutableValidator#validateParameters(Object, Method, Object[], Class...)
+     */
+    public <T> ValidateParameters.ForMethod<T> validateParameters(T object, Method method, Object[] parameterValues,
+        Class<?>... groups) {
+        return new ValidateParameters.ForMethod<T>(validatorContext, object, method, parameterValues, groups).init();
+    }
+
+    /**
+     * @see ExecutableValidator#validateReturnValue(Object, Method, Object, Class...)
+     */
+    public <T> ValidateReturnValue.ForMethod<T> validateReturnValue(T object, Method method, Object returnValue,
+        Class<?>... groups) {
+        return new ValidateReturnValue.ForMethod<>(validatorContext, object, method, returnValue, groups).init();
+    }
+
+    /**
+     * @see ExecutableValidator#validateConstructorParameters(Constructor, Object[], Class...)
+     */
+    public <T> ValidateParameters.ForConstructor<T> validateConstructorParameters(Constructor<? extends T> constructor,
+        Object[] parameterValues, Class<?>... groups) {
+        return new ValidateParameters.ForConstructor<T>(validatorContext, constructor, parameterValues, groups).init();
+    }
+
+    /**
+     * @see ExecutableValidator#validateConstructorReturnValue(Constructor, Object, Class...)
+     */
+    public <T> ValidateReturnValue.ForConstructor<T> validateConstructorReturnValue(
+        Constructor<? extends T> constructor, T createdObject, Class<?>... groups) {
+        return new ValidateReturnValue.ForConstructor<T>(validatorContext, constructor, createdObject, groups).init();
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehavior.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehavior.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehavior.java
new file mode 100644
index 0000000..887bccd
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehavior.java
@@ -0,0 +1,34 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.bval.jsr.metadata;
+
+import org.apache.bval.jsr.metadata.MetadataBuilder;
+
+/**
+ * Models the behavior of a {@link MetadataBuilder} with regard to bean validation annotations.
+ * @see ParallelBuilder
+ */
+public enum AnnotationBehavior implements AnnotationBehaviorMergeStrategy {
+    //@formatter:off
+    INCLUDE, EXCLUDE, ABSTAIN;
+    //@formatter:on
+
+    @Override
+    public AnnotationBehavior apply(Iterable<? extends MetadataBuilder.Level> t) {
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehaviorMergeStrategy.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehaviorMergeStrategy.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehaviorMergeStrategy.java
new file mode 100644
index 0000000..837801b
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehaviorMergeStrategy.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.bval.jsr.metadata;
+
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.bval.util.Validate;
+
+@FunctionalInterface
+public interface AnnotationBehaviorMergeStrategy
+    extends Function<Iterable<? extends MetadataBuilder.Level>, AnnotationBehavior> {
+
+    public static AnnotationBehaviorMergeStrategy first() {
+        return coll -> {
+            final Iterator<? extends MetadataBuilder.Level> iterator = coll.iterator();
+            return iterator.hasNext() ? iterator.next().getAnnotationBehavior() : AnnotationBehavior.ABSTAIN;
+        };
+    }
+
+    public static AnnotationBehaviorMergeStrategy consensus() {
+        return coll -> {
+            final Stream.Builder<MetadataBuilder.Level> b = Stream.builder();
+            coll.forEach(b);
+            final Set<AnnotationBehavior> annotationBehaviors =
+                b.build().map(MetadataBuilder.Level::getAnnotationBehavior).filter(Objects::nonNull)
+                    .filter(Predicate.isEqual(AnnotationBehavior.ABSTAIN).negate())
+                    .collect(Collectors.toCollection(() -> EnumSet.noneOf(AnnotationBehavior.class)));
+            Validate.validState(annotationBehaviors.size() <= 1,
+                "Conflicting annotation inclusion behaviors found among %s", coll);
+            return annotationBehaviors.isEmpty() ? AnnotationBehavior.ABSTAIN : annotationBehaviors.iterator().next();
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/a43c0b0c/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
new file mode 100644
index 0000000..1d8fc81
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
@@ -0,0 +1,232 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.bval.jsr.metadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import javax.validation.metadata.Scope;
+
+import org.apache.bval.jsr.descriptor.GroupConversion;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Validate;
+
+public class CompositeBuilder {
+
+    class Delegator<DELEGATE extends MetadataBuilder.Level> implements MetadataBuilder.Level {
+
+        protected final List<DELEGATE> delegates;
+
+        Delegator(List<DELEGATE> delegates) {
+            this.delegates = Validate.notNull(delegates, "delegates");
+            Validate.isTrue(!delegates.isEmpty(), "no delegates specified");
+            Validate.isTrue(delegates.stream().noneMatch(Objects::isNull), "One or more supplied delegates was null");
+        }
+
+        @Override
+        public AnnotationBehavior getAnnotationBehavior() {
+            return annotationBehaviorStrategy.apply(delegates);
+        }
+
+        <K, D> Map<K, D> merge(Function<DELEGATE, Map<K, D>> toMap, Function<List<D>, D> merge) {
+            final List<Map<K, D>> maps = delegates.stream().map(toMap).collect(Collectors.toList());
+
+            final Function<? super K, ? extends D> valueMapper = k -> {
+                final List<D> mappedDelegates =
+                    maps.stream().map(m -> m.get(k)).filter(Objects::nonNull).collect(Collectors.toList());
+                return mappedDelegates.size() == 1 ? mappedDelegates.get(0) : merge.apply(mappedDelegates);
+            };
+
+            return maps.stream().map(Map::keySet).flatMap(Collection::stream).distinct()
+                .collect(Collectors.toMap(Function.identity(), valueMapper));
+        }
+    }
+
+    private class ForBean extends CompositeBuilder.Delegator<MetadataBuilder.ForBean>
+        implements MetadataBuilder.ForBean {
+
+        ForBean(List<MetadataBuilder.ForBean> delegates) {
+            super(delegates);
+        }
+
+        @Override
+        public MetadataBuilder.ForClass getClass(Metas<Class<?>> meta) {
+            return new CompositeBuilder.ForClass(
+                delegates.stream().map(d -> d.getClass(meta)).collect(Collectors.toList()));
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Metas<Class<?>> meta) {
+            return merge(b -> b.getFields(meta), CompositeBuilder.ForContainer::new);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Metas<Class<?>> meta) {
+            return merge(b -> b.getGetters(meta), CompositeBuilder.ForContainer::new);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Metas<Class<?>> meta) {
+            return merge(b -> b.getConstructors(meta), CompositeBuilder.ForExecutable::new);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Metas<Class<?>> meta) {
+            return merge(b -> b.getMethods(meta), CompositeBuilder.ForExecutable::new);
+        }
+    }
+
+    class ForElement<DELEGATE extends MetadataBuilder.ForElement<E>, E extends AnnotatedElement>
+        extends Delegator<DELEGATE> implements MetadataBuilder.ForElement<E> {
+
+        ForElement(List<DELEGATE> delegates) {
+            super(delegates);
+        }
+
+        @Override
+        public Map<Scope, Annotation[]> getConstraintsByScope(Metas<E> meta) {
+            return CompositeBuilder.this.getConstraintsByScope(this, meta);
+        }
+
+        @Override
+        public final Annotation[] getDeclaredConstraints(Metas<E> meta) {
+            return delegates.stream().map(d -> d.getDeclaredConstraints(meta)).flatMap(Stream::of)
+                .toArray(Annotation[]::new);
+        }
+    }
+
+    class ForClass extends ForElement<MetadataBuilder.ForClass, Class<?>> implements MetadataBuilder.ForClass {
+
+        ForClass(List<MetadataBuilder.ForClass> delegates) {
+            super(delegates);
+        }
+
+        @Override
+        public List<Class<?>> getGroupSequence(Metas<Class<?>> meta) {
+            return CompositeBuilder.this.getGroupSequence(this, meta);
+        }
+    }
+
+    private class ForContainer<DELEGATE extends MetadataBuilder.ForContainer<E>, E extends AnnotatedElement>
+        extends CompositeBuilder.ForElement<DELEGATE, E> implements MetadataBuilder.ForContainer<E> {
+
+        ForContainer(List<DELEGATE> delegates) {
+            super(delegates);
+        }
+
+        @Override
+        public final boolean isCascade(Metas<E> meta) {
+            return delegates.stream().anyMatch(d -> d.isCascade(meta));
+        }
+
+        @Override
+        public final Set<GroupConversion> getGroupConversions(Metas<E> meta) {
+            return delegates.stream().map(d -> d.getGroupConversions(meta)).flatMap(Collection::stream)
+                .collect(ToUnmodifiable.set());
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public final Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+            Metas<E> meta) {
+            return merge(b -> b.getContainerElementTypes(meta), CompositeBuilder.ForContainer::new);
+        }
+    }
+
+    private class ForExecutable<DELEGATE extends MetadataBuilder.ForExecutable<E>, E extends Executable>
+        extends Delegator<DELEGATE> implements MetadataBuilder.ForExecutable<E> {
+
+        ForExecutable(List<DELEGATE> delegates) {
+            super(delegates);
+        }
+
+        @Override
+        public MetadataBuilder.ForContainer<E> getReturnValue(Metas<E> meta) {
+            return new CompositeBuilder.ForContainer<>(
+                delegates.stream().map(d -> d.getReturnValue(meta)).collect(Collectors.toList()));
+        }
+
+        @Override
+        public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Metas<E> meta) {
+            final List<List<MetadataBuilder.ForContainer<Parameter>>> parameterLists =
+                delegates.stream().map(d -> d.getParameters(meta)).collect(Collectors.toList());
+
+            final Set<Integer> parameterCounts = parameterLists.stream().map(List::size).collect(Collectors.toSet());
+            Validate.validState(parameterCounts.size() == 1, "Mismatched parameter counts: %s", parameterCounts);
+
+            return IntStream.range(0, parameterCounts.iterator().next().intValue())
+                .mapToObj(n -> new CompositeBuilder.ForContainer<>(parameterLists.get(n)))
+                .collect(ToUnmodifiable.list());
+        }
+
+        @Override
+        public MetadataBuilder.ForElement<E> getCrossParameter(Metas<E> meta) {
+            return new CompositeBuilder.ForElement<MetadataBuilder.ForElement<E>, E>(
+                delegates.stream().map(d -> d.getCrossParameter(meta)).collect(Collectors.toList()));
+        }
+    }
+
+    public static CompositeBuilder with(AnnotationBehaviorMergeStrategy annotationBehaviorStrategy) {
+        return new CompositeBuilder(annotationBehaviorStrategy);
+    }
+
+    private final AnnotationBehaviorMergeStrategy annotationBehaviorStrategy;
+
+    CompositeBuilder(AnnotationBehaviorMergeStrategy annotationBehaviorMergeStrategy) {
+        super();
+        this.annotationBehaviorStrategy =
+            Validate.notNull(annotationBehaviorMergeStrategy, "annotationBehaviorMergeStrategy");
+    }
+
+    public Collector<MetadataBuilder.ForBean, ?, MetadataBuilder.ForBean> compose() {
+        return Collectors.collectingAndThen(Collectors.toList(), CompositeBuilder.ForBean::new);
+    }
+
+    protected <E extends AnnotatedElement> Map<Scope, Annotation[]> getConstraintsByScope(
+        CompositeBuilder.ForElement<? extends MetadataBuilder.ForElement<E>, E> composite, Metas<E> meta) {
+        return Collections.singletonMap(Scope.LOCAL_ELEMENT, composite.getDeclaredConstraints(meta));
+    }
+
+    protected List<Class<?>> getGroupSequence(CompositeBuilder.ForClass composite, Metas<Class<?>> meta) {
+        final List<List<Class<?>>> groupSequence =
+            composite.delegates.stream().map(d -> d.getGroupSequence(meta)).collect(Collectors.toList());
+        Validate.validState(groupSequence.size() <= 1,
+            "group sequence returned from multiple composite class metadata builders");
+        return groupSequence.isEmpty() ? null : groupSequence.get(0);
+    }
+}


Mime
View raw message