separate constraint filtering by group for purposes of validation vs. metadata inspection (ConstraintFinder)
Project: http://git-wip-us.apache.org/repos/asf/bval/repo Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/f02b9b80 Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/f02b9b80 Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/f02b9b80 Branch: refs/heads/master Commit: f02b9b805a131746b711a81f8c07268a578df906 Parents: cc95a74 Author: Matt Benson <[email protected]> Authored: Sat Mar 31 10:58:46 2018 -0500 Committer: Matt Benson <[email protected]> Committed: Tue Oct 16 12:28:20 2018 -0500 ---------------------------------------------------------------------- .../apache/bval/jsr/ApacheFactoryContext.java | 6 +- .../apache/bval/jsr/ApacheValidatorFactory.java | 18 +++-- .../apache/bval/jsr/descriptor/ComposedD.java | 6 +- .../apache/bval/jsr/descriptor/ElementD.java | 5 +- .../org/apache/bval/jsr/descriptor/Finder.java | 78 +++++++++++++++----- .../bval/jsr/descriptor/MetadataReader.java | 4 + .../apache/bval/jsr/groups/GroupsComputer.java | 55 ++++++++------ .../org/apache/bval/jsr/job/ValidationJob.java | 19 ++--- .../bval/jsr/util/AnnotationsManager.java | 4 +- 9 files changed, 133 insertions(+), 62 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/bval/blob/f02b9b80/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java index 7093ad9..1dcc0d3 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheFactoryContext.java @@ -30,7 +30,6 @@ import javax.validation.valueextraction.ValueExtractor; import org.apache.bval.jsr.descriptor.DescriptorManager; import org.apache.bval.jsr.groups.GroupsComputer; import org.apache.bval.jsr.valueextraction.ValueExtractors; -import org.apache.bval.util.Lazy; import org.apache.bval.util.reflection.Reflection; import org.apache.commons.weaver.privilizer.Privilizing; import org.apache.commons.weaver.privilizer.Privilizing.CallTo; @@ -40,7 +39,6 @@ import org.apache.commons.weaver.privilizer.Privilizing.CallTo; */ @Privilizing(@CallTo(Reflection.class)) public class ApacheFactoryContext implements ValidatorContext { - private final Lazy<GroupsComputer> groupsComputer = new Lazy<>(GroupsComputer::new); private final ApacheValidatorFactory factory; private final ValueExtractors valueExtractors; @@ -172,12 +170,12 @@ public class ApacheFactoryContext implements ValidatorContext { } public DescriptorManager getDescriptorManager() { - // TODO handle context customizations + // TODO implementation-specific feature to handle context-local descriptor customizations return factory.getDescriptorManager(); } public GroupsComputer getGroupsComputer() { - return groupsComputer.get(); + return factory.getGroupsComputer(); } public ConstraintCached getConstraintsCache() { http://git-wip-us.apache.org/repos/asf/bval/blob/f02b9b80/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java index 5e5118f..de1ce3f 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java @@ -39,6 +39,7 @@ import javax.validation.spi.ConfigurationState; import javax.validation.valueextraction.ValueExtractor; import org.apache.bval.jsr.descriptor.DescriptorManager; +import org.apache.bval.jsr.groups.GroupsComputer; import org.apache.bval.jsr.metadata.MetadataBuilder; import org.apache.bval.jsr.metadata.MetadataBuilder.ForBean; import org.apache.bval.jsr.metadata.MetadataBuilders; @@ -47,6 +48,7 @@ import org.apache.bval.jsr.util.AnnotationsManager; import org.apache.bval.jsr.valueextraction.ValueExtractors; import org.apache.bval.jsr.valueextraction.ValueExtractors.OnDuplicateContainerElementKey; import org.apache.bval.util.CloseableAble; +import org.apache.bval.util.Lazy; import org.apache.bval.util.reflection.Reflection; import org.apache.commons.weaver.privilizer.Privilizing; import org.apache.commons.weaver.privilizer.Privilizing.CallTo; @@ -93,11 +95,7 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable { return result; } - private MessageInterpolator messageResolver; - private TraversableResolver traversableResolver; - private ConstraintValidatorFactory constraintValidatorFactory; - private ParameterNameProvider parameterNameProvider; - private ClockProvider clockProvider; + private final Lazy<GroupsComputer> groupsComputer = new Lazy<>(GroupsComputer::new); private final Map<String, String> properties; private final AnnotationsManager annotationsManager; private final DescriptorManager descriptorManager = new DescriptorManager(this); @@ -107,6 +105,12 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable { private final ParticipantFactory participantFactory; private final ValueExtractors valueExtractors; + private MessageInterpolator messageResolver; + private TraversableResolver traversableResolver; + private ConstraintValidatorFactory constraintValidatorFactory; + private ParameterNameProvider parameterNameProvider; + private ClockProvider clockProvider; + /** * Create a new ApacheValidatorFactory instance. */ @@ -355,6 +359,10 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable { return metadataBuilders; } + public GroupsComputer getGroupsComputer() { + return groupsComputer.get(); + } + private void loadAndVerifyUserCustomizations(ConfigurationState configuration) { @SuppressWarnings({ "unchecked", "rawtypes" }) final BiConsumer<Class<?>, ForBean<?>> addBuilder = (t, b) -> { http://git-wip-us.apache.org/repos/asf/bval/blob/f02b9b80/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComposedD.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComposedD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComposedD.java index 6d3b004..321d425 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComposedD.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComposedD.java @@ -32,6 +32,7 @@ import javax.validation.metadata.ElementDescriptor; import javax.validation.metadata.GroupConversionDescriptor; import javax.validation.metadata.PropertyDescriptor; +import org.apache.bval.jsr.groups.GroupsComputer; import org.apache.bval.jsr.util.ToUnmodifiable; import org.apache.bval.util.Validate; @@ -118,6 +119,9 @@ public abstract class ComposedD<D extends ElementD<?, ?>> implements ElementDesc @Override public ConstraintFinder findConstraints() { - return new Finder(this); + final GroupsComputer groupsComputer = + unwrap(this, ElementD.class).findFirst().orElseThrow(IllegalStateException::new).groupsComputer; + + return new Finder(groupsComputer, this); } } http://git-wip-us.apache.org/repos/asf/bval/blob/f02b9b80/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ElementD.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ElementD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ElementD.java index b1afa9b..e7c81c6 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ElementD.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ElementD.java @@ -29,6 +29,7 @@ import java.util.Set; import javax.validation.metadata.ConstraintDescriptor; import javax.validation.metadata.ElementDescriptor; +import org.apache.bval.jsr.groups.GroupsComputer; import org.apache.bval.jsr.metadata.Meta; import org.apache.bval.util.Validate; import org.apache.bval.util.reflection.TypeUtils; @@ -72,6 +73,7 @@ public abstract class ElementD<E extends AnnotatedElement, R extends MetadataRea } protected final Type genericType; + final GroupsComputer groupsComputer; private final Meta<E> meta; private final Set<ConstraintD<?>> constraints; @@ -82,6 +84,7 @@ public abstract class ElementD<E extends AnnotatedElement, R extends MetadataRea this.meta = reader.meta; this.genericType = reader.meta.getType(); this.constraints = reader.getConstraints(); + this.groupsComputer = reader.getValidatorFactory().getGroupsComputer(); } @Override @@ -97,7 +100,7 @@ public abstract class ElementD<E extends AnnotatedElement, R extends MetadataRea @Override public final ConstraintFinder findConstraints() { - return new Finder(this); + return new Finder(groupsComputer, this); } public final ElementType getElementType() { http://git-wip-us.apache.org/repos/asf/bval/blob/f02b9b80/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/Finder.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/Finder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/Finder.java index c01e069..ad5d541 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/Finder.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/Finder.java @@ -19,41 +19,57 @@ package org.apache.bval.jsr.descriptor; import java.lang.annotation.ElementType; +import java.util.Collection; +import java.util.Collections; import java.util.EnumSet; +import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; import java.util.function.Predicate; -import java.util.function.Supplier; +import java.util.function.UnaryOperator; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.validation.GroupDefinitionException; +import javax.validation.GroupSequence; import javax.validation.groups.Default; import javax.validation.metadata.ConstraintDescriptor; import javax.validation.metadata.ElementDescriptor; import javax.validation.metadata.ElementDescriptor.ConstraintFinder; import javax.validation.metadata.Scope; +import org.apache.bval.jsr.groups.Group; +import org.apache.bval.jsr.groups.Groups; +import org.apache.bval.jsr.groups.GroupsComputer; import org.apache.bval.jsr.util.ToUnmodifiable; +import org.apache.bval.util.Exceptions; +import org.apache.bval.util.Lazy; import org.apache.bval.util.Validate; -class Finder implements ConstraintFinder, Supplier<Stream<ConstraintD<?>>> { - private Predicate<ConstraintD<?>> groups = c -> true; - private Predicate<ConstraintD<?>> scope; - private Predicate<ConstraintD<?>> elements; +class Finder implements ConstraintFinder { + private static Stream<Group> allGroups(Groups groups) { + return Stream.concat(groups.getGroups().stream(), groups.getSequences().stream().flatMap(Collection::stream)); + } + + private volatile Predicate<ConstraintD<?>> groups = c -> true; + private volatile Predicate<ConstraintD<?>> scope; + private volatile Predicate<ConstraintD<?>> elements; + private final GroupsComputer groupsComputer; private final ElementDescriptor owner; + private final Lazy<Groups> getDefaultSequence = new Lazy<>(this::computeDefaultSequence); + private final Lazy<Class<?>> beanClass; - Finder(ElementDescriptor owner) { + Finder(GroupsComputer groupsComputer, ElementDescriptor owner) { + this.groupsComputer = Validate.notNull(groupsComputer, "groupsComputer"); this.owner = Validate.notNull(owner, "owner"); + this.beanClass = new Lazy<>(() -> firstAtomicElementDescriptor().getBean().getElementClass()); } @Override public ConstraintFinder unorderedAndMatchingGroups(Class<?>... groups) { - this.groups = c -> Stream.of(groups).anyMatch(t -> { - final Set<Class<?>> constraintGroups = c.getGroups(); - return constraintGroups.contains(t) - || constraintGroups.contains(Default.class) && c.getDeclaringClass().isAssignableFrom(t); - }); + final Set<Class<?>> allGroups = computeAll(groups); + this.groups = c -> !Collections.disjoint(allGroups, c.getGroups()); return this; } @@ -73,12 +89,7 @@ class Finder implements ConstraintFinder, Supplier<Stream<ConstraintD<?>>> { @Override public Set<ConstraintDescriptor<?>> getConstraintDescriptors() { - return get().collect(ToUnmodifiable.set()); - } - - @Override - public Stream<ConstraintD<?>> get() { - return getConstraints().filter(filter()); + return getConstraints().filter(filter()).collect(ToUnmodifiable.set()); } @Override @@ -100,4 +111,37 @@ class Finder implements ConstraintFinder, Supplier<Stream<ConstraintD<?>>> { } return result; } + + private ElementD<?,?> firstAtomicElementDescriptor() { + return ComposedD.unwrap(owner, ElementD.class).findFirst().orElseThrow(IllegalStateException::new); + } + + private Groups computeDefaultSequence() { + final ElementD<?, ?> element = firstAtomicElementDescriptor(); + Collection<Class<?>> redef = element.getGroupSequence(); + if (redef == null) { + return GroupsComputer.DEFAULT_GROUPS; + } + final Class<?> t = this.beanClass.get(); + if (redef.contains(Default.class)) { + Exceptions.raise(GroupDefinitionException::new, "%s for %s cannot include %s.class", + GroupSequence.class.getSimpleName(), t, Default.class.getSimpleName()); + } + redef = redef.stream() + .map(substituteDefaultGroup()) + .collect(Collectors.toCollection(LinkedHashSet::new)); + + return groupsComputer.computeGroups(redef); + } + + private Set<Class<?>> computeAll(Class<?>[] groups) { + final Groups preliminaryGroups = groupsComputer.computeGroups(Stream.of(groups).map(substituteDefaultGroup())); + return allGroups(preliminaryGroups) + .flatMap(g -> g.isDefault() ? allGroups(getDefaultSequence.get()) : Stream.of(g)).map(Group::getGroup) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + + private UnaryOperator<Class<?>> substituteDefaultGroup() { + return t -> t.isAssignableFrom(beanClass.get()) ? Default.class : t; + } } http://git-wip-us.apache.org/repos/asf/bval/blob/f02b9b80/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 5f54279..e5e4db6 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 @@ -79,6 +79,10 @@ class MetadataReader { .flatMap(e -> describe(e.getValue(), e.getKey(), meta)).collect(ToUnmodifiable.set()); } + ApacheValidatorFactory getValidatorFactory() { + return validatorFactory; + } + private Stream<ConstraintD<?>> describe(Annotation[] constraints, Scope scope, Meta<?> meta) { return Stream.of(constraints).map(c -> new ConstraintD<>(c, scope, meta, validatorFactory)); } http://git-wip-us.apache.org/repos/asf/bval/blob/f02b9b80/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupsComputer.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupsComputer.java b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupsComputer.java index 30a47cd..3a895d4 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupsComputer.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupsComputer.java @@ -24,10 +24,10 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.validation.GroupDefinitionException; import javax.validation.GroupSequence; @@ -50,7 +50,7 @@ public class GroupsComputer { * The default group array used in case any of the validate methods is * called without a group. */ - private static final Groups DEFAULT_GROUPS; + public static final Groups DEFAULT_GROUPS; static { DEFAULT_GROUPS = new Groups(); for (Class<?> g : DEFAULT_GROUP) { @@ -82,7 +82,7 @@ public class GroupsComputer { * @return {@link Groups} */ public final Groups computeCascadingGroups(Set<GroupConversionDescriptor> groupConversions, Class<?> group) { - final Groups preliminaryResult = computeGroups(group); + final Groups preliminaryResult = computeGroups(Stream.of(group)); final Map<Class<?>, Class<?>> gcMap = groupConversions.stream() .collect(Collectors.toMap(GroupConversionDescriptor::getFrom, GroupConversionDescriptor::getTo)); @@ -91,9 +91,8 @@ public class GroupsComputer { // conversion of a simple (non-sequence) group: if (simpleGroup && gcMap.containsKey(group)) { - return computeGroups(gcMap.get(group)); + return computeGroups(Stream.of(gcMap.get(group))); } - final Groups result = new Groups(); if (simpleGroup) { @@ -107,7 +106,7 @@ public class GroupsComputer { for (Group gg : seq) { final Class<?> c = gg.getGroup(); if (gcMap.containsKey(c)) { - final Groups convertedGroupExpansion = computeGroups(gcMap.get(c)); + final Groups convertedGroupExpansion = computeGroups(Stream.of(gcMap.get(c))); if (convertedGroupExpansion.getSequences().isEmpty()) { converted.add(gg); } else { @@ -123,35 +122,49 @@ public class GroupsComputer { } /** - * Main compute implementation. + * Compute groups from a {@link Collection}. * * @param groups * @return {@link Groups} */ - protected Groups computeGroups(Collection<Class<?>> groups) { + public Groups computeGroups(Collection<Class<?>> groups) { Validate.notNull(groups, "groups"); if (groups.isEmpty() || Arrays.asList(DEFAULT_GROUP).equals(new ArrayList<>(groups))) { return DEFAULT_GROUPS; } - Exceptions.raiseIf(groups.stream().anyMatch(Objects::isNull), IllegalArgumentException::new, - "Null group specified"); + return computeGroups(groups.stream()); + } + + /** + * Compute groups from a {@link Stream}. + * + * @param groups + * @return {@link Groups} + */ + public Groups computeGroups(Stream<Class<?>> groups) { + final Groups result = new Groups(); - groups.forEach(g -> Exceptions.raiseUnless(g.isInterface(), ValidationException::new, - "A group must be an interface. %s is not.", g)); + groups.peek(g -> { + Exceptions.raiseIf(g == null, IllegalArgumentException::new, "Null group specified"); - final Groups chain = new Groups(); - for (Class<?> clazz : groups) { - final GroupSequence anno = clazz.getAnnotation(GroupSequence.class); + Exceptions.raiseUnless(g.isInterface(), ValidationException::new, + "A group must be an interface. %s is not.", g); + + }).forEach(g -> { + final GroupSequence anno = g.getAnnotation(GroupSequence.class); if (anno == null) { - chain.insertGroup(new Group(clazz)); - insertInheritedGroups(clazz, chain); - continue; + result.insertGroup(new Group(g)); + insertInheritedGroups(g, result); + } else { + result.insertSequence( + resolvedSequences.computeIfAbsent(g, gg -> resolveSequence(gg, anno, new HashSet<>()))); } - chain.insertSequence( - resolvedSequences.computeIfAbsent(clazz, g -> resolveSequence(g, anno, new HashSet<>()))); + }); + if (Arrays.asList(DEFAULT_GROUP).equals(result.getGroups()) && result.getSequences().isEmpty()) { + return DEFAULT_GROUPS; } - return chain; + return result; } private void insertInheritedGroups(Class<?> clazz, Groups chain) { http://git-wip-us.apache.org/repos/asf/bval/blob/f02b9b80/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 6542717..e161879 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 @@ -32,7 +32,6 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentSkipListSet; import java.util.function.BiConsumer; import java.util.function.Consumer; -import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -49,7 +48,6 @@ import javax.validation.constraintvalidation.ValidationTarget; import javax.validation.groups.Default; import javax.validation.metadata.CascadableDescriptor; 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.ValueExtractor; @@ -113,7 +111,7 @@ public abstract class ValidationJob<T> { abstract Object getBean(); void validateDescriptorConstraints(Class<?> group, Consumer<ConstraintViolation<T>> sink) { - constraintsFrom(descriptor.findConstraints().unorderedAndMatchingGroups(group)) + constraintsFor(group) .forEach(c -> unwrap(c.getValueUnwrapping()).forEach(f -> f.validate(c, sink))); } @@ -133,14 +131,13 @@ public abstract class ValidationJob<T> { return Stream.of(this); } - @SuppressWarnings("unchecked") - private Stream<ConstraintD<?>> constraintsFrom(ConstraintFinder finder) { - // our ConstraintFinder implementation is a Stream supplier; reference without exposing it beyond its - // package: - if (finder instanceof Supplier<?>) { - return (Stream<ConstraintD<?>>) ((Supplier<?>) finder).get(); - } - return finder.getConstraintDescriptors().stream().map(ConstraintD.class::cast); + private Stream<ConstraintD<?>> constraintsFor(Class<?> group) { + return descriptor.getConstraintDescriptors().stream().<ConstraintD<?>> map(ConstraintD.class::cast) + .filter(c -> Stream.of(group).anyMatch(t -> { + final Set<Class<?>> constraintGroups = c.getGroups(); + return constraintGroups.contains(t) + || constraintGroups.contains(Default.class) && c.getDeclaringClass().isAssignableFrom(t); + })); } @SuppressWarnings({ "rawtypes", "unchecked" }) http://git-wip-us.apache.org/repos/asf/bval/blob/f02b9b80/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java index a7262d0..3b41ec9 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java @@ -241,8 +241,8 @@ public class AnnotationsManager { final GroupsComputer groupsComputer = new GroupsComputer(); // ensure interface group is implied by Default group: Stream.of(result).map(c -> { - final Groups groups = groupsComputer - .computeGroups(ConstraintAnnotationAttributes.GROUPS.analyze(c.annotationType()).read(c)); + final Groups groups = groupsComputer.computeGroups( + ConstraintAnnotationAttributes.GROUPS.analyze(c.annotationType()).<Class<?>[]> read(c)); if (groups.getGroups().stream().anyMatch(Group::isDefault)) { final Set<Class<?>> groupClasses = groups.getGroups().stream().map(Group::getGroup) .collect(Collectors.toCollection(LinkedHashSet::new));
