Skip to content

Commit 4da2440

Browse files
committed
Polishing
Closes gh-31530
1 parent 9bbe3aa commit 4da2440

File tree

2 files changed

+57
-54
lines changed

2 files changed

+57
-54
lines changed

spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationAdapter.java

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,8 @@ private MethodValidationResult adaptViolations(
301301
Function<Integer, MethodParameter> parameterFunction,
302302
Function<Integer, Object> argumentFunction) {
303303

304-
Map<MethodParameter, ValueResultBuilder> parameterViolations = new LinkedHashMap<>();
305-
Map<CascadedViolationsKey, BeanResultBuilder> cascadedViolations = new LinkedHashMap<>();
304+
Map<MethodParameter, ParamResultBuilder> paramViolations = new LinkedHashMap<>();
305+
Map<BeanResultKey, BeanResultBuilder> beanViolations = new LinkedHashMap<>();
306306

307307
for (ConstraintViolation<Object> violation : violations) {
308308
Iterator<Path.Node> itr = violation.getPropertyPath().iterator();
@@ -321,28 +321,29 @@ else if (node.getKind().equals(ElementKind.RETURN_VALUE)) {
321321
continue;
322322
}
323323

324-
Object argument = argumentFunction.apply(parameter.getParameterIndex());
324+
Object arg = argumentFunction.apply(parameter.getParameterIndex());
325325
if (!itr.hasNext()) {
326-
parameterViolations
327-
.computeIfAbsent(parameter, p -> new ValueResultBuilder(target, parameter, argument))
326+
paramViolations
327+
.computeIfAbsent(parameter, p -> new ParamResultBuilder(target, parameter, arg))
328328
.addViolation(violation);
329329
}
330330
else {
331-
cascadedViolations
332-
.computeIfAbsent(new CascadedViolationsKey(node, violation.getLeafBean()),
333-
n -> new BeanResultBuilder(parameter, argument, itr.next(), violation.getLeafBean()))
331+
Object leafBean = violation.getLeafBean();
332+
BeanResultKey key = new BeanResultKey(node, leafBean);
333+
beanViolations
334+
.computeIfAbsent(key, k -> new BeanResultBuilder(parameter, arg, itr.next(), leafBean))
334335
.addViolation(violation);
335336
}
336337
break;
337338
}
338339
}
339340

340-
List<ParameterValidationResult> validatonResultList = new ArrayList<>();
341-
parameterViolations.forEach((parameter, builder) -> validatonResultList.add(builder.build()));
342-
cascadedViolations.forEach((violationsKey, builder) -> validatonResultList.add(builder.build()));
343-
validatonResultList.sort(resultComparator);
341+
List<ParameterValidationResult> resultList = new ArrayList<>();
342+
paramViolations.forEach((param, builder) -> resultList.add(builder.build()));
343+
beanViolations.forEach((key, builder) -> resultList.add(builder.build()));
344+
resultList.sort(resultComparator);
344345

345-
return MethodValidationResult.create(target, method, validatonResultList);
346+
return MethodValidationResult.create(target, method, resultList);
346347
}
347348

