You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bval.apache.org by mb...@apache.org on 2018/04/12 18:01:28 UTC
[1/2] bval git commit: rework use of groups,
introducing GroupStrategy interface + implementations. Passes 1
additional TCK test AND allows validation of non-sequential groups without
making multiple passes over the graph
Repository: bval
Updated Branches:
refs/heads/bv2 d1ddb57b8 -> f5bdeaf10
rework use of groups, introducing GroupStrategy interface + implementations. Passes 1 additional TCK test AND allows validation of non-sequential groups without making multiple passes over the graph
Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/657def16
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/657def16
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/657def16
Branch: refs/heads/bv2
Commit: 657def169b32d209c200c1aecb2a534be892d208
Parents: 5fd197b
Author: Matt Benson <mb...@apache.org>
Authored: Thu Apr 12 13:00:58 2018 -0500
Committer: Matt Benson <mb...@apache.org>
Committed: Thu Apr 12 13:00:58 2018 -0500
----------------------------------------------------------------------
.../java/org/apache/bval/jsr/GraphContext.java | 18 ++
.../org/apache/bval/jsr/descriptor/BeanD.java | 10 +-
.../apache/bval/jsr/descriptor/ConstraintD.java | 12 +-
.../bval/jsr/descriptor/DescriptorManager.java | 6 +
.../apache/bval/jsr/descriptor/ElementD.java | 8 +-
.../org/apache/bval/jsr/descriptor/Finder.java | 7 +-
.../bval/jsr/descriptor/MetadataReader.java | 111 ++++++---
.../java/org/apache/bval/jsr/groups/Group.java | 120 ++++++++-
.../apache/bval/jsr/groups/GroupStrategy.java | 241 +++++++++++++++++++
.../java/org/apache/bval/jsr/groups/Groups.java | 56 +++--
.../apache/bval/jsr/groups/GroupsComputer.java | 11 +-
.../apache/bval/jsr/job/ValidateParameters.java | 17 +-
.../apache/bval/jsr/job/ValidateProperty.java | 5 +-
.../org/apache/bval/jsr/job/ValidationJob.java | 201 ++++++++--------
.../bval/jsr/groups/GroupSequenceTest.java | 42 ++--
.../bval/jsr/groups/GroupsComputerTest.java | 11 +-
16 files changed, 649 insertions(+), 227 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/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 6b93940..9ab4141 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
@@ -21,6 +21,7 @@ package org.apache.bval.jsr;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Map;
+import java.util.Objects;
import javax.validation.Path;
import javax.validation.ValidationException;
@@ -100,6 +101,23 @@ public class GraphContext {
public String toString() {
return String.format("%s: %s at '%s'", getClass().getSimpleName(), value, path);
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj == null || !obj.getClass().equals(getClass())) {
+ return false;
+ }
+ final GraphContext other = (GraphContext) obj;
+ return other.validatorContext == validatorContext && other.value == value && other.getPath().equals(path);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(validatorContext, value, path);
+ }
public ContainerElementKey runtimeKey(ContainerElementKey key) {
Validate.notNull(key);
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/BeanD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/BeanD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/BeanD.java
index 5feaddf..f6b6473 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/BeanD.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/BeanD.java
@@ -20,7 +20,6 @@ package org.apache.bval.jsr.descriptor;
import java.lang.reflect.Type;
import java.util.EnumSet;
-import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -31,6 +30,7 @@ import javax.validation.metadata.MethodDescriptor;
import javax.validation.metadata.MethodType;
import javax.validation.metadata.PropertyDescriptor;
+import org.apache.bval.jsr.groups.GroupStrategy;
import org.apache.bval.jsr.metadata.Signature;
import org.apache.bval.jsr.util.ToUnmodifiable;
import org.apache.bval.util.Exceptions;
@@ -39,17 +39,17 @@ import org.apache.bval.util.StringUtils;
public class BeanD<T> extends ElementD<Class<T>, MetadataReader.ForBean<T>> implements BeanDescriptor {
private final Class<T> beanClass;
- private final List<Class<?>> groupSequence;
private final Map<String, PropertyDescriptor> propertiesMap;
private final Set<PropertyDescriptor> properties;
private final Map<Signature, ConstructorD<T>> constructors;
private final Map<Signature, MethodD> methods;
+ private final GroupStrategy groupStrategy;
BeanD(MetadataReader.ForBean<T> reader) {
super(reader);
this.beanClass = reader.meta.getHost();
- groupSequence = reader.getGroupSequence();
+ groupStrategy = reader.getGroupStrategy();
propertiesMap = reader.getProperties(this);
properties = propertiesMap.values().stream().filter(DescriptorManager::isConstrained).collect(ToUnmodifiable.set());
constructors = reader.getConstructors(this);
@@ -112,8 +112,8 @@ public class BeanD<T> extends ElementD<Class<T>, MetadataReader.ForBean<T>> impl
}
@Override
- public List<Class<?>> getGroupSequence() {
- return groupSequence;
+ public GroupStrategy getGroupStrategy() {
+ return groupStrategy;
}
public final Type getGenericType() {
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java
index a5e528b..423520d 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java
@@ -23,7 +23,6 @@ import java.lang.annotation.ElementType;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Executable;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -38,7 +37,6 @@ import javax.validation.ConstraintValidator;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.ValidationException;
-import javax.validation.groups.Default;
import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.Scope;
import javax.validation.metadata.ValidateUnwrappedValue;
@@ -91,7 +89,7 @@ public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A
this.meta = Validate.notNull(meta, "meta");
payload = computePayload();
- groups = computeGroups();
+ groups = set(() -> read(ConstraintAnnotationAttributes.GROUPS, Optionality.REQUIRED));
reportAsSingle = annotation.annotationType().isAnnotationPresent(ReportAsSingleViolation.class);
valueUnwrapping = computeValidateUnwrappedValue();
attributes = AnnotationsManager.readAttributes(annotation);
@@ -218,14 +216,6 @@ public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A
return skip ? ValidateUnwrappedValue.SKIP : ValidateUnwrappedValue.DEFAULT;
}
- private Set<Class<?>> computeGroups() {
- final Class<?>[] groups = read(ConstraintAnnotationAttributes.GROUPS, Optionality.REQUIRED);
- if (groups.length == 0) {
- return Collections.singleton(Default.class);
- }
- return set(() -> groups);
- }
-
private Set<Class<? extends Payload>> computePayload() {
final Set<Class<? extends Payload>> result =
set(() -> read(ConstraintAnnotationAttributes.PAYLOAD, Optionality.REQUIRED));
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java
index dff4f77..9495f7a 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/DescriptorManager.java
@@ -43,6 +43,12 @@ public class DescriptorManager {
|| !descriptor.getConstrainedContainerElementTypes().isEmpty());
}
+ public static <D extends ElementDescriptor & CascadableDescriptor & ContainerDescriptor> boolean isCascaded(
+ D descriptor) {
+ return descriptor != null && (descriptor.isCascaded()
+ || descriptor.getConstrainedContainerElementTypes().stream().anyMatch(DescriptorManager::isCascaded));
+ }
+
public static <E extends ExecutableDescriptor> boolean isConstrained(E descriptor) {
return descriptor != null && (descriptor.hasConstrainedParameters() || descriptor.hasConstrainedReturnValue());
}
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/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 e7c81c6..9fb5c98 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
@@ -22,13 +22,13 @@ import java.lang.annotation.ElementType;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.ElementDescriptor;
+import org.apache.bval.jsr.groups.GroupStrategy;
import org.apache.bval.jsr.groups.GroupsComputer;
import org.apache.bval.jsr.metadata.Meta;
import org.apache.bval.util.Validate;
@@ -67,8 +67,8 @@ public abstract class ElementD<E extends AnnotatedElement, R extends MetadataRea
}
@Override
- public final List<Class<?>> getGroupSequence() {
- return getBean().getGroupSequence();
+ public final GroupStrategy getGroupStrategy() {
+ return getBean().getGroupStrategy();
}
}
@@ -117,7 +117,7 @@ public abstract class ElementD<E extends AnnotatedElement, R extends MetadataRea
public abstract Type getGenericType();
- public abstract List<Class<?>> getGroupSequence();
+ public abstract GroupStrategy getGroupStrategy();
@Override
public String toString() {
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/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 ad5d541..600b44e 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
@@ -48,7 +48,8 @@ import org.apache.bval.util.Validate;
class Finder implements ConstraintFinder {
private static Stream<Group> allGroups(Groups groups) {
- return Stream.concat(groups.getGroups().stream(), groups.getSequences().stream().flatMap(Collection::stream));
+ return Stream.concat(groups.getGroups().stream(),
+ groups.getSequences().stream().map(Group.Sequence::getGroups).flatMap(Collection::stream));
}
private volatile Predicate<ConstraintD<?>> groups = c -> true;
@@ -118,7 +119,9 @@ class Finder implements ConstraintFinder {
private Groups computeDefaultSequence() {
final ElementD<?, ?> element = firstAtomicElementDescriptor();
- Collection<Class<?>> redef = element.getGroupSequence();
+ Collection<Class<?>> redef =
+ element.getGroupStrategy().getGroups().stream().map(Group::getGroup).collect(Collectors.toList());
+
if (redef == null) {
return GroupsComputer.DEFAULT_GROUPS;
}
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/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 1673107..112aa81 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
@@ -26,15 +26,20 @@ import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Deque;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
@@ -51,13 +56,16 @@ import javax.validation.metadata.Scope;
import org.apache.bval.jsr.ApacheValidatorFactory;
import org.apache.bval.jsr.ConstraintAnnotationAttributes;
+import org.apache.bval.jsr.groups.Group;
import org.apache.bval.jsr.groups.GroupConversion;
+import org.apache.bval.jsr.groups.GroupStrategy;
import org.apache.bval.jsr.groups.GroupsComputer;
import org.apache.bval.jsr.metadata.ContainerElementKey;
import org.apache.bval.jsr.metadata.EmptyBuilder;
import org.apache.bval.jsr.metadata.Meta;
import org.apache.bval.jsr.metadata.MetadataBuilder;
import org.apache.bval.jsr.metadata.Signature;
+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.jsr.xml.AnnotationProxyBuilder;
@@ -65,6 +73,7 @@ import org.apache.bval.util.Exceptions;
import org.apache.bval.util.ObjectUtils;
import org.apache.bval.util.Validate;
import org.apache.bval.util.reflection.Reflection;
+import org.apache.bval.util.reflection.Reflection.Interfaces;
class MetadataReader {
@@ -79,16 +88,16 @@ class MetadataReader {
}
Set<ConstraintD<?>> getConstraints() {
- return builder.getConstraintDeclarationMap(meta).entrySet().stream().flatMap(e -> {
- final Meta<E> m = e.getKey();
- final Class<?> declaredBy = m.getDeclaringClass();
- final Scope scope = declaredBy.equals(beanClass) ? Scope.LOCAL_ELEMENT : Scope.HIERARCHY;
- return Stream.of(e.getValue())
- .peek(
+ return builder.getConstraintDeclarationMap(meta).entrySet().stream().filter(e -> e.getValue().length > 0)
+ .flatMap(e -> {
+ final Meta<E> m = e.getKey();
+ final Class<?> declaredBy = m.getDeclaringClass();
+ final Scope scope = declaredBy.equals(beanClass) ? Scope.LOCAL_ELEMENT : Scope.HIERARCHY;
+ return Stream.of(e.getValue()).peek(
c -> validatorFactory.getAnnotationsManager().validateConstraintDefinition(c.annotationType()))
- .map(c -> rewriteConstraint(c, declaredBy))
- .map(c -> new ConstraintD<>(c, scope, m, validatorFactory));
- }).collect(ToUnmodifiable.set());
+ .map(c -> rewriteConstraint(c, declaredBy))
+ .map(c -> new ConstraintD<>(c, scope, m, validatorFactory));
+ }).collect(ToUnmodifiable.set());
}
ApacheValidatorFactory getValidatorFactory() {
@@ -133,8 +142,8 @@ class MetadataReader {
beanBuilder.getFields(meta).forEach((f, builder) -> {
final Field fld = Reflection.find(meta.getHost(), t -> Reflection.getDeclaredField(t, f));
- properties.computeIfAbsent(f, descriptorList).add(new PropertyD.ForField(
- new MetadataReader.ForContainer<>(new Meta.ForField(fld), builder), parent));
+ properties.computeIfAbsent(f, descriptorList).add(
+ new PropertyD.ForField(new MetadataReader.ForContainer<>(new Meta.ForField(fld), builder), parent));
});
beanBuilder.getGetters(meta).forEach((g, builder) -> {
final Method getter = Methods.getter(meta.getHost(), g);
@@ -205,34 +214,68 @@ class MetadataReader {
return Collections.unmodifiableMap(result);
}
- List<Class<?>> getGroupSequence() {
- List<Class<?>> result = builder.getGroupSequence(meta);
+ GroupStrategy getGroupStrategy() {
final Class<T> host = meta.getHost();
- if (result == null) {
- // resolve group sequence/Default redefinition up class hierarchy:
- final Class<?> superclass = host.getSuperclass();
- if (superclass != null) {
- // attempt to mock parent sequence intent by appending this type immediately after supertype:
- result = ((ElementD<?, ?>) validatorFactory.getDescriptorManager().getBeanDescriptor(superclass))
- .getGroupSequence();
- if (result != null) {
- result = new ArrayList<>(result);
- result.add(result.indexOf(superclass) + 1, host);
+ if (host.isInterface()) {
+ return validatorFactory.getGroupsComputer().computeGroups(host).asStrategy();
+ }
+ final GroupStrategy parentStrategy = Optional.ofNullable(host.getSuperclass()).filter(JDK.negate())
+ .map(validatorFactory.getDescriptorManager()::getBeanDescriptor).map(BeanD.class::cast)
+ .map(BeanD::getGroupStrategy).orElse(null);
+
+ final List<Class<?>> groupSequence = builder.getGroupSequence(meta);
+
+ final Set<Group> parentGroups = parentStrategy == null ? null : parentStrategy.getGroups();
+
+ Group localGroup = Group.of(host);
+ if (groupSequence == null) {
+ final Set<Group> groups = new HashSet<>();
+ groups.add(localGroup);
+
+ for (Class<?> t : Reflection.hierarchy(host, Interfaces.INCLUDE)) {
+ if (JDK.test(t)) {
+ continue;
}
+ if (!t.isInterface()) {
+ continue;
+ }
+ if (AnnotationsManager.isAnnotationDirectlyPresent(t, GroupSequence.class)) {
+ continue;
+ }
+ final Group g = Group.of(t);
+ if (parentGroups != null && parentGroups.contains(g)) {
+ continue;
+ }
+ groups.add(g);
}
+ final GroupStrategy strategy = GroupStrategy.simple(groups);
+ return parentStrategy == null ? strategy : GroupStrategy.composite(strategy, parentStrategy);
}
- if (result == null) {
- return null;
+ if (groupSequence.contains(Default.class)) {
+ Exceptions.raise(GroupDefinitionException::new, "@%s for %s must not contain %s",
+ GroupSequence.class.getSimpleName(), host, Default.class.getName());
}
- if (!result.contains(host)) {
+ if (!groupSequence.contains(host)) {
Exceptions.raise(GroupDefinitionException::new, "@%s for %s must contain %<s",
GroupSequence.class.getSimpleName(), host);
}
- if (result.contains(Default.class)) {
- Exceptions.raise(GroupDefinitionException::new, "@%s for %s must not contain %s",
- GroupSequence.class.getSimpleName(), host, Default.class.getName());
+ final Group.Sequence result =
+ Group.sequence(groupSequence.stream().map(Group::of).collect(Collectors.toList()));
+
+ final Deque<Group> expanded = new ArrayDeque<>();
+ for (Class<?> t : Reflection.hierarchy(host, Interfaces.INCLUDE)) {
+ if (JDK.test(t)) {
+ continue;
+ }
+ if (t.isInterface() && AnnotationsManager.isAnnotationDirectlyPresent(t, GroupSequence.class)) {
+ continue;
+ }
+ expanded.push(Group.of(t));
}
- return Collections.unmodifiableList(result);
+ if (expanded.size() == 1) {
+ return result;
+ }
+ return result.redefining(Collections.singletonMap(localGroup, GroupStrategy.simple(expanded)));
}
}
@@ -261,7 +304,8 @@ class MetadataReader {
groupConversions.stream().map(GroupConversion::getFrom)
.forEach(from -> Exceptions.raiseIf(from.isAnnotationPresent(GroupSequence.class),
ConstraintDeclarationException::new,
- "Invalid group conversion declared on %s from group sequence %s", f -> f.args(meta.describeHost(), from)));
+ "Invalid group conversion declared on %s from group sequence %s",
+ f -> f.args(meta.describeHost(), from)));
}
return groupConversions;
}
@@ -336,7 +380,8 @@ class MetadataReader {
class ForConstructor<T> extends ForExecutable<Constructor<? extends T>, ForConstructor<T>> {
- ForConstructor(Meta<Constructor<? extends T>> meta, MetadataBuilder.ForExecutable<Constructor<? extends T>> builder) {
+ ForConstructor(Meta<Constructor<? extends T>> meta,
+ MetadataBuilder.ForExecutable<Constructor<? extends T>> builder) {
super(meta, builder);
}
@@ -346,6 +391,8 @@ class MetadataReader {
}
}
+ private static final Predicate<Class<?>> JDK = t -> t.getName().startsWith("java.");
+
private final ApacheValidatorFactory validatorFactory;
private final Class<?> beanClass;
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Group.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Group.java b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Group.java
index 6a211ed..d7dd994 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Group.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Group.java
@@ -18,19 +18,109 @@
*/
package org.apache.bval.jsr.groups;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import javax.validation.GroupDefinitionException;
import javax.validation.groups.Default;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.ObjectWrapper;
+import org.apache.bval.util.Validate;
+
/**
* Immutable object that wraps an interface representing a single group.
*/
-public final class Group {
+public final class Group implements GroupStrategy {
+ /**
+ * Models a group sequence.
+ */
+ public static final class Sequence extends GroupStrategy.Composite {
+ private static Set<Group> validGroups(Collection<Group> groups, Function<Group, ? extends GroupStrategy> mapper) {
+ final Set<Group> result = new LinkedHashSet<>();
+ final ObjectWrapper<Group> prev = new ObjectWrapper<>();
+
+ groups.stream().map(g -> Optional.of(g).<GroupStrategy> map(mapper).orElse(g)).map(GroupStrategy::getGroups)
+ .flatMap(Collection::stream).forEach(g -> {
+ // only permit duplicates if they are contiguous:
+ if (result.add(g)) {
+ prev.accept(g);
+ return;
+ }
+ if (!g.equals(prev.get())) {
+ Exceptions.raise(GroupDefinitionException::new, "Invalid group sequence %s specified", groups);
+ }
+ });
+ return result;
+ }
+
+ private final Set<Group> groups;
+
+ private Sequence(Collection<Group> groups) {
+ super(groups, true);
+ this.groups = Collections.unmodifiableSet(validGroups(groups, Function.identity()));
+ }
+
+ @Override
+ public Set<Group> getGroups() {
+ return groups;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Group sequence: %s", groups);
+ }
+
+ @Override
+ public GroupStrategy redefining(Map<Group, ? extends GroupStrategy> redefinitions) {
+ if (Collections.disjoint(redefinitions.keySet(), groups)) {
+ return this;
+ }
+ final Set<GroupStrategy> components = new LinkedHashSet<>();
+
+ final Set<Group> mappedGroups;
+ try {
+ mappedGroups = validGroups(groups, g -> {
+ final GroupStrategy result = Optional.of(g).<GroupStrategy> map(redefinitions::get).orElse(g);
+ components.add(result);
+ return result;
+ });
+ } catch (GroupDefinitionException e) {
+ throw Exceptions.create(GroupDefinitionException::new, "Could not expand %s using %s", this,
+ redefinitions);
+ }
+ if (components.equals(mappedGroups)) {
+ return new Sequence(mappedGroups);
+ }
+ return new GroupStrategy.Composite(components, ordered);
+ }
+ }
+
/**
* the Default Group
*/
public static final Group DEFAULT = new Group(Default.class);
+ public static final Group of(Class<?> group) {
+ return new Group(group);
+ }
+
+ public static final Sequence sequence(Group... groups) {
+ return sequence(Arrays.asList(groups));
+ }
+
+ public static final Sequence sequence(Collection<Group> groups) {
+ return new Sequence(groups);
+ }
+
private final Class<?> group;
/**
@@ -38,7 +128,7 @@ public final class Group {
* @param group
*/
public Group(Class<?> group) {
- this.group = group;
+ this.group = Validate.notNull(group);
}
/**
@@ -70,13 +160,7 @@ public final class Group {
*/
@Override
public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || !getClass().equals(o.getClass())) {
- return false;
- }
- return Objects.equals(group, ((Group) o).group);
+ return this == o || o != null && getClass().equals(o.getClass()) && Objects.equals(group, ((Group) o).group);
}
/**
@@ -84,6 +168,22 @@ public final class Group {
*/
@Override
public int hashCode() {
- return Objects.hashCode(group);
+ return group.hashCode();
+ }
+
+ @Override
+ public Set<Group> getGroups() {
+ return Collections.singleton(this);
+ }
+
+ @Override
+ public boolean applyTo(Predicate<GroupStrategy> operation) {
+ return operation.test(this);
+ }
+
+ @Override
+ public GroupStrategy redefining(Map<Group, ? extends GroupStrategy> redefinitions) {
+ final GroupStrategy redefined = redefinitions.get(this);
+ return redefined == null ? this : redefined;
}
}
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupStrategy.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupStrategy.java b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupStrategy.java
new file mode 100644
index 0000000..0b53a26
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/GroupStrategy.java
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr.groups;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Validate;
+
+/**
+ * Group strategy interface.
+ */
+public interface GroupStrategy {
+ public static class Simple implements GroupStrategy {
+ private final Set<Group> groups;
+
+ private Simple(Set<Group> groups) {
+ this.groups = groups;
+ }
+
+ @Override
+ public Set<Group> getGroups() {
+ return groups;
+ }
+
+ @Override
+ public GroupStrategy redefining(Map<Group, ? extends GroupStrategy> redefinitions) {
+ if (Collections.disjoint(redefinitions.keySet(), groups)) {
+ return this;
+ }
+ return groups.stream().map(g -> redefinitions.containsKey(g) ? redefinitions.get(g) : g)
+ .collect(Collectors.collectingAndThen(Collectors.toList(), GroupStrategy::composite));
+ }
+
+ @Override
+ public int hashCode() {
+ return groups.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj == this
+ || obj != null && obj.getClass().equals(getClass()) && ((Simple) obj).groups.equals(groups);
+ }
+
+ @Override
+ public String toString() {
+ return groups.toString();
+ }
+ }
+
+ public static class Composite implements GroupStrategy {
+ private final Set<? extends GroupStrategy> components;
+ protected final boolean ordered;
+
+ public Composite(Collection<? extends GroupStrategy> components, boolean ordered) {
+ Validate.isTrue(Validate.notNull(components).stream().noneMatch(Objects::isNull),
+ "null component not permitted");
+ this.components = new LinkedHashSet<>(components);
+ this.ordered = ordered;
+ }
+
+ @Override
+ public Set<Group> getGroups() {
+ return components.stream().map(GroupStrategy::getGroups).flatMap(Collection::stream)
+ .collect(ToUnmodifiable.set());
+ }
+
+ @Override
+ public GroupStrategy redefining(Map<Group, ? extends GroupStrategy> redefinitions) {
+ if (!components.isEmpty()) {
+ final Set<GroupStrategy> redef =
+ components.stream().map(cmp -> cmp.redefining(redefinitions)).collect(Collectors.toSet());
+ if (!redef.equals(components)) {
+ return new Composite(redef, ordered);
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public boolean applyTo(Predicate<GroupStrategy> operation) {
+ if (components.isEmpty()) {
+ return true;
+ }
+ final boolean applyAll = !ordered;
+ boolean result = true;
+ for (GroupStrategy gs : components) {
+ result = gs.applyTo(operation) && result;
+ if (!(applyAll || result)) {
+ return false;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(components, ordered);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || !obj.getClass().equals(getClass())) {
+ return false;
+ }
+ final Composite other = (Composite) obj;
+ return other.components.equals(components) && other.ordered == ordered;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%sordered: %s", ordered ? "" : "un", components);
+ }
+ }
+
+ public static final GroupStrategy EMPTY = new GroupStrategy() {
+
+ @Override
+ public GroupStrategy redefining(Map<Group, ? extends GroupStrategy> redefinitions) {
+ return this;
+ }
+
+ @Override
+ public Set<Group> getGroups() {
+ return Collections.emptySet();
+ }
+ };
+
+ public static GroupStrategy redefining(GroupStrategy source, Map<Group, ? extends GroupStrategy> redefinitions) {
+ Validate.notNull(source, "source");
+
+ if (!(redefinitions == null || redefinitions.isEmpty())) {
+ if (redefinitions.containsValue(null)) {
+ redefinitions = redefinitions.entrySet().stream().filter(e -> e.getValue() != null)
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+ if (!redefinitions.isEmpty()) {
+ return source.redefining(redefinitions);
+ }
+ }
+ return source;
+ }
+
+ public static GroupStrategy simple(Group... groups) {
+ return simple(Arrays.asList(groups));
+ }
+
+ public static GroupStrategy simple(Collection<? extends Group> coll) {
+ Validate.notNull(coll);
+ if (coll.size() == 1) {
+ return coll.iterator().next();
+ }
+ final Set<Group> groups = Collections.unmodifiableSet(new LinkedHashSet<>(coll));
+ return new Simple(groups);
+ }
+
+ public static GroupStrategy composite(GroupStrategy... components) {
+ return composite(Arrays.asList(components));
+ }
+
+ public static GroupStrategy composite(Collection<? extends GroupStrategy> components) {
+ if (components.isEmpty()) {
+ return EMPTY;
+ }
+ if (components.size() == 1) {
+ return components.iterator().next();
+ }
+ final Set<GroupStrategy> compressedComponents = new LinkedHashSet<>();
+
+ final Consumer<Set<Group>> addGroups = s -> {
+ if (!s.isEmpty()) {
+ compressedComponents.add(simple(s));
+ s.clear();
+ }
+ };
+ final Set<Group> unorderedGroups = new HashSet<>();
+ for (GroupStrategy component : components) {
+ if (component instanceof Composite && ((Composite) component).ordered) {
+ addGroups.accept(unorderedGroups);
+ compressedComponents.add(component);
+ continue;
+ }
+ unorderedGroups.addAll(component.getGroups());
+ }
+ addGroups.accept(unorderedGroups);
+ if (compressedComponents.size() == 1) {
+ return compressedComponents.iterator().next();
+ }
+ return new Composite(compressedComponents, false);
+ }
+
+ /**
+ * Get the associated groups.
+ * @return {@link Set} of {@link Group}
+ */
+ Set<Group> getGroups();
+
+ /**
+ * Get an equivalent strategy making group substitutions specified by {@code redefinitions}.
+ * @param redefinitions
+ * @return {@link GroupStrategy}
+ */
+ GroupStrategy redefining(Map<Group, ? extends GroupStrategy> redefinitions);
+
+ /**
+ * Apply the specified {@code boolean}-returning {@code operation}.
+ * @param operation
+ * @return {@code boolean}
+ */
+ default boolean applyTo(Predicate<GroupStrategy> operation) {
+ return operation.test(this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Groups.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Groups.java b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Groups.java
index 990cdaa..e51c47f 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Groups.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/groups/Groups.java
@@ -19,42 +19,42 @@
package org.apache.bval.jsr.groups;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
import javax.validation.GroupDefinitionException;
import org.apache.bval.util.Exceptions;
/**
- * Defines the order to validate groups during validation. with some inspiration
- * from reference implementation
+ * Defines the order to validate groups during validation. with some inspiration from reference implementation
*
* @author Roman Stumm
*/
public class Groups {
- /** The list of single groups. */
- private final List<Group> groups = new ArrayList<>();
-
- /** The list of sequences. */
- private final List<List<Group>> sequences = new ArrayList<>();
+ private final Set<Group> groups = new LinkedHashSet<>();
+ private final Set<Group.Sequence> sequences = new LinkedHashSet<>();
/**
* Get the Groups.
*
- * @return {@link List} of {@link Group}.
+ * @return {@link Set} of {@link Group}.
*/
- public List<Group> getGroups() {
- return Collections.unmodifiableList(groups);
+ public Set<Group> getGroups() {
+ return Collections.unmodifiableSet(groups);
}
/**
* Get the Group sequences.
*
- * @return {@link List} of {@link List} of {@link Group}
+ * @return {@link List} of {@link Group.Sequence}
*/
- public List<List<Group>> getSequences() {
- return Collections.unmodifiableList(sequences);
+ public Collection<Group.Sequence> getSequences() {
+ return Collections.unmodifiableSet(sequences);
}
/**
@@ -62,11 +62,10 @@ public class Groups {
*
* @param group
* to insert
+ * @return success
*/
- void insertGroup(Group group) {
- if (!groups.contains(group)) {
- groups.add(group);
- }
+ boolean insertGroup(Group group) {
+ return groups.add(group);
}
/**
@@ -74,26 +73,26 @@ public class Groups {
*
* @param groups
* {@link List} of {@link Group} to insert
+ * @return success
*/
- void insertSequence(List<Group> groups) {
- if (!(groups == null || groups.isEmpty() || sequences.contains(groups))) {
- sequences.add(Collections.unmodifiableList(groups));
- }
+ boolean insertSequence(Collection<Group> groups) {
+ return !(groups == null || groups.isEmpty()) && sequences.add(Group.sequence(groups));
}
/**
- * Assert that the default group can be expanded to
- * <code>defaultGroups</code>.
+ * Assert that the default group can be expanded to <code>defaultGroups</code>.
*
* @param defaultGroups
*/
+ @Deprecated
public void assertDefaultGroupSequenceIsExpandable(List<Group> defaultGroups) {
- for (List<Group> groupList : sequences) {
+ Consumer<List<Group>> action = (groupList) -> {
final int idx = groupList.indexOf(Group.DEFAULT);
if (idx >= 0) {
ensureExpandable(groupList, defaultGroups, idx);
}
- }
+ };
+ sequences.stream().map(Group.Sequence::getGroups).map(ArrayList::new).forEach(action);
}
private void ensureExpandable(List<Group> groupList, List<Group> defaultGroupList, int defaultGroupIndex) {
@@ -119,4 +118,11 @@ public class Groups {
defaultGroupList, groupList);
}
}
+
+ public GroupStrategy asStrategy() {
+ final List<GroupStrategy> components = new ArrayList<>();
+ components.addAll(groups);
+ components.addAll(sequences);
+ return GroupStrategy.composite(components);
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/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 3a895d4..b6b7a3a 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
@@ -81,6 +81,7 @@ public class GroupsComputer {
* @param group
* @return {@link Groups}
*/
+ @Deprecated
public final Groups computeCascadingGroups(Set<GroupConversionDescriptor> groupConversions, Class<?> group) {
final Groups preliminaryResult = computeGroups(Stream.of(group));
@@ -97,21 +98,21 @@ public class GroupsComputer {
if (simpleGroup) {
// ignore group inheritance from initial argument as that is handled elsewhere:
- result.insertGroup(preliminaryResult.getGroups().get(0));
+ result.insertGroup(preliminaryResult.getGroups().iterator().next());
} else {
// expand group sequence conversions in place:
- for (List<Group> seq : preliminaryResult.getSequences()) {
+ for (Group.Sequence seq : preliminaryResult.getSequences()) {
final List<Group> converted = new ArrayList<>();
- for (Group gg : seq) {
+ for (Group gg : seq.getGroups()) {
final Class<?> c = gg.getGroup();
if (gcMap.containsKey(c)) {
final Groups convertedGroupExpansion = computeGroups(Stream.of(gcMap.get(c)));
if (convertedGroupExpansion.getSequences().isEmpty()) {
converted.add(gg);
} else {
- convertedGroupExpansion.getSequences().stream().flatMap(Collection::stream)
- .forEach(converted::add);
+ convertedGroupExpansion.getSequences().stream().map(Group.Sequence::getGroups)
+ .flatMap(Collection::stream).forEach(converted::add);
}
}
}
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/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 af511f9..eef57f5 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
@@ -22,6 +22,7 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
@@ -39,6 +40,8 @@ import org.apache.bval.jsr.GraphContext;
import org.apache.bval.jsr.descriptor.ConstraintD;
import org.apache.bval.jsr.descriptor.CrossParameterD;
import org.apache.bval.jsr.descriptor.ParameterD;
+import org.apache.bval.jsr.groups.Group;
+import org.apache.bval.jsr.groups.GroupStrategy;
import org.apache.bval.jsr.metadata.Meta;
import org.apache.bval.jsr.util.NodeImpl;
import org.apache.bval.jsr.util.PathImpl;
@@ -127,14 +130,16 @@ public abstract class ValidateParameters<E extends Executable, T> extends Valida
}
@Override
- void process(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+ void process(GroupStrategy groups, Consumer<ConstraintViolation<T>> sink) {
Validate.notNull(sink, "sink");
final Lazy<Set<Frame<?>>> parameterFrames = new Lazy<>(this::parameterFrames);
- each(expand(group), (t, u) -> {
- validateDescriptorConstraints(t, u);
- parameterFrames.get().forEach(p -> p.validateDescriptorConstraints(t, u));
- }, sink);
- parameterFrames.get().forEach(p -> p.recurse(group, sink));
+
+ GroupStrategy.redefining(groups, Collections.singletonMap(Group.DEFAULT, descriptor.getGroupStrategy()))
+ .applyTo(noViolations(gs -> {
+ validateDescriptorConstraints(gs, sink);
+ parameterFrames.get().forEach(p -> p.validateDescriptorConstraints(gs, sink));
+ }));
+ parameterFrames.get().forEach(p -> p.recurse(groups, sink));
}
@Override
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/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 b87f98c..a489d1c 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
@@ -52,6 +52,7 @@ import org.apache.bval.jsr.descriptor.ContainerElementTypeD;
import org.apache.bval.jsr.descriptor.DescriptorManager;
import org.apache.bval.jsr.descriptor.ElementD;
import org.apache.bval.jsr.descriptor.PropertyD;
+import org.apache.bval.jsr.groups.GroupStrategy;
import org.apache.bval.jsr.metadata.ContainerElementKey;
import org.apache.bval.jsr.util.PathImpl;
import org.apache.bval.jsr.util.PathNavigation;
@@ -457,9 +458,9 @@ public final class ValidateProperty<T> extends ValidationJob<T> {
}
@Override
- void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+ void recurse(GroupStrategy groups, Consumer<ConstraintViolation<T>> sink) {
if (cascade) {
- super.recurse(group, sink);
+ super.recurse(groups, sink);
}
}
}
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/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 839c14c..b86a79d 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,6 +22,7 @@ import java.lang.reflect.Array;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collections;
+import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -29,9 +30,9 @@ import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ConcurrentSkipListSet;
-import java.util.function.BiConsumer;
+import java.util.concurrent.ConcurrentSkipListMap;
import java.util.function.Consumer;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
@@ -59,9 +60,11 @@ import org.apache.bval.jsr.descriptor.BeanD;
import org.apache.bval.jsr.descriptor.ComposedD;
import org.apache.bval.jsr.descriptor.ConstraintD;
import org.apache.bval.jsr.descriptor.ContainerElementTypeD;
+import org.apache.bval.jsr.descriptor.DescriptorManager;
import org.apache.bval.jsr.descriptor.ElementD;
import org.apache.bval.jsr.descriptor.PropertyD;
import org.apache.bval.jsr.groups.Group;
+import org.apache.bval.jsr.groups.GroupStrategy;
import org.apache.bval.jsr.groups.Groups;
import org.apache.bval.jsr.metadata.ContainerElementKey;
import org.apache.bval.jsr.util.NodeImpl;
@@ -97,28 +100,31 @@ public abstract class ValidationJob<T> {
return ValidationJob.this;
}
- void process(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+ void process(GroupStrategy groups, Consumer<ConstraintViolation<T>> sink) {
Validate.notNull(sink, "sink");
- each(expand(group), this::validateDescriptorConstraints, sink);
- recurse(group, sink);
+
+ GroupStrategy.redefining(groups, Collections.singletonMap(Group.DEFAULT, descriptor.getGroupStrategy()))
+ .applyTo(noViolations(gs -> validateDescriptorConstraints(gs, sink)));
+
+ recurse(groups, sink);
}
- void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+ void recurse(GroupStrategy groups, Consumer<ConstraintViolation<T>> sink) {
throw new UnsupportedOperationException();
}
abstract Object getBean();
- void validateDescriptorConstraints(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
- constraintsFor(descriptor, group)
+ void validateDescriptorConstraints(GroupStrategy groups, Consumer<ConstraintViolation<T>> sink) {
+ constraintsFor(descriptor, groups)
.forEach(c -> unwrap(c.getValueUnwrapping()).forEach(f -> f.validate(c, sink)));
}
private Stream<Frame<D>> unwrap(ValidateUnwrappedValue valueUnwrapping) {
if (valueUnwrapping != ValidateUnwrappedValue.SKIP && context.getValue() != null) {
final Optional<ValueExtractors.UnwrappingInfo> valueExtractorAndAssociatedContainerElementKey =
- validatorContext.getValueExtractors().
- findUnwrappingInfo(context.getValue().getClass(), valueUnwrapping);
+ validatorContext.getValueExtractors().findUnwrappingInfo(context.getValue().getClass(),
+ valueUnwrapping);
if (valueExtractorAndAssociatedContainerElementKey.isPresent()) {
return ExtractValues
@@ -132,14 +138,14 @@ public abstract class ValidationJob<T> {
@SuppressWarnings({ "rawtypes", "unchecked" })
private boolean validate(ConstraintD<?> constraint, Consumer<ConstraintViolation<T>> sink) {
- if (!validatedPathsByConstraint
- .computeIfAbsent(constraint, k -> new ConcurrentSkipListSet<>(PathImpl.PATH_COMPARATOR))
- .add(context.getPath())) {
- // seen, ignore:
+ final ConcurrentMap<Path, Set<Object>> pathMap = completedValidations.computeIfAbsent(constraint,
+ k -> new ConcurrentSkipListMap<>(PathImpl.PATH_COMPARATOR));
+ final Set<Object> objectSet =
+ pathMap.computeIfAbsent(context.getPath(), p -> Collections.newSetFromMap(new IdentityHashMap<>()));
+ if (!objectSet.add(context.getValue())) {
return true;
}
final ConstraintValidator constraintValidator = getConstraintValidator(constraint);
-
final ConstraintValidatorContextImpl<T> constraintValidatorContext =
new ConstraintValidatorContextImpl<>(this, constraint);
@@ -181,10 +187,10 @@ public abstract class ValidationJob<T> {
} : sink;
// collect validation results to set of Boolean, ensuring all are evaluated:
- final Set<Boolean> results = constraint.getComposingConstraints().stream().map(ConstraintD.class::cast)
+ final Set<Boolean> validationResults = constraint.getComposingConstraints().stream().map(ConstraintD.class::cast)
.map(c -> validate(c, effectiveSink)).collect(Collectors.toSet());
- return Collections.singleton(Boolean.TRUE).equals(results);
+ return Collections.singleton(Boolean.TRUE).equals(validationResults);
}
@SuppressWarnings({ "rawtypes" })
@@ -227,18 +233,6 @@ public abstract class ValidationJob<T> {
return extractedType.orElse(elementClass);
}
-
- Stream<Class<?>> expand(Class<?> group) {
- if (Default.class.equals(group)) {
- final List<Class<?>> groupSequence = descriptor.getGroupSequence();
- if (groupSequence != null) {
- groups.assertDefaultGroupSequenceIsExpandable(
- groupSequence.stream().map(Group::new).collect(Collectors.toList()));
- return groupSequence.stream();
- }
- }
- return Stream.of(group);
- }
}
public class BeanFrame<B> extends Frame<BeanD<B>> {
@@ -254,14 +248,25 @@ public abstract class ValidationJob<T> {
this.realContext = context;
}
- void process(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+ void process(GroupStrategy groups, Consumer<ConstraintViolation<T>> sink) {
Validate.notNull(sink, "sink");
final Lazy<Set<Frame<?>>> propertyFrames = new Lazy<>(this::propertyFrames);
- each(expand(group), (t, u) -> {
- validateDescriptorConstraints(t, u);
- propertyFrames.get().forEach(p -> p.validateDescriptorConstraints(t, u));
- }, sink);
- propertyFrames.get().forEach(p -> p.recurse(group, sink));
+
+ final GroupStrategy localGroupStrategy = GroupStrategy.redefining(groups,
+ Collections.singletonMap(Group.DEFAULT, descriptor.getGroupStrategy()));
+
+ localGroupStrategy.applyTo(noViolations(gs -> {
+ validateDescriptorConstraints(gs, sink);
+ propertyFrames.get().forEach(p -> {
+ p.validateDescriptorConstraints(gs, sink);
+ if (localGroupStrategy == groups) {
+ p.recurse(gs, sink);
+ }
+ });
+ }));
+ if (localGroupStrategy != groups) {
+ propertyFrames.get().forEach(p -> p.recurse(groups, sink));
+ }
}
protected Frame<?> propertyFrame(PropertyD<?> d, GraphContext context) {
@@ -305,54 +310,48 @@ public abstract class ValidationJob<T> {
public SproutFrame(Frame<?> parent, D descriptor, GraphContext context) {
super(parent, descriptor, context);
}
-
+
@Override
- void validateDescriptorConstraints(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
- super.validateDescriptorConstraints(group, sink);
+ void validateDescriptorConstraints(GroupStrategy groups, Consumer<ConstraintViolation<T>> sink) {
+ super.validateDescriptorConstraints(groups, sink);
if (context.getValue() != null) {
descriptor.getConstrainedContainerElementTypes().stream()
.flatMap(d -> ComposedD.unwrap(d, ContainerElementTypeD.class)).forEach(d -> {
- if (constraintsFor(d, group).findFirst().isPresent()
+ if (constraintsFor(d, groups).findFirst().isPresent()
|| !d.getConstrainedContainerElementTypes().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));
+ .forEach(f -> f.validateDescriptorConstraints(groups, sink));
}
});
}
}
@Override
- void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
- final Groups convertedGroups =
- validatorContext.getGroupsComputer().computeCascadingGroups(descriptor.getGroupConversions(),
- descriptor.getDeclaringClass().isAssignableFrom(group) ? Default.class : group);
-
- convertedGroups.getGroups().stream().map(Group::getGroup).forEach(g -> cascade(g, sink));
-
- sequences: for (List<Group> seq : convertedGroups.getSequences()) {
- final boolean proceed = each(seq.stream().map(Group::getGroup), this::cascade, sink);
- if (!proceed) {
- break sequences;
- }
- }
- }
-
- private void cascade(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
- if (context.getValue() != null) {
- descriptor.getConstrainedContainerElementTypes().stream()
- .filter(d -> d.isCascaded() || !d.getConstrainedContainerElementTypes().isEmpty())
- .flatMap(d -> ComposedD.unwrap(d, ContainerElementTypeD.class)).forEach(d -> {
- 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));
- });
+ void recurse(GroupStrategy groups, Consumer<ConstraintViolation<T>> sink) {
+ if (context.getValue() == null || !DescriptorManager.isCascaded(descriptor)) {
+ return;
}
+ final Map<Group, GroupStrategy> conversions =
+ descriptor.getGroupConversions().stream().collect(Collectors.toMap(gc -> Group.of(gc.getFrom()),
+ gc -> validatorContext.getGroupsComputer().computeGroups(gc.getTo()).asStrategy()));
+
+ GroupStrategy.redefining(groups, conversions).applyTo(noViolations(gs -> cascade(gs, sink)));
+ }
+
+ private void cascade(GroupStrategy groups, Consumer<ConstraintViolation<T>> sink) {
+ descriptor.getConstrainedContainerElementTypes().stream()
+ .filter(d -> d.isCascaded() || !d.getConstrainedContainerElementTypes().isEmpty())
+ .flatMap(d -> ComposedD.unwrap(d, ContainerElementTypeD.class)).forEach(d -> {
+ 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(groups, sink));
+ });
if (!descriptor.isCascaded()) {
return;
}
@@ -377,7 +376,7 @@ public abstract class ValidationJob<T> {
}
}
multiplex().filter(context -> context.getValue() != null && !context.isRecursive())
- .map(context -> new BeanFrame<>(this, context)).forEach(b -> b.process(group, sink));
+ .map(context -> new BeanFrame<>(this, context)).forEach(b -> b.process(groups, sink));
}
protected GraphContext getMultiplexContext() {
@@ -450,9 +449,9 @@ public abstract class ValidationJob<T> {
GraphContext context) {
super(parent, descriptor, context);
}
-
+
@Override
- void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+ void recurse(GroupStrategy groups, Consumer<ConstraintViolation<T>> sink) {
}
}
@@ -464,7 +463,7 @@ public abstract class ValidationJob<T> {
}
@Override
- void validateDescriptorConstraints(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+ void validateDescriptorConstraints(GroupStrategy groups, Consumer<ConstraintViolation<T>> sink) {
}
@Override
@@ -472,19 +471,19 @@ public abstract class ValidationJob<T> {
final PathImpl path = context.getPath();
GraphContext ancestor = context.getParent();
- Validate.validState(ancestor!= null, "Expected parent context");
+ 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");
+ Validate.validState(ancestor != null, "Expected parent context");
}
newLeaf = new NodeImpl.PropertyNodeImpl(leafNode);
newLeaf.setName(null);
@@ -508,12 +507,12 @@ public abstract class ValidationJob<T> {
}
@Override
- void validateDescriptorConstraints(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+ void validateDescriptorConstraints(GroupStrategy groups, Consumer<ConstraintViolation<T>> sink) {
throw exc.get();
}
@Override
- void recurse(Class<?> group, Consumer<ConstraintViolation<T>> sink) {
+ void recurse(GroupStrategy groups, Consumer<ConstraintViolation<T>> sink) {
throw exc.get();
}
@@ -526,21 +525,21 @@ public abstract class ValidationJob<T> {
protected static final TypeVariable<?> MAP_VALUE = Map.class.getTypeParameters()[1];
protected static final TypeVariable<?> ITERABLE_ELEMENT = Iterable.class.getTypeParameters()[0];
- private static Stream<ConstraintD<?>> constraintsFor(ElementD<?, ?> descriptor, Class<?> group) {
+ private static Stream<ConstraintD<?>> constraintsFor(ElementD<?, ?> descriptor, GroupStrategy groups) {
return descriptor.getConstraintDescriptors().stream().<ConstraintD<?>> map(ConstraintD.class::cast)
.filter(c -> {
final Set<Class<?>> constraintGroups = c.getGroups();
- return constraintGroups.contains(group)
- || constraintGroups.contains(Default.class) && c.getDeclaringClass().isAssignableFrom(group);
+ return groups.getGroups().stream().map(Group::getGroup).anyMatch(g -> constraintGroups.contains(g)
+ || constraintGroups.contains(Default.class) && c.getDeclaringClass().equals(g));
});
}
protected final ApacheFactoryContext validatorContext;
+ protected final Groups groups;
- private final Groups groups;
private final Lazy<Set<ConstraintViolation<T>>> results = new Lazy<>(LinkedHashSet::new);
- private ConcurrentMap<ConstraintD<?>, Set<Path>> validatedPathsByConstraint;
+ private ConcurrentMap<ConstraintD<?>, ConcurrentMap<Path, Set<Object>>> completedValidations;
ValidationJob(ApacheFactoryContext validatorContext, Class<?>[] groups) {
super();
@@ -558,19 +557,12 @@ public abstract class ValidationJob<T> {
final Consumer<ConstraintViolation<T>> sink = results.consumer(Set::add);
- validatedPathsByConstraint = new ConcurrentHashMap<>();
+ completedValidations = new ConcurrentHashMap<>();
try {
- groups.getGroups().stream().map(Group::getGroup).forEach(g -> baseFrame.process(g, sink));
-
- sequences: for (List<Group> seq : groups.getSequences()) {
- final boolean proceed = each(seq.stream().map(Group::getGroup), baseFrame::process, sink);
- if (!proceed) {
- break sequences;
- }
- }
+ baseFrame.process(groups.asStrategy(), sink);
} finally {
- validatedPathsByConstraint = null;
+ completedValidations = null;
}
if (results.optional().isPresent()) {
return Collections.unmodifiableSet(results.get());
@@ -579,20 +571,6 @@ public abstract class ValidationJob<T> {
return results.reset(Collections::emptySet).get();
}
- protected boolean each(Stream<Class<?>> groupSequence,
- BiConsumer<Class<?>, Consumer<ConstraintViolation<T>>> closure, Consumer<ConstraintViolation<T>> sink) {
- final Lazy<Set<ConstraintViolation<T>>> sequenceViolations = new Lazy<>(LinkedHashSet::new);
- final Consumer<ConstraintViolation<T>> addSequenceViolation = sequenceViolations.consumer(Set::add);
- for (Class<?> g : (Iterable<Class<?>>) groupSequence::iterator) {
- closure.accept(g, addSequenceViolation);
- if (sequenceViolations.optional().isPresent()) {
- sequenceViolations.get().forEach(sink);
- return false;
- }
- }
- return true;
- }
-
@SuppressWarnings("unchecked")
private <O> BeanD<O> getBeanDescriptor(Object bean) {
final Class<? extends Object> t = Proxies.classFor(Validate.notNull(bean, "bean").getClass());
@@ -621,6 +599,19 @@ public abstract class ValidationJob<T> {
return true;
}
+ protected <U> Predicate<U> noViolations(Consumer<? super U> consumer) {
+ return u -> {
+ final int originalCount = violationCount();
+ consumer.accept(u);
+ return violationCount() == originalCount;
+ };
+ }
+
+ private int violationCount() {
+ final Optional<Set<ConstraintViolation<T>>> maybeResults = results.optional();
+ return maybeResults.isPresent() ? maybeResults.get().size() : 0;
+ }
+
private final String interpolate(String messageTemplate, MessageInterpolator.Context context) {
try {
return validatorContext.getMessageInterpolator().interpolate(messageTemplate, context);
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupSequenceTest.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupSequenceTest.java b/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupSequenceTest.java
index 85e969a..cb30598 100644
--- a/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupSequenceTest.java
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupSequenceTest.java
@@ -20,13 +20,15 @@ package org.apache.bval.jsr.groups;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
+import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
+import java.util.List;
import java.util.Set;
+import java.util.function.Predicate;
import javax.validation.ConstraintViolation;
+import javax.validation.GroupDefinitionException;
import javax.validation.GroupSequence;
import javax.validation.constraints.NotNull;
@@ -45,13 +47,9 @@ import org.junit.Test;
* Description: test of group sequence behavior<br/>
*/
public class GroupSequenceTest extends ValidationTestBase {
-
- @Test
+ @Test(expected = GroupDefinitionException.class)
public void testGroupSequence1() {
- BeanD<?> bean = (BeanD<?>) ApacheValidatorFactory.getDefault().usingContext().getValidator()
- .getConstraintsForClass(GInterface1.class);
-
- assertEquals(Collections.singletonList(GInterface1.class), bean.getGroupSequence());
+ ApacheValidatorFactory.getDefault().usingContext().getValidator().getConstraintsForClass(GInterface1.class);
}
@Test
@@ -59,23 +57,40 @@ public class GroupSequenceTest extends ValidationTestBase {
BeanD<?> bean = (BeanD<?>) ApacheValidatorFactory.getDefault().usingContext().getValidator()
.getConstraintsForClass(GClass1.class);
- assertNull(bean.getGroupSequence());
+ assertEquals(Group.of(GClass1.class), bean.getGroupStrategy());
}
@Test
public void testGroupSequence3() {
BeanD<?> bean = (BeanD<?>) ApacheValidatorFactory.getDefault().usingContext().getValidator()
- .getConstraintsForClass(GClass2.class);
+ .getConstraintsForClass(GClass2.class);
+
+ class TestPredicate implements Predicate<GroupStrategy> {
+
+ final List<GroupStrategy> strategies = new ArrayList<>();
- assertEquals(Arrays.asList(GClass1.class, GClass2.class), bean.getGroupSequence());
+ @Override
+ public boolean test(GroupStrategy t) {
+ return strategies.add(t);
+ }
+ }
+
+ final TestPredicate p = new TestPredicate();
+
+ bean.getGroupStrategy().applyTo(p);
+
+ Group g1 = Group.of(GClass1.class);
+ Group g2 = Group.of(GClass2.class);
+
+ assertEquals(Arrays.asList(g1, GroupStrategy.simple(g1, g2)), p.strategies);
}
@Test
public void testGroupSequence4() {
BeanD<?> bean = (BeanD<?>) ApacheValidatorFactory.getDefault().usingContext().getValidator()
- .getConstraintsForClass(GClass3.class);
+ .getConstraintsForClass(GClass3.class);
- assertEquals(Arrays.asList(GClass3.class, GClass1.class), bean.getGroupSequence());
+ assertEquals(Group.sequence(Group.of(GClass3.class), Group.of(GClass1.class)), bean.getGroupStrategy());
}
@Test
@@ -188,5 +203,4 @@ public class GroupSequenceTest extends ValidationTestBase {
interface Group1 {
}
}
-
}
http://git-wip-us.apache.org/repos/asf/bval/blob/657def16/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupsComputerTest.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupsComputerTest.java b/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupsComputerTest.java
index 092d527..62fb18a 100644
--- a/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupsComputerTest.java
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/groups/GroupsComputerTest.java
@@ -23,7 +23,6 @@ import static org.junit.Assert.assertEquals;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
import java.util.Set;
import javax.validation.GroupDefinitionException;
@@ -64,7 +63,7 @@ public class GroupsComputerTest {
@Test
public void testGroupChainForEmptySet() {
- assertEquals(Collections.singletonList(Group.DEFAULT),
+ assertEquals(Collections.singleton(Group.DEFAULT),
groupsComputer.computeGroups(new HashSet<Class<?>>()).getGroups());
}
@@ -106,10 +105,10 @@ public class GroupsComputerTest {
Set<Class<?>> groups = new HashSet<Class<?>>();
groups.add(Address.Complete.class);
Groups chain = groupsComputer.computeGroups(groups);
- Iterator<List<Group>> sequences = chain.getSequences().iterator();
- List<Group> sequence = sequences.next();
+ Iterator<Group.Sequence> sequences = chain.getSequences().iterator();
+ Iterator<Group> sequence = sequences.next().getGroups().iterator();
- assertEquals(Default.class, sequence.get(0).getGroup());
- assertEquals(Address.HighLevelCoherence.class, sequence.get(1).getGroup());
+ assertEquals(Default.class, sequence.next().getGroup());
+ assertEquals(Address.HighLevelCoherence.class, sequence.next().getGroup());
}
}
[2/2] bval git commit: Merge remote-tracking branch 'origin/bv2' into
bv2
Posted by mb...@apache.org.
Merge remote-tracking branch 'origin/bv2' into bv2
Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/f5bdeaf1
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/f5bdeaf1
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/f5bdeaf1
Branch: refs/heads/bv2
Commit: f5bdeaf102d148e94c41dae14348d6505a7e6a80
Parents: 657def1 d1ddb57
Author: Matt Benson <mb...@apache.org>
Authored: Thu Apr 12 13:01:19 2018 -0500
Committer: Matt Benson <mb...@apache.org>
Committed: Thu Apr 12 13:01:19 2018 -0500
----------------------------------------------------------------------
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------