18
18
19
19
import java .io .Serializable ;
20
20
import java .lang .annotation .Annotation ;
21
+ import java .lang .reflect .AnnotatedElement ;
21
22
import java .lang .reflect .Field ;
22
23
import java .lang .reflect .Type ;
24
+ import java .util .Arrays ;
23
25
import java .util .Collection ;
24
26
import java .util .HashMap ;
25
27
import java .util .Map ;
26
28
import java .util .stream .Stream ;
27
29
28
30
import org .springframework .core .MethodParameter ;
29
31
import org .springframework .core .ResolvableType ;
30
- import org .springframework .core .annotation .AnnotationUtils ;
32
+ import org .springframework .core .annotation .AnnotatedElementUtils ;
31
33
import org .springframework .util .Assert ;
32
34
import org .springframework .util .ClassUtils ;
33
35
import org .springframework .util .ObjectUtils ;
@@ -66,7 +68,7 @@ public class TypeDescriptor implements Serializable {
66
68
67
69
private final ResolvableType resolvableType ;
68
70
69
- private final Annotation [] annotations ;
71
+ private final AnnotatedElement annotatedElement ;
70
72
71
73
72
74
/**
@@ -79,9 +81,8 @@ public TypeDescriptor(MethodParameter methodParameter) {
79
81
Assert .notNull (methodParameter , "MethodParameter must not be null" );
80
82
this .resolvableType = ResolvableType .forMethodParameter (methodParameter );
81
83
this .type = this .resolvableType .resolve (methodParameter .getParameterType ());
82
- this .annotations = (methodParameter .getParameterIndex () == -1 ?
83
- nullSafeAnnotations (methodParameter .getMethodAnnotations ()) :
84
- nullSafeAnnotations (methodParameter .getParameterAnnotations ()));
84
+ this .annotatedElement = new AnnotatedElementAdapter (methodParameter .getParameterIndex () == -1 ?
85
+ methodParameter .getMethodAnnotations () : methodParameter .getParameterAnnotations ());
85
86
}
86
87
87
88
/**
@@ -93,7 +94,7 @@ public TypeDescriptor(Field field) {
93
94
Assert .notNull (field , "Field must not be null" );
94
95
this .resolvableType = ResolvableType .forField (field );
95
96
this .type = this .resolvableType .resolve (field .getType ());
96
- this .annotations = nullSafeAnnotations (field .getAnnotations ());
97
+ this .annotatedElement = new AnnotatedElementAdapter (field .getAnnotations ());
97
98
}
98
99
99
100
/**
@@ -106,7 +107,7 @@ public TypeDescriptor(Property property) {
106
107
Assert .notNull (property , "Property must not be null" );
107
108
this .resolvableType = ResolvableType .forMethodParameter (property .getMethodParameter ());
108
109
this .type = this .resolvableType .resolve (property .getType ());
109
- this .annotations = nullSafeAnnotations (property .getAnnotations ());
110
+ this .annotatedElement = new AnnotatedElementAdapter (property .getAnnotations ());
110
111
}
111
112
112
113
/**
@@ -120,14 +121,10 @@ public TypeDescriptor(Property property) {
120
121
protected TypeDescriptor (ResolvableType resolvableType , Class <?> type , Annotation [] annotations ) {
121
122
this .resolvableType = resolvableType ;
122
123
this .type = (type != null ? type : resolvableType .resolve (Object .class ));
123
- this .annotations = nullSafeAnnotations (annotations );
124
+ this .annotatedElement = new AnnotatedElementAdapter (annotations );
124
125
}
125
126
126
127
127
- private Annotation [] nullSafeAnnotations (Annotation [] annotations ) {
128
- return (annotations != null ? annotations : EMPTY_ANNOTATION_ARRAY );
129
- }
130
-
131
128
/**
132
129
* Variation of {@link #getType()} that accounts for a primitive type by
133
130
* returning its object wrapper type.
@@ -189,8 +186,8 @@ public TypeDescriptor narrow(Object value) {
189
186
if (value == null ) {
190
187
return this ;
191
188
}
192
- ResolvableType narrowed = ResolvableType .forType (value .getClass (), this . resolvableType );
193
- return new TypeDescriptor (narrowed , null , this . annotations );
189
+ ResolvableType narrowed = ResolvableType .forType (value .getClass (), getResolvableType () );
190
+ return new TypeDescriptor (narrowed , null , getAnnotations () );
194
191
}
195
192
196
193
/**
@@ -206,7 +203,7 @@ public TypeDescriptor upcast(Class<?> superType) {
206
203
return null ;
207
204
}
208
205
Assert .isAssignable (superType , getType ());
209
- return new TypeDescriptor (this . resolvableType . as (superType ), superType , this . annotations );
206
+ return new TypeDescriptor (getResolvableType (). as (superType ), superType , getAnnotations () );
210
207
}
211
208
212
209
/**
@@ -228,7 +225,7 @@ public boolean isPrimitive() {
228
225
* @return the annotations, or an empty array if none
229
226
*/
230
227
public Annotation [] getAnnotations () {
231
- return this .annotations ;
228
+ return this .annotatedElement . getAnnotations () ;
232
229
}
233
230
234
231
/**
@@ -239,7 +236,7 @@ public Annotation[] getAnnotations() {
239
236
* @return <tt>true</tt> if the annotation is present
240
237
*/
241
238
public boolean hasAnnotation (Class <? extends Annotation > annotationType ) {
242
- return ( getAnnotation ( annotationType ) != null );
239
+ return AnnotatedElementUtils . isAnnotated ( this . annotatedElement , annotationType );
243
240
}
244
241
245
242
/**
@@ -250,22 +247,7 @@ public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
250
247
*/
251
248
@ SuppressWarnings ("unchecked" )
252
249
public <T extends Annotation > T getAnnotation (Class <T > annotationType ) {
253
- // Search in annotations that are "present" (i.e., locally declared or inherited)
254
- // NOTE: this unfortunately favors inherited annotations over locally declared composed annotations.
255
- for (Annotation annotation : getAnnotations ()) {
256
- if (annotation .annotationType () == annotationType ) {
257
- return (T ) annotation ;
258
- }
259
- }
260
-
261
- // Search in annotation hierarchy
262
- for (Annotation composedAnnotation : getAnnotations ()) {
263
- T ann = AnnotationUtils .findAnnotation (composedAnnotation .annotationType (), annotationType );
264
- if (ann != null ) {
265
- return ann ;
266
- }
267
- }
268
- return null ;
250
+ return AnnotatedElementUtils .getMergedAnnotation (this .annotatedElement , annotationType );
269
251
}
270
252
271
253
/**
@@ -333,13 +315,13 @@ public boolean isArray() {
333
315
* @throws IllegalStateException if this type is not a {@code java.util.Collection} or array type
334
316
*/
335
317
public TypeDescriptor getElementTypeDescriptor () {
336
- if (this . resolvableType .isArray ()) {
337
- return new TypeDescriptor (this . resolvableType . getComponentType (), null , this . annotations );
318
+ if (getResolvableType () .isArray ()) {
319
+ return new TypeDescriptor (getResolvableType (). getComponentType (), null , getAnnotations () );
338
320
}
339
- if (Stream .class .isAssignableFrom (this . type )) {
340
- return getRelatedIfResolvable (this , this . resolvableType .as (Stream .class ).getGeneric (0 ));
321
+ if (Stream .class .isAssignableFrom (getType () )) {
322
+ return getRelatedIfResolvable (this , getResolvableType () .as (Stream .class ).getGeneric (0 ));
341
323
}
342
- return getRelatedIfResolvable (this , this . resolvableType .asCollection ().getGeneric (0 ));
324
+ return getRelatedIfResolvable (this , getResolvableType () .asCollection ().getGeneric (0 ));
343
325
}
344
326
345
327
/**
@@ -380,8 +362,8 @@ public boolean isMap() {
380
362
* @throws IllegalStateException if this type is not a {@code java.util.Map}
381
363
*/
382
364
public TypeDescriptor getMapKeyTypeDescriptor () {
383
- Assert .state (isMap (), "Not a java.util.Map" );
384
- return getRelatedIfResolvable (this , this . resolvableType .asMap ().getGeneric (0 ));
365
+ Assert .state (isMap (), "Not a [ java.util.Map] " );
366
+ return getRelatedIfResolvable (this , getResolvableType () .asMap ().getGeneric (0 ));
385
367
}
386
368
387
369
/**
@@ -415,8 +397,8 @@ public TypeDescriptor getMapKeyTypeDescriptor(Object mapKey) {
415
397
* @throws IllegalStateException if this type is not a {@code java.util.Map}
416
398
*/
417
399
public TypeDescriptor getMapValueTypeDescriptor () {
418
- Assert .state (isMap (), "Not a java.util.Map" );
419
- return getRelatedIfResolvable (this , this . resolvableType .asMap ().getGeneric (1 ));
400
+ Assert .state (isMap (), "Not a [ java.util.Map] " );
401
+ return getRelatedIfResolvable (this , getResolvableType () .asMap ().getGeneric (1 ));
420
402
}
421
403
422
404
/**
@@ -444,7 +426,7 @@ private TypeDescriptor narrow(Object value, TypeDescriptor typeDescriptor) {
444
426
if (typeDescriptor != null ) {
445
427
return typeDescriptor .narrow (value );
446
428
}
447
- return (value != null ? new TypeDescriptor (this . resolvableType , value .getClass (), this . annotations ) : null );
429
+ return (value != null ? new TypeDescriptor (getResolvableType () , value .getClass (), getAnnotations () ) : null );
448
430
}
449
431
450
432
@ Override
@@ -490,7 +472,7 @@ public String toString() {
490
472
for (Annotation ann : getAnnotations ()) {
491
473
builder .append ("@" ).append (ann .annotationType ().getName ()).append (' ' );
492
474
}
493
- builder .append (this . resolvableType .toString ());
475
+ builder .append (getResolvableType () .toString ());
494
476
return builder .toString ();
495
477
}
496
478
@@ -525,9 +507,9 @@ public static TypeDescriptor valueOf(Class<?> type) {
525
507
* @return the collection type descriptor
526
508
*/
527
509
public static TypeDescriptor collection (Class <?> collectionType , TypeDescriptor elementTypeDescriptor ) {
528
- Assert .notNull (collectionType , "collectionType must not be null" );
510
+ Assert .notNull (collectionType , "Collection type must not be null" );
529
511
if (!Collection .class .isAssignableFrom (collectionType )) {
530
- throw new IllegalArgumentException ("collectionType must be a java.util.Collection" );
512
+ throw new IllegalArgumentException ("Collection type must be a [ java.util.Collection] " );
531
513
}
532
514
ResolvableType element = (elementTypeDescriptor != null ? elementTypeDescriptor .resolvableType : null );
533
515
return new TypeDescriptor (ResolvableType .forClassWithGenerics (collectionType , element ), null , null );
@@ -548,8 +530,9 @@ public static TypeDescriptor collection(Class<?> collectionType, TypeDescriptor
548
530
* @return the map type descriptor
549
531
*/
550
532
public static TypeDescriptor map (Class <?> mapType , TypeDescriptor keyTypeDescriptor , TypeDescriptor valueTypeDescriptor ) {
533
+ Assert .notNull (mapType , "Map type must not be null" );
551
534
if (!Map .class .isAssignableFrom (mapType )) {
552
- throw new IllegalArgumentException ("mapType must be a java.util.Map" );
535
+ throw new IllegalArgumentException ("Map type must be a [ java.util.Map] " );
553
536
}
554
537
ResolvableType key = (keyTypeDescriptor != null ? keyTypeDescriptor .resolvableType : null );
555
538
ResolvableType value = (valueTypeDescriptor != null ? valueTypeDescriptor .resolvableType : null );
@@ -687,7 +670,60 @@ private static TypeDescriptor getRelatedIfResolvable(TypeDescriptor source, Reso
687
670
if (type .resolve () == null ) {
688
671
return null ;
689
672
}
690
- return new TypeDescriptor (type , null , source .annotations );
673
+ return new TypeDescriptor (type , null , source .getAnnotations ());
674
+ }
675
+
676
+
677
+ /**
678
+ * Adapter class for exposing a {@code TypeDescriptor}'s annotations as an
679
+ * {@link AnnotatedElement}, in particular to {@link AnnotatedElementUtils}.
680
+ * @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class)
681
+ * @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class)
682
+ */
683
+ private class AnnotatedElementAdapter implements AnnotatedElement , Serializable {
684
+
685
+ private final Annotation [] annotations ;
686
+
687
+ public AnnotatedElementAdapter (Annotation [] annotations ) {
688
+ this .annotations = annotations ;
689
+ }
690
+
691
+ @ Override
692
+ @ SuppressWarnings ("unchecked" )
693
+ public <T extends Annotation > T getAnnotation (Class <T > annotationClass ) {
694
+ for (Annotation annotation : getAnnotations ()) {
695
+ if (annotation .annotationType () == annotationClass ) {
696
+ return (T ) annotation ;
697
+ }
698
+ }
699
+ return null ;
700
+ }
701
+
702
+ @ Override
703
+ public Annotation [] getAnnotations () {
704
+ return (this .annotations != null ? this .annotations : EMPTY_ANNOTATION_ARRAY );
705
+ }
706
+
707
+ @ Override
708
+ public Annotation [] getDeclaredAnnotations () {
709
+ return getAnnotations ();
710
+ }
711
+
712
+ @ Override
713
+ public boolean equals (Object other ) {
714
+ return (this == other || (other instanceof AnnotatedElementAdapter &&
715
+ Arrays .equals (this .annotations , ((AnnotatedElementAdapter ) other ).annotations )));
716
+ }
717
+
718
+ @ Override
719
+ public int hashCode () {
720
+ return Arrays .hashCode (this .annotations );
721
+ }
722
+
723
+ @ Override
724
+ public String toString () {
725
+ return TypeDescriptor .this .toString ();
726
+ }
691
727
}
692
728
693
729
}
0 commit comments