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.metadata.provider;
8
9 import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
10 import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
11 import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
12 import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES;
13
14 import java.lang.annotation.Annotation;
15 import java.lang.invoke.MethodHandles;
16 import java.lang.reflect.AnnotatedArrayType;
17 import java.lang.reflect.AnnotatedParameterizedType;
18 import java.lang.reflect.AnnotatedType;
19 import java.lang.reflect.Executable;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.Method;
22 import java.lang.reflect.Modifier;
23 import java.lang.reflect.ParameterizedType;
24 import java.lang.reflect.Type;
25 import java.lang.reflect.TypeVariable;
26 import java.security.AccessController;
27 import java.security.PrivilegedAction;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.stream.Collectors;
36
37 import javax.validation.GroupSequence;
38 import javax.validation.Valid;
39 import javax.validation.groups.ConvertGroup;
40
41 import org.hibernate.validator.group.GroupSequenceProvider;
42 import org.hibernate.validator.internal.engine.ConstraintCreationContext;
43 import org.hibernate.validator.internal.engine.valueextraction.ArrayElement;
44 import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder;
45 import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions;
46 import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl;
47 import org.hibernate.validator.internal.metadata.core.MetaConstraint;
48 import org.hibernate.validator.internal.metadata.core.MetaConstraints;
49 import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
50 import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType;
51 import org.hibernate.validator.internal.metadata.location.ConstraintLocation;
52 import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind;
53 import org.hibernate.validator.internal.metadata.raw.BeanConfiguration;
54 import org.hibernate.validator.internal.metadata.raw.ConfigurationSource;
55 import org.hibernate.validator.internal.metadata.raw.ConstrainedElement;
56 import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable;
57 import org.hibernate.validator.internal.metadata.raw.ConstrainedField;
58 import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter;
59 import org.hibernate.validator.internal.metadata.raw.ConstrainedType;
60 import org.hibernate.validator.internal.properties.Callable;
61 import org.hibernate.validator.internal.properties.Constrainable;
62 import org.hibernate.validator.internal.properties.Getter;
63 import org.hibernate.validator.internal.properties.javabean.JavaBeanAnnotatedConstrainable;
64 import org.hibernate.validator.internal.properties.javabean.JavaBeanAnnotatedElement;
65 import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable;
66 import org.hibernate.validator.internal.properties.javabean.JavaBeanField;
67 import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper;
68 import org.hibernate.validator.internal.properties.javabean.JavaBeanParameter;
69 import org.hibernate.validator.internal.util.CollectionHelper;
70 import org.hibernate.validator.internal.util.ReflectionHelper;
71 import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor;
72 import org.hibernate.validator.internal.util.logging.Log;
73 import org.hibernate.validator.internal.util.logging.LoggerFactory;
74 import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredConstructors;
75 import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredFields;
76 import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethods;
77 import org.hibernate.validator.internal.util.privilegedactions.GetMethods;
78 import org.hibernate.validator.internal.util.privilegedactions.NewInstance;
79 import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;
80
81 /**
82  * {@code MetaDataProvider} which reads the metadata from annotations which is the default configuration source.
83  *
84  * @author Gunnar Morling
85  * @author Hardy Ferentschik
86  * @author Guillaume Smet
87  */

