TCK: GroupConversionValidationTest
Project: http://git-wip-us.apache.org/repos/asf/bval/repo Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/51b9ab27 Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/51b9ab27 Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/51b9ab27 Branch: refs/heads/bv2 Commit: 51b9ab27b910e180caae05c2e46f846c935560c3 Parents: 617fb7c Author: Matt Benson <[email protected]> Authored: Mon Mar 12 13:39:53 2018 -0500 Committer: Matt Benson <[email protected]> Committed: Mon Mar 12 13:39:53 2018 -0500 ---------------------------------------------------------------------- .../apache/bval/jsr/groups/GroupsComputer.java | 51 ++++++++++++++++++++ .../org/apache/bval/jsr/job/ValidationJob.java | 16 +++++- .../org/apache/bval/jsr/metadata/Liskov.java | 32 ++++++++++-- 3 files changed, 93 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/bval/blob/51b9ab27/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 ae6f629..8f4cdda 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 @@ -27,11 +27,13 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import javax.validation.GroupDefinitionException; import javax.validation.GroupSequence; import javax.validation.ValidationException; import javax.validation.groups.Default; +import javax.validation.metadata.GroupConversionDescriptor; import org.apache.bval.util.Exceptions; import org.apache.bval.util.Validate; @@ -72,6 +74,55 @@ public class GroupsComputer { } /** + * Compute groups for a single cascading validation taking into account the specified set of + * {@link GroupConversionDescriptor}s. + * + * @param groupConversions + * @param group + * @return {@link Groups} + */ + public final Groups computeCascadingGroups(Set<GroupConversionDescriptor> groupConversions, Class<?> group) { + final Groups preliminaryResult = computeGroups(group); + + final Map<Class<?>, Class<?>> gcMap = groupConversions.stream() + .collect(Collectors.toMap(GroupConversionDescriptor::getFrom, GroupConversionDescriptor::getTo)); + + final boolean simpleGroup = preliminaryResult.getSequences().isEmpty(); + + // conversion of a simple (non-sequence) group: + if (simpleGroup && gcMap.containsKey(group)) { + return computeGroups(gcMap.get(group)); + } + + final Groups result = new Groups(); + + if (simpleGroup) { + // ignore group inheritance from initial argument as that is handled elsewhere: + result.insertGroup(preliminaryResult.getGroups().get(0)); + } else { + // expand group sequence conversions in place: + + for (List<Group> seq : preliminaryResult.getSequences()) { + final List<Group> converted = new ArrayList<>(); + for (Group gg : seq) { + final Class<?> c = gg.getGroup(); + if (gcMap.containsKey(c)) { + final Groups convertedGroupExpansion = computeGroups(gcMap.get(c)); + if (convertedGroupExpansion.getSequences().isEmpty()) { + converted.add(gg); + } else { + convertedGroupExpansion.getSequences().stream().flatMap(Collection::stream) + .forEach(converted::add); + } + } + } + result.insertSequence(converted); + } + } + return result; + } + + /** * Main compute implementation. * * @param groups http://git-wip-us.apache.org/repos/asf/bval/blob/51b9ab27/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 70905d0..f2eaa26 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 @@ -271,6 +271,20 @@ public abstract class ValidationJob<T> { @Override void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink) { + final Groups convertedGroups = + validatorContext.getGroupsComputer().computeCascadingGroups(descriptor.getGroupConversions(), group); + + convertedGroups.getGroups().stream().map(Group::getGroup).forEach(g -> recurseSingleExpandedGroup(g, sink)); + + sequences: for (List<Group> seq : convertedGroups.getSequences()) { + final boolean proceed = each(seq.stream().map(Group::getGroup), this::recurseSingleExpandedGroup, sink); + if (!proceed) { + break sequences; + } + } + } + + 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)); @@ -363,7 +377,7 @@ public abstract class ValidationJob<T> { } @Override - void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink) { + protected void recurseSingleExpandedGroup(Class<?> group, Consumer<ConstraintViolation<T>> sink) { final PathImpl path = context.getPath(); final NodeImpl leafNode = path.getLeafNode(); http://git-wip-us.apache.org/repos/asf/bval/blob/51b9ab27/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 index a3701d4..2440948 100644 --- 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 @@ -18,6 +18,7 @@ package org.apache.bval.jsr.metadata; import java.lang.annotation.ElementType; import java.util.Collection; +import java.util.Collections; import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -115,8 +116,17 @@ class Liskov { } switch (elementKind) { case RETURN_VALUE: - noStrengtheningOfPreconditions(delegates, detectGroupConversion()); noRedeclarationOfReturnValueCascading(delegates); + + final Map<Meta<?>, Set<ValidationElement>> detectedValidationElements = + detectValidationElements(delegates, detectGroupConversion()); + + // pre-check return value overridden hierarchy: + Stream.of(StrengtheningIssue.values()) + .filter((Predicate<? super StrengtheningIssue>) si -> !(si == StrengtheningIssue.overriddenHierarchy + && detectedValidationElements.values().stream().filter(s -> !s.isEmpty()).count() < 2)) + .forEach(si -> si.check(detectedValidationElements)); + break; case PARAMETER: noStrengtheningOfPreconditions(delegates, detectConstraints(), detectCascading(), detectGroupConversion()); @@ -160,6 +170,20 @@ class Liskov { private static <D extends ElementDelegate<?, ?>> void noStrengtheningOfPreconditions(List<? extends D> delegates, Function<? super D, ValidationElement>... detectors) { + final Map<Meta<?>, Set<ValidationElement>> detectedValidationElements = + detectValidationElements(delegates, detectors); + + if (detectedValidationElements.isEmpty()) { + return; + } + for (StrengtheningIssue s : StrengtheningIssue.values()) { + s.check(detectedValidationElements); + } + } + + @SafeVarargs + private static <D extends ElementDelegate<?, ?>> Map<Meta<?>, Set<ValidationElement>> detectValidationElements( + List<? extends D> delegates, Function<? super D, ValidationElement>... detectors) { final Map<Meta<?>, Set<ValidationElement>> detectedValidationElements = new LinkedHashMap<>(); delegates.forEach(d -> { detectedValidationElements.put(d.getHierarchyElement(), @@ -168,11 +192,9 @@ class Liskov { }); if (detectedValidationElements.values().stream().allMatch(Collection::isEmpty)) { // nothing declared - return; - } - for (StrengtheningIssue s : StrengtheningIssue.values()) { - s.check(detectedValidationElements); + return Collections.emptyMap(); } + return detectedValidationElements; } private static boolean related(Class<?> c1, Class<?> c2) {
