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/27 23:45:15 UTC

[1/4] bval git commit: default method

Repository: bval
Updated Branches:
  refs/heads/bv2 0e874301a -> faec747db


default method


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

Branch: refs/heads/bv2
Commit: 09152bc18bfee450f9a8af783933f7ae692f3aa8
Parents: 0e87430
Author: Matt Benson <mb...@apache.org>
Authored: Mon Feb 26 13:31:39 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Mon Feb 26 13:31:39 2018 -0600

----------------------------------------------------------------------
 .../org/apache/bval/constraints/FrenchZipcodeValidator.java     | 5 -----
 1 file changed, 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/09152bc1/bval-jsr/src/test/java/org/apache/bval/constraints/FrenchZipcodeValidator.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/java/org/apache/bval/constraints/FrenchZipcodeValidator.java b/bval-jsr/src/test/java/org/apache/bval/constraints/FrenchZipcodeValidator.java
index 542882c..d6384f1 100644
--- a/bval-jsr/src/test/java/org/apache/bval/constraints/FrenchZipcodeValidator.java
+++ b/bval-jsr/src/test/java/org/apache/bval/constraints/FrenchZipcodeValidator.java
@@ -26,11 +26,6 @@ import javax.validation.ConstraintValidatorContext;
  */
 public class FrenchZipcodeValidator implements ConstraintValidator<FrenchZipCode, Object> {
     @Override
-    public void initialize(FrenchZipCode constraintAnnotation) {
-        // do nothing
-    }
-
-    @Override
     public boolean isValid(Object object, ConstraintValidatorContext validationContext) {
         return null != object;
     }


[4/4] bval git commit: initial BVal 2 implementation of method inheritance rules

Posted by mb...@apache.org.
initial BVal 2 implementation of method inheritance rules


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

Branch: refs/heads/bv2
Commit: faec747db9653ac9cce3c253ffa09a9d89d0adf7
Parents: f2acb64
Author: Matt Benson <mb...@apache.org>
Authored: Tue Feb 27 17:44:59 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Tue Feb 27 17:44:59 2018 -0600

----------------------------------------------------------------------
 .../bval/jsr/descriptor/DescriptorManager.java  |   7 +-
 .../bval/jsr/descriptor/MetadataReader.java     |  25 +-
 .../bval/jsr/metadata/CompositeBuilder.java     | 134 +++++++--
 .../bval/jsr/metadata/HierarchyBuilder.java     | 291 +++++++++++++++----
 .../org/apache/bval/jsr/metadata/Liskov.java    | 197 +++++++++++++
 .../java/org/apache/bval/jsr/metadata/Meta.java |  25 +-
 .../java/org/apache/bval/jsr/util/Methods.java  |  10 +
 7 files changed, 595 insertions(+), 94 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/faec747d/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java
index 01a1b5c..12c9a55 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java
@@ -68,9 +68,10 @@ public class DescriptorManager {
 
     private MetadataBuilder.ForBean builder(Class<?> beanClass) {
         final MetadataBuilder.ForBean primaryBuilder =
-            new HierarchyBuilder(reflectionBuilder::forBean).forBean(beanClass);
+            new HierarchyBuilder(validatorFactory, reflectionBuilder::forBean).forBean(beanClass);
 
-        final MetadataBuilder.ForBean customBuilder = new HierarchyBuilder(this::customBuilder).forBean(beanClass);
+        final MetadataBuilder.ForBean customBuilder =
+            new HierarchyBuilder(validatorFactory, this::customBuilder).forBean(beanClass);
 
         return customBuilder.isEmpty() ? primaryBuilder : DualBuilder.forBean(primaryBuilder, customBuilder);
     }
@@ -86,6 +87,6 @@ public class DescriptorManager {
             return customBuilders.get(0);
         }
         return customBuilders.stream()
-            .collect(CompositeBuilder.with(AnnotationBehaviorMergeStrategy.consensus()).compose());
+            .collect(CompositeBuilder.with(validatorFactory, AnnotationBehaviorMergeStrategy.consensus()).compose());
     }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/faec747d/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
index 77cf9fc..2e9a31f 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/MetadataReader.java
@@ -39,9 +39,12 @@ import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 import java.util.stream.Stream;
 
+import javax.validation.ConstraintDeclarationException;
 import javax.validation.GroupDefinitionException;
 import javax.validation.GroupSequence;
 import javax.validation.ParameterNameProvider;
+import javax.validation.Valid;
+import javax.validation.groups.ConvertGroup;
 import javax.validation.groups.Default;
 import javax.validation.metadata.PropertyDescriptor;
 import javax.validation.metadata.Scope;
@@ -100,10 +103,8 @@ class MetadataReader {
             });
 
             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);
-                });
+                final Method getter = Methods.getter(meta.getHost(), g);
+
                 Exceptions.raiseIf(getter == null, IllegalStateException::new,
                     "Getter method for property %s not found", g);
 