88 public class AnnotationMetaDataProvider implements MetaDataProvider {
89
90     private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
91
92     private final ConstraintCreationContext constraintCreationContext;
93     private final AnnotationProcessingOptions annotationProcessingOptions;
94     private final JavaBeanHelper javaBeanHelper;
95
96     private final BeanConfiguration<Object> objectBeanConfiguration;
97
98     public AnnotationMetaDataProvider(ConstraintCreationContext constraintCreationContext,
99             JavaBeanHelper javaBeanHelper,
100             AnnotationProcessingOptions annotationProcessingOptions) {
101         this.constraintCreationContext = constraintCreationContext;
102         this.javaBeanHelper = javaBeanHelper;
103         this.annotationProcessingOptions = annotationProcessingOptions;
104
105         this.objectBeanConfiguration = retrieveBeanConfiguration( Object.class );
106     }
107
108     @Override
109     public AnnotationProcessingOptions getAnnotationProcessingOptions() {
110         return new AnnotationProcessingOptionsImpl();
111     }
112
113     @Override
114     @SuppressWarnings("unchecked")
115     public <T> BeanConfiguration<T> getBeanConfiguration(Class<T> beanClass) {
116         if ( Object.class.equals( beanClass ) ) {
117             return (BeanConfiguration<T>) objectBeanConfiguration;
118         }
119
120         return retrieveBeanConfiguration( beanClass );
121     }
122
123     /**
124      * @param beanClass The bean class for which to retrieve the meta data
125      *
126      * @return Retrieves constraint related meta data from the annotations of the given type.
127      */

128     private <T> BeanConfiguration<T> retrieveBeanConfiguration(Class<T> beanClass) {
129         Set<ConstrainedElement> constrainedElements = getFieldMetaData( beanClass );
130         constrainedElements.addAll( getMethodMetaData( beanClass ) );
131         constrainedElements.addAll( getConstructorMetaData( beanClass ) );
132
133         Set<MetaConstraint<?>> classLevelConstraints = getClassLevelConstraints( beanClass );
134         if ( !classLevelConstraints.isEmpty() ) {
135             ConstrainedType classLevelMetaData =
136                     new ConstrainedType(
137                             ConfigurationSource.ANNOTATION,
138                             beanClass,
139                             classLevelConstraints
140                     );
141             constrainedElements.add( classLevelMetaData );
142         }
143
144         return new BeanConfiguration<>(
145                 ConfigurationSource.ANNOTATION,
146                 beanClass,
147                 constrainedElements,
148                 getDefaultGroupSequence( beanClass ),
149                 getDefaultGroupSequenceProvider( beanClass )
150         );
151     }
152
153     private List<Class<?>> getDefaultGroupSequence(Class<?> beanClass) {
154         GroupSequence groupSequenceAnnotation = beanClass.getAnnotation( GroupSequence.class );
155         return groupSequenceAnnotation != null ? Arrays.asList( groupSequenceAnnotation.value() ) : null;
156     }
157
158     private <T> DefaultGroupSequenceProvider<? super T> getDefaultGroupSequenceProvider(Class<T> beanClass) {
159         GroupSequenceProvider groupSequenceProviderAnnotation = beanClass.getAnnotation( GroupSequenceProvider.class );
160
161         if ( groupSequenceProviderAnnotation != null ) {
162             @SuppressWarnings("unchecked")
163             Class<? extends DefaultGroupSequenceProvider<? super T>> providerClass =
164                     (Class<? extends DefaultGroupSequenceProvider<? super T>>) groupSequenceProviderAnnotation.value();
165             return newGroupSequenceProviderClassInstance( beanClass, providerClass );
166         }
167
168         return null;
169     }
170
171     private <T> DefaultGroupSequenceProvider<? super T> newGroupSequenceProviderClassInstance(Class<T> beanClass,
172             Class<? extends DefaultGroupSequenceProvider<? super T>> providerClass) {
173         Method[] providerMethods = run( GetMethods.action( providerClass ) );
174         for ( Method method : providerMethods ) {
175             Class<?>[] paramTypes = method.getParameterTypes();
176             if ( "getValidationGroups".equals( method.getName() ) && !method.isBridge()
177                     && paramTypes.length == 1 && paramTypes[0].isAssignableFrom( beanClass ) ) {
178
179                 return run(
180                         NewInstance.action( providerClass, "the default group sequence provider" )
181                 );
182             }
183         }
184
185         throw LOG.getWrongDefaultGroupSequenceProviderTypeException( beanClass );
186     }
187
188     private Set<MetaConstraint<?>> getClassLevelConstraints(Class<?> clazz) {
189         if ( annotationProcessingOptions.areClassLevelConstraintsIgnoredFor( clazz ) ) {
190             return Collections.emptySet();
191         }
192
193         List<ConstraintDescriptorImpl<?>> classLevelConstraintDescriptors = findConstraints( null, clazz.getDeclaredAnnotations(),
194                 ConstraintLocationKind.TYPE );
195
196         if ( classLevelConstraintDescriptors.isEmpty() ) {
197             return Collections.emptySet();
198         }
199
200         Set<MetaConstraint<?>> classLevelConstraints = newHashSet( classLevelConstraintDescriptors.size() );
201         ConstraintLocation location = ConstraintLocation.forClass( clazz );
202
203         for ( ConstraintDescriptorImpl<?> constraintDescriptor : classLevelConstraintDescriptors ) {
204             classLevelConstraints.add( MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(),
205                     constraintCreationContext.getValueExtractorManager(),
206                     constraintCreationContext.getConstraintValidatorManager(),
207                     constraintDescriptor,
208                     location ) );
209         }
210
211         return classLevelConstraints;
212     }
213
214     private Set<ConstrainedElement> getFieldMetaData(Class<?> beanClass) {
215         Set<ConstrainedElement> propertyMetaData = newHashSet();
216
217         for ( Field field : run( GetDeclaredFields.action( beanClass ) ) ) {
218             // HV-172
219             if ( Modifier.isStatic( field.getModifiers() ) || field.isSynthetic() ) {
220                 continue;
221             }
222
223             JavaBeanField javaBeanField = javaBeanHelper.field( field );
224
225             if ( annotationProcessingOptions.areMemberConstraintsIgnoredFor( javaBeanField ) ) {
226                 continue;
227             }
228
229             propertyMetaData.add( findPropertyMetaData( javaBeanField ) );
230         }
231         return propertyMetaData;
232     }
233
234     private ConstrainedField findPropertyMetaData(JavaBeanField javaBeanField) {
235         Set<MetaConstraint<?>> constraints = convertToMetaConstraints(
236                 findConstraints( javaBeanField, ConstraintLocationKind.FIELD ),
237                 javaBeanField
238         );
239
240         CascadingMetaDataBuilder cascadingMetaDataBuilder = findCascadingMetaData( javaBeanField );
241         Set<MetaConstraint<?>> typeArgumentsConstraints = findTypeAnnotationConstraints( javaBeanField );
242
243         return new ConstrainedField(
244                 ConfigurationSource.ANNOTATION,
245                 javaBeanField,
246                 constraints,
247                 typeArgumentsConstraints,
248                 cascadingMetaDataBuilder
249         );
250     }
251
252     private Set<MetaConstraint<?>> convertToMetaConstraints(List<ConstraintDescriptorImpl<?>> constraintDescriptors, JavaBeanField javaBeanField) {
253         if ( constraintDescriptors.isEmpty() ) {
254             return Collections.emptySet();
255         }
256
257         Set<MetaConstraint<?>> constraints = newHashSet( constraintDescriptors.size() );
258
259         ConstraintLocation location = ConstraintLocation.forField( javaBeanField );
260
261         for ( ConstraintDescriptorImpl<?> constraintDescription : constraintDescriptors ) {
262             constraints.add( MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(),
263                     constraintCreationContext.getValueExtractorManager(),
264                     constraintCreationContext.getConstraintValidatorManager(),
265                     constraintDescription, location ) );
266         }
267         return constraints;
268     }
269
270     private Set<ConstrainedExecutable> getConstructorMetaData(Class<?> clazz) {
271         Executable[] declaredConstructors = run( GetDeclaredConstructors.action( clazz ) );
272
273         return getMetaData( declaredConstructors );
274     }
275
276     private Set<ConstrainedExecutable> getMethodMetaData(Class<?> clazz) {
277         Executable[] declaredMethods = run( GetDeclaredMethods.action( clazz ) );
278
279         return getMetaData( declaredMethods );
280     }
281
282     private Set<ConstrainedExecutable> getMetaData(Executable[] executableElements) {
283         Set<ConstrainedExecutable> executableMetaData = newHashSet();
284
285         for ( Executable executable : executableElements ) {
286             // HV-172; ignoring synthetic methods (inserted by the compiler), as they can't have any constraints
287             // anyway and possibly hide the actual method with the same signature in the built meta model
288             if ( Modifier.isStatic( executable.getModifiers() ) || executable.isSynthetic() ) {
289                 continue;
290             }
291
292             executableMetaData.add( findExecutableMetaData( executable ) );
293         }
294
295         return executableMetaData;
296     }
297
298     /**
299      * Finds all constraint annotations defined for the given method or constructor.
300      *
301      * @param executable The executable element to check for constraints annotations.
302      *
303      * @return A meta data object describing the constraints specified for the
304      * given element.
305      */

