1
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
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
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
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
287
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
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
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
460 private List<ConstraintDescriptorImpl<?>> findConstraints(JavaBeanAnnotatedConstrainable constrainable, ConstraintLocationKind kind) {
461 return findConstraints( constrainable, constrainable, kind );
462 }
463
464
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
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
520 protected <A extends Annotation> List<ConstraintDescriptorImpl<?>> findConstraintAnnotations(
521 Constrainable constrainable,
522 A annotation,
523 ConstraintLocationKind type) {
524
525
526
527
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
600 private <T> T run(PrivilegedAction<T> action) {
601 return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
602 }
603
604
607 protected Set<MetaConstraint<?>> findTypeAnnotationConstraints(JavaBeanField javaBeanField) {
608 return findTypeArgumentsConstraints(
609 javaBeanField,
610 new TypeArgumentFieldLocation( javaBeanField ),
611 javaBeanField.getAnnotatedType()
612 );
613 }
614
615
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
689 return Collections.emptyMap();
690
691
692
693
694
695
696
697
698
699
700
701
702
703
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
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
743 if ( !(annotatedType instanceof AnnotatedParameterizedType) ) {
744 return Collections.emptySet();
745 }
746
747 Set<MetaConstraint<?>> typeArgumentConstraints = new HashSet<>();
748
749
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
769
770
771
772
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
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
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