1
7 package org.hibernate.validator.internal.engine;
8
9 import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES;
10
11 import java.lang.invoke.MethodHandles;
12 import java.lang.reflect.Constructor;
13 import java.lang.reflect.Executable;
14 import java.lang.reflect.Method;
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Optional;
23 import java.util.Set;
24
25 import javax.validation.ConstraintValidatorFactory;
26 import javax.validation.ConstraintViolation;
27 import javax.validation.ElementKind;
28 import javax.validation.Path;
29 import javax.validation.TraversableResolver;
30 import javax.validation.Validator;
31 import javax.validation.executable.ExecutableValidator;
32 import javax.validation.groups.Default;
33 import javax.validation.metadata.BeanDescriptor;
34 import javax.validation.valueextraction.ValueExtractor;
35
36 import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext;
37 import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager;
38 import org.hibernate.validator.internal.engine.groups.Group;
39 import org.hibernate.validator.internal.engine.groups.GroupWithInheritance;
40 import org.hibernate.validator.internal.engine.groups.Sequence;
41 import org.hibernate.validator.internal.engine.groups.ValidationOrder;
42 import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator;
43 import org.hibernate.validator.internal.engine.path.NodeImpl;
44 import org.hibernate.validator.internal.engine.path.PathImpl;
45 import org.hibernate.validator.internal.engine.resolver.TraversableResolvers;
46 import org.hibernate.validator.internal.engine.validationcontext.BaseBeanValidationContext;
47 import org.hibernate.validator.internal.engine.validationcontext.ExecutableValidationContext;
48 import org.hibernate.validator.internal.engine.validationcontext.ValidationContextBuilder;
49 import org.hibernate.validator.internal.engine.validationcontext.ValidatorScopedContext;
50 import org.hibernate.validator.internal.engine.valuecontext.BeanValueContext;
51 import org.hibernate.validator.internal.engine.valuecontext.ValueContext;
52 import org.hibernate.validator.internal.engine.valuecontext.ValueContexts;
53 import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor;
54 import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorHelper;
55 import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager;
56 import org.hibernate.validator.internal.metadata.BeanMetaDataManager;
57 import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData;
58 import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData;
59 import org.hibernate.validator.internal.metadata.aggregated.ContainerCascadingMetaData;
60 import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData;
61 import org.hibernate.validator.internal.metadata.aggregated.ParameterMetaData;
62 import org.hibernate.validator.internal.metadata.aggregated.PropertyMetaData;
63 import org.hibernate.validator.internal.metadata.aggregated.ReturnValueMetaData;
64 import org.hibernate.validator.internal.metadata.core.MetaConstraint;
65 import org.hibernate.validator.internal.metadata.facets.Cascadable;
66 import org.hibernate.validator.internal.metadata.facets.Validatable;
67 import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind;
68 import org.hibernate.validator.internal.util.Contracts;
69 import org.hibernate.validator.internal.util.ExecutableHelper;
70 import org.hibernate.validator.internal.util.ReflectionHelper;
71 import org.hibernate.validator.internal.util.TypeHelper;
72 import org.hibernate.validator.internal.util.logging.Log;
73 import org.hibernate.validator.internal.util.logging.LoggerFactory;
74
75
84 public class ValidatorImpl implements Validator, ExecutableValidator {
85
86 private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
87
88
91 private static final Collection<Class<?>> DEFAULT_GROUPS = Collections.<Class<?>>singletonList( Default.class );
92
93
96 private final transient ValidationOrderGenerator validationOrderGenerator;
97
98
101 private final ConstraintValidatorFactory constraintValidatorFactory;
102
103
107 private final TraversableResolver traversableResolver;
108
109
113 private final BeanMetaDataManager beanMetaDataManager;
114
115
118 private final ConstraintValidatorManager constraintValidatorManager;
119
120 private final ValueExtractorManager valueExtractorManager;
121
122
125 private final ValidatorScopedContext validatorScopedContext;
126
127
131 private final HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext;
132
133 public ValidatorImpl(ConstraintValidatorFactory constraintValidatorFactory,
134 BeanMetaDataManager beanMetaDataManager,
135 ValueExtractorManager valueExtractorManager,
136 ConstraintValidatorManager constraintValidatorManager,
137 ValidationOrderGenerator validationOrderGenerator,
138 ValidatorFactoryScopedContext validatorFactoryScopedContext) {
139 this.constraintValidatorFactory = constraintValidatorFactory;
140 this.beanMetaDataManager = beanMetaDataManager;
141 this.valueExtractorManager = valueExtractorManager;
142 this.constraintValidatorManager = constraintValidatorManager;
143 this.validationOrderGenerator = validationOrderGenerator;
144 this.validatorScopedContext = new ValidatorScopedContext( validatorFactoryScopedContext );
145 this.traversableResolver = validatorFactoryScopedContext.getTraversableResolver();
146 this.constraintValidatorInitializationContext = validatorFactoryScopedContext.getConstraintValidatorInitializationContext();
147 }
148
149 @Override
150 public final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
151 Contracts.assertNotNull( object, MESSAGES.validatedObjectMustNotBeNull() );
152 sanityCheckGroups( groups );
153
154 @SuppressWarnings("unchecked")
155 Class<T> rootBeanClass = (Class<T>) object.getClass();
156 BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass );
157
158 if ( !rootBeanMetaData.hasConstraints() ) {
159 return Collections.emptySet();
160 }
161
162 BaseBeanValidationContext<T> validationContext = getValidationContextBuilder().forValidate( rootBeanClass, rootBeanMetaData, object );
163
164 ValidationOrder validationOrder = determineGroupValidationOrder( groups );
165 BeanValueContext<?, Object> valueContext = ValueContexts.getLocalExecutionContextForBean(
166 validatorScopedContext.getParameterNameProvider(),
167 object,
168 validationContext.getRootBeanMetaData(),
169 PathImpl.createRootPath()
170 );
171
172 return validateInContext( validationContext, valueContext, validationOrder );
173 }
174
175 @Override
176 public final <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups) {
177 Contracts.assertNotNull( object, MESSAGES.validatedObjectMustNotBeNull() );
178 sanityCheckPropertyPath( propertyName );
179 sanityCheckGroups( groups );
180
181 @SuppressWarnings("unchecked")
182 Class<T> rootBeanClass = (Class<T>) object.getClass();
183 BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass );
184
185 if ( !rootBeanMetaData.hasConstraints() ) {
186 return Collections.emptySet();
187 }
188
189 PathImpl propertyPath = PathImpl.createPathFromString( propertyName );
190 BaseBeanValidationContext<T> validationContext = getValidationContextBuilder().forValidateProperty( rootBeanClass, rootBeanMetaData, object,
191 propertyPath );
192
193 BeanValueContext<?, Object> valueContext = getValueContextForPropertyValidation( validationContext, propertyPath );
194
195 if ( valueContext.getCurrentBean() == null ) {
196 throw LOG.getUnableToReachPropertyToValidateException( validationContext.getRootBean(), propertyPath );
197 }
198
199 ValidationOrder validationOrder = determineGroupValidationOrder( groups );
200
201 return validateInContext( validationContext, valueContext, validationOrder );
202 }
203
204 @Override
205 public final <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups) {
206 Contracts.assertNotNull( beanType, MESSAGES.beanTypeCannotBeNull() );
207 sanityCheckPropertyPath( propertyName );
208 sanityCheckGroups( groups );
209
210 BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( beanType );
211
212 if ( !rootBeanMetaData.hasConstraints() ) {
213 return Collections.emptySet();
214 }
215
216 PathImpl propertyPath = PathImpl.createPathFromString( propertyName );
217 BaseBeanValidationContext<T> validationContext = getValidationContextBuilder().forValidateValue( beanType, rootBeanMetaData, propertyPath );
218
219 ValidationOrder validationOrder = determineGroupValidationOrder( groups );
220
221 return validateValueInContext(
222 validationContext,
223 value,
224 propertyPath,
225 validationOrder
226 );
227 }
228
229 @Override
230 public <T> Set<ConstraintViolation<T>> validateParameters(T object, Method method, Object[] parameterValues, Class<?>... groups) {
231 Contracts.assertNotNull( object, MESSAGES.validatedObjectMustNotBeNull() );
232 Contracts.assertNotNull( method, MESSAGES.validatedMethodMustNotBeNull() );
233 Contracts.assertNotNull( parameterValues, MESSAGES.validatedParameterArrayMustNotBeNull() );
234
235 return validateParameters( object, (Executable) method, parameterValues, groups );
236 }
237
238 @Override
239 public <T> Set<ConstraintViolation<T>> validateConstructorParameters(Constructor<? extends T> constructor, Object[] parameterValues, Class<?>... groups) {
240 Contracts.assertNotNull( constructor, MESSAGES.validatedConstructorMustNotBeNull() );
241 Contracts.assertNotNull( parameterValues, MESSAGES.validatedParameterArrayMustNotBeNull() );
242
243 return validateParameters( null, constructor, parameterValues, groups );
244 }
245
246 @Override
247 public <T> Set<ConstraintViolation<T>> validateConstructorReturnValue(Constructor<? extends T> constructor, T createdObject, Class<?>... groups) {
248 Contracts.assertNotNull( constructor, MESSAGES.validatedConstructorMustNotBeNull() );
249 Contracts.assertNotNull( createdObject, MESSAGES.validatedConstructorCreatedInstanceMustNotBeNull() );
250
251 return validateReturnValue( null, constructor, createdObject, groups );
252 }
253
254 @Override
255 public <T> Set<ConstraintViolation<T>> validateReturnValue(T object, Method method, Object returnValue, Class<?>... groups) {
256 Contracts.assertNotNull( object, MESSAGES.validatedObjectMustNotBeNull() );
257 Contracts.assertNotNull( method, MESSAGES.validatedMethodMustNotBeNull() );
258
259 return validateReturnValue( object, (Executable) method, returnValue, groups );
260 }
261
262 private <T> Set<ConstraintViolation<T>> validateParameters(T object, Executable executable, Object[] parameterValues, Class<?>... groups) {
263 sanityCheckGroups( groups );
264
265 @SuppressWarnings("unchecked")
266 Class<T> rootBeanClass = object != null ? (Class<T>) object.getClass() : (Class<T>) executable.getDeclaringClass();
267 BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass );
268
269 if ( !rootBeanMetaData.hasConstraints() ) {
270 return Collections.emptySet();
271 }
272
273 ExecutableValidationContext<T> validationContext = getValidationContextBuilder().forValidateParameters(
274 rootBeanClass,
275 rootBeanMetaData,
276 object,
277 executable,
278 parameterValues
279 );
280
281 ValidationOrder validationOrder = determineGroupValidationOrder( groups );
282
283 validateParametersInContext( validationContext, parameterValues, validationOrder );
284
285 return validationContext.getFailingConstraints();
286 }
287
288 private <T> Set<ConstraintViolation<T>> validateReturnValue(T object, Executable executable, Object returnValue, Class<?>... groups) {
289 sanityCheckGroups( groups );
290
291 @SuppressWarnings("unchecked")
292 Class<T> rootBeanClass = object != null ? (Class<T>) object.getClass() : (Class<T>) executable.getDeclaringClass();
293 BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass );
294
295 if ( !rootBeanMetaData.hasConstraints() ) {
296 return Collections.emptySet();
297 }
298
299 ExecutableValidationContext<T> validationContext = getValidationContextBuilder().forValidateReturnValue(
300 rootBeanClass,
301 rootBeanMetaData,
302 object,
303 executable,
304 returnValue
305 );
306
307 ValidationOrder validationOrder = determineGroupValidationOrder( groups );
308
309 validateReturnValueInContext( validationContext, object, returnValue, validationOrder );
310
311 return validationContext.getFailingConstraints();
312 }
313
314 @Override
315 public final BeanDescriptor getConstraintsForClass(Class<?> clazz) {
316 return beanMetaDataManager.getBeanMetaData( clazz ).getBeanDescriptor();
317 }
318
319 @Override
320 public final <T> T unwrap(Class<T> type) {
321
322
323
324 if ( type.isAssignableFrom( Validator.class ) ) {
325 return type.cast( this );
326 }
327
328 throw LOG.getTypeNotSupportedForUnwrappingException( type );
329 }
330
331 @Override
332 public ExecutableValidator forExecutables() {
333 return this;
334 }
335
336 private ValidationContextBuilder getValidationContextBuilder() {
337 return new ValidationContextBuilder(
338 constraintValidatorManager,
339 constraintValidatorFactory,
340 validatorScopedContext,
341 TraversableResolvers.wrapWithCachingForSingleValidation( traversableResolver, validatorScopedContext.isTraversableResolverResultCacheEnabled() ),
342 constraintValidatorInitializationContext
343 );
344 }
345
346 private void sanityCheckPropertyPath(String propertyName) {
347 if ( propertyName == null || propertyName.length() == 0 ) {
348 throw LOG.getInvalidPropertyPathException();
349 }
350 }
351
352 private void sanityCheckGroups(Class<?>[] groups) {
353 Contracts.assertNotNull( groups, MESSAGES.groupMustNotBeNull() );
354 for ( Class<?> clazz : groups ) {
355 if ( clazz == null ) {
356 throw new IllegalArgumentException( MESSAGES.groupMustNotBeNull() );
357 }
358 }
359 }
360
361 private ValidationOrder determineGroupValidationOrder(Class<?>[] groups) {
362 Collection<Class<?>> resultGroups;
363
364 if ( groups.length == 0 ) {
365 resultGroups = DEFAULT_GROUPS;
366 }
367 else {
368 resultGroups = Arrays.asList( groups );
369 }
370 return validationOrderGenerator.getValidationOrder( resultGroups );
371 }
372
373
383 private <T, U> Set<ConstraintViolation<T>> validateInContext(BaseBeanValidationContext<T> validationContext, BeanValueContext<U, Object> valueContext,
384 ValidationOrder validationOrder) {
385 if ( valueContext.getCurrentBean() == null ) {
386 return Collections.emptySet();
387 }
388
389 BeanMetaData<U> beanMetaData = valueContext.getCurrentBeanMetaData();
390 if ( beanMetaData.isDefaultGroupSequenceRedefined() ) {
391 validationOrder.assertDefaultGroupSequenceIsExpandable( beanMetaData.getDefaultGroupSequence( valueContext.getCurrentBean() ) );
392 }
393
394
395
396 Iterator<Group> groupIterator = validationOrder.getGroupIterator();
397 while ( groupIterator.hasNext() ) {
398 Group group = groupIterator.next();
399 valueContext.setCurrentGroup( group.getDefiningClass() );
400 validateConstraintsForCurrentGroup( validationContext, valueContext );
401 if ( shouldFailFast( validationContext ) ) {
402 return validationContext.getFailingConstraints();
403 }
404 }
405 groupIterator = validationOrder.getGroupIterator();
406 while ( groupIterator.hasNext() ) {
407 Group group = groupIterator.next();
408 valueContext.setCurrentGroup( group.getDefiningClass() );
409 validateCascadedConstraints( validationContext, valueContext );
410 if ( shouldFailFast( validationContext ) ) {
411 return validationContext.getFailingConstraints();
412 }
413 }
414
415
416 Iterator<Sequence> sequenceIterator = validationOrder.getSequenceIterator();
417 while ( sequenceIterator.hasNext() ) {
418 Sequence sequence = sequenceIterator.next();
419 for ( GroupWithInheritance groupOfGroups : sequence ) {
420 int numberOfViolations = validationContext.getFailingConstraints().size();
421
422 for ( Group group : groupOfGroups ) {
423 valueContext.setCurrentGroup( group.getDefiningClass() );
424
425 validateConstraintsForCurrentGroup( validationContext, valueContext );
426 if ( shouldFailFast( validationContext ) ) {
427 return validationContext.getFailingConstraints();
428 }
429
430 validateCascadedConstraints( validationContext, valueContext );
431 if ( shouldFailFast( validationContext ) ) {
432 return validationContext.getFailingConstraints();
433 }
434 }
435 if ( validationContext.getFailingConstraints().size() > numberOfViolations ) {
436 break;
437 }
438 }
439 }
440 return validationContext.getFailingConstraints();
441 }
442
443 private void validateConstraintsForCurrentGroup(BaseBeanValidationContext<?> validationContext, BeanValueContext<?, Object> valueContext) {
444
445
446 if ( !valueContext.validatingDefault() ) {
447 validateConstraintsForNonDefaultGroup( validationContext, valueContext );
448 }
449 else {
450 validateConstraintsForDefaultGroup( validationContext, valueContext );
451 }
452 }
453
454 private <U> void validateConstraintsForDefaultGroup(BaseBeanValidationContext<?> validationContext, BeanValueContext<U, Object> valueContext) {
455 final BeanMetaData<U> beanMetaData = valueContext.getCurrentBeanMetaData();
456 final Map<Class<?>, Class<?>> validatedInterfaces = new HashMap<>();
457
458
459 for ( Class<? super U> clazz : beanMetaData.getClassHierarchy() ) {
460 BeanMetaData<? super U> hostingBeanMetaData = beanMetaDataManager.getBeanMetaData( clazz );
461 boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.isDefaultGroupSequenceRedefined();
462
463
464 if ( defaultGroupSequenceIsRedefined ) {
465 Iterator<Sequence> defaultGroupSequence = hostingBeanMetaData.getDefaultValidationSequence( valueContext.getCurrentBean() );
466 Set<MetaConstraint<?>> metaConstraints = hostingBeanMetaData.getMetaConstraints();
467
468 while ( defaultGroupSequence.hasNext() ) {
469 for ( GroupWithInheritance groupOfGroups : defaultGroupSequence.next() ) {
470 boolean validationSuccessful = true;
471
472 for ( Group defaultSequenceMember : groupOfGroups ) {
473 validationSuccessful = validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz,
474 metaConstraints, defaultSequenceMember ) && validationSuccessful;
475 }
476
477 validationContext.markCurrentBeanAsProcessed( valueContext );
478
479 if ( !validationSuccessful ) {
480 break;
481 }
482 }
483 }
484 }
485
486 else {
487 Set<MetaConstraint<?>> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints();
488 validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, metaConstraints,
489 Group.DEFAULT_GROUP );
490 validationContext.markCurrentBeanAsProcessed( valueContext );
491 }
492
493
494 if ( defaultGroupSequenceIsRedefined ) {
495 break;
496 }
497 }
498 }
499
500 private <U> boolean validateConstraintsForSingleDefaultGroupElement(BaseBeanValidationContext<?> validationContext, ValueContext<U, Object> valueContext, final Map<Class<?>, Class<?>> validatedInterfaces,
501 Class<? super U> clazz, Set<MetaConstraint<?>> metaConstraints, Group defaultSequenceMember) {
502 boolean validationSuccessful = true;
503
504 valueContext.setCurrentGroup( defaultSequenceMember.getDefiningClass() );
505
506 for ( MetaConstraint<?> metaConstraint : metaConstraints ) {
507
508
509 final Class<?> declaringClass = metaConstraint.getLocation().getDeclaringClass();
510 if ( declaringClass.isInterface() ) {
511 Class<?> validatedForClass = validatedInterfaces.get( declaringClass );
512 if ( validatedForClass != null && !validatedForClass.equals( clazz ) ) {
513 continue;
514 }
515 validatedInterfaces.put( declaringClass, clazz );
516 }
517
518 boolean tmp = validateMetaConstraint( validationContext, valueContext, valueContext.getCurrentBean(), metaConstraint );
519 if ( shouldFailFast( validationContext ) ) {
520 return false;
521 }
522
523 validationSuccessful = validationSuccessful && tmp;
524 }
525 return validationSuccessful;
526 }
527
528 private void validateConstraintsForNonDefaultGroup(BaseBeanValidationContext<?> validationContext, BeanValueContext<?, Object> valueContext) {
529 validateMetaConstraints( validationContext, valueContext, valueContext.getCurrentBean(), valueContext.getCurrentBeanMetaData().getMetaConstraints() );
530 validationContext.markCurrentBeanAsProcessed( valueContext );
531 }
532
533 private void validateMetaConstraints(BaseBeanValidationContext<?> validationContext, ValueContext<?, Object> valueContext, Object parent,
534 Iterable<MetaConstraint<?>> constraints) {
535
536 for ( MetaConstraint<?> metaConstraint : constraints ) {
537 validateMetaConstraint( validationContext, valueContext, parent, metaConstraint );
538 if ( shouldFailFast( validationContext ) ) {
539 break;
540 }
541 }
542 }
543
544 private boolean validateMetaConstraint(BaseBeanValidationContext<?> validationContext, ValueContext<?, Object> valueContext, Object parent, MetaConstraint<?> metaConstraint) {
545 BeanValueContext.ValueState<Object> originalValueState = valueContext.getCurrentValueState();
546 valueContext.appendNode( metaConstraint.getLocation() );
547 boolean success = true;
548
549 if ( isValidationRequired( validationContext, valueContext, metaConstraint ) ) {
550
551 if ( parent != null ) {
552 valueContext.setCurrentValidatedValue( valueContext.getValue( parent, metaConstraint.getLocation() ) );
553 }
554
555 success = metaConstraint.validateConstraint( validationContext, valueContext );
556
557 validationContext.markConstraintProcessed( valueContext.getCurrentBean(), valueContext.getPropertyPath(), metaConstraint );
558 }
559
560
561 valueContext.resetValueState( originalValueState );
562
563 return success;
564 }
565
566
573 private void validateCascadedConstraints(BaseBeanValidationContext<?> validationContext, ValueContext<?, Object> valueContext) {
574 Validatable validatable = valueContext.getCurrentValidatable();
575 BeanValueContext.ValueState<Object> originalValueState = valueContext.getCurrentValueState();
576
577 for ( Cascadable cascadable : validatable.getCascadables() ) {
578 valueContext.appendNode( cascadable );
579
580 if ( isCascadeRequired( validationContext, valueContext.getCurrentBean(), valueContext.getPropertyPath(),
581 cascadable.getConstraintLocationKind() ) ) {
582 Object value = getCascadableValue( validationContext, valueContext.getCurrentBean(), cascadable );
583 CascadingMetaData cascadingMetaData = cascadable.getCascadingMetaData();
584
585 if ( value != null ) {
586 CascadingMetaData effectiveCascadingMetaData = cascadingMetaData.addRuntimeContainerSupport( valueExtractorManager, value.getClass() );
587
588
589 if ( effectiveCascadingMetaData.isCascading() ) {
590 validateCascadedAnnotatedObjectForCurrentGroup( value, validationContext, valueContext, effectiveCascadingMetaData );
591 }
592
593 if ( effectiveCascadingMetaData.isContainer() ) {
594 ContainerCascadingMetaData containerCascadingMetaData = effectiveCascadingMetaData.as( ContainerCascadingMetaData.class );
595
596 if ( containerCascadingMetaData.hasContainerElementsMarkedForCascading() ) {
597
598 validateCascadedContainerElementsForCurrentGroup( value, validationContext, valueContext,
599 containerCascadingMetaData.getContainerElementTypesCascadingMetaData() );
600 }
601 }
602 }
603 }
604
605
606 valueContext.resetValueState( originalValueState );
607 }
608 }
609
610 private void validateCascadedAnnotatedObjectForCurrentGroup(Object value, BaseBeanValidationContext<?> validationContext, ValueContext<?, Object> valueContext,
611 CascadingMetaData cascadingMetaData) {
612
613
614 Class<?> originalGroup = valueContext.getCurrentGroup();
615 Class<?> currentGroup = cascadingMetaData.convertGroup( originalGroup );
616
617 if ( validationContext.isBeanAlreadyValidated( value, currentGroup, valueContext.getPropertyPath() ) ||
618 shouldFailFast( validationContext ) ) {
619 return;
620 }
621
622
623
624
625 ValidationOrder validationOrder = validationOrderGenerator.getValidationOrder( currentGroup, currentGroup != originalGroup );
626
627 BeanValueContext<?, Object> cascadedValueContext = buildNewLocalExecutionContext( valueContext, value );
628
629 validateInContext( validationContext, cascadedValueContext, validationOrder );
630 }
631
632 private void validateCascadedContainerElementsForCurrentGroup(Object value, BaseBeanValidationContext<?> validationContext, ValueContext<?, ?> valueContext,
633 List<ContainerCascadingMetaData> containerElementTypesCascadingMetaData) {
634 for ( ContainerCascadingMetaData cascadingMetaData : containerElementTypesCascadingMetaData ) {
635 if ( !cascadingMetaData.isMarkedForCascadingOnAnnotatedObjectOrContainerElements() ) {
636 continue;
637 }
638
639 ValueExtractorDescriptor extractor = valueExtractorManager.getMaximallySpecificAndRuntimeContainerElementCompliantValueExtractor(
640 cascadingMetaData.getEnclosingType(),
641 cascadingMetaData.getTypeParameter(),
642 value.getClass(),
643 cascadingMetaData.getValueExtractorCandidates()
644 );
645
646 if ( extractor == null ) {
647 throw LOG.getNoValueExtractorFoundForTypeException( cascadingMetaData.getEnclosingType(), cascadingMetaData.getTypeParameter(), value.getClass() );
648 }
649
650 CascadingValueReceiver receiver = new CascadingValueReceiver( validationContext, valueContext, cascadingMetaData );
651 ValueExtractorHelper.extractValues( extractor, value, receiver );
652 }
653 }
654
655 private class CascadingValueReceiver implements ValueExtractor.ValueReceiver {
656
657 private final BaseBeanValidationContext<?> validationContext;
658 private final ValueContext<?, ?> valueContext;
659 private final ContainerCascadingMetaData cascadingMetaData;
660
661 public CascadingValueReceiver(BaseBeanValidationContext<?> validationContext, ValueContext<?, ?> valueContext, ContainerCascadingMetaData cascadingMetaData) {
662 this.validationContext = validationContext;
663 this.valueContext = valueContext;
664 this.cascadingMetaData = cascadingMetaData;
665 }
666
667 @Override
668 public void value(String nodeName, Object value) {
669 doValidate( value, nodeName );
670 }
671
672 @Override
673 public void iterableValue(String nodeName, Object value) {
674 valueContext.markCurrentPropertyAsIterable();
675 doValidate( value, nodeName );
676 }
677
678 @Override
679 public void indexedValue(String nodeName, int index, Object value) {
680 valueContext.markCurrentPropertyAsIterableAndSetIndex( index );
681 doValidate( value, nodeName );
682 }
683
684 @Override
685 public void keyedValue(String nodeName, Object key, Object value) {
686 valueContext.markCurrentPropertyAsIterableAndSetKey( key );
687 doValidate( value, nodeName );
688 }
689
690 private void doValidate(Object value, String nodeName) {
691
692
693 Class<?> originalGroup = valueContext.getCurrentGroup();
694 Class<?> currentGroup = cascadingMetaData.convertGroup( originalGroup );
695
696 if ( value == null ||
697 validationContext.isBeanAlreadyValidated( value, currentGroup, valueContext.getPropertyPath() ) ||
698 shouldFailFast( validationContext ) ) {
699 return;
700 }
701
702
703
704
705 ValidationOrder validationOrder = validationOrderGenerator.getValidationOrder( currentGroup, currentGroup != originalGroup );
706
707 BeanValueContext<?, Object> cascadedValueContext = buildNewLocalExecutionContext( valueContext, value );
708
709 if ( cascadingMetaData.getDeclaredContainerClass() != null ) {
710 cascadedValueContext.setTypeParameter( cascadingMetaData.getDeclaredContainerClass(), cascadingMetaData.getDeclaredTypeParameterIndex() );
711 }
712
713
714 if ( cascadingMetaData.isCascading() ) {
715 validateInContext( validationContext, cascadedValueContext, validationOrder );
716 }
717
718
719 if ( cascadingMetaData.hasContainerElementsMarkedForCascading() ) {
720 ValueContext<?, Object> cascadedTypeArgumentValueContext = buildNewLocalExecutionContext( valueContext, value );
721 if ( cascadingMetaData.getTypeParameter() != null ) {
722 cascadedValueContext.setTypeParameter( cascadingMetaData.getDeclaredContainerClass(), cascadingMetaData.getDeclaredTypeParameterIndex() );
723 }
724
725 if ( nodeName != null ) {
726 cascadedTypeArgumentValueContext.appendTypeParameterNode( nodeName );
727 }
728
729 validateCascadedContainerElementsInContext( value, validationContext, cascadedTypeArgumentValueContext, cascadingMetaData, validationOrder );
730 }
731 }
732 }
733
734 private void validateCascadedContainerElementsInContext(Object value, BaseBeanValidationContext<?> validationContext, ValueContext<?, ?> valueContext,
735 ContainerCascadingMetaData cascadingMetaData, ValidationOrder validationOrder) {
736 Iterator<Group> groupIterator = validationOrder.getGroupIterator();
737 while ( groupIterator.hasNext() ) {
738 Group group = groupIterator.next();
739 valueContext.setCurrentGroup( group.getDefiningClass() );
740 validateCascadedContainerElementsForCurrentGroup( value, validationContext, valueContext,
741 cascadingMetaData.getContainerElementTypesCascadingMetaData() );
742 if ( shouldFailFast( validationContext ) ) {
743 return;
744 }
745 }
746
747 Iterator<Sequence> sequenceIterator = validationOrder.getSequenceIterator();
748 while ( sequenceIterator.hasNext() ) {
749 Sequence sequence = sequenceIterator.next();
750 for ( GroupWithInheritance groupOfGroups : sequence ) {
751 int numberOfViolations = validationContext.getFailingConstraints().size();
752
753 for ( Group group : groupOfGroups ) {
754 valueContext.setCurrentGroup( group.getDefiningClass() );
755
756 validateCascadedContainerElementsForCurrentGroup( value, validationContext, valueContext,
757 cascadingMetaData.getContainerElementTypesCascadingMetaData() );
758 if ( shouldFailFast( validationContext ) ) {
759 return;
760 }
761 }
762 if ( validationContext.getFailingConstraints().size() > numberOfViolations ) {
763 break;
764 }
765 }
766 }
767 }
768
769 private BeanValueContext<?, Object> buildNewLocalExecutionContext(ValueContext<?, ?> valueContext, Object value) {
770 BeanValueContext<?, Object> newValueContext;
771 Contracts.assertNotNull( value, "value cannot be null" );
772 BeanMetaData<?> beanMetaData = beanMetaDataManager.getBeanMetaData( value.getClass() );
773 newValueContext = ValueContexts.getLocalExecutionContextForBean(
774 validatorScopedContext.getParameterNameProvider(),
775 value,
776 beanMetaData,
777 valueContext.getPropertyPath()
778 );
779 newValueContext.setCurrentValidatedValue( value );
780
781 return newValueContext;
782 }
783
784 private <T> Set<ConstraintViolation<T>> validateValueInContext(BaseBeanValidationContext<T> validationContext, Object value, PathImpl propertyPath,
785 ValidationOrder validationOrder) {
786 BeanValueContext<?, Object> valueContext = getValueContextForValueValidation( validationContext.getRootBeanClass(), propertyPath );
787 valueContext.setCurrentValidatedValue( value );
788
789 BeanMetaData<?> beanMetaData = valueContext.getCurrentBeanMetaData();
790 if ( beanMetaData.isDefaultGroupSequenceRedefined() ) {
791 validationOrder.assertDefaultGroupSequenceIsExpandable( beanMetaData.getDefaultGroupSequence( null ) );
792 }
793
794
795 Iterator<Group> groupIterator = validationOrder.getGroupIterator();
796 while ( groupIterator.hasNext() ) {
797 Group group = groupIterator.next();
798 valueContext.setCurrentGroup( group.getDefiningClass() );
799 validateConstraintsForCurrentGroup( validationContext, valueContext );
800 if ( shouldFailFast( validationContext ) ) {
801 return validationContext.getFailingConstraints();
802 }
803 }
804
805
806 Iterator<Sequence> sequenceIterator = validationOrder.getSequenceIterator();
807 while ( sequenceIterator.hasNext() ) {
808 Sequence sequence = sequenceIterator.next();
809 for ( GroupWithInheritance groupOfGroups : sequence ) {
810 int numberOfConstraintViolationsBefore = validationContext.getFailingConstraints().size();
811 for ( Group group : groupOfGroups ) {
812 valueContext.setCurrentGroup( group.getDefiningClass() );
813 validateConstraintsForCurrentGroup( validationContext, valueContext );
814 if ( shouldFailFast( validationContext ) ) {
815 return validationContext.getFailingConstraints();
816 }
817 }
818 if ( validationContext.getFailingConstraints().size() > numberOfConstraintViolationsBefore ) {
819 break;
820 }
821 }
822 }
823
824 return validationContext.getFailingConstraints();
825 }
826
827 private <T> void validateParametersInContext(ExecutableValidationContext<T> validationContext,
828 Object[] parameterValues,
829 ValidationOrder validationOrder) {
830 BeanMetaData<T> beanMetaData = validationContext.getRootBeanMetaData();
831
832 Optional<ExecutableMetaData> executableMetaDataOptional = validationContext.getExecutableMetaData();
833
834 if ( !executableMetaDataOptional.isPresent() ) {
835
836 return;
837 }
838
839 ExecutableMetaData executableMetaData = executableMetaDataOptional.get();
840
841 if ( parameterValues.length != executableMetaData.getParameterTypes().length ) {
842 throw LOG.getInvalidParameterCountForExecutableException(
843 ExecutableHelper.getExecutableAsString(
844 executableMetaData.getType().toString() + "#" + executableMetaData.getName(),
845 executableMetaData.getParameterTypes()
846 ),
847 executableMetaData.getParameterTypes().length,
848 parameterValues.length
849 );
850 }
851
852 if ( beanMetaData.isDefaultGroupSequenceRedefined() ) {
853 validationOrder.assertDefaultGroupSequenceIsExpandable(
854 beanMetaData.getDefaultGroupSequence(
855 validationContext.getRootBean()
856 )
857 );
858 }
859
860
861 Iterator<Group> groupIterator = validationOrder.getGroupIterator();
862 while ( groupIterator.hasNext() ) {
863 validateParametersForGroup( validationContext, executableMetaData, parameterValues, groupIterator.next() );
864 if ( shouldFailFast( validationContext ) ) {
865 return;
866 }
867 }
868
869 ValueContext<Object[], Object> cascadingValueContext = ValueContexts.getLocalExecutionContextForExecutable(
870 validatorScopedContext.getParameterNameProvider(),
871 parameterValues,
872 executableMetaData.getValidatableParametersMetaData(),
873 PathImpl.createPathForExecutable( executableMetaData )
874 );
875
876 groupIterator = validationOrder.getGroupIterator();
877 while ( groupIterator.hasNext() ) {
878 Group group = groupIterator.next();
879 cascadingValueContext.setCurrentGroup( group.getDefiningClass() );
880 validateCascadedConstraints( validationContext, cascadingValueContext );
881 if ( shouldFailFast( validationContext ) ) {
882 return;
883 }
884 }
885
886
887 Iterator<Sequence> sequenceIterator = validationOrder.getSequenceIterator();
888 while ( sequenceIterator.hasNext() ) {
889 Sequence sequence = sequenceIterator.next();
890 for ( GroupWithInheritance groupOfGroups : sequence ) {
891 int numberOfViolations = validationContext.getFailingConstraints().size();
892
893 for ( Group group : groupOfGroups ) {
894 validateParametersForGroup( validationContext, executableMetaData, parameterValues, group );
895 if ( shouldFailFast( validationContext ) ) {
896 return;
897 }
898
899 cascadingValueContext.setCurrentGroup( group.getDefiningClass() );
900 validateCascadedConstraints( validationContext, cascadingValueContext );
901
902 if ( shouldFailFast( validationContext ) ) {
903 return;
904 }
905 }
906
907 if ( validationContext.getFailingConstraints().size() > numberOfViolations ) {
908 break;
909 }
910 }
911 }
912 }
913
914 private <T> void validateParametersForGroup(ExecutableValidationContext<T> validationContext, ExecutableMetaData executableMetaData, Object[] parameterValues,
915 Group group) {
916 Contracts.assertNotNull( executableMetaData, "executableMetaData may not be null" );
917
918
919
920
921
922
923 if ( group.isDefaultGroup() ) {
924 Iterator<Sequence> defaultGroupSequence = validationContext.getRootBeanMetaData().getDefaultValidationSequence( validationContext.getRootBean() );
925
926 while ( defaultGroupSequence.hasNext() ) {
927 Sequence sequence = defaultGroupSequence.next();
928 int numberOfViolations = validationContext.getFailingConstraints().size();
929
930 for ( GroupWithInheritance expandedGroup : sequence ) {
931 for ( Group defaultGroupSequenceElement : expandedGroup ) {
932 validateParametersForSingleGroup( validationContext, parameterValues, executableMetaData, defaultGroupSequenceElement.getDefiningClass() );
933
934 if ( shouldFailFast( validationContext ) ) {
935 return;
936 }
937 }
938
939
940 if ( validationContext.getFailingConstraints().size() > numberOfViolations ) {
941 return;
942 }
943 }
944 }
945 }
946 else {
947 validateParametersForSingleGroup( validationContext, parameterValues, executableMetaData, group.getDefiningClass() );
948 }
949 }
950
951 private <T> void validateParametersForSingleGroup(ExecutableValidationContext<T> validationContext, Object[] parameterValues, ExecutableMetaData executableMetaData, Class<?> currentValidatedGroup) {
952 if ( !executableMetaData.getCrossParameterConstraints().isEmpty() ) {
953 ValueContext<T, Object> valueContext = getExecutableValueContext(
954 validationContext.getRootBean(), executableMetaData, executableMetaData.getValidatableParametersMetaData(), currentValidatedGroup
955 );
956
957
958 validateMetaConstraints( validationContext, valueContext, parameterValues, executableMetaData.getCrossParameterConstraints() );
959 if ( shouldFailFast( validationContext ) ) {
960 return;
961 }
962 }
963
964 ValueContext<T, Object> valueContext = getExecutableValueContext(
965 validationContext.getRootBean(), executableMetaData, executableMetaData.getValidatableParametersMetaData(), currentValidatedGroup
966 );
967
968
969 for ( int i = 0; i < parameterValues.length; i++ ) {
970 ParameterMetaData parameterMetaData = executableMetaData.getParameterMetaData( i );
971 Object value = parameterValues[i];
972
973 if ( value != null ) {
974 Class<?> valueType = value.getClass();
975 if ( parameterMetaData.getType() instanceof Class && ( (Class<?>) parameterMetaData.getType() ).isPrimitive() ) {
976 valueType = ReflectionHelper.unBoxedType( valueType );
977 }
978 if ( !TypeHelper.isAssignable(
979 TypeHelper.getErasedType( parameterMetaData.getType() ),
980 valueType
981 ) ) {
982 throw LOG.getParameterTypesDoNotMatchException(
983 valueType,
984 parameterMetaData.getType(),
985 i,
986 validationContext.getExecutable()
987 );
988 }
989 }
990
991 validateMetaConstraints( validationContext, valueContext, parameterValues, parameterMetaData );
992 if ( shouldFailFast( validationContext ) ) {
993 return;
994 }
995 }
996 }
997
998 private <T> ValueContext<T, Object> getExecutableValueContext(T object, ExecutableMetaData executableMetaData, Validatable validatable, Class<?> group) {
999 ValueContext<T, Object> valueContext;
1000
1001 valueContext = ValueContexts.getLocalExecutionContextForExecutable(
1002 validatorScopedContext.getParameterNameProvider(),
1003 object,
1004 validatable,
1005 PathImpl.createPathForExecutable( executableMetaData )
1006 );
1007
1008 valueContext.setCurrentGroup( group );
1009
1010 return valueContext;
1011 }
1012
1013 private <V, T> void validateReturnValueInContext(ExecutableValidationContext<T> validationContext, T bean, V value, ValidationOrder validationOrder) {
1014 BeanMetaData<T> beanMetaData = validationContext.getRootBeanMetaData();
1015
1016 Optional<ExecutableMetaData> executableMetaDataOptional = validationContext.getExecutableMetaData();
1017
1018 if ( !executableMetaDataOptional.isPresent() ) {
1019
1020 return;
1021 }
1022
1023 ExecutableMetaData executableMetaData = executableMetaDataOptional.get();
1024
1025 if ( beanMetaData.isDefaultGroupSequenceRedefined() ) {
1026 validationOrder.assertDefaultGroupSequenceIsExpandable( beanMetaData.getDefaultGroupSequence( bean ) );
1027 }
1028
1029 Iterator<Group> groupIterator = validationOrder.getGroupIterator();
1030
1031
1032 while ( groupIterator.hasNext() ) {
1033 validateReturnValueForGroup( validationContext, executableMetaData, bean, value, groupIterator.next() );
1034 if ( shouldFailFast( validationContext ) ) {
1035 return;
1036 }
1037 }
1038
1039 ValueContext<V, Object> cascadingValueContext = null;
1040
1041 boolean isCascadingRequired = value != null && executableMetaData.isCascading();
1042
1043 if ( isCascadingRequired ) {
1044 cascadingValueContext = ValueContexts.getLocalExecutionContextForExecutable(
1045 validatorScopedContext.getParameterNameProvider(),
1046 value,
1047 executableMetaData.getReturnValueMetaData(),
1048 PathImpl.createPathForExecutable( executableMetaData )
1049 );
1050
1051 groupIterator = validationOrder.getGroupIterator();
1052 while ( groupIterator.hasNext() ) {
1053 Group group = groupIterator.next();
1054 cascadingValueContext.setCurrentGroup( group.getDefiningClass() );
1055 validateCascadedConstraints( validationContext, cascadingValueContext );
1056 if ( shouldFailFast( validationContext ) ) {
1057 return;
1058 }
1059 }
1060 }
1061
1062
1063 Iterator<Sequence> sequenceIterator = validationOrder.getSequenceIterator();
1064 while ( sequenceIterator.hasNext() ) {
1065 Sequence sequence = sequenceIterator.next();
1066 for ( GroupWithInheritance groupOfGroups : sequence ) {
1067 int numberOfFailingConstraintsBeforeGroup = validationContext.getFailingConstraints().size();
1068 for ( Group group : groupOfGroups ) {
1069 validateReturnValueForGroup( validationContext, executableMetaData, bean, value, group );
1070 if ( shouldFailFast( validationContext ) ) {
1071 return;
1072 }
1073
1074 if ( isCascadingRequired ) {
1075 cascadingValueContext.setCurrentGroup( group.getDefiningClass() );
1076 validateCascadedConstraints( validationContext, cascadingValueContext );
1077
1078 if ( shouldFailFast( validationContext ) ) {
1079 return;
1080 }
1081 }
1082 }
1083
1084 if ( validationContext.getFailingConstraints().size() > numberOfFailingConstraintsBeforeGroup ) {
1085 break;
1086 }
1087 }
1088 }
1089 }
1090
1091
1092 private <T> void validateReturnValueForGroup(BaseBeanValidationContext<T> validationContext, ExecutableMetaData executableMetaData, T bean, Object value,
1093 Group group) {
1094 Contracts.assertNotNull( executableMetaData, "executableMetaData may not be null" );
1095
1096
1097
1098
1099
1100
1101
1102 if ( group.isDefaultGroup() ) {
1103 Iterator<Sequence> defaultGroupSequence = validationContext.getRootBeanMetaData().getDefaultValidationSequence( bean );
1104
1105 while ( defaultGroupSequence.hasNext() ) {
1106 Sequence sequence = defaultGroupSequence.next();
1107 int numberOfViolations = validationContext.getFailingConstraints().size();
1108
1109 for ( GroupWithInheritance expandedGroup : sequence ) {
1110 for ( Group defaultGroupSequenceElement : expandedGroup ) {
1111 validateReturnValueForSingleGroup( validationContext, executableMetaData, bean, value, defaultGroupSequenceElement.getDefiningClass() );
1112
1113 if ( shouldFailFast( validationContext ) ) {
1114 return;
1115 }
1116 }
1117
1118
1119 if ( validationContext.getFailingConstraints().size() > numberOfViolations ) {
1120 return;
1121 }
1122 }
1123 }
1124 }
1125 else {
1126 validateReturnValueForSingleGroup( validationContext, executableMetaData, bean, value, group.getDefiningClass() );
1127 }
1128 }
1129
1130 private <T> void validateReturnValueForSingleGroup(BaseBeanValidationContext<T> validationContext, ExecutableMetaData executableMetaData, T bean, Object value, Class<?> oneGroup) {
1131
1132 ValueContext<?, Object> valueContext = getExecutableValueContext(
1133 executableMetaData.getKind() == ElementKind.CONSTRUCTOR ? value : bean,
1134 executableMetaData,
1135 executableMetaData.getReturnValueMetaData(),
1136 oneGroup
1137 );
1138
1139 ReturnValueMetaData returnValueMetaData = executableMetaData.getReturnValueMetaData();
1140
1141 validateMetaConstraints( validationContext, valueContext, value, returnValueMetaData );
1142 }
1143
1144
1154 private <V> BeanValueContext<?, V> getValueContextForPropertyValidation(BaseBeanValidationContext<?> validationContext, PathImpl propertyPath) {
1155 Class<?> clazz = validationContext.getRootBeanClass();
1156 BeanMetaData<?> beanMetaData = validationContext.getRootBeanMetaData();
1157 Object value = validationContext.getRootBean();
1158 PropertyMetaData propertyMetaData = null;
1159
1160 Iterator<Path.Node> propertyPathIter = propertyPath.iterator();
1161
1162 while ( propertyPathIter.hasNext() ) {
1163
1164 NodeImpl propertyPathNode = (NodeImpl) propertyPathIter.next();
1165 propertyMetaData = getBeanPropertyMetaData( beanMetaData, propertyPathNode );
1166
1167
1168 if ( propertyPathIter.hasNext() ) {
1169 if ( !propertyMetaData.isCascading() ) {
1170 throw LOG.getInvalidPropertyPathException( validationContext.getRootBeanClass(), propertyPath.asString() );
1171 }
1172
1173
1174 value = getCascadableValue( validationContext, value, propertyMetaData.getCascadables().iterator().next() );
1175 if ( value == null ) {
1176 throw LOG.getUnableToReachPropertyToValidateException( validationContext.getRootBean(), propertyPath );
1177 }
1178 clazz = value.getClass();
1179
1180
1181
1182 if ( propertyPathNode.isIterable() ) {
1183 propertyPathNode = (NodeImpl) propertyPathIter.next();
1184
1185 if ( propertyPathNode.getIndex() != null ) {
1186 value = ReflectionHelper.getIndexedValue( value, propertyPathNode.getIndex() );
1187 }
1188 else if ( propertyPathNode.getKey() != null ) {
1189 value = ReflectionHelper.getMappedValue( value, propertyPathNode.getKey() );
1190 }
1191 else {
1192 throw LOG.getPropertyPathMustProvideIndexOrMapKeyException();
1193 }
1194
1195 if ( value == null ) {
1196 throw LOG.getUnableToReachPropertyToValidateException( validationContext.getRootBean(), propertyPath );
1197 }
1198
1199 clazz = value.getClass();
1200 beanMetaData = beanMetaDataManager.getBeanMetaData( clazz );
1201 propertyMetaData = getBeanPropertyMetaData( beanMetaData, propertyPathNode );
1202 }
1203 else {
1204 beanMetaData = beanMetaDataManager.getBeanMetaData( clazz );
1205 }
1206 }
1207 }
1208
1209 if ( propertyMetaData == null ) {
1210
1211 throw LOG.getInvalidPropertyPathException( clazz, propertyPath.asString() );
1212 }
1213
1214 propertyPath.removeLeafNode();
1215
1216 return ValueContexts.getLocalExecutionContextForBean( validatorScopedContext.getParameterNameProvider(), value, beanMetaData, propertyPath );
1217 }
1218
1219
1230 private <V> BeanValueContext<?, V> getValueContextForValueValidation(Class<?> rootBeanClass,
1231 PathImpl propertyPath) {
1232 Class<?> clazz = rootBeanClass;
1233 BeanMetaData<?> beanMetaData = null;
1234 PropertyMetaData propertyMetaData = null;
1235
1236 Iterator<Path.Node> propertyPathIter = propertyPath.iterator();
1237
1238 while ( propertyPathIter.hasNext() ) {
1239
1240 NodeImpl propertyPathNode = (NodeImpl) propertyPathIter.next();
1241 beanMetaData = beanMetaDataManager.getBeanMetaData( clazz );
1242 propertyMetaData = getBeanPropertyMetaData( beanMetaData, propertyPathNode );
1243
1244
1245 if ( propertyPathIter.hasNext() ) {
1246
1247
1248 if ( propertyPathNode.isIterable() ) {
1249 propertyPathNode = (NodeImpl) propertyPathIter.next();
1250
1251 clazz = ReflectionHelper.getClassFromType( ReflectionHelper.getCollectionElementType( propertyMetaData.getType() ) );
1252 beanMetaData = beanMetaDataManager.getBeanMetaData( clazz );
1253 propertyMetaData = getBeanPropertyMetaData( beanMetaData, propertyPathNode );
1254 }
1255 else {
1256 clazz = ReflectionHelper.getClassFromType( propertyMetaData.getType() );
1257 }
1258 }
1259 }
1260
1261 if ( propertyMetaData == null ) {
1262
1263 throw LOG.getInvalidPropertyPathException( clazz, propertyPath.asString() );
1264 }
1265
1266 propertyPath.removeLeafNode();
1267
1268 return ValueContexts.getLocalExecutionContextForValueValidation( validatorScopedContext.getParameterNameProvider(), beanMetaData, propertyPath );
1269 }
1270
1271 private boolean isValidationRequired(BaseBeanValidationContext<?> validationContext,
1272 ValueContext<?, ?> valueContext,
1273 MetaConstraint<?> metaConstraint) {
1274
1275
1276
1277 if ( !validationContext.appliesTo( metaConstraint ) ) {
1278 return false;
1279 }
1280 if ( validationContext.hasMetaConstraintBeenProcessed(
1281 valueContext.getCurrentBean(),
1282 valueContext.getPropertyPath(),
1283 metaConstraint
1284 ) ) {
1285 return false;
1286 }
1287
1288 if ( !metaConstraint.getGroupList().contains( valueContext.getCurrentGroup() ) ) {
1289 return false;
1290 }
1291 return isReachable(
1292 validationContext,
1293 valueContext.getCurrentBean(),
1294 valueContext.getPropertyPath(),
1295 metaConstraint.getConstraintLocationKind()
1296 );
1297 }
1298
1299 private boolean isReachable(BaseBeanValidationContext<?> validationContext, Object traversableObject, PathImpl path,
1300 ConstraintLocationKind constraintLocationKind) {
1301 if ( needToCallTraversableResolver( path, constraintLocationKind ) ) {
1302 return true;
1303 }
1304
1305 Path pathToObject = PathImpl.createCopyWithoutLeafNode( path );
1306 try {
1307 return validationContext.getTraversableResolver().isReachable(
1308 traversableObject,
1309 path.getLeafNode(),
1310 validationContext.getRootBeanClass(),
1311 pathToObject,
1312 constraintLocationKind.getElementType()
1313 );
1314 }
1315 catch (RuntimeException e) {
1316 throw LOG.getErrorDuringCallOfTraversableResolverIsReachableException( e );
1317 }
1318 }
1319
1320 private boolean needToCallTraversableResolver(PathImpl path, ConstraintLocationKind constraintLocationKind) {
1321
1322
1323
1324
1325 return isClassLevelConstraint( constraintLocationKind )
1326 || isCrossParameterValidation( path )
1327 || isParameterValidation( path )
1328 || isReturnValueValidation( path );
1329 }
1330
1331 private boolean isCascadeRequired(BaseBeanValidationContext<?> validationContext, Object traversableObject, PathImpl path,
1332 ConstraintLocationKind constraintLocationKind) {
1333 if ( needToCallTraversableResolver( path, constraintLocationKind ) ) {
1334 return true;
1335 }
1336
1337 boolean isReachable = isReachable( validationContext, traversableObject, path, constraintLocationKind );
1338 if ( !isReachable ) {
1339 return false;
1340 }
1341
1342 Path pathToObject = PathImpl.createCopyWithoutLeafNode( path );
1343 try {
1344 return validationContext.getTraversableResolver().isCascadable(
1345 traversableObject,
1346 path.getLeafNode(),
1347 validationContext.getRootBeanClass(),
1348 pathToObject,
1349 constraintLocationKind.getElementType()
1350 );
1351 }
1352 catch (RuntimeException e) {
1353 throw LOG.getErrorDuringCallOfTraversableResolverIsCascadableException( e );
1354 }
1355 }
1356
1357 private boolean isClassLevelConstraint(ConstraintLocationKind constraintLocationKind) {
1358 return ConstraintLocationKind.TYPE.equals( constraintLocationKind );
1359 }
1360
1361 private boolean isCrossParameterValidation(PathImpl path) {
1362 return path.getLeafNode().getKind() == ElementKind.CROSS_PARAMETER;
1363 }
1364
1365 private boolean isParameterValidation(PathImpl path) {
1366 return path.getLeafNode().getKind() == ElementKind.PARAMETER;
1367 }
1368
1369 private boolean isReturnValueValidation(PathImpl path) {
1370 return path.getLeafNode().getKind() == ElementKind.RETURN_VALUE;
1371 }
1372
1373 private boolean shouldFailFast(BaseBeanValidationContext<?> validationContext) {
1374 return validationContext.isFailFastModeEnabled() && !validationContext.getFailingConstraints().isEmpty();
1375 }
1376
1377 private PropertyMetaData getBeanPropertyMetaData(BeanMetaData<?> beanMetaData, Path.Node propertyNode) {
1378 if ( !ElementKind.PROPERTY.equals( propertyNode.getKind() ) ) {
1379 throw LOG.getInvalidPropertyPathException( beanMetaData.getBeanClass(), propertyNode.getName() );
1380 }
1381
1382 return beanMetaData.getMetaDataFor( propertyNode.getName() );
1383 }
1384
1385 private Object getCascadableValue(BaseBeanValidationContext<?> validationContext, Object object, Cascadable cascadable) {
1386 return cascadable.getValue( object );
1387 }
1388 }
1389