306     private ConstrainedExecutable findExecutableMetaData(Executable executable) {
307         JavaBeanExecutable<?> javaBeanExecutable = javaBeanHelper.executable( executable );
308         List<ConstrainedParameter> parameterConstraints = getParameterMetaData( javaBeanExecutable );
309
310         Map<ConstraintType, List<ConstraintDescriptorImpl<?>>> executableConstraints = findConstraints(
311                 javaBeanExecutable,
312                 ConstraintLocationKind.of( javaBeanExecutable.getConstrainedElementKind() )
313         ).stream().collect( Collectors.groupingBy( ConstraintDescriptorImpl::getConstraintType ) );
314
315         Set<MetaConstraint<?>> crossParameterConstraints;
316         if ( annotationProcessingOptions.areCrossParameterConstraintsIgnoredFor( javaBeanExecutable ) ) {
317             crossParameterConstraints = Collections.emptySet();
318         }
319         else {
320             crossParameterConstraints = convertToMetaConstraints(
321                     executableConstraints.get( ConstraintType.CROSS_PARAMETER ),
322                     javaBeanExecutable
323             );
324         }
325
326         Set<MetaConstraint<?>> returnValueConstraints;
327         Set<MetaConstraint<?>> typeArgumentsConstraints;
328         CascadingMetaDataBuilder cascadingMetaDataBuilder;
329
330         if ( annotationProcessingOptions.areReturnValueConstraintsIgnoredFor( javaBeanExecutable ) ) {
331             returnValueConstraints = Collections.emptySet();
332             typeArgumentsConstraints = Collections.emptySet();
333             cascadingMetaDataBuilder = CascadingMetaDataBuilder.nonCascading();
334         }
335         else {
336             typeArgumentsConstraints = findTypeAnnotationConstraints( javaBeanExecutable );
337             returnValueConstraints = convertToMetaConstraints(
338                     executableConstraints.get( ConstraintType.GENERIC ),
339                     javaBeanExecutable
340             );
341             cascadingMetaDataBuilder = findCascadingMetaData( javaBeanExecutable );
342         }
343
344         return new ConstrainedExecutable(
345                 ConfigurationSource.ANNOTATION,
346                 javaBeanExecutable,
347                 parameterConstraints,
348                 crossParameterConstraints,
349                 returnValueConstraints,
350                 typeArgumentsConstraints,
351                 cascadingMetaDataBuilder
352         );
353     }
354
355     private Set<MetaConstraint<?>> convertToMetaConstraints(List<ConstraintDescriptorImpl<?>> constraintDescriptors, Callable callable) {
356         if ( constraintDescriptors == null || constraintDescriptors.isEmpty() ) {
357             return Collections.emptySet();
358         }
359
360         Set<MetaConstraint<?>> constraints = newHashSet( constraintDescriptors.size() );
361
362         ConstraintLocation returnValueLocation = ConstraintLocation.forReturnValue( callable );
363         ConstraintLocation crossParameterLocation = ConstraintLocation.forCrossParameter( callable );
364
365         for ( ConstraintDescriptorImpl<?> constraintDescriptor : constraintDescriptors ) {
366             ConstraintLocation location = constraintDescriptor.getConstraintType() == ConstraintType.GENERIC
367                     ? returnValueLocation
368                     : crossParameterLocation;
369             constraints.add( MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(),
370                     constraintCreationContext.getValueExtractorManager(),
371                     constraintCreationContext.getConstraintValidatorManager(), constraintDescriptor, location ) );
372         }
373
374         return constraints;
375     }
376
377     /**
378      * Retrieves constraint related meta data for the parameters of the given
379      * executable.
380      *
381      * @param javaBeanExecutable The executable of interest.
382      *
383      * @return A list with parameter meta data for the given executable.
384      */

