value extraction work
Project: http://git-wip-us.apache.org/repos/asf/bval/repo Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/4dbe0936 Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/4dbe0936 Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/4dbe0936 Branch: refs/heads/bv2 Commit: 4dbe0936bcd2117e0f82c4495968c9d6434edb9e Parents: 06f831d Author: Matt Benson <[email protected]> Authored: Thu Mar 29 16:55:47 2018 -0500 Committer: Matt Benson <[email protected]> Committed: Thu Mar 29 16:55:47 2018 -0500 ---------------------------------------------------------------------- .../java/org/apache/bval/jsr/GraphContext.java | 39 ++++- .../jsr/descriptor/CascadableContainerD.java | 20 --- .../jsr/descriptor/ContainerElementTypeD.java | 58 ------- .../apache/bval/jsr/descriptor/PropertyD.java | 21 ++- .../jsr/job/ConstraintValidatorContextImpl.java | 2 +- .../org/apache/bval/jsr/job/ValidateBean.java | 4 +- .../apache/bval/jsr/job/ValidateParameters.java | 3 +- .../apache/bval/jsr/job/ValidateProperty.java | 3 +- .../bval/jsr/job/ValidateReturnValue.java | 4 +- .../org/apache/bval/jsr/job/ValidationJob.java | 152 ++++++++++--------- .../bval/jsr/metadata/ContainerElementKey.java | 12 +- .../java/org/apache/bval/jsr/util/NodeImpl.java | 27 +++- .../java/org/apache/bval/jsr/util/PathImpl.java | 3 + .../bval/jsr/valueextraction/ExtractValues.java | 9 +- .../jsr/valueextraction/ValueExtractors.java | 121 ++++++++++++--- 15 files changed, 275 insertions(+), 203 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/bval/blob/4dbe0936/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java b/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java index 26350d6..3d40e75 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java @@ -18,11 +18,19 @@ */ package org.apache.bval.jsr; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.Map; + import javax.validation.Path; +import javax.validation.ValidationException; +import org.apache.bval.jsr.metadata.ContainerElementKey; import org.apache.bval.jsr.util.NodeImpl; import org.apache.bval.jsr.util.PathImpl; +import org.apache.bval.util.Exceptions; import org.apache.bval.util.Validate; +import org.apache.bval.util.reflection.TypeUtils; public class GraphContext { @@ -64,9 +72,9 @@ public class GraphContext { public GraphContext child(Path p, Object value) { Validate.notNull(p, "Path"); - final PathImpl impl = PathImpl.copy(p); + final PathImpl impl = PathImpl.of(p); Validate.isTrue(impl.isSubPathOf(path), "%s is not a subpath of %s", p, path); - return new GraphContext(validatorContext, impl, value, this); + return new GraphContext(validatorContext, impl == p ? PathImpl.copy(impl) : impl, value, this); } public boolean isRoot() { @@ -92,4 +100,31 @@ public class GraphContext { public String toString() { return String.format("%s: %s at '%s'", getClass().getSimpleName(), value, path); } + + public ContainerElementKey runtimeKey(ContainerElementKey key) { + 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 ContainerElementKey.forTypeVariable(var); + } + } + } + return key; + } } http://git-wip-us.apache.org/repos/asf/bval/blob/4dbe0936/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java index a4e05fd..6894e34 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java @@ -20,18 +20,14 @@ package org.apache.bval.jsr.descriptor; import java.lang.reflect.AnnotatedElement; import java.util.Set; -import java.util.stream.Stream; -import javax.validation.ValidationException; import javax.validation.metadata.CascadableDescriptor; import javax.validation.metadata.ContainerDescriptor; import javax.validation.metadata.ContainerElementTypeDescriptor; import javax.validation.metadata.GroupConversionDescriptor; -import org.apache.bval.jsr.GraphContext; import org.apache.bval.jsr.groups.GroupConversion; import org.apache.bval.jsr.util.ToUnmodifiable; -import org.apache.bval.util.Validate; import org.apache.bval.util.reflection.TypeUtils; public abstract class CascadableContainerD<P extends ElementD<?, ?>, E extends AnnotatedElement> extends @@ -69,20 +65,4 @@ public abstract class CascadableContainerD<P extends ElementD<?, ?>, E extends A return containerElementTypes.stream().filter(DescriptorManager::isConstrained) .collect(ToUnmodifiable.set()); } - - public final Stream<GraphContext> read(GraphContext context) { - Validate.notNull(context); - if (context.getValue() == null) { - return Stream.empty(); - } - try { - return readImpl(context); - } catch (Exception e) { - throw e instanceof ValidationException ? (ValidationException) e : new ValidationException(e); - } - } - - protected Stream<GraphContext> readImpl(GraphContext context) throws Exception { - throw new UnsupportedOperationException(); - } } http://git-wip-us.apache.org/repos/asf/bval/blob/4dbe0936/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 fc97df8..a64e0a0 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,33 +19,15 @@ 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; -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, @@ -67,44 +49,4 @@ public class ContainerElementTypeD extends CascadableContainerD<CascadableContai public ContainerElementKey getKey() { return key; } - - @Override - protected Stream<GraphContext> readImpl(GraphContext context) throws Exception { - 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/4dbe0936/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 index 54a69f0..69a482e 100644 --- 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 @@ -22,11 +22,13 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.stream.Stream; +import javax.validation.ValidationException; import javax.validation.metadata.PropertyDescriptor; import org.apache.bval.jsr.GraphContext; import org.apache.bval.jsr.util.Methods; import org.apache.bval.jsr.util.PathImpl; +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; @@ -94,12 +96,19 @@ public abstract class PropertyD<E extends AnnotatedElement> extends CascadableCo this.host = reader.meta.getHost(); } - @Override - protected Stream<GraphContext> readImpl(GraphContext context) throws Exception { - final Object value = getValue(context.getValue()); - final PathImpl p = PathImpl.copy(context.getPath()); - p.addProperty(getPropertyName()); - return Stream.of(context.child(p, value)); + public final Stream<GraphContext> read(GraphContext context) { + Validate.notNull(context); + if (context.getValue() == null) { + return Stream.empty(); + } + try { + final Object value = getValue(context.getValue()); + final PathImpl p = PathImpl.copy(context.getPath()); + p.addProperty(getPropertyName()); + return Stream.of(context.child(p, value)); + } catch (Exception e) { + throw e instanceof ValidationException ? (ValidationException) e : new ValidationException(e); + } } public abstract Object getValue(Object parent) throws Exception; http://git-wip-us.apache.org/repos/asf/bval/blob/4dbe0936/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 index 07c0c96..a822aa8 100644 --- 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 @@ -165,7 +165,7 @@ public class ConstraintValidatorContextImpl<T> implements ConstraintValidatorCon } @SuppressWarnings({ "unchecked", "rawtypes" }) - public void addError(String messageTemplate, Path propertyPath) { + public void addError(String messageTemplate, PathImpl propertyPath) { violations.get().add(((ValidationJob) frame.getJob()).createViolation(messageTemplate, this, propertyPath)); } http://git-wip-us.apache.org/repos/asf/bval/blob/4dbe0936/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 index f533314..bd20548 100644 --- 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 @@ -18,8 +18,6 @@ */ package org.apache.bval.jsr.job; -import javax.validation.Path; - import org.apache.bval.jsr.ApacheFactoryContext; import org.apache.bval.jsr.ConstraintViolationImpl; import org.apache.bval.jsr.GraphContext; @@ -50,7 +48,7 @@ public final class ValidateBean<T> extends ValidationJob<T> { @Override ConstraintViolationImpl<T> createViolation(String messageTemplate, String message, - ConstraintValidatorContextImpl<T> context, Path propertyPath) { + ConstraintValidatorContextImpl<T> context, PathImpl propertyPath) { return new ConstraintViolationImpl<>(messageTemplate, message, bean, context.getFrame().getBean(), propertyPath, context.getFrame().context.getValue(), context.getConstraintDescriptor(), getRootBeanClass(), http://git-wip-us.apache.org/repos/asf/bval/blob/4dbe0936/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 index 0e2b30c..777ff1a 100644 --- 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 @@ -28,7 +28,6 @@ import java.util.stream.IntStream; import javax.validation.ConstraintViolation; import javax.validation.ParameterNameProvider; -import javax.validation.Path; import javax.validation.constraintvalidation.ValidationTarget; import javax.validation.metadata.ExecutableDescriptor; @@ -181,7 +180,7 @@ public abstract class ValidateParameters<E extends Executable, T> extends Valida @Override ConstraintViolationImpl<T> createViolation(String messageTemplate, String message, - ConstraintValidatorContextImpl<T> context, Path propertyPath) { + ConstraintValidatorContextImpl<T> context, PathImpl propertyPath) { return new ConstraintViolationImpl<T>(messageTemplate, message, getRootBean(), context.getFrame().getBean(), propertyPath, context.getFrame().context.getValue(), context.getConstraintDescriptor(), getRootBeanClass(), context.getConstraintDescriptor().unwrap(ConstraintD.class).getDeclaredOn(), null, parameterValues); http://git-wip-us.apache.org/repos/asf/bval/blob/4dbe0936/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 index ce33840..11b6338 100644 --- 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 @@ -31,7 +31,6 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import javax.validation.ConstraintViolation; -import javax.validation.Path; import javax.validation.metadata.BeanDescriptor; import javax.validation.metadata.CascadableDescriptor; import javax.validation.metadata.ContainerDescriptor; @@ -515,7 +514,7 @@ public final class ValidateProperty<T> extends ValidationJob<T> { @Override ConstraintViolationImpl<T> createViolation(String messageTemplate, String message, - ConstraintValidatorContextImpl<T> context, Path propertyPath) { + ConstraintValidatorContextImpl<T> context, PathImpl propertyPath) { return new ConstraintViolationImpl<>(messageTemplate, message, rootBean, context.getFrame().getBean(), propertyPath, 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/4dbe0936/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 index 8ffd5f5..f8d030b 100644 --- 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 @@ -21,8 +21,6 @@ import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.lang.reflect.Type; -import javax.validation.Path; - import org.apache.bval.jsr.ApacheFactoryContext; import org.apache.bval.jsr.ConstraintViolationImpl; import org.apache.bval.jsr.GraphContext; @@ -125,7 +123,7 @@ public abstract class ValidateReturnValue<E extends Executable, T> extends Valid @Override ConstraintViolationImpl<T> createViolation(String messageTemplate, String message, - ConstraintValidatorContextImpl<T> context, Path propertyPath) { + ConstraintValidatorContextImpl<T> context, PathImpl propertyPath) { return new ConstraintViolationImpl<>(messageTemplate, message, getRootBean(), context.getFrame().getBean(), propertyPath, context.getFrame().context.getValue(), context.getConstraintDescriptor(), getRootBeanClass(), context.getConstraintDescriptor().unwrap(ConstraintD.class).getDeclaredOn(), returnValue, null); http://git-wip-us.apache.org/repos/asf/bval/blob/4dbe0936/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 index 4385724..68af377 100644 --- 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 @@ -22,7 +22,6 @@ import java.lang.reflect.Array; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Collections; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -33,13 +32,11 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentSkipListSet; import java.util.function.BiConsumer; import java.util.function.Consumer; -import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; -import javax.validation.ConstraintDeclarationException; import javax.validation.ConstraintValidator; import javax.validation.ConstraintViolation; import javax.validation.ElementKind; @@ -55,7 +52,6 @@ import javax.validation.metadata.ContainerDescriptor; import javax.validation.metadata.ElementDescriptor.ConstraintFinder; import javax.validation.metadata.PropertyDescriptor; import javax.validation.metadata.ValidateUnwrappedValue; -import javax.validation.valueextraction.UnwrapByDefault; import javax.validation.valueextraction.ValueExtractor; import org.apache.bval.jsr.ApacheFactoryContext; @@ -118,55 +114,25 @@ public abstract class ValidationJob<T> { void validateDescriptorConstraints(Class<?> group, Consumer<ConstraintViolation<T>> sink) { constraintsFrom(descriptor.findConstraints().unorderedAndMatchingGroups(group)) - .forEach(c -> expand(c.getValueUnwrapping()).forEach(f -> f.validate(c, sink))); + .forEach(c -> unwrap(c.getValueUnwrapping()).forEach(f -> f.validate(c, sink))); } - private Stream<Frame<D>> expand(ValidateUnwrappedValue valueUnwrapping) { + private Stream<Frame<D>> unwrap(ValidateUnwrappedValue valueUnwrapping) { if (valueUnwrapping != ValidateUnwrappedValue.SKIP && context.getValue() != null) { - final Optional<Map.Entry<ContainerElementKey, ValueExtractor<?>>> valueExtractorAndAssociatedContainerElementKey = - findValueExtractorAndAssociatedContainerElementKey(context.getValue().getClass(), valueUnwrapping); + final Optional<ValueExtractors.UnwrappingInfo> valueExtractorAndAssociatedContainerElementKey = + validatorContext.getValueExtractors(). + findUnwrappingInfo(context.getValue().getClass(), valueUnwrapping); if (valueExtractorAndAssociatedContainerElementKey.isPresent()) { return ExtractValues - .extract(context, valueExtractorAndAssociatedContainerElementKey.get().getKey(), - valueExtractorAndAssociatedContainerElementKey.get().getValue()) + .extract(context, valueExtractorAndAssociatedContainerElementKey.get().containerElementKey, + valueExtractorAndAssociatedContainerElementKey.get().valueExtractor) .stream().map(child -> new UnwrappedElementConstraintValidationPseudoFrame<>(this, child)); } } return Stream.of(this); } - private Optional<Map.Entry<ContainerElementKey, ValueExtractor<?>>> findValueExtractorAndAssociatedContainerElementKey( - Class<?> containerClass, ValidateUnwrappedValue valueUnwrapping) { - final Map<ContainerElementKey, ValueExtractor<?>> m = new HashMap<>(); - final Predicate<ValueExtractor<?>> valueExtractorFilter = - x -> valueUnwrapping == ValidateUnwrappedValue.UNWRAP || ValueExtractors.isUnwrapByDefault(x); - - final ContainerElementKey nonGenericKey = new ContainerElementKey(containerClass, null); - - Optional.of(nonGenericKey).map(validatorContext.getValueExtractors()::find).filter(valueExtractorFilter) - .ifPresent(x -> m.put(nonGenericKey, x)); - - if (containerClass.getTypeParameters().length == 1) { - final ContainerElementKey genericKey = new ContainerElementKey(containerClass, Integer.valueOf(0)); - - Optional.of(genericKey).map(validatorContext.getValueExtractors()::find).filter(valueExtractorFilter) - .ifPresent(x -> m.put(genericKey, x)); - } - if (m.isEmpty()) { - if (valueUnwrapping == ValidateUnwrappedValue.UNWRAP) { - Exceptions.raise(ConstraintDeclarationException::new, "No %s found for %s", - ValueExtractor.class.getSimpleName(), containerClass); - } - return Optional.empty(); - } - if (m.size() > 1) { - Exceptions.raise(ConstraintDeclarationException::new, "Found generic and non-generic %ss for %s", - ValueExtractor.class.getSimpleName(), containerClass); - } - return Optional.of(m.entrySet().iterator().next()); - } - @SuppressWarnings("unchecked") private Stream<ConstraintD<?>> constraintsFrom(ConstraintFinder finder) { // our ConstraintFinder implementation is a Stream supplier; reference without exposing it beyond its @@ -263,30 +229,16 @@ public abstract class ValidationJob<T> { } private Class<?> computeValidatedType(ConstraintD<?> constraint) { - final Class<?> elementClass = descriptor.getElementClass(); - - if (constraint.getValueUnwrapping() == ValidateUnwrappedValue.SKIP) { - return elementClass; + if (context.getValue() != null) { + return context.getValue().getClass(); } - final ValueExtractor<?> valueExtractor = - validatorContext.getValueExtractors().find(new ContainerElementKey(elementClass, null)); + final Class<?> elementClass = descriptor.getElementClass(); - final boolean unwrap = constraint.getValueUnwrapping() == ValidateUnwrappedValue.UNWRAP; + final Optional<Class<?>> extractedType = + validatorContext.getValueExtractors().findUnwrappingInfo(elementClass, constraint.getValueUnwrapping()) + .map(info -> ValueExtractors.getExtractedType(info.valueExtractor, elementClass)); - if (valueExtractor == null) { - if (unwrap) { - Exceptions.raise(ConstraintDeclarationException::new, "No compatible %s found for %s", - ValueExtractor.class.getSimpleName(), elementClass); - } - } else { - @SuppressWarnings("unchecked") - final Class<? extends ValueExtractor<?>> extractorClass = - (Class<? extends ValueExtractor<?>>) valueExtractor.getClass(); - if (unwrap || extractorClass.isAnnotationPresent(UnwrapByDefault.class)) { - return ValueExtractors.getExtractedType(valueExtractor, elementClass); - } - } - return elementClass; + return extractedType.orElse(elementClass); } private Stream<Class<?>> expand(Class<?> group) { @@ -368,12 +320,7 @@ public abstract class ValidationJob<T> { } protected void recurseSingleExpandedGroup(Class<?> group, Consumer<ConstraintViolation<T>> sink) { - @SuppressWarnings({ "unchecked", "rawtypes" }) - final Stream<ContainerElementTypeD> containerElements = descriptor.getConstrainedContainerElementTypes() - .stream().flatMap(d -> ComposedD.unwrap(d, (Class) ContainerElementTypeD.class)); - - containerElements.flatMap(d -> d.read(context).map(child -> new ContainerElementFrame(this, d, child))) - .forEach(f -> f.process(group, sink)); + processContainerElements(group, sink); if (!descriptor.isCascaded()) { return; @@ -393,6 +340,30 @@ public abstract class ValidationJob<T> { .map(context -> new BeanFrame<>(this, context)).forEach(b -> b.process(group, sink)); } + private void processContainerElements(Class<?> group, Consumer<ConstraintViolation<T>> sink) { + if (context.getValue() == null) { + return; + } + // handle spec dichotomy: declared type for constraints; runtime type for cascades. Bypass #process() + descriptor.getConstrainedContainerElementTypes().stream() + .flatMap(d -> ComposedD.unwrap(d, ContainerElementTypeD.class)).forEach(d -> { + if (!d.findConstraints().unorderedAndMatchingGroups(group).getConstraintDescriptors().isEmpty()) { + final ValueExtractor<?> declaredTypeValueExtractor = + context.getValidatorContext().getValueExtractors().find(d.getKey()); + ExtractValues.extract(context, d.getKey(), declaredTypeValueExtractor).stream() + .filter(e -> !e.isRecursive()).map(e -> new ContainerElementConstraintsFrame(this, d, e)) + .forEach(f -> f.validateDescriptorConstraints(group, sink)); + } + if (d.isCascaded() || !d.getConstrainedContainerElementTypes().isEmpty()) { + final ValueExtractor<?> runtimeTypeValueExtractor = + context.getValidatorContext().getValueExtractors().find(context.runtimeKey(d.getKey())); + ExtractValues.extract(context, d.getKey(), runtimeTypeValueExtractor).stream() + .filter(e -> !e.isRecursive()).map(e -> new ContainerElementCascadeFrame(this, d, e)) + .forEach(f -> f.recurse(group, sink)); + } + }); + } + protected GraphContext getMultiplexContext() { return context; } @@ -457,23 +428,48 @@ public abstract class ValidationJob<T> { } } - private class ContainerElementFrame extends SproutFrame<ContainerElementTypeD> { + private class ContainerElementConstraintsFrame extends SproutFrame<ContainerElementTypeD> { - ContainerElementFrame(ValidationJob<T>.Frame<?> parent, ContainerElementTypeD descriptor, + ContainerElementConstraintsFrame(ValidationJob<T>.Frame<?> parent, ContainerElementTypeD descriptor, GraphContext context) { super(parent, descriptor, context); } + + @Override + void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink) { + } + } + + private class ContainerElementCascadeFrame extends SproutFrame<ContainerElementTypeD> { + + ContainerElementCascadeFrame(ValidationJob<T>.Frame<?> parent, ContainerElementTypeD descriptor, + GraphContext context) { + super(parent, descriptor, context); + } + + @Override + void validateDescriptorConstraints(Class<?> group, Consumer<ConstraintViolation<T>> sink) { + } @Override protected GraphContext getMultiplexContext() { final PathImpl path = context.getPath(); - final NodeImpl leafNode = path.getLeafNode(); - final NodeImpl newLeaf; + GraphContext ancestor = context.getParent(); + Validate.validState(ancestor!= null, "Expected parent context"); + final NodeImpl leafNode = path.getLeafNode(); + + final NodeImpl newLeaf; + if (leafNode.getKind() == ElementKind.CONTAINER_ELEMENT) { // recurse using elided path: path.removeLeafNode(); + + while (!path.equals(ancestor.getPath())) { + ancestor = ancestor.getParent(); + Validate.validState(ancestor!= null, "Expected parent context"); + } newLeaf = new NodeImpl.PropertyNodeImpl(leafNode); newLeaf.setName(null); } else { @@ -483,7 +479,7 @@ public abstract class ValidationJob<T> { } path.addNode(newLeaf); - return context.getParent().child(path, context.getValue()); + return ancestor.child(path, context.getValue()); } } @@ -579,12 +575,18 @@ public abstract class ValidationJob<T> { } final ConstraintViolationImpl<T> createViolation(String messageTemplate, ConstraintValidatorContextImpl<T> context, - Path propertyPath) { + PathImpl propertyPath) { + if (!propertyPath.isRootPath()) { + final NodeImpl leafNode = propertyPath.getLeafNode(); + if (leafNode.getName() == null && !leafNode.isInIterable()) { + propertyPath.removeLeafNode(); + } + } return createViolation(messageTemplate, interpolate(messageTemplate, context), context, propertyPath); } abstract ConstraintViolationImpl<T> createViolation(String messageTemplate, String message, - ConstraintValidatorContextImpl<T> context, Path propertyPath); + ConstraintValidatorContextImpl<T> context, PathImpl propertyPath); protected abstract Frame<?> computeBaseFrame(); http://git-wip-us.apache.org/repos/asf/bval/blob/4dbe0936/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ContainerElementKey.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ContainerElementKey.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ContainerElementKey.java index 529ccda..2d7102b 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ContainerElementKey.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ContainerElementKey.java @@ -41,6 +41,7 @@ import org.apache.bval.util.EmulatedAnnotatedType; import org.apache.bval.util.Exceptions; import org.apache.bval.util.Lazy; import org.apache.bval.util.LazyInt; +import org.apache.bval.util.ObjectUtils; import org.apache.bval.util.Validate; import org.apache.bval.util.reflection.TypeUtils; @@ -89,6 +90,12 @@ public class ContainerElementKey implements Comparable<ContainerElementKey> { .orElseThrow(() -> new ValueExtractorDefinitionException(extractorType.getName())).iterator().next(); } + public static ContainerElementKey forTypeVariable(TypeVariable<?> var) { + final Class<?> container = (Class<?>) var.getGenericDeclaration(); + final int argIndex = ObjectUtils.indexOf(container.getTypeParameters(), var); + return new ContainerElementKey(container, Integer.valueOf(argIndex)); + } + private static Integer validTypeArgumentIndex(Integer typeArgumentIndex, Class<?> containerClass) { if (typeArgumentIndex != null) { final int i = typeArgumentIndex.intValue(); @@ -154,8 +161,9 @@ public class ContainerElementKey implements Comparable<ContainerElementKey> { @Override public int compareTo(ContainerElementKey o) { - return Comparator.comparing(ContainerElementKey::containerClassName) - .thenComparing(Comparator.nullsFirst(Comparator.comparing(ContainerElementKey::getTypeArgumentIndex))) + return Comparator + .nullsFirst(Comparator.comparing(ContainerElementKey::containerClassName) + .thenComparing(Comparator.nullsFirst(Comparator.comparing(ContainerElementKey::getTypeArgumentIndex)))) .compare(this, o); } http://git-wip-us.apache.org/repos/asf/bval/blob/4dbe0936/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java index 1d49a2a..9f7b0c3 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeImpl.java @@ -48,12 +48,18 @@ public abstract class NodeImpl implements Path.Node, Serializable { private static final long serialVersionUID = 1L; /** - * Comparator for any path {@link Node}. + * Comparator for any path {@link Node}. For iterable nodes with no, or {@code null}, key and index values + * the left operand is always treated as less than the right. */ public static final Comparator<Path.Node> NODE_COMPARATOR = nullsFirst(comparing(Node::getName, nullsFirst(naturalOrder())).thenComparing(NodeImpl::compareIterability) .thenComparing(NodeImpl::compareSpecificNodeInfo)); + private static final Comparator<Path.Node> NODE_EQUALITY_COMPARATOR = + nullsFirst(comparing(Node::getName, nullsFirst(naturalOrder())) + .thenComparing((o1, o2) -> NodeImpl.compareIterability(o1, o2, false)) + .thenComparing(NodeImpl::compareSpecificNodeInfo)); + private static final Comparator<Class<?>> CLASS_COMPARATOR = Comparator.nullsFirst( Comparator.<Class<?>, Boolean> comparing(Class::isPrimitive).reversed().thenComparing(Class::getName)); @@ -130,16 +136,27 @@ public abstract class NodeImpl implements Path.Node, Serializable { } private static int compareIterability(Node quid, Node quo) { + final boolean strict = true; + return compareIterability(quid, quo, strict); + } + + private static int compareIterability(Node quid, Node quo, boolean strict) { if (quid.isInIterable()) { if (quo.isInIterable()) { if (quid.getKey() != null) { return Comparator.comparing(Node::getKey, KEY_COMPARATOR).compare(quid, quo); } - if (quid.getIndex() == null) { - // this method cannot consistently order iterables without key or index; the first argument is - // always assumed to be less: + if (quo.getKey() != null) { return -1; } + if (quid.getIndex() == null) { + if (strict) { + // this method cannot consistently order iterables without key or index; the first argument is + // always assumed to be less: + return -1; + } + return quo.getIndex() == null ? 0 : -1; + } return quo.getIndex() == null ? 1 : quid.getIndex().compareTo(quo.getIndex()); } return 1; @@ -336,7 +353,7 @@ public abstract class NodeImpl implements Path.Node, Serializable { if (o == null || !getClass().equals(o.getClass())) { return false; } - return NODE_COMPARATOR.compare(this, (NodeImpl) o) == 0; + return NODE_EQUALITY_COMPARATOR.compare(this, (NodeImpl) o) == 0; } /** http://git-wip-us.apache.org/repos/asf/bval/blob/4dbe0936/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java index 3289a47..54ad138 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/PathImpl.java @@ -41,6 +41,9 @@ public class PathImpl implements Path, Serializable { private static final long serialVersionUID = 1L; + /** + * @see NodeImpl#NODE_COMPARATOR + */ public static final Comparator<Path> PATH_COMPARATOR = Comparators.comparingIterables(NodeImpl.NODE_COMPARATOR); static final String PROPERTY_PATH_SEPARATOR = "."; http://git-wip-us.apache.org/repos/asf/bval/blob/4dbe0936/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ExtractValues.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ExtractValues.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ExtractValues.java index cde4618..01295c6 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ExtractValues.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ExtractValues.java @@ -29,6 +29,7 @@ import org.apache.bval.jsr.GraphContext; import org.apache.bval.jsr.metadata.ContainerElementKey; import org.apache.bval.jsr.util.NodeImpl; import org.apache.bval.jsr.util.PathImpl; +import org.apache.bval.util.Exceptions; import org.apache.bval.util.Lazy; import org.apache.bval.util.Validate; @@ -76,10 +77,8 @@ public final class ExtractValues { private void addChild(NodeImpl node, Object value) { final PathImpl path = context.getPath(); - if (node.getName() != null) { - path.addNode(node.inContainer(containerElementKey.getContainerClass(), - containerElementKey.getTypeArgumentIndex())); - } + path.addNode( + node.inContainer(containerElementKey.getContainerClass(), containerElementKey.getTypeArgumentIndex())); result.get().add(context.child(path, value)); } } @@ -93,6 +92,8 @@ public final class ExtractValues { Validate.notNull(context, "context"); Validate.notNull(containerElementKey, "containerElementKey"); if (valueExtractor != null) { + Exceptions.raiseIf(context.getValue() == null, IllegalStateException::new, + "Cannot extract values from null"); final Receiver receiver = new Receiver(context, containerElementKey); try { ((ValueExtractor) valueExtractor).extractValues(context.getValue(), receiver); http://git-wip-us.apache.org/repos/asf/bval/blob/4dbe0936/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 2ebc2c7..f635c92 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 @@ -19,9 +19,12 @@ package org.apache.bval.jsr.valueextraction; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; @@ -37,6 +40,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import javax.validation.ConstraintDeclarationException; +import javax.validation.metadata.ValidateUnwrappedValue; import javax.validation.valueextraction.UnwrapByDefault; import javax.validation.valueextraction.ValueExtractor; import javax.validation.valueextraction.ValueExtractorDeclarationException; @@ -45,6 +49,7 @@ import javax.validation.valueextraction.ValueExtractorDefinitionException; import org.apache.bval.jsr.metadata.ContainerElementKey; import org.apache.bval.util.Exceptions; import org.apache.bval.util.Lazy; +import org.apache.bval.util.ObjectUtils; import org.apache.bval.util.StringUtils; import org.apache.bval.util.Validate; import org.apache.bval.util.reflection.Reflection; @@ -59,6 +64,17 @@ public class ValueExtractors { EXCEPTION, OVERWRITE; } + public static class UnwrappingInfo { + public final ContainerElementKey containerElementKey; + public final ValueExtractor<?> valueExtractor; + + private UnwrappingInfo(ContainerElementKey containerElementKey, ValueExtractor<?> valueExtractor) { + super(); + this.containerElementKey = containerElementKey; + this.valueExtractor = valueExtractor; + } + } + public static final ValueExtractors EMPTY = new ValueExtractors(null, OnDuplicateContainerElementKey.EXCEPTION, Collections.emptyMap()); @@ -151,12 +167,27 @@ public class ValueExtractors { }).map(ValueExtractors::newInstance); } - private static boolean related(Class<?> c1, Class<?> c2) { - return c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1); + private static <T> Optional<T> maximallySpecific(Collection<T> candidates, Function<? super T, Class<?>> toType) { + final Collection<T> result; + if (candidates.size() > 1) { + result = new HashSet<>(); + for (T candidate : candidates) { + final Class<?> candidateType = toType.apply(candidate); + if (candidates.stream().filter(Predicate.isEqual(candidate).negate()).map(toType) + .allMatch(t -> t.isAssignableFrom(candidateType))) { + result.add(candidate); + } + } + } else { + result = candidates; + } + return result.size() == 1 ? Optional.of(result.iterator().next()) : Optional.empty(); } private final ValueExtractors parent; private final Lazy<Map<ContainerElementKey, ValueExtractor<?>>> valueExtractors = new Lazy<>(TreeMap::new); + private final Lazy<Set<ValueExtractors>> children = new Lazy<>(HashSet::new); + private final Lazy<Map<ContainerElementKey, ValueExtractor<?>>> searchCache = new Lazy<>(HashMap::new); private final OnDuplicateContainerElementKey onDuplicateContainerElementKey; public ValueExtractors() { @@ -183,7 +214,9 @@ public class ValueExtractors { } public ValueExtractors createChild(OnDuplicateContainerElementKey onDuplicateContainerElementKey) { - return new ValueExtractors(this, onDuplicateContainerElementKey); + final ValueExtractors child = new ValueExtractors(this, onDuplicateContainerElementKey); + children.get().add(child); + return child; } public void add(ValueExtractor<?> extractor) { @@ -205,6 +238,7 @@ public class ValueExtractors { } else { m.put(key, extractor); } + children.optional().ifPresent(s -> s.stream().forEach(ValueExtractors::clearCache)); } public Map<ContainerElementKey, ValueExtractor<?>> getValueExtractors() { @@ -214,37 +248,84 @@ public class ValueExtractors { } public ValueExtractor<?> find(ContainerElementKey key) { + final Optional<ValueExtractor<?>> cacheHit = searchCache.optional().map(m -> m.get(key)); + if (cacheHit.isPresent()) { + return cacheHit.get(); + } final Map<ContainerElementKey, ValueExtractor<?>> allValueExtractors = getValueExtractors(); if (allValueExtractors.containsKey(key)) { return allValueExtractors.get(key); } - // search for assignable ContainerElementKey: - final Set<ContainerElementKey> assignableKeys = key.getAssignableKeys(); - if (assignableKeys.isEmpty()) { - return null; + final Map<ValueExtractor<?>, ContainerElementKey> candidates = Stream + .concat(Stream.of(key), key.getAssignableKeys().stream()).filter(allValueExtractors::containsKey).collect( + Collectors.toMap(allValueExtractors::get, Function.identity(), (quid, quo) -> quo, LinkedHashMap::new)); + + final Optional<ValueExtractor<?>> result = + maximallySpecific(candidates.keySet(), ve -> candidates.get(ve).getContainerClass()); + if (result.isPresent()) { + searchCache.get().put(key, result.get()); + return result.get(); } - final Map<ContainerElementKey, ValueExtractor<?>> candidateMap = - assignableKeys.stream().filter(allValueExtractors::containsKey).collect( - Collectors.toMap(Function.identity(), allValueExtractors::get, (quid, quo) -> quo, LinkedHashMap::new)); + throw Exceptions.create(ConstraintDeclarationException::new, "Could not determine %s for %s", + ValueExtractor.class.getSimpleName(), key); + } - if (candidateMap.isEmpty()) { - return null; + public Optional<UnwrappingInfo> findUnwrappingInfo(Class<?> containerClass, + ValidateUnwrappedValue valueUnwrapping) { + if (valueUnwrapping == ValidateUnwrappedValue.SKIP) { + return Optional.empty(); } - if (candidateMap.size() > 1) { - final Set<Class<?>> containerTypes = - candidateMap.keySet().stream().map(ContainerElementKey::getContainerClass).collect(Collectors.toSet()); + final Map<ContainerElementKey, ValueExtractor<?>> allValueExtractors = getValueExtractors(); + + final Set<UnwrappingInfo> unwrapping = allValueExtractors.entrySet().stream() + .filter(e -> e.getKey().getContainerClass().isAssignableFrom(containerClass)) + .map(e -> new UnwrappingInfo(e.getKey(), e.getValue())).collect(Collectors.toSet()); - final boolean allRelated = containerTypes.stream().allMatch(quid -> containerTypes.stream() - .filter(Predicate.isEqual(quid).negate()).allMatch(quo -> related(quid, quo))); + final Optional<UnwrappingInfo> result = + maximallySpecific(unwrapping, u -> u.containerElementKey.getContainerClass()); - Exceptions.raiseUnless(allRelated, ConstraintDeclarationException::new, - "> 1 maximally specific %s found for %s", f -> f.args(ValueExtractor.class.getSimpleName(), key)); + if (result.isPresent()) { + if (valueUnwrapping == ValidateUnwrappedValue.UNWRAP || isUnwrapByDefault(result.get().valueExtractor)) { + return result + .map(u -> new UnwrappingInfo(translateTo(containerClass, u.containerElementKey), u.valueExtractor)); + } + } else if (valueUnwrapping == ValidateUnwrappedValue.UNWRAP) { + Exceptions.raise(ConstraintDeclarationException::new, "Could not determine %s for %s", + ValueExtractor.class.getSimpleName(), containerClass); } - return candidateMap.values().iterator().next(); + return Optional.empty(); + } + + private static ContainerElementKey translateTo(Class<?> containerClass, ContainerElementKey key) { + final Class<?> keyContainer = key.getContainerClass(); + if (keyContainer.equals(containerClass)) { + return key; + } + Validate.validState(keyContainer.isAssignableFrom(containerClass), "Cannot render %s in terms of %s", key, + containerClass); + if (key.getTypeArgumentIndex() == null) { + return new ContainerElementKey(containerClass, null); + } + Integer typeArgumentIndex = null; + final Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(containerClass, keyContainer); + Type t = typeArguments.get(keyContainer.getTypeParameters()[key.getTypeArgumentIndex().intValue()]); + while (t instanceof TypeVariable<?>) { + final TypeVariable<?> var = (TypeVariable<?>) t; + if (containerClass.equals(var.getGenericDeclaration())) { + typeArgumentIndex = Integer.valueOf(ObjectUtils.indexOf(containerClass.getTypeParameters(), var)); + break; + } + t = typeArguments.get(t); + } + return new ContainerElementKey(containerClass, typeArgumentIndex); } private void populate(Supplier<Map<ContainerElementKey, ValueExtractor<?>>> target) { Optional.ofNullable(parent).ifPresent(p -> p.populate(target)); valueExtractors.optional().ifPresent(m -> target.get().putAll(m)); } + + private void clearCache() { + searchCache.optional().ifPresent(Map::clear); + } }
