use runtime type to find valueExtractor for container element keys
Project: http://git-wip-us.apache.org/repos/asf/bval/repo Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/f69d9b97 Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/f69d9b97 Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/f69d9b97 Branch: refs/heads/bv2 Commit: f69d9b97398866f7b0348b52319b73b55da022ad Parents: 85ea6c6 Author: Matt Benson <[email protected]> Authored: Tue Mar 27 17:58:23 2018 -0500 Committer: Matt Benson <[email protected]> Committed: Tue Mar 27 17:58:23 2018 -0500 ---------------------------------------------------------------------- .../jsr/descriptor/ContainerElementTypeD.java | 44 +++++++++++++++++++- .../jsr/valueextraction/ValueExtractors.java | 5 ++- 2 files changed, 46 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/bval/blob/f69d9b97/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ContainerElementTypeD.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ContainerElementTypeD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ContainerElementTypeD.java index ef00edc..fc97df8 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ContainerElementTypeD.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ContainerElementTypeD.java @@ -19,9 +19,13 @@ package org.apache.bval.jsr.descriptor; import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.Map; import java.util.stream.Stream; import javax.validation.ConstraintDeclarationException; +import javax.validation.ValidationException; import javax.validation.metadata.ContainerElementTypeDescriptor; import javax.validation.valueextraction.ValueExtractor; @@ -29,11 +33,19 @@ import org.apache.bval.jsr.GraphContext; import org.apache.bval.jsr.metadata.ContainerElementKey; import org.apache.bval.jsr.valueextraction.ExtractValues; import org.apache.bval.util.Exceptions; +import org.apache.bval.util.ObjectUtils; import org.apache.bval.util.Validate; +import org.apache.bval.util.reflection.TypeUtils; public class ContainerElementTypeD extends CascadableContainerD<CascadableContainerD<?, ?>, AnnotatedType> implements ContainerElementTypeDescriptor { + private static ContainerElementKey toContainerElementKey(TypeVariable<?> var) { + final Class<?> container = (Class<?>) var.getGenericDeclaration(); + final int argIndex = ObjectUtils.indexOf(container.getTypeParameters(), var); + return new ContainerElementKey(container, Integer.valueOf(argIndex)); + } + private final ContainerElementKey key; ContainerElementTypeD(ContainerElementKey key, MetadataReader.ForContainer<AnnotatedType> reader, @@ -58,11 +70,41 @@ public class ContainerElementTypeD extends CascadableContainerD<CascadableContai @Override protected Stream<GraphContext> readImpl(GraphContext context) throws Exception { - final ValueExtractor<?> valueExtractor = context.getValidatorContext().getValueExtractors().find(key); + final ContainerElementKey runtimeKey = runtimeKey(context.getValue()); + final ValueExtractor<?> valueExtractor = + context.getValidatorContext().getValueExtractors().find(runtimeKey); + if (valueExtractor == null) { Exceptions.raise(ConstraintDeclarationException::new, "No %s found for %s", ValueExtractor.class.getSimpleName(), key); } return ExtractValues.extract(context, key, valueExtractor).stream(); } + + private ContainerElementKey runtimeKey(Object value) { + final Class<?> containerClass = key.getContainerClass(); + final Class<? extends Object> runtimeType = value.getClass(); + if (!runtimeType.equals(containerClass)) { + Exceptions.raiseUnless(containerClass.isAssignableFrom(runtimeType), ValidationException::new, + "Value %s is not assignment-compatible with %s", value, containerClass); + + if (key.getTypeArgumentIndex() == null) { + return new ContainerElementKey(runtimeType, null); + } + final Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(runtimeType, containerClass); + + Type type = typeArguments.get(containerClass.getTypeParameters()[key.getTypeArgumentIndex().intValue()]); + + while (type instanceof TypeVariable<?>) { + final TypeVariable<?> var = (TypeVariable<?>) type; + final Type nextType = typeArguments.get(var); + if (nextType instanceof TypeVariable<?>) { + type = nextType; + } else { + return toContainerElementKey(var); + } + } + } + return key; + } } http://git-wip-us.apache.org/repos/asf/bval/blob/f69d9b97/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java index ca45701..2ebc2c7 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java @@ -22,6 +22,7 @@ import java.lang.reflect.Type; import java.lang.reflect.WildcardType; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; import java.util.Properties; @@ -223,8 +224,8 @@ public class ValueExtractors { return null; } final Map<ContainerElementKey, ValueExtractor<?>> candidateMap = - assignableKeys.stream().filter(allValueExtractors::containsKey) - .collect(Collectors.toMap(Function.identity(), allValueExtractors::get)); + assignableKeys.stream().filter(allValueExtractors::containsKey).collect( + Collectors.toMap(Function.identity(), allValueExtractors::get, (quid, quo) -> quo, LinkedHashMap::new)); if (candidateMap.isEmpty()) { return null;