385     private List<ConstrainedParameter> getParameterMetaData(JavaBeanExecutable<?> javaBeanExecutable) {
386         if ( !javaBeanExecutable.hasParameters() ) {
387             return Collections.emptyList();
388         }
389
390         List<JavaBeanParameter> parameters = javaBeanExecutable.getParameters();
391
392         List<ConstrainedParameter> metaData = new ArrayList<>( parameters.size() );
393
394         int i = 0;
395         for ( JavaBeanParameter parameter : parameters ) {
396             if ( annotationProcessingOptions.areParameterConstraintsIgnoredFor( javaBeanExecutable, i ) ) {
397                 metaData.add(
398                         new ConstrainedParameter(
399                                 ConfigurationSource.ANNOTATION,
400                                 javaBeanExecutable,
401                                 parameter.getGenericType(),
402                                 i,
403                                 Collections.emptySet(),
404                                 Collections.emptySet(),
405                                 CascadingMetaDataBuilder.nonCascading()
406                         )
407                 );
408                 i++;
409                 continue;
410             }
411
412             List<ConstraintDescriptorImpl<?>> constraintDescriptors = findConstraints( javaBeanExecutable, parameter, ConstraintLocationKind.PARAMETER );
413             Set<MetaConstraint<?>> parameterConstraints;
414
415             if ( !constraintDescriptors.isEmpty() ) {
416                 parameterConstraints = newHashSet( constraintDescriptors.size() );
417                 ConstraintLocation location = ConstraintLocation.forParameter( javaBeanExecutable, i );
418
419                 for ( ConstraintDescriptorImpl<?> constraintDescriptorImpl : constraintDescriptors ) {
420                     parameterConstraints.add(
421                             MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(),
422                                     constraintCreationContext.getValueExtractorManager(),
423                                     constraintCreationContext.getConstraintValidatorManager(), constraintDescriptorImpl,
424                                     location ) );
425                 }
426             }
427             else {
428                 parameterConstraints = Collections.emptySet();
429             }
430
431             Set<MetaConstraint<?>> typeArgumentsConstraints = findTypeAnnotationConstraintsForExecutableParameter( javaBeanExecutable, parameter );
432             CascadingMetaDataBuilder cascadingMetaData = findCascadingMetaData( parameter );
433
434             metaData.add(
435                     new ConstrainedParameter(
436                             ConfigurationSource.ANNOTATION,
437                             javaBeanExecutable,
438                             parameter.getGenericType(),
439                             i,
440                             parameterConstraints,
441                             typeArgumentsConstraints,
442                             cascadingMetaData
443                     )
444             );
445             i++;
446         }
447
448         return metaData;
449     }
450
451     /**
452      * Finds all constraint annotations defined for the given constrainable and returns them in a list of
453      * constraint descriptors.
454      *
455      * @param constrainable The constrainable to check for constraint annotations.
456      * @param kind The constraint location kind.
457      *
458      * @return A list of constraint descriptors for all constraint specified for the given member.
459      */

460     private List<ConstraintDescriptorImpl<?>> findConstraints(JavaBeanAnnotatedConstrainable constrainable, ConstraintLocationKind kind) {
461         return findConstraints( constrainable, constrainable, kind );
462     }
463
464     /**
465      * Finds all constraint annotations defined for the given constrainable and returns them in a list of constraint
466      * descriptors.
467      *
468      * @param constrainable The constrainable element (will be the executable for a method parameter).
469      * @param annotatedElement The annotated element. Usually the same as the constrainable except in the case of method
470      * parameters constraints when it is the parameter.
471      * @param kind The constraint location kind.
472      *
473      * @return A list of constraint descriptors for all constraint specified for the given member.
474      */