@@ -199,7 +200,21 @@ class MetadataReader {
         }
 
         Set<GroupConversion> getGroupConversions() {
-            return builder.getGroupConversions(meta);
+            final Set<GroupConversion> groupConversions = builder.getGroupConversions(meta);
+            Exceptions.raiseUnless(groupConversions.isEmpty() || isCascaded(), ConstraintDeclarationException::new,
+                "@%s declared without @%s on %s", ConvertGroup.class.getSimpleName(), Valid.class.getSimpleName(),
+                meta.describeHost());
+
+            Exceptions.raiseIf(
+                groupConversions.stream().map(GroupConversion::getFrom).distinct().count() < groupConversions.size(),
+                ConstraintDeclarationException::new, "%s has duplicate 'from' group conversions", meta.describeHost());
+
+            groupConversions.stream().map(GroupConversion::getFrom)
+                .forEach(f -> Exceptions.raiseIf(f.isAnnotationPresent(GroupSequence.class),
+                    ConstraintDeclarationException::new,
+                    "Invalid group conversion declared on %s from group sequence %s", meta.describeHost(), f));
+
+            return groupConversions;
         }
 
         Set<ContainerElementTypeD> getContainerElementTypes(CascadableContainerD<?, ?> parent) {

http://git-wip-us.apache.org/repos/asf/bval/blob/faec747d/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
index 3aed1d0..56dd1f8 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
@@ -30,17 +30,24 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.function.BiFunction;
 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.ElementKind;
+import javax.validation.ParameterNameProvider;
 import javax.validation.metadata.Scope;
 
+import org.apache.bval.jsr.ApacheValidatorFactory;
 import org.apache.bval.jsr.groups.GroupConversion;
+import org.apache.bval.jsr.util.Methods;
 import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Exceptions;
 import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
 
 public class CompositeBuilder {
 
@@ -60,14 +67,17 @@ public class CompositeBuilder {
         }
 
         <K, D> Map<K, D> merge(Function<DELEGATE, Map<K, D>> toMap, Function<List<D>, D> merge) {
+            return merge(toMap, (k, l) -> merge.apply(l));
+        }
+
+        <K, D> Map<K, D> merge(Function<DELEGATE, Map<K, D>> toMap, BiFunction<K, 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 mappedDelegates.size() == 1 ? mappedDelegates.get(0) : merge.apply(k, mappedDelegates);
             };
-
             return maps.stream().map(Map::keySet).flatMap(Collection::stream).distinct()
                 .collect(Collectors.toMap(Function.identity(), valueMapper));
         }
@@ -88,22 +98,34 @@ public class CompositeBuilder {
 
         @Override
         public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Meta<Class<?>> meta) {
-            return merge(b -> b.getFields(meta), CompositeBuilder.ForContainer::new);
+            return merge(b -> b.getFields(meta), (f, l) -> {
+                final Field fld = Reflection.find(meta.getHost(), t -> Reflection.getDeclaredField(t, f));
+                Exceptions.raiseIf(fld == null, IllegalStateException::new, "Could not find field %s of %s", f,
+                    meta.getHost());
+                return forContainer(l, new Meta.ForField(fld), ElementKind.PROPERTY);
+            });
         }
 
         @Override
         public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Meta<Class<?>> meta) {
-            return merge(b -> b.getGetters(meta), CompositeBuilder.ForContainer::new);
+            return merge(b -> b.getGetters(meta), (g, l) -> {
+                final Method getter = Methods.getter(meta.getHost(), g);
+                Exceptions.raiseIf(getter == null, IllegalStateException::new,
+                    "Could not find getter for property %s of %s", g, meta.getHost());
+                return forContainer(l, new Meta.ForMethod(getter), ElementKind.PROPERTY);
+            });
         }
 
         @Override
         public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Meta<Class<?>> meta) {
-            return merge(b -> b.getConstructors(meta), CompositeBuilder.ForExecutable::new);
+            return merge(b -> b.getConstructors(meta),
+                d -> new CompositeBuilder.ForExecutable<>(d, ParameterNameProvider::getParameterNames));
         }
 
         @Override
         public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Meta<Class<?>> meta) {
-            return merge(b -> b.getMethods(meta), CompositeBuilder.ForExecutable::new);
+            return merge(b -> b.getMethods(meta),
+                d -> new CompositeBuilder.ForExecutable<>(d, ParameterNameProvider::getParameterNames));
         }
     }
 
@@ -138,7 +160,7 @@ public class CompositeBuilder {
         }
     }
 
