handle multiple methods with same signature per JLS 8
Project: http://git-wip-us.apache.org/repos/asf/bval/repo Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/65de7121 Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/65de7121 Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/65de7121 Branch: refs/heads/bv2 Commit: 65de71211d0ad8ff4be8b09402a00cc35b26e0f3 Parents: 7ea28b2 Author: Matt Benson <[email protected]> Authored: Tue Mar 27 15:03:57 2018 -0500 Committer: Matt Benson <[email protected]> Committed: Tue Mar 27 15:03:57 2018 -0500 ---------------------------------------------------------------------- .../bval/jsr/metadata/ReflectionBuilder.java | 94 +++++++++++++++++--- .../org/apache/bval/jsr/metadata/Signature.java | 13 ++- 2 files changed, 93 insertions(+), 14 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/bval/blob/65de7121/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ReflectionBuilder.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ReflectionBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ReflectionBuilder.java index 338e1d2..df8319b 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ReflectionBuilder.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ReflectionBuilder.java @@ -30,12 +30,15 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; +import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.TreeMap; -import java.util.function.Function; +import java.util.function.BiFunction; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -43,6 +46,7 @@ import java.util.stream.Stream; import javax.validation.ConstraintDeclarationException; import javax.validation.ConstraintTarget; import javax.validation.GroupSequence; +import javax.validation.ParameterNameProvider; import javax.validation.Valid; import javax.validation.constraintvalidation.ValidationTarget; import javax.validation.groups.ConvertGroup; @@ -54,6 +58,7 @@ import org.apache.bval.jsr.util.AnnotationsManager; import org.apache.bval.jsr.util.Methods; import org.apache.bval.jsr.util.ToUnmodifiable; import org.apache.bval.util.Exceptions; +import org.apache.bval.util.Lazy; import org.apache.bval.util.ObjectUtils; import org.apache.bval.util.Validate; import org.apache.bval.util.reflection.Reflection; @@ -82,15 +87,42 @@ public class ReflectionBuilder { if (declaredFields.length == 0) { return Collections.emptyMap(); } - return Stream.of(declaredFields).filter(f -> !Modifier.isStatic(f.getModifiers())).collect( - Collectors.toMap(Field::getName, f -> new ReflectionBuilder.ForContainer<>(new Meta.ForField(f)))); + return Stream.of(declaredFields).filter(f -> !(Modifier.isStatic(f.getModifiers()) || f.isSynthetic())) + .collect( + Collectors.toMap(Field::getName, f -> new ReflectionBuilder.ForContainer<>(new Meta.ForField(f)))); } @Override public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Meta<Class<T>> ignored) { - return Stream.of(Reflection.getDeclaredMethods(meta.getHost())).filter(Methods::isGetter) - .collect(ToUnmodifiable.map(Methods::propertyName, - g -> new ReflectionBuilder.ForContainer<>(new Meta.ForMethod(g)))); + final Method[] declaredMethods = Reflection.getDeclaredMethods(meta.getHost()); + if (declaredMethods.length == 0) { + return Collections.emptyMap(); + } + final Map<String, Set<Method>> getters = new HashMap<>(); + for (Method m : declaredMethods) { + if (Methods.isGetter(m)) { + getters.computeIfAbsent(Methods.propertyName(m), k -> new LinkedHashSet<>()).add(m); + } + } + final Map<String, MetadataBuilder.ForContainer<Method>> result = new TreeMap<>(); + getters.forEach((k, methods) -> { + if ("class".equals(k)) { + return; + } + final List<MetadataBuilder.ForContainer<Method>> delegates = methods.stream() + .map(g -> new ReflectionBuilder.ForContainer<>(new Meta.ForMethod(g))).collect(Collectors.toList()); + if (delegates.isEmpty()) { + return; + } + final MetadataBuilder.ForContainer<Method> builder; + if (delegates.size() == 1) { + builder = delegates.get(0); + } else { + builder = compositeBuilder.get().new ForContainer<>(delegates); + } + result.put(k, builder); + }); + return result; } @Override @@ -101,7 +133,7 @@ public class ReflectionBuilder { } return Stream.of(declaredConstructors).collect( Collectors.toMap(Signature::of, c -> new ReflectionBuilder.ForExecutable<>(new Meta.ForConstructor<>(c), - validatorFactory.getParameterNameProvider()::getParameterNames))); + ParameterNameProvider::getParameterNames))); } @Override @@ -110,10 +142,33 @@ public class ReflectionBuilder { if (declaredMethods.length == 0) { return Collections.emptyMap(); } + final Map<Signature, Set<Method>> methodsBySignature = new HashMap<>(); + for (Method m : declaredMethods) { + if (!Modifier.isStatic(m.getModifiers())) { + methodsBySignature.computeIfAbsent(Signature.of(m), k -> new LinkedHashSet<>()).add(m); + } + } + final Map<Signature, MetadataBuilder.ForExecutable<Method>> result = new TreeMap<>(); + // we can't filter the getters since they can be validated, todo: read the config to know if we need or not - return Stream.of(declaredMethods).collect( - Collectors.toMap(Signature::of, m -> new ReflectionBuilder.ForExecutable<>(new Meta.ForMethod(m), - validatorFactory.getParameterNameProvider()::getParameterNames))); + + methodsBySignature.forEach((sig, methods) -> { + final List<MetadataBuilder.ForExecutable<Method>> delegates = + methods.stream().map(g -> new ReflectionBuilder.ForExecutable<>(new Meta.ForMethod(g), + ParameterNameProvider::getParameterNames)).collect(Collectors.toList()); + if (delegates.isEmpty()) { + return; + } + final MetadataBuilder.ForExecutable<Method> builder; + if (delegates.size() == 1) { + builder = delegates.get(0); + } else { + builder = compositeBuilder.get().new ForExecutable<MetadataBuilder.ForExecutable<Method>, Method>( + delegates, ParameterNameProvider::getParameterNames); + } + result.put(sig, builder); + }); + return result; } } @@ -129,6 +184,16 @@ public class ReflectionBuilder { public Annotation[] getDeclaredConstraints(Meta<E> ignored) { return AnnotationsManager.getDeclaredConstraints(meta); } + + @Override + public boolean equals(Object obj) { + return obj == this || this.getClass().isInstance(obj) && ((ForElement<?>) obj).meta.equals(meta); + } + + @Override + public int hashCode() { + return Objects.hash(getClass(), meta); + } } private class ForClass<T> extends ForElement<Class<T>> implements MetadataBuilder.ForClass<T> { @@ -186,9 +251,9 @@ public class ReflectionBuilder { private class ForExecutable<E extends Executable> implements MetadataBuilder.ForExecutable<E> { final Meta<E> meta; - final Function<E, List<String>> getParameterNames; + final BiFunction<ParameterNameProvider, E, List<String>> getParameterNames; - ForExecutable(Meta<E> meta, Function<E, List<String>> getParameterNames) { + ForExecutable(Meta<E> meta, BiFunction<ParameterNameProvider,E, List<String>> getParameterNames) { super(); this.meta = Validate.notNull(meta, "meta"); this.getParameterNames = Validate.notNull(getParameterNames, "getParameterNames"); @@ -200,7 +265,7 @@ public class ReflectionBuilder { if (parameters.length == 0) { return Collections.emptyList(); } - final List<String> parameterNames = getParameterNames.apply(meta.getHost()); + final List<String> parameterNames = getParameterNames.apply(validatorFactory.getParameterNameProvider(),meta.getHost()); return IntStream.range(0, parameters.length).mapToObj( n -> new ReflectionBuilder.ForContainer<>(new Meta.ForParameter(parameters[n], parameterNames.get(n)))) @@ -296,10 +361,13 @@ public class ReflectionBuilder { } private final ApacheValidatorFactory validatorFactory; + private final Lazy<CompositeBuilder> compositeBuilder; public ReflectionBuilder(ApacheValidatorFactory validatorFactory) { super(); this.validatorFactory = Validate.notNull(validatorFactory, "validatorFactory"); + this.compositeBuilder = + new Lazy<>(() -> new CompositeBuilder(this.validatorFactory, x -> AnnotationBehavior.ABSTAIN)); } public <T> MetadataBuilder.ForBean<T> forBean(Class<T> beanClass) { http://git-wip-us.apache.org/repos/asf/bval/blob/65de7121/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Signature.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Signature.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Signature.java index 8def7ae..2f28a3e 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Signature.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Signature.java @@ -18,17 +18,23 @@ package org.apache.bval.jsr.metadata; import java.lang.reflect.Executable; import java.util.Arrays; +import java.util.Comparator; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.bval.util.Comparators; import org.apache.bval.util.Lazy; import org.apache.bval.util.LazyInt; import org.apache.bval.util.StringUtils; import org.apache.bval.util.Validate; -public final class Signature { +public final class Signature implements Comparable<Signature> { + private static final Comparator<Signature> COMPARATOR = Comparator.nullsFirst(Comparator + .comparing(Signature::getName).thenComparing(Comparator.comparing(s -> Arrays.asList(s.getParameterTypes()), + Comparators.comparingIterables(Comparator.comparing(Class::getName))))); + public static Signature of(Executable x) { return new Signature(x.getName(), x.getParameterTypes()); } @@ -72,4 +78,9 @@ public final class Signature { public String toString() { return toString.get(); } + + @Override + public int compareTo(Signature sig) { + return COMPARATOR.compare(this, sig); + } }