475     private List<ConstraintDescriptorImpl<?>> findConstraints(Constrainable constrainable, JavaBeanAnnotatedElement annotatedElement,
476             ConstraintLocationKind kind) {
477         List<ConstraintDescriptorImpl<?>> metaData = newArrayList();
478         for ( Annotation annotation : annotatedElement.getDeclaredAnnotations() ) {
479             metaData.addAll( findConstraintAnnotations( constrainable, annotation, kind ) );
480         }
481
482         return metaData;
483     }
484
485     /**
486      * Finds all constraint annotations defined for the given constrainable and returns them in a list of constraint
487      * descriptors.
488      *
489      * @param constrainable The constrainable element (will be the executable for a method parameter).
490      * @param annotations The annotations.
491      * @param kind The constraint location kind.
492      *
493      * @return A list of constraint descriptors for all constraint specified for the given member.
494      */

495     private List<ConstraintDescriptorImpl<?>> findConstraints(Constrainable constrainable, Annotation[] annotations,
496             ConstraintLocationKind kind) {
497         if ( annotations.length == 0 ) {
498             return Collections.emptyList();
499         }
500
501         List<ConstraintDescriptorImpl<?>> metaData = newArrayList();
502         for ( Annotation annotation : annotations ) {
503             metaData.addAll( findConstraintAnnotations( constrainable, annotation, kind ) );
504         }
505
506         return metaData;
507     }
508
509     /**
510      * Examines the given annotation to see whether it is a single- or multi-valued constraint annotation.
511      *
512      * @param constrainable The constrainable to check for constraints annotations
513      * @param annotation The annotation to examine
514      * @param type the element type on which the annotation/constraint is placed on
515      * @param <A> the annotation type
516      *
517      * @return A list of constraint descriptors or the empty list in case {@code annotation} is neither a
518      * single nor multi-valued annotation.
519      */

520     protected <A extends Annotation> List<ConstraintDescriptorImpl<?>> findConstraintAnnotations(
521             Constrainable constrainable,
522             A annotation,
523             ConstraintLocationKind type) {
524
525         // HV-1049 and HV-1311 - Ignore annotations from the JDK (jdk.internal.* and java.*); They cannot be constraint
526         // annotations so skip them right here, as for the proper check we'd need package access permission for
527         // "jdk.internal" and "java".
528         if ( constraintCreationContext.getConstraintHelper().isJdkAnnotation( annotation.annotationType() ) ) {
529             return Collections.emptyList();
530         }
531
532         List<Annotation> constraints = newArrayList();
533         Class<? extends Annotation> annotationType = annotation.annotationType();
534         if ( constraintCreationContext.getConstraintHelper().isConstraintAnnotation( annotationType ) ) {
535             constraints.add( annotation );
536         }
537         else if ( constraintCreationContext.getConstraintHelper().isMultiValueConstraint( annotationType ) ) {
538             constraints.addAll( constraintCreationContext.getConstraintHelper().getConstraintsFromMultiValueConstraint( annotation ) );
539         }
540
541         return constraints.stream()
542                 .map( c -> buildConstraintDescriptor( constrainable, c, type ) )
543                 .collect( Collectors.toList() );
544     }
545
546     private Map<Class<?>, Class<?>> getGroupConversions(AnnotatedType annotatedType) {
547         return getGroupConversions(
548                 annotatedType.getAnnotation( ConvertGroup.class ),
549                 annotatedType.getAnnotation( ConvertGroup.List.class )
550         );
551     }
552
553     private Map<Class<?>, Class<?>> getGroupConversions(ConvertGroup groupConversion, ConvertGroup.List groupConversionList) {
554         if ( groupConversion == null && ( groupConversionList == null || groupConversionList.value().length == 0 ) ) {
555             return Collections.emptyMap();
556         }
557
558         Map<Class<?>, Class<?>> groupConversions = newHashMap();
559
560         if ( groupConversion != null ) {
561             groupConversions.put( groupConversion.from(), groupConversion.to() );
562         }
563
564         if ( groupConversionList != null ) {
565             for ( ConvertGroup conversion : groupConversionList.value() ) {
566                 if ( groupConversions.containsKey( conversion.from() ) ) {
567                     throw LOG.getMultipleGroupConversionsForSameSourceException(
568                             conversion.from(),
569                             CollectionHelper.<Class<?>>asSet(
570                                     groupConversions.get( conversion.from() ),
571                                     conversion.to()
572                             )
573                     );
574                 }
575
576                 groupConversions.put( conversion.from(), conversion.to() );
577             }
578         }
579
580         return groupConversions;
581     }
582
583     private <A extends Annotation> ConstraintDescriptorImpl<A> buildConstraintDescriptor(Constrainable constrainable,
584             A annotation,
585             ConstraintLocationKind type) {
586         return new ConstraintDescriptorImpl<>(
587                 constraintCreationContext.getConstraintHelper(),
588                 constrainable,
589                 new ConstraintAnnotationDescriptor<>( annotation ),
590                 type
591         );
592     }
593
594     /**
595      * Runs the given privileged action, using a privileged block if required.
596      * <p>
597      * <b>NOTE:</b> This must never be changed into a publicly available method to avoid execution of arbitrary
598      * privileged actions within HV's protection domain.
599      */

