Polishing

Closes gh-31530
This commit is contained in:
rstoyanchev
2023-11-02 11:05:07 +00:00
parent 9bbe3aa52a
commit 4da2440f63
2 changed files with 57 additions and 54 deletions

View File

@@ -301,8 +301,8 @@ public class MethodValidationAdapter implements MethodValidator {
Function<Integer, MethodParameter> parameterFunction,
Function<Integer, Object> argumentFunction) {
Map<MethodParameter, ValueResultBuilder> parameterViolations = new LinkedHashMap<>();
Map<CascadedViolationsKey, BeanResultBuilder> cascadedViolations = new LinkedHashMap<>();
Map<MethodParameter, ParamResultBuilder> paramViolations = new LinkedHashMap<>();
Map<BeanResultKey, BeanResultBuilder> beanViolations = new LinkedHashMap<>();
for (ConstraintViolation<Object> violation : violations) {
Iterator<Path.Node> itr = violation.getPropertyPath().iterator();
@@ -321,28 +321,29 @@ public class MethodValidationAdapter implements MethodValidator {
continue;
}
Object argument = argumentFunction.apply(parameter.getParameterIndex());
Object arg = argumentFunction.apply(parameter.getParameterIndex());
if (!itr.hasNext()) {
parameterViolations
.computeIfAbsent(parameter, p -> new ValueResultBuilder(target, parameter, argument))
paramViolations
.computeIfAbsent(parameter, p -> new ParamResultBuilder(target, parameter, arg))
.addViolation(violation);
}
else {
cascadedViolations
.computeIfAbsent(new CascadedViolationsKey(node, violation.getLeafBean()),
n -> new BeanResultBuilder(parameter, argument, itr.next(), violation.getLeafBean()))
Object leafBean = violation.getLeafBean();
BeanResultKey key = new BeanResultKey(node, leafBean);
beanViolations
.computeIfAbsent(key, k -> new BeanResultBuilder(parameter, arg, itr.next(), leafBean))
.addViolation(violation);
}
break;
}
}
List<ParameterValidationResult> validatonResultList = new ArrayList<>();
parameterViolations.forEach((parameter, builder) -> validatonResultList.add(builder.build()));
cascadedViolations.forEach((violationsKey, builder) -> validatonResultList.add(builder.build()));
validatonResultList.sort(resultComparator);
List<ParameterValidationResult> resultList = new ArrayList<>();
paramViolations.forEach((param, builder) -> resultList.add(builder.build()));
beanViolations.forEach((key, builder) -> resultList.add(builder.build()));
resultList.sort(resultComparator);
return MethodValidationResult.create(target, method, validatonResultList);
return MethodValidationResult.create(target, method, resultList);
}
private MethodParameter initMethodParameter(Method method, int index) {
@@ -373,14 +374,6 @@ public class MethodValidationAdapter implements MethodValidator {
return result;
}
/**
* A unique key for the cascaded violations map. Individually, the node and leaf bean may not be unique for all
* collection types ({@link Set} will have the same node and {@link List} may have the same leaf), but together
* they should represent a distinct pairing.
* @param node the path of the violation
* @param leafBean the validated object
*/
record CascadedViolationsKey(Path.Node node, Object leafBean) { }
/**
* Strategy to resolve the name of an {@code @Valid} method parameter to
@@ -403,7 +396,7 @@ public class MethodValidationAdapter implements MethodValidator {
* Builds a validation result for a value method parameter with constraints
* declared directly on it.
*/
private final class ValueResultBuilder {
private final class ParamResultBuilder {
private final Object target;
@@ -414,7 +407,7 @@ public class MethodValidationAdapter implements MethodValidator {
private final List<MessageSourceResolvable> resolvableErrors = new ArrayList<>();
public ValueResultBuilder(Object target, MethodParameter parameter, @Nullable Object argument) {
public ParamResultBuilder(Object target, MethodParameter parameter, @Nullable Object argument) {
this.target = target;
this.parameter = parameter;
this.argument = argument;
@@ -440,7 +433,7 @@ public class MethodValidationAdapter implements MethodValidator {
private final MethodParameter parameter;
@Nullable
private final Object argument;
private final Object bean;
@Nullable
private final Object container;
@@ -455,20 +448,13 @@ public class MethodValidationAdapter implements MethodValidator {
private final Set<ConstraintViolation<Object>> violations = new LinkedHashSet<>();
public BeanResultBuilder(MethodParameter parameter, @Nullable Object argument, Path.Node node, @Nullable Object leafBean) {
this.parameter = parameter;
public BeanResultBuilder(MethodParameter param, @Nullable Object arg, Path.Node node, @Nullable Object leafBean) {
this.parameter = param;
this.bean = leafBean;
this.container = (arg != null && !arg.equals(leafBean) ? arg : null);
this.containerIndex = node.getIndex();
this.containerKey = node.getKey();
if (argument != null && !argument.equals(leafBean)) {
this.container = argument;
}
else {
this.container = null;
}
this.argument = leafBean;
this.errors = createBindingResult(parameter, leafBean);
this.errors = createBindingResult(param, leafBean);
}
public void addViolation(ConstraintViolation<Object> violation) {
@@ -478,12 +464,28 @@ public class MethodValidationAdapter implements MethodValidator {
public ParameterErrors build() {
validatorAdapter.get().processConstraintViolations(this.violations, this.errors);
return new ParameterErrors(
this.parameter, this.argument, this.errors, this.container,
this.parameter, this.bean, this.errors, this.container,
this.containerIndex, this.containerKey);
}
}
/**
* Unique key for cascaded violations associated with a bean.
* <p>The bean may be an element within a container such as a List, Set, array,
* Map, Optional, and others. In that case the {@link Path.Node} represents
* the container element with its index or key, if applicable, while the
* {@link ConstraintViolation#getLeafBean() leafBean} is the actual
* element instance. The pair should be unique. For example in a Set, the
* node is the same but element instances are unique. In a List or Map the
* node is further qualified by an index or key while element instances
* may be the same.
* @param node the path to the bean associated with the violation
* @param leafBean the bean instance
*/
record BeanResultKey(Path.Node node, Object leafBean) { }
/**
* Default algorithm to select an object name, as described in
* {@link #setObjectNameResolver(ObjectNameResolver)}.

View File

@@ -32,10 +32,12 @@ import org.springframework.validation.ObjectError;
* {@link Errors#getAllErrors()}, but this subclass provides access to the same
* as {@link FieldError}s.
*
* <p>When the method parameter is a multi-element container like {@link List} or
* {@link java.util.Map}, a separate {@link ParameterErrors} is created for each
* value for which there are validation errors. Otherwise, only a single
* {@link ParameterErrors} will be created.
* <p>When the method parameter is a container with multiple elements such as a
* {@link List}, {@link java.util.Set}, array, {@link java.util.Map}, or others,
* then a separate {@link ParameterErrors} is created for each element that has
* errors. In that case, the {@link #getContainer() container},
* {@link #getContainerIndex() containerIndex}, and {@link #getContainerKey() containerKey}
* provide additional context.
*
* @author Rossen Stoyanchev
* @since 6.1
@@ -70,12 +72,12 @@ public class ParameterErrors extends ParameterValidationResult implements Errors
/**
* When {@code @Valid} is declared on a container type method parameter such as
* {@link java.util.Collection}, {@link java.util.Optional} or {@link java.util.Map},
* this method returns the parent that contained the validated object
* {@link #getArgument() argument}, while {@link #getContainerIndex()} and
* {@link #getContainerKey()} returns the respective index or key if the parameter's
* datatype supports such access.
* When {@code @Valid} is declared on a container of elements such as
* {@link java.util.Collection}, {@link java.util.Map},
* {@link java.util.Optional}, and others, this method returns the container
* of the validated {@link #getArgument() argument}, while
* {@link #getContainerIndex()} and {@link #getContainerKey()} provide
* information about the index or key if applicable.
*/
@Nullable
public Object getContainer() {
@@ -83,10 +85,9 @@ public class ParameterErrors extends ParameterValidationResult implements Errors
}
/**
* When {@code @Valid} is declared on an indexed type, such as {@link List},
* this method returns the index under which the validated object
* {@link #getArgument() argument} is stored in the list
* {@link #getContainer() container}.
* When {@code @Valid} is declared on an indexed container of elements such as
* {@link List} or array, this method returns the index of the validated
* {@link #getArgument() argument}.
*/
@Nullable
public Integer getContainerIndex() {
@@ -94,9 +95,9 @@ public class ParameterErrors extends ParameterValidationResult implements Errors
}
/**
* When {@code @Valid} is declared on a keyed typed, such as {@link java.util.Map},
* this method returns the key under which the validated object {@link #getArgument()
* argument} is stored in the map {@link #getContainer()}.
* When {@code @Valid} is declared on a container of elements referenced by
* key such as {@link java.util.Map}, this method returns the key of the
* validated {@link #getArgument() argument}.
*/
@Nullable
public Object getContainerKey() {