1 /*
2  * Hibernate Validator, declare and validate application constraints
3  *
4  * License: Apache License, Version 2.0
5  * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6  */

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 /**
76  * The main Bean Validation class. This is the core processing class of Hibernate Validator.
77  *
78  * @author Emmanuel Bernard
79  * @author Hardy Ferentschik
80  * @author Gunnar Morling
81  * @author Kevin Pollet &lt;kevin.pollet@serli.com&gt; (C) 2011 SERLI
82  * @author Guillaume Smet
83  */

84 public class ValidatorImpl implements Validator, ExecutableValidator {
85
86     private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
87
88     /**
89      * The default group array used in case any of the validate methods is called without a group.
90      */

91     private static final Collection<Class<?>> DEFAULT_GROUPS = Collections.<Class<?>>singletonList( Default.class );
92
93     /**
94      * Used to resolve the group execution order for a validate call.
95      */

96     private final transient ValidationOrderGenerator validationOrderGenerator;
97
98     /**
99      * Reference to shared {@code ConstraintValidatorFactory}.
100      */

101     private final ConstraintValidatorFactory constraintValidatorFactory;
102
103     /**
104      * {@link TraversableResolver} as passed to the constructor of this instance.
105      * Never use it directly, always use {@link TraversableResolvers#wrapWithCachingForSingleValidation(TraversableResolver, boolean)} to retrieved the single threaded caching wrapper.
106      */

107     private final TraversableResolver traversableResolver;
108
109     /**
110      * Used to get access to the bean meta data. Used to avoid to parsing the constraint configuration for each call
111      * of a given entity.
112      */

113     private final BeanMetaDataManager beanMetaDataManager;
114
115     /**
116      * Manages the life cycle of constraint validator instances
117      */

118     private final ConstraintValidatorManager constraintValidatorManager;
119
120     private final ValueExtractorManager valueExtractorManager;
121
122     /**
123      * Context containing all {@link Validator} level helpers and configuration properties.
124      */

125     private final ValidatorScopedContext validatorScopedContext;
126
127     /**
128      * The constraint initialization context is stored at this level to prevent creating a new instance each time we
129      * initialize a new constraint validator as, for now, it only contains Validator scoped objects.
130      */

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         //allow unwrapping into public super types; intentionally not exposing the
322         //fact that ExecutableValidator is implemented by this class as well as this
323         //might change
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         // if no groups is specified use the default
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     /**
374      * Validates the given object using the available context information.
375      *
376      * @param validationContext the global validation context
377      * @param valueContext the current validation context
378      * @param validationOrder Contains the information which and in which order groups have to be executed
379      * @param <T> The root bean type
380      *
381      * @return Set of constraint violations or the empty set if there were no violations.
382      */

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         // process first single groups. For these we can optimise object traversal by first running all validations on the current bean
395         // before traversing the object.
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         // now we process sequences. For sequences I have to traverse the object graph since I have to stop processing when an error occurs.
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         // we are not validating the default group there is nothing special to consider. If we are validating the default
445         // group sequence we have to consider that a class in the hierarchy could redefine the default group sequence.
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         // evaluating the constraints of a bean per class in hierarchy, this is necessary to detect potential default group re-definitions
459         for ( Class<? super U> clazz : beanMetaData.getClassHierarchy() ) {
460             BeanMetaData<? super U> hostingBeanMetaData = beanMetaDataManager.getBeanMetaData( clazz );
461             boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.isDefaultGroupSequenceRedefined();
462
463             // if the current class redefined the default group sequence, this sequence has to be applied to all the class hierarchy.
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             // fast path in case the default group sequence hasn't been redefined
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             // all constraints in the hierarchy has been validated, stop validation.
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             // HV-466, an interface implemented more than one time in the hierarchy has to be validated only one
508             // time. An interface can define more than one constraint, we have to check the class we are validating.
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         // reset the value context to the state before this call
561         valueContext.resetValueState( originalValueState );
562
563         return success;
564     }
565
566     /**
567      * Validates all cascaded constraints for the given bean using the current group set in the execution context.
568      * This method must always be called after validateConstraints for the same context.
569      *
570      * @param validationContext The execution context
571      * @param valueContext Collected information for single validation
572      */

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                     // validate cascading on the annotated object
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                             // validate cascading on the container elements
598                             validateCascadedContainerElementsForCurrentGroup( value, validationContext, valueContext,
599                                     containerCascadingMetaData.getContainerElementTypesCascadingMetaData() );
600                         }
601                     }
602                 }
603             }
604
605             // reset the value context
606             valueContext.resetValueState( originalValueState );
607         }
608     }
609
610     private void validateCascadedAnnotatedObjectForCurrentGroup(Object value, BaseBeanValidationContext<?> validationContext, ValueContext<?, Object> valueContext,
611             CascadingMetaData cascadingMetaData) {
612         // We need to convert the group before checking if the bean was processed or not
613         // as group defines the processed status.
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         // expand the group only if was created by group conversion;
623         // otherwise we're looping through the right validation order
624         // already and need only to pass the current element
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             // We need to convert the group before checking if the bean was processed or not
692             // as group defines the processed status.
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             // expand the group only if was created by group conversion;
703             // otherwise we're looping through the right validation order
704             // already and need only to pass the current element
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             // Cascade validation
714             if ( cascadingMetaData.isCascading() ) {
715                 validateInContext( validationContext, cascadedValueContext, validationOrder );
716             }
717
718             // Cascade validation to container elements if we are dealing with a container element
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         // process first single groups
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         // now process sequences, stop after the first erroneous group
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             // the method is unconstrained
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         // process first single groups
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         // now process sequences, stop after the first erroneous group
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         // TODO GM: define behavior with respect to redefined default sequences. Should only the
919         // sequence from the validated bean be honored or also default sequence definitions up in
920         // the inheritance tree?
921         // For now a redefined default sequence will only be considered if specified at the bean
922         // hosting the validated itself, but no other default sequence from parent types
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                     //stop processing after first group with errors occurred
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             // 1. validate cross-parameter constraints
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         // 2. validate parameter constraints
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             // the method is unconstrained
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         // process first single groups
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         // now process sequences, stop after the first erroneous group
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     //TODO GM: if possible integrate with validateParameterForGroup()
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         // TODO GM: define behavior with respect to redefined default sequences. Should only the
1097         // sequence from the validated bean be honored or also default sequence definitions up in
1098         // the inheritance tree?
1099         // For now a redefined default sequence will only be considered if specified at the bean
1100         // hosting the validated itself, but no other default sequence from parent types
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                     //stop processing after first group with errors occurred
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         // validate constraints at return value itself
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     /**
1145      * Returns a value context pointing to the given property path relative to the specified root class for a given
1146      * value.
1147      *
1148      * @param validationContext The validation context.
1149      * @param propertyPath The property path for which constraints have to be collected.
1150      *
1151      * @return Returns an instance of {@code ValueContext} which describes the local validation context associated to
1152      *         the given property path.
1153      */

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             // cast is ok, since we are dealing with engine internal classes
1164             NodeImpl propertyPathNode = (NodeImpl) propertyPathIter.next();
1165             propertyMetaData = getBeanPropertyMetaData( beanMetaData, propertyPathNode );
1166
1167             // if the property is not the leaf property, we set up the context for the next iteration
1168             if ( propertyPathIter.hasNext() ) {
1169                 if ( !propertyMetaData.isCascading() ) {
1170                     throw LOG.getInvalidPropertyPathException( validationContext.getRootBeanClass(), propertyPath.asString() );
1171                 }
1172
1173                 // TODO which cascadable???
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                 // if we are in the case of an iterable and we want to validate an element of this iterable, we have to get the
1181                 // element value
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             // should only happen if the property path is empty, which should never happen
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     /**
1220      * Returns a value context pointing to the given property path relative to the specified root class without a value.
1221      * <p>
1222      * We are only able to use the static types as we don't have the value.
1223      * </p>
1224      *
1225      * @param rootBeanClass The class of the root bean.
1226      * @param propertyPath The property path for which constraints have to be collected.
1227      * @return Returns an instance of {@code ValueContext} which describes the local validation context associated to
1228      * the given property path.
1229      */

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             // cast is ok, since we are dealing with engine internal classes
1240             NodeImpl propertyPathNode = (NodeImpl) propertyPathIter.next();
1241             beanMetaData = beanMetaDataManager.getBeanMetaData( clazz );
1242             propertyMetaData = getBeanPropertyMetaData( beanMetaData, propertyPathNode );
1243
1244             // if the property is not the leaf property, we set up the context for the next iteration
1245             if ( propertyPathIter.hasNext() ) {
1246                 // if we are in the case of an iterable and we want to validate an element of this iterable, we have to get the
1247                 // type from the parameterized type
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             // should only happen if the property path is empty, which should never happen
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         // check if this validation context is qualified to validate the current meta constraint.
1275         // For instance, in the case of validateProperty()/validateValue(), the current meta constraint
1276         // could be for another property and, in this case, we don't validate it.
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         // as the TraversableResolver interface is designed right now it does not make sense to call it when
1322         // there is no traversable object hosting the property to be accessed. For this reason we don't call the resolver
1323         // for class level constraints (ElementType.TYPE) or top level method parameters or return values.
1324         // see also BV expert group discussion - http://lists.jboss.org/pipermail/beanvalidation-dev/2013-January/000722.html
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