600     private <T> T run(PrivilegedAction<T> action) {
601         return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
602     }
603
604     /**
605      * Finds type arguments constraints for fields.
606      */

607     protected Set<MetaConstraint<?>> findTypeAnnotationConstraints(JavaBeanField javaBeanField) {
608         return findTypeArgumentsConstraints(
609                 javaBeanField,
610                 new TypeArgumentFieldLocation( javaBeanField ),
611                 javaBeanField.getAnnotatedType()
612         );
613     }
614
615     /**
616      * Finds type arguments constraints for method return values.
617      */

618     protected Set<MetaConstraint<?>> findTypeAnnotationConstraints(JavaBeanExecutable<?> javaBeanExecutable) {
619         return findTypeArgumentsConstraints(
620                 javaBeanExecutable,
621                 new TypeArgumentReturnValueLocation( javaBeanExecutable ),
622                 javaBeanExecutable.getAnnotatedType()
623         );
624     }
625
626     private CascadingMetaDataBuilder findCascadingMetaData(JavaBeanParameter javaBeanParameter) {
627         Map<TypeVariable<?>, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( javaBeanParameter.getAnnotatedType(),
628                 javaBeanParameter.getTypeParameters() );
629
630         try {
631             return getCascadingMetaData( javaBeanParameter, containerElementTypesCascadingMetaData );
632         }
633         catch (ArrayIndexOutOfBoundsException ex) {
634             LOG.warn( MESSAGES.constraintOnConstructorOfNonStaticInnerClass(), ex );
635             return CascadingMetaDataBuilder.nonCascading();
636         }
637     }
638
639     private CascadingMetaDataBuilder findCascadingMetaData(JavaBeanField javaBeanField) {
640         Map<TypeVariable<?>, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata(
641                 javaBeanField.getAnnotatedType(),
642                 javaBeanField.getTypeParameters() );
643
644         return getCascadingMetaData( javaBeanField, containerElementTypesCascadingMetaData );
645     }
646
647     private CascadingMetaDataBuilder findCascadingMetaData(JavaBeanExecutable<?> javaBeanExecutable) {
648         Map<TypeVariable<?>, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( javaBeanExecutable.getAnnotatedType(),
649                 javaBeanExecutable.getTypeParameters() );
650
651         return getCascadingMetaData( javaBeanExecutable, containerElementTypesCascadingMetaData );
652     }
653
654     private Map<TypeVariable<?>, CascadingMetaDataBuilder> getTypeParametersCascadingMetadata(AnnotatedType annotatedType,
655             TypeVariable<?>[] typeParameters) {
656         if ( annotatedType instanceof AnnotatedArrayType ) {
657             return getTypeParametersCascadingMetaDataForArrayType( (AnnotatedArrayType) annotatedType );
658         }
659         else if ( annotatedType instanceof AnnotatedParameterizedType ) {
660             return getTypeParametersCascadingMetaDataForParameterizedType( (AnnotatedParameterizedType) annotatedType, typeParameters );
661         }
662         else {
663             return Collections.emptyMap();
664         }
665     }
666
667     private Map<TypeVariable<?>, CascadingMetaDataBuilder> getTypeParametersCascadingMetaDataForParameterizedType(
668             AnnotatedParameterizedType annotatedParameterizedType, TypeVariable<?>[] typeParameters) {
669         Map<TypeVariable<?>, CascadingMetaDataBuilder> typeParametersCascadingMetadata = CollectionHelper.newHashMap( typeParameters.length );
670
671         AnnotatedType[] annotatedTypeArguments = annotatedParameterizedType.getAnnotatedActualTypeArguments();
672         int i = 0;
673
674         for ( AnnotatedType annotatedTypeArgument : annotatedTypeArguments ) {
675             Map<TypeVariable<?>, CascadingMetaDataBuilder> nestedTypeParametersCascadingMetadata = getTypeParametersCascadingMetaDataForAnnotatedType(
676                     annotatedTypeArgument );
677
678             typeParametersCascadingMetadata.put( typeParameters[i], new CascadingMetaDataBuilder( annotatedParameterizedType.getType(), typeParameters[i],
679                     annotatedTypeArgument.isAnnotationPresent( Valid.class ), nestedTypeParametersCascadingMetadata,
680                     getGroupConversions( annotatedTypeArgument ) ) );
681             i++;
682         }
683
684         return typeParametersCascadingMetadata;
685     }
686
687     private Map<TypeVariable<?>, CascadingMetaDataBuilder> getTypeParametersCascadingMetaDataForArrayType(AnnotatedArrayType annotatedArrayType) {
688         // HV-1428 Container element support is disabled for arrays
689         return Collections.emptyMap();
690 //        Map<TypeVariable<?>, CascadingTypeParameter> typeParametersCascadingMetadata = CollectionHelper.newHashMap( 1 );
691 //        AnnotatedType containerElementAnnotatedType = annotatedArrayType.getAnnotatedGenericComponentType();
692 //
693 //        Map<TypeVariable<?>, CascadingTypeParameter> nestedTypeParametersCascadingMetadata = getTypeParametersCascadingMetaDataForAnnotatedType(
694 //                containerElementAnnotatedType );
695 //
696 //        TypeVariable<?> arrayElement = new ArrayElement( annotatedArrayType );
697 //        typeParametersCascadingMetadata.put( arrayElement, new CascadingTypeParameter( annotatedArrayType.getType(),
698 //                arrayElement,
699 //                annotatedArrayType.isAnnotationPresent( Valid.class ),
700 //                nestedTypeParametersCascadingMetadata,
701 //                getGroupConversions( annotatedArrayType ) ) );
702 //
703 //        return typeParametersCascadingMetadata;
704     }
705
706     private Map<TypeVariable<?>, CascadingMetaDataBuilder> getTypeParametersCascadingMetaDataForAnnotatedType(AnnotatedType annotatedType) {
707         if ( annotatedType instanceof AnnotatedArrayType ) {
708             return getTypeParametersCascadingMetaDataForArrayType( (AnnotatedArrayType) annotatedType );
709         }
710         else if ( annotatedType instanceof AnnotatedParameterizedType ) {
711             return getTypeParametersCascadingMetaDataForParameterizedType( (AnnotatedParameterizedType) annotatedType,
712                     ReflectionHelper.getClassFromType( annotatedType.getType() ).getTypeParameters() );
713         }
714         else {
715             return Collections.emptyMap();
716         }
717     }
718
719     /**
720      * Finds type arguments constraints for parameters.
721      *
722      * @param javaBeanParameter the parameter
723      *
724      * @return a set of type arguments constraints, or an empty set if no constrained type arguments are found
725      */

