@@ -301,8 +301,8 @@ private MethodValidationResult adaptViolations(
301
301
Function <Integer , MethodParameter > parameterFunction ,
302
302
Function <Integer , Object > argumentFunction ) {
303
303
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 <>();
306
306
307
307
for (ConstraintViolation <Object > violation : violations ) {
308
308
Iterator <Path .Node > itr = violation .getPropertyPath ().iterator ();
@@ -321,28 +321,29 @@ else if (node.getKind().equals(ElementKind.RETURN_VALUE)) {
321
321
continue ;
322
322
}
323
323
324
- Object argument = argumentFunction .apply (parameter .getParameterIndex ());
324
+ Object arg = argumentFunction .apply (parameter .getParameterIndex ());
325
325
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 ))
328
328
.addViolation (violation );
329
329
}
330
330
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 ))
334
335
.addViolation (violation );
335
336
}
336
337
break ;
337
338
}
338
339
}
339
340
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 );
344
345
345
- return MethodValidationResult .create (target , method , validatonResultList );
346
+ return MethodValidationResult .create (target , method , resultList );
346
347
}
347
348
348
349
private MethodParameter initMethodParameter (Method method , int index ) {
@@ -373,14 +374,6 @@ private BindingResult createBindingResult(MethodParameter parameter, @Nullable O
373
374
return result ;
374
375
}
375
376
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 ) { }
384
377
385
378
/**
386
379
* Strategy to resolve the name of an {@code @Valid} method parameter to
@@ -403,7 +396,7 @@ public interface ObjectNameResolver {
403
396
* Builds a validation result for a value method parameter with constraints
404
397
* declared directly on it.
405
398
*/
406
- private final class ValueResultBuilder {
399
+ private final class ParamResultBuilder {
407
400
408
401
private final Object target ;
409
402
@@ -414,7 +407,7 @@ private final class ValueResultBuilder {
414
407
415
408
private final List <MessageSourceResolvable > resolvableErrors = new ArrayList <>();
416
409
417
- public ValueResultBuilder (Object target , MethodParameter parameter , @ Nullable Object argument ) {
410
+ public ParamResultBuilder (Object target , MethodParameter parameter , @ Nullable Object argument ) {
418
411
this .target = target ;
419
412
this .parameter = parameter ;
420
413
this .argument = argument ;
@@ -440,7 +433,7 @@ private final class BeanResultBuilder {
440
433
private final MethodParameter parameter ;
441
434
442
435
@ Nullable
443
- private final Object argument ;
436
+ private final Object bean ;
444
437
445
438
@ Nullable
446
439
private final Object container ;
@@ -455,20 +448,13 @@ private final class BeanResultBuilder {
455
448
456
449
private final Set <ConstraintViolation <Object >> violations = new LinkedHashSet <>();
457
450
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 );
461
455
this .containerIndex = node .getIndex ();
462
456
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 );
472
458
}
473
459
474
460
public void addViolation (ConstraintViolation <Object > violation ) {
@@ -478,12 +464,28 @@ public void addViolation(ConstraintViolation<Object> violation) {
478
464
public ParameterErrors build () {
479
465
validatorAdapter .get ().processConstraintViolations (this .violations , this .errors );
480
466
return new ParameterErrors (
481
- this .parameter , this .argument , this .errors , this .container ,
467
+ this .parameter , this .bean , this .errors , this .container ,
482
468
this .containerIndex , this .containerKey );
483
469
}
484
470
}
485
471
486
472
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
+
487
489
/**
488
490
* Default algorithm to select an object name, as described in
489
491
* {@link #setObjectNameResolver(ObjectNameResolver)}.
0 commit comments