-    private class ForContainer<DELEGATE extends MetadataBuilder.ForContainer<E>, E extends AnnotatedElement>
+    class ForContainer<DELEGATE extends MetadataBuilder.ForContainer<E>, E extends AnnotatedElement>
         extends CompositeBuilder.ForElement<DELEGATE, E> implements MetadataBuilder.ForContainer<E> {
 
         ForContainer(List<DELEGATE> delegates) {
@@ -146,34 +168,68 @@ public class CompositeBuilder {
         }
 
         @Override
-        public final boolean isCascade(Meta<E> meta) {
+        public boolean isCascade(Meta<E> meta) {
             return delegates.stream().anyMatch(d -> d.isCascade(meta));
         }
 
         @Override
-        public final Set<GroupConversion> getGroupConversions(Meta<E> meta) {
+        public Set<GroupConversion> getGroupConversions(Meta<E> meta) {
             return delegates.stream().map(d -> d.getGroupConversions(meta)).flatMap(Collection::stream)
                 .collect(ToUnmodifiable.set());
         }
 
         @Override
-        public final Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+        public Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
             Meta<E> meta) {
-            return merge(b -> b.getContainerElementTypes(meta), CompositeBuilder.ForContainer::new);
+            return merge(b -> b.getContainerElementTypes(meta),
+                (k, l) -> forContainer(l, new Meta.ForContainerElement(meta, k), ElementKind.CONTAINER_ELEMENT));
         }
     }
 
-    private class ForExecutable<DELEGATE extends MetadataBuilder.ForExecutable<E>, E extends Executable>
+    class ForReturnValue<E extends Executable> implements MetadataBuilder.ForContainer<E> {
+        private final MetadataBuilder.ForContainer<E> delegate;
+
+        ForReturnValue(MetadataBuilder.ForContainer<E> delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        public Annotation[] getDeclaredConstraints(Meta<E> meta) {
+            return delegate.getDeclaredConstraints(meta);
+        }
+
+        @Override
+        public boolean isCascade(Meta<E> meta) {
+            return delegate.isCascade(meta);
+        }
+
+        @Override
+        public Set<GroupConversion> getGroupConversions(Meta<E> meta) {
+            return delegate.getGroupConversions(meta);
+        }
+
+        @Override
+        public Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+            Meta<E> meta) {
+            return delegate.getContainerElementTypes(meta);
+        }
+    }
+
+    class ForExecutable<DELEGATE extends MetadataBuilder.ForExecutable<E>, E extends Executable>
         extends Delegator<DELEGATE> implements MetadataBuilder.ForExecutable<E> {
 
-        ForExecutable(List<DELEGATE> delegates) {
+        private final BiFunction<ParameterNameProvider, E, List<String>> getParameterNames;
+
+        ForExecutable(List<DELEGATE> delegates, BiFunction<ParameterNameProvider, E, List<String>> getParameterNames) {
             super(delegates);
+            this.getParameterNames = Validate.notNull(getParameterNames, "getParameterNames");
         }
 
         @Override
-        public MetadataBuilder.ForContainer<E> getReturnValue(Meta<E> meta) {
-            return new CompositeBuilder.ForContainer<>(
-                delegates.stream().map(d -> d.getReturnValue(meta)).collect(Collectors.toList()));
+        public ForReturnValue<E> getReturnValue(Meta<E> meta) {
+            return new ForReturnValue<>(CompositeBuilder.this.forContainer(
+                delegates.stream().map(d -> d.getReturnValue(meta)).collect(Collectors.toList()), meta,
+                ElementKind.RETURN_VALUE));
         }
 
         @Override
@@ -185,35 +241,53 @@ public class CompositeBuilder {
             Validate.validState(parameterCounts.size() == 1, "Mismatched parameter counts: %s", parameterCounts);
 
             final int parameterCount = parameterCounts.iterator().next().intValue();
-            return IntStream.range(0, parameterCount)
-                .mapToObj(n -> new CompositeBuilder.ForContainer<>(
-                    parameterLists.stream().map(l -> l.get(n)).collect(Collectors.toList())))
-                .collect(ToUnmodifiable.list());
+            final List<Meta<Parameter>> metaParams = getMetaParameters(meta, getParameterNames);
+            return IntStream.range(0, parameterCount).mapToObj(n -> {
+                return forContainer(parameterLists.stream().map(l -> l.get(n)).collect(Collectors.toList()),
+                    metaParams.get(n), ElementKind.PARAMETER);
+            }).collect(ToUnmodifiable.list());
         }
 
         @Override
         public MetadataBuilder.ForElement<E> getCrossParameter(Meta<E> meta) {
-            return new CompositeBuilder.ForElement<MetadataBuilder.ForElement<E>, E>(
-                delegates.stream().map(d -> d.getCrossParameter(meta)).collect(Collectors.toList()));
+            return forCrossParameter(
+                delegates.stream().map(d -> d.getCrossParameter(meta)).collect(Collectors.toList()), meta);
         }
     }
 
-    public static CompositeBuilder with(AnnotationBehaviorMergeStrategy annotationBehaviorStrategy) {
-        return new CompositeBuilder(annotationBehaviorStrategy);
+    public static CompositeBuilder with(ApacheValidatorFactory validatorFactory,
+        AnnotationBehaviorMergeStrategy annotationBehaviorStrategy) {
+        return new CompositeBuilder(validatorFactory, annotationBehaviorStrategy);
     }
 
     private final AnnotationBehaviorMergeStrategy annotationBehaviorStrategy;
+    protected final ApacheValidatorFactory validatorFactory;
 
-    CompositeBuilder(AnnotationBehaviorMergeStrategy annotationBehaviorMergeStrategy) {
+    CompositeBuilder(ApacheValidatorFactory validatorFactory,
+        AnnotationBehaviorMergeStrategy annotationBehaviorMergeStrategy) {
         super();
         this.annotationBehaviorStrategy =
             Validate.notNull(annotationBehaviorMergeStrategy, "annotationBehaviorMergeStrategy");
+        this.validatorFactory = Validate.notNull(validatorFactory, "validatorFactory");
     }
 
     public Collector<MetadataBuilder.ForBean, ?, MetadataBuilder.ForBean> compose() {
         return Collectors.collectingAndThen(Collectors.toList(), CompositeBuilder.ForBean::new);
     }
 
+    protected final <E extends Executable> List<Meta<Parameter>> getMetaParameters(Meta<E> meta,
+        BiFunction<ParameterNameProvider, E, List<String>> getParameterNames) {
+        final Parameter[] parameters = meta.getHost().getParameters();
+        final List<String> parameterNames =
+            getParameterNames.apply(validatorFactory.getParameterNameProvider(), meta.getHost());
+
+        Exceptions.raiseUnless(parameterNames.size() == parameters.length, IllegalStateException::new,
+            "%s returned wrong number of parameter names", validatorFactory.getParameterNameProvider());
+
+        return IntStream.range(0, parameters.length)
+            .mapToObj(n -> new Meta.ForParameter(parameters[n], parameterNames.get(n))).collect(Collectors.toList());
+    }
+
     protected <E extends AnnotatedElement> Map<Scope, Annotation[]> getConstraintsByScope(
         CompositeBuilder.ForElement<? extends MetadataBuilder.ForElement<E>, E> composite, Meta<E> meta) {
         return Collections.singletonMap(Scope.LOCAL_ELEMENT, composite.getDeclaredConstraints(meta));
@@ -226,4 +300,14 @@ public class CompositeBuilder {
             "group sequence returned from multiple composite class metadata builders");
         return groupSequence.isEmpty() ? null : groupSequence.get(0);
     }
+
+    protected <DELEGATE extends MetadataBuilder.ForContainer<E>, E extends AnnotatedElement> MetadataBuilder.ForContainer<E> forContainer(
+        List<DELEGATE> delegates, Meta<E> meta, ElementKind elementKind) {
+        return new CompositeBuilder.ForContainer<>(delegates);
+    }
+
+    protected <DELEGATE extends MetadataBuilder.ForElement<E>, E extends Executable> MetadataBuilder.ForElement<E> forCrossParameter(
+        List<DELEGATE> delegates, Meta<E> meta) {
+        return new CompositeBuilder.ForElement<>(delegates);
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/faec747d/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
index 9cac057..a900501 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
@@ -18,21 +18,35 @@ 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.ArrayList;
 import java.util.Collections;
 import java.util.EnumMap;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
+import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
+import javax.validation.ElementKind;
+import javax.validation.ParameterNameProvider;
 import javax.validation.metadata.Scope;
 
+import org.apache.bval.jsr.ApacheValidatorFactory;
+import org.apache.bval.jsr.groups.GroupConversion;
+import org.apache.bval.jsr.util.Methods;
+import org.apache.bval.util.Exceptions;
 import org.apache.bval.util.Validate;
 import org.apache.bval.util.reflection.Reflection;
 import org.apache.bval.util.reflection.Reflection.Interfaces;
@@ -41,78 +55,217 @@ import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
 
 @Privilizing(@CallTo(Reflection.class))
 public class HierarchyBuilder extends CompositeBuilder {
-    private static abstract class HierarchyDelegate<T> {
+    static abstract class HierarchyDelegate<E extends AnnotatedElement, T> {
         final T delegate;
+        final Meta<E> hierarchyElement;
 
-        HierarchyDelegate(T delegate) {
+        HierarchyDelegate(T delegate, Meta<E> hierarchyElement) {
             super();
             this.delegate = Validate.notNull(delegate, "delegate");
+            this.hierarchyElement = Validate.notNull(hierarchyElement, "hierarchyElement");
         }
 
-        static class ForBean extends HierarchyDelegate<MetadataBuilder.ForBean> implements MetadataBuilder.ForBean {
-            final Meta<Class<?>> hierarchyType;
+        Meta<E> getHierarchyElement() {
+            return hierarchyElement;
+        }
+    }
 
-            ForBean(MetadataBuilder.ForBean delegate, Class<?> hierarchyType) {
-                super(delegate);
-                this.hierarchyType = new Meta.ForClass(hierarchyType);
-            }
+    static abstract class ElementDelegate<E extends AnnotatedElement, T extends MetadataBuilder.ForElement<E>>
+        extends HierarchyDelegate<E, T> implements MetadataBuilder.ForElement<E> {
 
-            @Override
-            public MetadataBuilder.ForClass getClass(Meta<Class<?>> meta) {
-                return new HierarchyDelegate.ForClass(delegate.getClass(hierarchyType), hierarchyType);
-            }
+        ElementDelegate(T delegate, Meta<E> hierarchyElement) {
+            super(delegate, hierarchyElement);
+        }
+        
+        Annotation[] getDeclaredConstraints() {
+            return delegate.getDeclaredConstraints(hierarchyElement);
+        }
 
-            @Override
-            public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Meta<Class<?>> meta) {
-                return delegate.getFields(hierarchyType);
-            }
+        @Override
+        public final Annotation[] getDeclaredConstraints(Meta<E> meta) {
+            return getDeclaredConstraints();
+        }
+    }
 
-            @Override
-            public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Meta<Class<?>> meta) {
-                return delegate.getGetters(hierarchyType);
-            }
+    private class BeanDelegate extends HierarchyDelegate<Class<?>, MetadataBuilder.ForBean>
+        implements MetadataBuilder.ForBean {
 
-            @SuppressWarnings("unlikely-arg-type")
-            @Override
-            public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Meta<Class<?>> meta) {
-                // suppress hierarchical ctors:
-                return hierarchyType.equals(meta.getHost()) ? delegate.getConstructors(hierarchyType)
-                    : Collections.emptyMap();
-            }
+        BeanDelegate(MetadataBuilder.ForBean delegate, Class<?> hierarchyType) {
+            super(delegate, new Meta.ForClass(hierarchyType));
+        }
 
-            @Override
-            public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Meta<Class<?>> meta) {
-                final Map<Signature, MetadataBuilder.ForExecutable<Method>> m = delegate.getMethods(hierarchyType);
+        @Override
+        public MetadataBuilder.ForClass getClass(Meta<Class<?>> meta) {
+            return new ClassDelegate(delegate.getClass(hierarchyElement), hierarchyElement);
+        }
 
-                return m;
-            }
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Meta<Class<?>> meta) {
+            return delegate.getFields(hierarchyElement);
+        }
+
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Meta<Class<?>> meta) {
+            // ignore hierarchical ctors:
+            return hierarchyElement.equals(meta) ? delegate.getConstructors(hierarchyElement) : Collections.emptyMap();
         }
 
-        static class ForClass extends HierarchyDelegate<MetadataBuilder.ForClass> implements MetadataBuilder.ForClass {
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Meta<Class<?>> meta) {
+            final Map<String, MetadataBuilder.ForContainer<Method>> getters = delegate.getGetters(hierarchyElement);
+            final Map<String, MetadataBuilder.ForContainer<Method>> result = new LinkedHashMap<>();
 
-            final Meta<Class<?>> hierarchyType;
+            getters.forEach((k, v) -> {
+                final Method getter = Methods.getter(hierarchyElement.getHost(), k);
 
-            ForClass(MetadataBuilder.ForClass delegate, Meta<Class<?>> hierarchyType) {
-                super(delegate);
-                this.hierarchyType = hierarchyType;
-            }
+                Exceptions.raiseIf(getter == null, IllegalStateException::new,
+                    "delegate builder specified unknown getter");
 
-            @Override
-            public Annotation[] getDeclaredConstraints(Meta<Class<?>> meta) {
-                return delegate.getDeclaredConstraints(hierarchyType);
+                result.put(k, new ContainerDelegate<Method>(v, new Meta.ForMethod(getter)));
+            });
+            return result;
+        }
+
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Meta<Class<?>> meta) {
+            final Map<Signature, MetadataBuilder.ForExecutable<Method>> methods = delegate.getMethods(hierarchyElement);
+            if (methods.isEmpty()) {
+                return methods;
             }
+            final Map<Signature, MetadataBuilder.ForExecutable<Method>> result = new LinkedHashMap<>();
+            methods
+                .forEach((k, v) -> result.put(k,
+                    new ExecutableDelegate<>(v, new Meta.ForMethod(
+                        Reflection.getDeclaredMethod(hierarchyElement.getHost(), k.getName(), k.getParameterTypes())),
+                        ParameterNameProvider::getParameterNames)));
+            return result;
+        }
+    }
+
+    private class ClassDelegate extends ElementDelegate<Class<?>, MetadataBuilder.ForClass>
+        implements MetadataBuilder.ForClass {
+
+        ClassDelegate(MetadataBuilder.ForClass delegate, Meta<Class<?>> hierarchyType) {
+            super(delegate, hierarchyType);
+        }
+
+        @Override
+        public List<Class<?>> getGroupSequence(Meta<Class<?>> meta) {
+            return delegate.getGroupSequence(hierarchyElement);
+        }
+    }
+
+    class ContainerDelegate<E extends AnnotatedElement> extends ElementDelegate<E, MetadataBuilder.ForContainer<E>>
+        implements MetadataBuilder.ForContainer<E> {
+
+        ContainerDelegate(MetadataBuilder.ForContainer<E> delegate, Meta<E> hierarchyElement) {
+            super(delegate, hierarchyElement);
+        }
+        
+        boolean isCascade() {
+            return delegate.isCascade(hierarchyElement);
+        }
+
+        @Override
+        public final boolean isCascade(Meta<E> meta) {
+            return isCascade();
+        }
+
+        Set<GroupConversion> getGroupConversions() {
+            return delegate.getGroupConversions(hierarchyElement);
+        }
+
+        @Override
+        public final Set<GroupConversion> getGroupConversions(Meta<E> meta) {
+            return getGroupConversions();
+        }
+
+        @Override
+        public Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+            Meta<E> meta) {
+            final Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> containerElementTypes =
+                delegate.getContainerElementTypes(hierarchyElement);
+
+            final Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> result = new LinkedHashMap<>();
 
-            @Override
-            public List<Class<?>> getGroupSequence(Meta<Class<?>> meta) {
-                return delegate.getGroupSequence(hierarchyType);
+            containerElementTypes.forEach((k, v) -> {
+                result.put(k, new ContainerDelegate<>(v, new Meta.ForContainerElement(hierarchyElement, k)));
+            });
+            return result;
+        }
+    }
+
+    private class ExecutableDelegate<E extends Executable>
+        extends HierarchyDelegate<E, MetadataBuilder.ForExecutable<E>> implements MetadataBuilder.ForExecutable<E> {
+
+        final BiFunction<ParameterNameProvider, E, List<String>> getParameterNames;
+
+        ExecutableDelegate(MetadataBuilder.ForExecutable<E> delegate, Meta<E> hierarchyElement,
+            BiFunction<ParameterNameProvider, E, List<String>> getParameterNames) {
+            super(delegate, hierarchyElement);
+            this.getParameterNames = Validate.notNull(getParameterNames, "getParameterNames");
+        }
+
+        @Override
+        public MetadataBuilder.ForContainer<E> getReturnValue(Meta<E> meta) {
+            return new ContainerDelegate<>(delegate.getReturnValue(hierarchyElement), hierarchyElement);
+        }
+
+        @Override
+        public MetadataBuilder.ForElement<E> getCrossParameter(Meta<E> meta) {
+            return new CrossParameterDelegate<>(delegate.getCrossParameter(hierarchyElement), hierarchyElement);
+        }
+
+        @Override
+        public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Meta<E> meta) {
+            final List<MetadataBuilder.ForContainer<Parameter>> parameterDelegates =
+                delegate.getParameters(hierarchyElement);
+
+            if (parameterDelegates.isEmpty()) {
+                return parameterDelegates;
             }
+            final List<Meta<Parameter>> metaParameters = getMetaParameters(hierarchyElement, getParameterNames);
+
+            Exceptions.raiseUnless(metaParameters.size() == parameterDelegates.size(), IllegalStateException::new,
+                "Got wrong number of parameter delegates for %s", meta.getHost());
+
+            return IntStream.range(0, parameterDelegates.size())
+                .mapToObj(n -> new ContainerDelegate<>(parameterDelegates.get(n), metaParameters.get(n)))
+                .collect(Collectors.toList());
+        }
+    }
+
+    private class CrossParameterDelegate<E extends Executable>
+        extends ElementDelegate<E, MetadataBuilder.ForElement<E>> implements MetadataBuilder.ForElement<E> {
+
+        CrossParameterDelegate(MetadataBuilder.ForElement<E> delegate, Meta<E> hierarchyElement) {
+            super(delegate, hierarchyElement);
+        }
+    }
+
+    private class ForCrossParameter<E extends Executable>
+        extends CompositeBuilder.ForElement<CrossParameterDelegate<E>, E> {
+
+        ForCrossParameter(List<CrossParameterDelegate<E>> delegates) {
+            super(delegates);
+            Liskov.validateCrossParameterHierarchy(delegates);
+        }
+    }
+
+    private class ForContainer<E extends AnnotatedElement>
+        extends CompositeBuilder.ForContainer<ContainerDelegate<E>, E> {
+
+        ForContainer(List<ContainerDelegate<E>> delegates, ElementKind elementKind) {
+            super(delegates);
+            Liskov.validateContainerHierarchy(delegates, Validate.notNull(elementKind, "elementKind"));
         }
     }
 
     private final Function<Class<?>, MetadataBuilder.ForBean> getBeanBuilder;
 
-    public HierarchyBuilder(Function<Class<?>, MetadataBuilder.ForBean> getBeanBuilder) {
-        super(AnnotationBehaviorMergeStrategy.first());
+    public HierarchyBuilder(ApacheValidatorFactory validatorFactory,
+        Function<Class<?>, MetadataBuilder.ForBean> getBeanBuilder) {
+        super(validatorFactory, AnnotationBehaviorMergeStrategy.first());
         this.getBeanBuilder = Validate.notNull(getBeanBuilder, "getBeanBuilder function was null");
     }
 
@@ -126,16 +279,13 @@ public class HierarchyBuilder extends CompositeBuilder {
          */
         delegates.add(Optional.of(beanClass).map(getBeanBuilder).orElseGet(() -> EmptyBuilder.instance().forBean()));
 
-        // iterate the hierarchy, skipping the first (i.e. beanClass handled
-        // above)
+        // iterate the hierarchy, skipping the first (i.e. beanClass handled above)
         final Iterator<Class<?>> hierarchy = Reflection.hierarchy(beanClass, Interfaces.INCLUDE).iterator();
         hierarchy.next();
 
-        // skip Object.class; skip null/empty hierarchy builders, mapping others
-        // to HierarchyDelegate
-        hierarchy
-            .forEachRemaining(t -> Optional.of(t).filter(Predicate.isEqual(Object.class).negate()).map(getBeanBuilder)
-                .filter(b -> !b.isEmpty()).map(b -> new HierarchyDelegate.ForBean(b, t)).ifPresent(delegates::add));
+        // skip Object.class; skip null/empty hierarchy builders, mapping others to BeanDelegate
+        hierarchy.forEachRemaining(t -> Optional.of(t).filter(Predicate.isEqual(Object.class).negate())
+            .map(getBeanBuilder).filter(b -> !b.isEmpty()).map(b -> new BeanDelegate(b, t)).ifPresent(delegates::add));
 
         // if we have nothing but empty builders (which should only happen for
         // absent custom metadata), return empty:
@@ -166,4 +316,39 @@ public class HierarchyBuilder extends CompositeBuilder {
     protected List<Class<?>> getGroupSequence(CompositeBuilder.ForClass composite, Meta<Class<?>> meta) {
         return composite.delegates.get(0).getGroupSequence(meta);
     }
+
+    @Override
+    protected <DELEGATE extends MetadataBuilder.ForContainer<E>, E extends AnnotatedElement> MetadataBuilder.ForContainer<E> forContainer(
+        List<DELEGATE> delegates, Meta<E> meta, ElementKind elementKind) {
+
+        if (delegates.isEmpty()) {
+            return super.forContainer(delegates, meta, elementKind);
+        }
+        final List<ContainerDelegate<E>> hierarchyDelegates = delegates.stream()
+            .<ContainerDelegate<E>> map(
+                d -> d instanceof ContainerDelegate<?> ? (ContainerDelegate<E>) d : new ContainerDelegate<>(d, meta))
+            .collect(Collectors.toList());
+
+        @SuppressWarnings("unchecked")
+        final CompositeBuilder.ForContainer<DELEGATE, E> result =
+            (CompositeBuilder.ForContainer<DELEGATE, E>) new HierarchyBuilder.ForContainer<E>(hierarchyDelegates,
+                elementKind);
+
+        return result;
+    }
+
+    @Override
+    protected <DELEGATE extends MetadataBuilder.ForElement<E>, E extends Executable> MetadataBuilder.ForElement<E> forCrossParameter(
+        List<DELEGATE> delegates, Meta<E> meta) {
+
+        if (delegates.isEmpty()) {
+            return super.forCrossParameter(delegates, meta);
+        }
+        final List<CrossParameterDelegate<E>> hierarchyDelegates =
+            delegates.stream()
+                .<CrossParameterDelegate<E>> map(d -> d instanceof CrossParameterDelegate<?>
+                    ? (CrossParameterDelegate<E>) d : new CrossParameterDelegate<>(d, meta))
+                .collect(Collectors.toList());
+        return new HierarchyBuilder.ForCrossParameter<>(hierarchyDelegates);
+    }
 }

http://git-wip-us.apache.org/repos/asf/bval/blob/faec747d/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Liskov.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Liskov.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Liskov.java
new file mode 100644
index 0000000..74bd8da
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Liskov.java
@@ -0,0 +1,197 @@
+/*
+ *  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.ElementType;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.ConstraintDeclarationException;
+import javax.validation.ElementKind;
+import javax.validation.Valid;
+
+import org.apache.bval.jsr.metadata.HierarchyBuilder.ContainerDelegate;
+import org.apache.bval.jsr.metadata.HierarchyBuilder.HierarchyDelegate;
+import org.apache.bval.jsr.metadata.HierarchyBuilder.ElementDelegate;
+import org.apache.bval.util.Exceptions;
+import org.apache.commons.lang3.Validate;
+
+class Liskov {
+    //@formatter:off
+    private enum ValidationElement {
+        constraints, cascades, groupConversions;
+    }
+
+    private enum StrengtheningIssue implements Predicate<Map<Meta<?>, Set<ValidationElement>>> {
+        overriddenHierarchy("overridden %s in inheritance hierarchy: %s") {
+
+            @Override
+            public boolean test(Map<Meta<?>, Set<ValidationElement>> detectedValidationElements) {
+                boolean lowestFound = false;
+
+                for (Set<ValidationElement> validated : detectedValidationElements.values()) {
+                    if (lowestFound) {
+                        return false;
+                    }
+                    lowestFound = !validated.isEmpty();
+                }
+                return true;
+            }
+        },
+        unrelatedInheritance("declared %s in unrelated inheritance hierarchies: %s") {
+
+            @Override
+            public boolean test(Map<Meta<?>, Set<ValidationElement>> detectedValidationElements) {
+                final Set<Class<?>> interfaces = detectedValidationElements.keySet().stream().map(Meta::getDeclaringClass)
+                        .filter(Class::isInterface).collect(Collectors.toSet());
+                if (interfaces.isEmpty()) {
+                    return true;
+                }
+                final boolean allRelated =
+                    detectedValidationElements.keySet().stream().map(Meta::getDeclaringClass).allMatch(ifc -> interfaces
+                        .stream().filter(Predicate.isEqual(ifc).negate()).allMatch(ifc2 -> related(ifc, ifc2)));
+
+                return allRelated;
+            }
+        };
+        //@formatter:on
+
+        final String format;
+
+        private StrengtheningIssue(String format) {
+            this.format = "Illegal strengthening: " + format;
+        }
+
+        Supplier<String> messageFor(Map<Meta<?>, Set<ValidationElement>> detectedValidationElements) {
+            return () -> {
+                final Set<ValidationElement> validationElements = detectedValidationElements.values().stream()
+                    .flatMap(Collection::stream).collect(Collectors.toSet());
+
+                final String describeHierarchy = detectedValidationElements.keySet().stream().map(Meta::describeHost)
+                    .collect(Collectors.joining(", ", "[", "]"));
+
+                return String.format(format, validationElements, describeHierarchy);
+            };
+        }
+
+        void check(Map<Meta<?>, Set<ValidationElement>> detectedValidationElements) {
+            Exceptions.raiseUnless(test(detectedValidationElements), ConstraintDeclarationException::new,
+                messageFor(detectedValidationElements));
+        }
+    }
+
+    static void validateContainerHierarchy(List<? extends ContainerDelegate<?>> delegates, ElementKind elementKind) {
+        if (Validate.notNull(delegates, "delegates").isEmpty()) {
+            return;
+        }
+        if (elementKind == ElementKind.CONTAINER_ELEMENT) {
+            elementKind = getContainer(delegates.get(0).getHierarchyElement());
+        }
+        switch (Validate.notNull(elementKind, "elementKind")) {
+        case PROPERTY:
+            break;
+        case RETURN_VALUE:
+            noRedeclarationOfReturnValueCascading(delegates);
+            break;
+        case PARAMETER:
+            noStrengtheningOfPreconditions(delegates, detectConstraints(), detectCascading(), detectGroupConversion());
+            break;
+        default:
+            Exceptions.raise(IllegalArgumentException::new, "Cannot validate %s.%s as %s",
+                ElementKind.class.getSimpleName(), elementKind, ContainerDelegate.class.getSimpleName());
+        }
+    }
+
+    static void validateCrossParameterHierarchy(List<? extends ElementDelegate<?, ?>> delegates) {
+        if (Validate.notNull(delegates, "delegates").isEmpty()) {
+            return;
+        }
+        noStrengtheningOfPreconditions(delegates, detectConstraints());
+    }
+
+    private static ElementKind getContainer(Meta<?> meta) {
+        Meta<?> m = meta;
+        while (m.getElementType() == ElementType.TYPE_USE) {
+            m = ((Meta.ForContainerElement) m).getParent();
+        }
+        switch (m.getElementType()) {
+        case METHOD:
+            return ElementKind.RETURN_VALUE;
+        case PARAMETER:
+            return ElementKind.PARAMETER;
+        default:
+            return ElementKind.PROPERTY;
+        }
+    }
+
+    private static void noRedeclarationOfReturnValueCascading(List<? extends ContainerDelegate<?>> delegates) {
+        final Set<Meta<?>> markedForCascade = delegates.stream().filter(ContainerDelegate::isCascade)
+            .map(HierarchyDelegate::getHierarchyElement).collect(Collectors.toCollection(LinkedHashSet::new));
+
+        Exceptions.raiseIf(markedForCascade.size() > 1, ConstraintDeclarationException::new,
+            "Multiple return values marked @%s in same hierarchy: %s", Valid.class.getSimpleName(), markedForCascade);
+    }
+
+    @SafeVarargs
+    private static <D extends ElementDelegate<?, ?>> void noStrengtheningOfPreconditions(List<? extends D> delegates,
+        Function<? super D, ValidationElement>... detectors) {
+
+        final Map<Meta<?>, Set<ValidationElement>> detectedValidationElements = new LinkedHashMap<>();
+        delegates.forEach(d -> {
+            detectedValidationElements.put(d.getHierarchyElement(),
+                Stream.of(detectors).map(dt -> dt.apply(d)).filter(Objects::nonNull)
+                    .collect(Collectors.toCollection(() -> EnumSet.noneOf(ValidationElement.class))));
+        });
+        if (detectedValidationElements.values().stream().allMatch(Collection::isEmpty)) {
+            // nothing declared
+            return;
+        }
+        for (StrengtheningIssue s : StrengtheningIssue.values()) {
+            s.check(detectedValidationElements);
+        }
+    }
+
+    private static boolean related(Class<?> c1, Class<?> c2) {
+        return c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1);
+    }
+
+    private static Function<ElementDelegate<?, ?>, ValidationElement> detectConstraints() {
+        return d -> d.getDeclaredConstraints().length > 0 ? ValidationElement.constraints : null;
+    }
+
+    private static Function<ContainerDelegate<?>, ValidationElement> detectCascading() {
+        return d -> d.isCascade() ? ValidationElement.cascades : null;
+    }
+
+    private static Function<ContainerDelegate<?>, ValidationElement> detectGroupConversion() {
+        return d -> d.getGroupConversions().isEmpty() ? null : ValidationElement.groupConversions;
+    }
+
+    private Liskov() {
+    }
+}

http://git-wip-us.apache.org/repos/asf/bval/blob/faec747d/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Meta.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Meta.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Meta.java
index dbcaeeb..ece48b0 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Meta.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Meta.java
@@ -196,8 +196,8 @@ public abstract class Meta<E extends AnnotatedElement> {
         }
 
         @Override
-        public String toString() {
-            return String.format("%s(%s of %s)", getStringPrefix(), getName(), getHost());
+        public String describeHost() {
+            return String.format("%s of %s", getName(), getHost());
         }
     }
 
@@ -233,6 +233,11 @@ public abstract class Meta<E extends AnnotatedElement> {
         public String getName() {
             return name;
         }
+
+        @Override
+        public String describeHost() {
+            return String.format("%s of %s", getName(), getHost().getDeclaringExecutable());
+        }
     }
 
     public static class ForContainerElement extends Meta<AnnotatedType> {
@@ -271,8 +276,12 @@ public abstract class Meta<E extends AnnotatedElement> {
         }
 
         @Override
-        public String toString() {
-            return String.format("%s(%s of %s)", getStringPrefix(), key, getHost());
+        public String describeHost() {
+            return String.format("%s of %s", key, parent);
+        }
+
+        public Meta<?> getParent() {
+            return parent;
         }
     }
 
@@ -311,12 +320,12 @@ public abstract class Meta<E extends AnnotatedElement> {
     }
 
     @Override
-    public String toString() {
-        return String.format("%s(%s)", getStringPrefix(), host);
+    public final String toString() {
+        return String.format("%s.%s(%s)", Meta.class.getSimpleName(), getClass().getSimpleName(), describeHost());
     }
 
-    protected String getStringPrefix() {
-        return Meta.class.getSimpleName() + '.' + getClass().getSimpleName();
+    public String describeHost() {
+        return host.toString();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/bval/blob/faec747d/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java
index 9f98311..4b5ce45 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java
@@ -18,9 +18,14 @@ package org.apache.bval.jsr.util;
 
 import java.beans.Introspector;
 import java.lang.reflect.Method;
+import java.util.stream.Stream;
 
 import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
 
+@Privilizing(@CallTo(Reflection.class))
 public final class Methods {
     public static boolean isGetter(Method m) {
         if (m.getParameterCount() > 0) {
@@ -40,6 +45,11 @@ public final class Methods {
         return Introspector.decapitalize(suffix);
     }
 
+    public static Method getter(Class<?> clazz, String property) {
+        return Reflection.find(clazz, t -> Stream.of(Reflection.getDeclaredMethods(t)).filter(Methods::isGetter)
+            .filter(m -> property.equals(Methods.propertyName(m))).findFirst().orElse(null));
+    }
+
     private Methods() {
     }
 }


[3/4] bval git commit: cleanup

Posted by mb...@apache.org.
cleanup


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

Branch: refs/heads/bv2
Commit: f2acb643662ebace2e5ffd371ffbace8140c424d
Parents: fe4c1d6
Author: Matt Benson <mb...@apache.org>
Authored: Tue Feb 27 17:43:57 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Tue Feb 27 17:43:57 2018 -0600

----------------------------------------------------------------------
 .../main/java/org/apache/bval/util/reflection/Reflection.java | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/f2acb643/bval-jsr/src/main/java/org/apache/bval/util/reflection/Reflection.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/util/reflection/Reflection.java b/bval-jsr/src/main/java/org/apache/bval/util/reflection/Reflection.java
index 33ff739..2c8b7b6 100644
--- a/bval-jsr/src/main/java/org/apache/bval/util/reflection/Reflection.java
+++ b/bval-jsr/src/main/java/org/apache/bval/util/reflection/Reflection.java
@@ -379,10 +379,7 @@ public class Reflection {
             @Override
             public Iterator<Class<?>> iterator() {
                 return new Iterator<Class<?>>() {
-                    Optional<Class<?>> next;
-                    {
-                        next = Optional.of(type);
-                    }
+                    Optional<Class<?>> next = Optional.of(type);
 
                     @Override
                     public boolean hasNext() {
@@ -412,7 +409,7 @@ public class Reflection {
             public Iterator<Class<?>> iterator() {
                 final Set<Class<?>> seenInterfaces = new HashSet<Class<?>>();
                 final Iterator<Class<?>> wrapped = classes.iterator();
-    
+
                 return new Iterator<Class<?>>() {
                     Iterator<Class<?>> interfaces = Collections.emptyIterator();
 


[2/4] bval git commit: ws

Posted by mb...@apache.org.
ws


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

Branch: refs/heads/bv2
Commit: fe4c1d6ce8b583b3b24ea53e7c2e0d19219aaf4c
Parents: 09152bc
Author: Matt Benson <mb...@apache.org>
Authored: Mon Feb 26 13:31:51 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Mon Feb 26 13:31:51 2018 -0600

----------------------------------------------------------------------
 .../src/main/java/org/apache/bval/util/reflection/Reflection.java  | 2 --
 1 file changed, 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bval/blob/fe4c1d6c/bval-jsr/src/main/java/org/apache/bval/util/reflection/Reflection.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/util/reflection/Reflection.java b/bval-jsr/src/main/java/org/apache/bval/util/reflection/Reflection.java
index 79e88fb..33ff739 100644
--- a/bval-jsr/src/main/java/org/apache/bval/util/reflection/Reflection.java
+++ b/bval-jsr/src/main/java/org/apache/bval/util/reflection/Reflection.java
@@ -212,11 +212,9 @@ public class Reflection {
         if (loader == null) {
             loader = Thread.currentThread().getContextClassLoader();
         }
-
         return Class.forName(className, resolve, loader);
     }
 
-
     /**
      * Convenient point for {@link Privilizing} {@link System#getProperty(String)}.
      * @param name