726     protected Set<MetaConstraint<?>> findTypeAnnotationConstraintsForExecutableParameter(JavaBeanExecutable<?> javaBeanExecutable,
727             JavaBeanParameter javaBeanParameter) {
728         try {
729             return findTypeArgumentsConstraints(
730                     javaBeanExecutable,
731                     new TypeArgumentExecutableParameterLocation( javaBeanExecutable, javaBeanParameter.getIndex() ),
732                     javaBeanParameter.getAnnotatedType()
733             );
734         }
735         catch (ArrayIndexOutOfBoundsException ex) {
736             LOG.warn( MESSAGES.constraintOnConstructorOfNonStaticInnerClass(), ex );
737             return Collections.emptySet();
738         }
739     }
740
741     private Set<MetaConstraint<?>> findTypeArgumentsConstraints(Constrainable constrainable, TypeArgumentLocation location, AnnotatedType annotatedType) {
742         // HV-1428 Container element support is disabled for arrays
743         if ( !(annotatedType instanceof AnnotatedParameterizedType) ) {
744             return Collections.emptySet();
745         }
746
747         Set<MetaConstraint<?>> typeArgumentConstraints = new HashSet<>();
748
749         // if we have an array, we need to unwrap the array first
750         if ( annotatedType instanceof AnnotatedArrayType ) {
751             AnnotatedArrayType annotatedArrayType = (AnnotatedArrayType) annotatedType;
752             Type validatedType = annotatedArrayType.getAnnotatedGenericComponentType().getType();
753             TypeVariable<?> arrayElementTypeArgument = new ArrayElement( annotatedArrayType );
754
755             typeArgumentConstraints.addAll( findTypeUseConstraints( constrainable, annotatedArrayType, arrayElementTypeArgument, location, validatedType ) );
756
757             typeArgumentConstraints.addAll( findTypeArgumentsConstraints( constrainable,
758                     new NestedTypeArgumentLocation( location, arrayElementTypeArgument, validatedType ),
759                     annotatedArrayType.getAnnotatedGenericComponentType() ) );
760         }
761         else if ( annotatedType instanceof AnnotatedParameterizedType ) {
762             AnnotatedParameterizedType annotatedParameterizedType = (AnnotatedParameterizedType) annotatedType;
763
764             int i = 0;
765             for ( TypeVariable<?> typeVariable : ReflectionHelper.getClassFromType( annotatedType.getType() ).getTypeParameters() ) {
766                 AnnotatedType annotatedTypeParameter = annotatedParameterizedType.getAnnotatedActualTypeArguments()[i];
767
768                 // HV-925
769                 // We need to determine the validated type used for constraint validator resolution.
770                 // Iterables and maps need special treatment at this point, since the validated type is the type of the
771                 // specified type parameter. In the other cases the validated type is the parameterized type, eg Optional<String>.
772                 // In the latter case a value unwrapping has to occur
773                 Type validatedType = annotatedTypeParameter.getType();
774
775                 typeArgumentConstraints.addAll( findTypeUseConstraints( constrainable, annotatedTypeParameter, typeVariable, location, validatedType ) );
776
777                 if ( validatedType instanceof ParameterizedType ) {
778                     typeArgumentConstraints.addAll( findTypeArgumentsConstraints( constrainable,
779                             new NestedTypeArgumentLocation( location, typeVariable, validatedType ),
780                             annotatedTypeParameter ) );
781                 }
782
783                 i++;
784             }
785         }
786
787         return typeArgumentConstraints.isEmpty() ? Collections.emptySet() : typeArgumentConstraints;
788     }
789
790     /**
791      * Finds type use annotation constraints defined on the type argument.
792      */