348349
private MethodParameter initMethodParameter(Method method, int index) {
@@ -373,14 +374,6 @@ private BindingResult createBindingResult(MethodParameter parameter, @Nullable O
373374
return result;
374375
}
375376

376-
/**
377-
* A unique key for the cascaded violations map. Individually, the node and leaf bean may not be unique for all
378-
* collection types ({@link Set} will have the same node and {@link List} may have the same leaf), but together
379-
* they should represent a distinct pairing.
380-
* @param node the path of the violation
381-
* @param leafBean the validated object
382-
*/
383-
record CascadedViolationsKey(Path.Node node, Object leafBean) { }
384377

385378
/**
386379
* Strategy to resolve the name of an {@code @Valid} method parameter to
@@ -403,7 +396,7 @@ public interface ObjectNameResolver {
403396
* Builds a validation result for a value method parameter with constraints
404397
* declared directly on it.
405398
*/
406-
private final class ValueResultBuilder {
399+
private final class ParamResultBuilder {
407400

408401
private final Object target;
409402

@@ -414,7 +407,7 @@ private final class ValueResultBuilder {
414407

415408
private final List<MessageSourceResolvable> resolvableErrors = new ArrayList<>();
416409

417-
public ValueResultBuilder(Object target, MethodParameter parameter, @Nullable Object argument) {
410+
public ParamResultBuilder(Object target, MethodParameter parameter, @Nullable Object argument) {
418411
this.target = target;
419412
this.parameter = parameter;
420413
this.argument = argument;
@@ -440,7 +433,7 @@ private final class BeanResultBuilder {
440433
private final MethodParameter parameter;
441434

442435
@Nullable
443-
private final Object argument;
436+
private final Object bean;
444437

445438
@Nullable
446439
private final Object container;
@@ -455,20 +448,13 @@ private final class BeanResultBuilder {
455448

456449
private final Set<ConstraintViolation<Object>> violations = new LinkedHashSet<>();
457450

458-
public BeanResultBuilder(MethodParameter parameter, @Nullable Object argument, Path.Node node, @Nullable Object leafBean) {
459-
this.parameter = parameter;
460-
451+
public BeanResultBuilder(MethodParameter param, @Nullable Object arg, Path.Node node, @Nullable Object leafBean) {
452+
this.parameter = param;
453+
this.bean = leafBean;
454+
this.container = (arg != null && !arg.equals(leafBean) ? arg : null);
461455
this.containerIndex = node.getIndex();
462456
this.containerKey = node.getKey();
463-
if (argument != null && !argument.equals(leafBean)) {
464-
this.container = argument;
465-
}
466-
else {
467-
this.container = null;
468-
}
469-
470-
this.argument = leafBean;
471-
this.errors = createBindingResult(parameter, leafBean);
457+
this.errors = createBindingResult(param, leafBean);
472458
}
473459

474460
public void addViolation(ConstraintViolation<Object> violation) {
@@ -478,12 +464,28 @@ public void addViolation(ConstraintViolation<Object> violation) {
478464
public ParameterErrors build() {
479465
validatorAdapter.get().processConstraintViolations(this.violations, this.errors);
480466
return new ParameterErrors(
481-
this.parameter, this.argument, this.errors, this.container,
467+
this.parameter, this.bean, this.errors, this.container,
482468
this.containerIndex, this.containerKey);
483469
}
484470
}
485471

486472

473+
/**
474+
* Unique key for cascaded violations associated with a bean.
475+
* <p>The bean may be an element within a container such as a List, Set, array,
476+
* Map, Optional, and others. In that case the {@link Path.Node} represents
477+
* the container element with its index or key, if applicable, while the
478+
* {@link ConstraintViolation#getLeafBean() leafBean} is the actual
479+
* element instance. The pair should be unique. For example in a Set, the
480+
* node is the same but element instances are unique. In a List or Map the
481+
* node is further qualified by an index or key while element instances
482+
* may be the same.
483+
* @param node the path to the bean associated with the violation
484+
* @param leafBean the bean instance
485+
*/
486+
record BeanResultKey(Path.Node node, Object leafBean) { }
487+
488+
487489
/**
488490
* Default algorithm to select an object name, as described in
489491
* {@link #setObjectNameResolver(ObjectNameResolver)}.

spring-context/src/main/java/org/springframework/validation/method/ParameterErrors.java

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@
3232
* {@link Errors#getAllErrors()}, but this subclass provides access to the same
3333
* as {@link FieldError}s.
3434
*
35-
* <p>When the method parameter is a multi-element container like {@link List} or
36-
* {@link java.util.Map}, a separate {@link ParameterErrors} is created for each
37-
* value for which there are validation errors. Otherwise, only a single
38-
* {@link ParameterErrors} will be created.
35+
* <p>When the method parameter is a container with multiple elements such as a
36+
* {@link List}, {@link java.util.Set}, array, {@link java.util.Map}, or others,
37+
* then a separate {@link ParameterErrors} is created for each element that has
38+
* errors. In that case, the {@link #getContainer() container},
39+
* {@link #getContainerIndex() containerIndex}, and {@link #getContainerKey() containerKey}
40+
* provide additional context.
3941
*
4042
* @author Rossen Stoyanchev
4143
* @since 6.1
@@ -70,33 +72,32 @@ public ParameterErrors(
7072

7173

7274
/**
73-
* When {@code @Valid} is declared on a container type method parameter such as
74-
* {@link java.util.Collection}, {@link java.util.Optional} or {@link java.util.Map},
75-
* this method returns the parent that contained the validated object
76-
* {@link #getArgument() argument}, while {@link #getContainerIndex()} and
77-
* {@link #getContainerKey()} returns the respective index or key if the parameter's
78-
* datatype supports such access.
75+
* When {@code @Valid} is declared on a container of elements such as
76+
* {@link java.util.Collection}, {@link java.util.Map},
77+
* {@link java.util.Optional}, and others, this method returns the container
78+
* of the validated {@link #getArgument() argument}, while
79+
* {@link #getContainerIndex()} and {@link #getContainerKey()} provide
80+
* information about the index or key if applicable.
7981
*/
8082
@Nullable
8183
public Object getContainer() {
8284
return this.container;
8385
}
8486

8587
/**
86-
* When {@code @Valid} is declared on an indexed type, such as {@link List},
87-
* this method returns the index under which the validated object
88-
* {@link #getArgument() argument} is stored in the list
89-
* {@link #getContainer() container}.
88+
* When {@code @Valid} is declared on an indexed container of elements such as
89+
* {@link List} or array, this method returns the index of the validated
90+
* {@link #getArgument() argument}.
9091
*/
9192
@Nullable
9293
public Integer getContainerIndex() {
9394
return this.containerIndex;
9495
}
9596

9697
/**
97-
* When {@code @Valid} is declared on a keyed typed, such as {@link java.util.Map},
98-
* this method returns the key under which the validated object {@link #getArgument()
99-
* argument} is stored in the map {@link #getContainer()}.
98+
* When {@code @Valid} is declared on a container of elements referenced by
99+
* key such as {@link java.util.Map}, this method returns the key of the
100+
* validated {@link #getArgument() argument}.
100101
*/
101102
@Nullable
102103
public Object getContainerKey() {

0 commit comments

Comments
 (0)