16
16
import java .util .Iterator ;
17
17
import java .util .List ;
18
18
import java .util .Map ;
19
+ import java .util .Optional ;
19
20
import java .util .Set ;
20
21
import java .util .stream .Collectors ;
21
22
38
39
import org .hibernate .validator .internal .engine .path .PathImpl ;
39
40
import org .hibernate .validator .internal .metadata .BeanMetaDataManager ;
40
41
import org .hibernate .validator .internal .metadata .aggregated .BeanMetaData ;
42
+ import org .hibernate .validator .internal .metadata .aggregated .ExecutableMetaData ;
41
43
import org .hibernate .validator .internal .metadata .core .MetaConstraint ;
42
44
import org .hibernate .validator .internal .util .ExecutableParameterNameProvider ;
43
45
import org .hibernate .validator .internal .util .logging .Log ;
@@ -99,6 +101,11 @@ public class ValidationContext<T> {
99
101
*/
100
102
private final Object executableReturnValue ;
101
103
104
+ /**
105
+ * The metadata of the Executable. Will be non empty if we are in the case of method validation and the method is constrained.
106
+ */
107
+ private final Optional <ExecutableMetaData > executableMetaData ;
108
+
102
109
/**
103
110
* The set of already processed meta constraints per bean - path ({@link BeanPathMetaConstraintProcessedUnit}).
104
111
*/
@@ -139,6 +146,11 @@ public class ValidationContext<T> {
139
146
*/
140
147
private final HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext ;
141
148
149
+ /**
150
+ * Indicates if the tracking of already validated bean should be disabled.
151
+ */
152
+ private final boolean disableAlreadyValidatedBeanTracking ;
153
+
142
154
/**
143
155
* The name of the validated (leaf) property in case of a validateProperty()/validateValue() call.
144
156
*/
@@ -155,7 +167,8 @@ private ValidationContext(ValidationOperation validationOperation,
155
167
BeanMetaData <T > rootBeanMetaData ,
156
168
Executable executable ,
157
169
Object [] executableParameters ,
158
- Object executableReturnValue ) {
170
+ Object executableReturnValue ,
171
+ Optional <ExecutableMetaData > executableMetaData ) {
159
172
this .validationOperation = validationOperation ;
160
173
161
174
this .constraintValidatorManager = constraintValidatorManager ;
@@ -175,6 +188,10 @@ private ValidationContext(ValidationOperation validationOperation,
175
188
this .processedPathUnits = new HashSet <>();
176
189
this .processedPathsPerBean = new IdentityHashMap <>();
177
190
this .failingConstraintViolations = newHashSet ();
191
+
192
+ this .executableMetaData = executableMetaData ;
193
+
194
+ this .disableAlreadyValidatedBeanTracking = buildDisableAlreadyValidatedBeanTracking ( validationOperation , rootBeanMetaData , executableMetaData );
178
195
}
179
196
180
197
public static ValidationContextBuilder getValidationContextBuilder (
@@ -211,6 +228,10 @@ public Executable getExecutable() {
211
228
return executable ;
212
229
}
213
230
231
+ public Optional <ExecutableMetaData > getExecutableMetaData () {
232
+ return executableMetaData ;
233
+ }
234
+
214
235
public TraversableResolver getTraversableResolver () {
215
236
return traversableResolver ;
216
237
}
@@ -259,6 +280,10 @@ public ConstraintValidatorFactory getConstraintValidatorFactory() {
259
280
}
260
281
261
282
public boolean isBeanAlreadyValidated (Object value , Class <?> group , PathImpl path ) {
283
+ if ( disableAlreadyValidatedBeanTracking ) {
284
+ return false ;
285
+ }
286
+
262
287
boolean alreadyValidated ;
263
288
alreadyValidated = isAlreadyValidatedForCurrentGroup ( value , group );
264
289
@@ -270,6 +295,10 @@ public boolean isBeanAlreadyValidated(Object value, Class<?> group, PathImpl pat
270
295
}
271
296
272
297
public void markCurrentBeanAsProcessed (ValueContext <?, ?> valueContext ) {
298
+ if ( disableAlreadyValidatedBeanTracking ) {
299
+ return ;
300
+ }
301
+
273
302
markCurrentBeanAsProcessedForCurrentGroup ( valueContext .getCurrentBean (), valueContext .getCurrentGroup () );
274
303
markCurrentBeanAsProcessedForCurrentPath ( valueContext .getCurrentBean (), valueContext .getPropertyPath () );
275
304
}
@@ -348,10 +377,22 @@ public ConstraintViolation<T> createConstraintViolation(ValueContext<?, ?> local
348
377
}
349
378
350
379
public boolean hasMetaConstraintBeenProcessed (Object bean , Path path , MetaConstraint <?> metaConstraint ) {
380
+ // this is only useful if the constraint is defined for more than 1 group as in the case it's only
381
+ // defined for one group, there is no chance it's going to be called twice.
382
+ if ( metaConstraint .isDefinedForOneGroupOnly () ) {
383
+ return false ;
384
+ }
385
+
351
386
return processedPathUnits .contains ( new BeanPathMetaConstraintProcessedUnit ( bean , path , metaConstraint ) );
352
387
}
353
388
354
389
public void markConstraintProcessed (Object bean , Path path , MetaConstraint <?> metaConstraint ) {
390
+ // this is only useful if the constraint is defined for more than 1 group as in the case it's only
391
+ // defined for one group, there is no chance it's going to be called twice.
392
+ if ( metaConstraint .isDefinedForOneGroupOnly () ) {
393
+ return ;
394
+ }
395
+
355
396
processedPathUnits .add ( new BeanPathMetaConstraintProcessedUnit ( bean , path , metaConstraint ) );
356
397
}
357
398
@@ -372,6 +413,33 @@ public String toString() {
372
413
return sb .toString ();
373
414
}
374
415
416
+ private static boolean buildDisableAlreadyValidatedBeanTracking (ValidationOperation validationOperation , BeanMetaData <?> rootBeanMetaData ,
417
+ Optional <ExecutableMetaData > executableMetaData ) {
418
+ switch ( validationOperation ) {
419
+ case BEAN_VALIDATION :
420
+ case PROPERTY_VALIDATION :
421
+ case VALUE_VALIDATION :
422
+ // note that in the case of property and value validation, we are considering the root bean, whereas we
423
+ // could consider the bean of the property or the value. We don't really have the info here though so it
424
+ // will do for now.
425
+ return !rootBeanMetaData .hasCascadingProperties ();
426
+ case PARAMETER_VALIDATION :
427
+ if ( !executableMetaData .isPresent () ) {
428
+ // the method is unconstrained so there's no need to worry about the tracking
429
+ return false ;
430
+ }
431
+ return !executableMetaData .get ().hasCascadingParameters ();
432
+ case RETURN_VALUE_VALIDATION :
433
+ if ( !executableMetaData .isPresent () ) {
434
+ // the method is unconstrained so there's no need to worry about the tracking
435
+ return false ;
436
+ }
437
+ return !executableMetaData .get ().getReturnValueMetaData ().isCascading ();
438
+ default :
439
+ return false ;
440
+ }
441
+ }
442
+
375
443
private String interpolate (String messageTemplate ,
376
444
Object validatedValue ,
377
445
ConstraintDescriptor <?> descriptor ,
@@ -487,7 +555,8 @@ public <T> ValidationContext<T> forValidate(T rootBean) {
487
555
beanMetaDataManager .getBeanMetaData ( rootBeanClass ),
488
556
null , //executable
489
557
null , //executable parameters
490
- null //executable return value
558
+ null , //executable return value
559
+ null //executable metadata
491
560
);
492
561
}
493
562
@@ -506,7 +575,8 @@ public <T> ValidationContext<T> forValidateProperty(T rootBean) {
506
575
beanMetaDataManager .getBeanMetaData ( rootBeanClass ),
507
576
null , //executable
508
577
null , //executable parameters
509
- null //executable return value
578
+ null , //executable return value
579
+ null //executable metadata
510
580
);
511
581
}
512
582
@@ -523,7 +593,8 @@ public <T> ValidationContext<T> forValidateValue(Class<T> rootBeanClass) {
523
593
beanMetaDataManager .getBeanMetaData ( rootBeanClass ),
524
594
null , //executable
525
595
null , //executable parameters
526
- null //executable return value
596
+ null , //executable return value
597
+ null //executable metadata
527
598
);
528
599
}
529
600
@@ -534,6 +605,8 @@ public <T> ValidationContext<T> forValidateParameters(
534
605
Object [] executableParameters ) {
535
606
@ SuppressWarnings ("unchecked" )
536
607
Class <T > rootBeanClass = rootBean != null ? (Class <T >) rootBean .getClass () : (Class <T >) executable .getDeclaringClass ();
608
+ BeanMetaData <T > rootBeanMetaData = beanMetaDataManager .getBeanMetaData ( rootBeanClass );
609
+
537
610
return new ValidationContext <>(
538
611
ValidationOperation .PARAMETER_VALIDATION ,
539
612
constraintValidatorManager ,
@@ -543,10 +616,11 @@ public <T> ValidationContext<T> forValidateParameters(
543
616
constraintValidatorInitializationContext ,
544
617
rootBean ,
545
618
rootBeanClass ,
546
- beanMetaDataManager . getBeanMetaData ( rootBeanClass ) ,
619
+ rootBeanMetaData ,
547
620
executable ,
548
621
executableParameters ,
549
- null //executable return value
622
+ null , //executable return value
623
+ rootBeanMetaData .getMetaDataFor ( executable )
550
624
);
551
625
}
552
626
@@ -556,6 +630,7 @@ public <T> ValidationContext<T> forValidateReturnValue(
556
630
Object executableReturnValue ) {
557
631
@ SuppressWarnings ("unchecked" )
558
632
Class <T > rootBeanClass = rootBean != null ? (Class <T >) rootBean .getClass () : (Class <T >) executable .getDeclaringClass ();
633
+ BeanMetaData <T > rootBeanMetaData = beanMetaDataManager .getBeanMetaData ( rootBeanClass );
559
634
return new ValidationContext <>(
560
635
ValidationOperation .RETURN_VALUE_VALIDATION ,
561
636
constraintValidatorManager ,
@@ -568,7 +643,8 @@ public <T> ValidationContext<T> forValidateReturnValue(
568
643
beanMetaDataManager .getBeanMetaData ( rootBeanClass ),
569
644
executable ,
570
645
null , //executable parameters
571
- executableReturnValue
646
+ executableReturnValue ,
647
+ rootBeanMetaData .getMetaDataFor ( executable )
572
648
);
573
649
}
574
650
}
0 commit comments