793     private Set<MetaConstraint<?>> findTypeUseConstraints(Constrainable constrainable, AnnotatedType typeArgument, TypeVariable<?> typeVariable,
794             TypeArgumentLocation location, Type type) {
795         List<ConstraintDescriptorImpl<?>> constraintDescriptors = findConstraints( constrainable, typeArgument.getAnnotations(), ConstraintLocationKind.TYPE_USE );
796
797         if ( constraintDescriptors.isEmpty() ) {
798             return Collections.emptySet();
799         }
800
801         Set<MetaConstraint<?>> constraints = newHashSet( constraintDescriptors.size() );
802         ConstraintLocation constraintLocation = ConstraintLocation.forTypeArgument( location.toConstraintLocation(), typeVariable, type );
803
804         for ( ConstraintDescriptorImpl<?> constraintDescriptor : constraintDescriptors ) {
805             constraints.add( MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(),
806                     constraintCreationContext.getValueExtractorManager(),
807                     constraintCreationContext.getConstraintValidatorManager(), constraintDescriptor,
808                     constraintLocation ) );
809         }
810
811         return constraints;
812     }
813
814     private CascadingMetaDataBuilder getCascadingMetaData(JavaBeanAnnotatedElement annotatedElement,
815             Map<TypeVariable<?>, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData) {
816         return CascadingMetaDataBuilder.annotatedObject( annotatedElement.getType(), annotatedElement.isAnnotationPresent( Valid.class ),
817                 containerElementTypesCascadingMetaData, getGroupConversions( annotatedElement.getAnnotatedType() ) );
818     }
819
820     /**
821      * The location of a type argument before it is really considered a constraint location.
822      * <p>
823      * It avoids initializing a constraint location if we did not find any constraints. This is especially useful in
824      * a Java 9 environment as {@link ConstraintLocation#forField(org.hibernate.validator.internal.properties.Field)}
825      * or {@link ConstraintLocation#forGetter(Getter)} tries to make the {@code Member} accessible
826      * which might not be possible (for instance for {@code java.util} classes).
827      */

828     private interface TypeArgumentLocation {
829         ConstraintLocation toConstraintLocation();
830     }
831
832     private static class TypeArgumentExecutableParameterLocation implements TypeArgumentLocation {
833         private final JavaBeanExecutable<?> javaBeanExecutable;
834
835         private final int index;
836
837         private TypeArgumentExecutableParameterLocation(JavaBeanExecutable<?> javaBeanExecutable, int index) {
838             this.javaBeanExecutable = javaBeanExecutable;
839             this.index = index;
840         }
841
842         @Override
843         public ConstraintLocation toConstraintLocation() {
844             return ConstraintLocation.forParameter( javaBeanExecutable, index );
845         }
846     }
847
848     private static class TypeArgumentFieldLocation implements TypeArgumentLocation {
849         private final JavaBeanField javaBeanField;
850
851         private TypeArgumentFieldLocation(JavaBeanField javaBeanField) {
852             this.javaBeanField = javaBeanField;
853         }
854
855         @Override
856         public ConstraintLocation toConstraintLocation() {
857             return ConstraintLocation.forField( javaBeanField );
858         }
859     }
860
861     private static class TypeArgumentReturnValueLocation implements TypeArgumentLocation {
862         private final JavaBeanExecutable<?> javaBeanExecutable;
863
864         private TypeArgumentReturnValueLocation(JavaBeanExecutable<?> javaBeanExecutable) {
865             this.javaBeanExecutable = javaBeanExecutable;
866         }
867
868         @Override
869         public ConstraintLocation toConstraintLocation() {
870             return ConstraintLocation.forReturnValue( javaBeanExecutable );
871         }
872     }
873
874     private static class NestedTypeArgumentLocation implements TypeArgumentLocation {
875         private final TypeArgumentLocation parentLocation;
876         private final TypeVariable<?> typeParameter;
877         private final Type typeOfAnnotatedElement;
878
879         private NestedTypeArgumentLocation(TypeArgumentLocation parentLocation, TypeVariable<?> typeParameter, Type typeOfAnnotatedElement) {
880             this.parentLocation = parentLocation;
881             this.typeParameter = typeParameter;
882             this.typeOfAnnotatedElement = typeOfAnnotatedElement;
883         }
884
885         @Override
886         public ConstraintLocation toConstraintLocation() {
887             return ConstraintLocation.forTypeArgument( parentLocation.toConstraintLocation(), typeParameter, typeOfAnnotatedElement );
888         }
889
890     }
891
892 }
893