Polishing
Closes gh-31530
This commit is contained in:
@@ -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)}.
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user