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 .lang .UsesJava8 ;
32
34
import org .springframework .util .Assert ;
33
35
import org .springframework .util .ClassUtils ;
@@ -70,7 +72,7 @@ public class TypeDescriptor implements Serializable {
70
72
71
73
private final ResolvableType resolvableType ;
72
74
73
- private final Annotation [] annotations ;
75
+ private final AnnotatedElement annotatedElement ;
74
76
75
77
76
78
/**
@@ -83,9 +85,8 @@ public TypeDescriptor(MethodParameter methodParameter) {
83
85
Assert .notNull (methodParameter , "MethodParameter must not be null" );
84
86
this .resolvableType = ResolvableType .forMethodParameter (methodParameter );
85
87
this .type = this .resolvableType .resolve (methodParameter .getParameterType ());
86
- this .annotations = (methodParameter .getParameterIndex () == -1 ?
87
- nullSafeAnnotations (methodParameter .getMethodAnnotations ()) :
88
- nullSafeAnnotations (methodParameter .getParameterAnnotations ()));
88
+ this .annotatedElement = new AnnotatedElementAdapter (methodParameter .getParameterIndex () == -1 ?
89
+ methodParameter .getMethodAnnotations () : methodParameter .getParameterAnnotations ());
89
90
}
90
91
91
92
/**
@@ -97,7 +98,7 @@ public TypeDescriptor(Field field) {
97
98
Assert .notNull (field , "Field must not be null" );
98
99
this .resolvableType = ResolvableType .forField (field );
99
100
this .type = this .resolvableType .resolve (field .getType ());
100
- this .annotations = nullSafeAnnotations (field .getAnnotations ());
101
+ this .annotatedElement = new AnnotatedElementAdapter (field .getAnnotations ());
101
102
}
102
103
103
104
/**
@@ -110,7 +111,7 @@ public TypeDescriptor(Property property) {
110
111
Assert .notNull (property , "Property must not be null" );
111
112
this .resolvableType = ResolvableType .forMethodParameter (property .getMethodParameter ());
112
113
this .type = this .resolvableType .resolve (property .getType ());
113
- this .annotations = nullSafeAnnotations (property .getAnnotations ());
114
+ this .annotatedElement = new AnnotatedElementAdapter (property .getAnnotations ());
114
115
}
115
116
116
117
/**
@@ -124,14 +125,10 @@ public TypeDescriptor(Property property) {
124
125
protected TypeDescriptor (ResolvableType resolvableType , Class <?> type , Annotation [] annotations ) {
125
126
this .resolvableType = resolvableType ;
126
127
this .type = (type != null ? type : resolvableType .resolve (Object .class ));
127
- this .annotations = nullSafeAnnotations (annotations );
128
+ this .annotatedElement = new AnnotatedElementAdapter (annotations );
128
129
}
129
130
130
131
131
- private Annotation [] nullSafeAnnotations (Annotation [] annotations ) {
132
- return (annotations != null ? annotations : EMPTY_ANNOTATION_ARRAY );
133
- }
134
-
135
132
/**
136
133
* Variation of {@link #getType()} that accounts for a primitive type by
137
134
* returning its object wrapper type.
@@ -193,8 +190,8 @@ public TypeDescriptor narrow(Object value) {
193
190
if (value == null ) {
194
191
return this ;
195
192
}
196
- ResolvableType narrowed = ResolvableType .forType (value .getClass (), this . resolvableType );
197
- return new TypeDescriptor (narrowed , null , this . annotations );
193
+ ResolvableType narrowed = ResolvableType .forType (value .getClass (), getResolvableType () );
194
+ return new TypeDescriptor (narrowed , null , getAnnotations () );
198
195
}
199
196
200
197
/**
@@ -210,7 +207,7 @@ public TypeDescriptor upcast(Class<?> superType) {
210
207
return null ;
211
208
}
212
209
Assert .isAssignable (superType , getType ());
213
- return new TypeDescriptor (this . resolvableType . as (superType ), superType , this . annotations );
210
+ return new TypeDescriptor (getResolvableType (). as (superType ), superType , getAnnotations () );
214
211
}
215
212
216
213
/**
@@ -232,7 +229,7 @@ public boolean isPrimitive() {
232
229
* @return the annotations, or an empty array if none
233
230
*/
234
231
public Annotation [] getAnnotations () {
235
- return this .annotations ;
232
+ return this .annotatedElement . getAnnotations () ;
236
233
}
237
234
238
235
/**
@@ -243,7 +240,7 @@ public Annotation[] getAnnotations() {
243
240
* @return <tt>true</tt> if the annotation is present
244
241
*/
245
242
public boolean hasAnnotation (Class <? extends Annotation > annotationType ) {
246
- return ( getAnnotation ( annotationType ) != null );
243
+ return AnnotatedElementUtils . isAnnotated ( this . annotatedElement , annotationType );
247
244
}
248
245
249
246
/**
@@ -254,22 +251,7 @@ public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
254
251
*/
255
252
@ SuppressWarnings ("unchecked" )
256
253
public <T extends Annotation > T getAnnotation (Class <T > annotationType ) {
257
- // Search in annotations that are "present" (i.e., locally declared or inherited)
258
- // NOTE: this unfortunately favors inherited annotations over locally declared composed annotations.
259
- for (Annotation annotation : getAnnotations ()) {
260
- if (annotation .annotationType () == annotationType ) {
261
- return (T ) annotation ;
262
- }
263
- }
264
-
265
- // Search in annotation hierarchy
266
- for (Annotation composedAnnotation : getAnnotations ()) {
267
- T ann = AnnotationUtils .findAnnotation (composedAnnotation .annotationType (), annotationType );
268
- if (ann != null ) {
269
- return ann ;
270
- }
271
- }
272
- return null ;
254
+ return AnnotatedElementUtils .getMergedAnnotation (this .annotatedElement , annotationType );
273
255
}
274
256
275
257
/**
@@ -337,13 +319,13 @@ public boolean isArray() {
337
319
* @throws IllegalStateException if this type is not a {@code java.util.Collection} or array type
338
320
*/
339
321
public TypeDescriptor getElementTypeDescriptor () {
340
- if (this . resolvableType .isArray ()) {
341
- return new TypeDescriptor (this . resolvableType . getComponentType (), null , this . annotations );
322
+ if (getResolvableType () .isArray ()) {
323
+ return new TypeDescriptor (getResolvableType (). getComponentType (), null , getAnnotations () );
342
324
}
343
- if (streamAvailable && StreamDelegate .isStream (this . type )) {
325
+ if (streamAvailable && StreamDelegate .isStream (getType () )) {
344
326
return StreamDelegate .getStreamElementType (this );
345
327
}
346
- return getRelatedIfResolvable (this , this . resolvableType .asCollection ().getGeneric (0 ));
328
+ return getRelatedIfResolvable (this , getResolvableType () .asCollection ().getGeneric (0 ));
347
329
}
348
330
349
331
/**
@@ -384,8 +366,8 @@ public boolean isMap() {
384
366
* @throws IllegalStateException if this type is not a {@code java.util.Map}
385
367
*/
386
368
public TypeDescriptor getMapKeyTypeDescriptor () {
387
- Assert .state (isMap (), "Not a java.util.Map" );
388
- return getRelatedIfResolvable (this , this . resolvableType .asMap ().getGeneric (0 ));
369
+ Assert .state (isMap (), "Not a [ java.util.Map] " );
370
+ return getRelatedIfResolvable (this , getResolvableType () .asMap ().getGeneric (0 ));
389
371
}
390
372
391
373
/**
@@ -419,8 +401,8 @@ public TypeDescriptor getMapKeyTypeDescriptor(Object mapKey) {
419
401
* @throws IllegalStateException if this type is not a {@code java.util.Map}
420
402
*/
421
403
public TypeDescriptor getMapValueTypeDescriptor () {
422
- Assert .state (isMap (), "Not a java.util.Map" );
423
- return getRelatedIfResolvable (this , this . resolvableType .asMap ().getGeneric (1 ));
404
+ Assert .state (isMap (), "Not a [ java.util.Map] " );
405
+ return getRelatedIfResolvable (this , getResolvableType () .asMap ().getGeneric (1 ));
424
406
}
425
407
426
408
/**
@@ -448,7 +430,7 @@ private TypeDescriptor narrow(Object value, TypeDescriptor typeDescriptor) {
448
430
if (typeDescriptor != null ) {
449
431
return typeDescriptor .narrow (value );
450
432
}
451
- return (value != null ? new TypeDescriptor (this . resolvableType , value .getClass (), this . annotations ) : null );
433
+ return (value != null ? new TypeDescriptor (getResolvableType () , value .getClass (), getAnnotations () ) : null );
452
434
}
453
435
454
436
@ Override
@@ -494,7 +476,7 @@ public String toString() {
494
476
for (Annotation ann : getAnnotations ()) {
495
477
builder .append ("@" ).append (ann .annotationType ().getName ()).append (' ' );
496
478
}
497
- builder .append (this . resolvableType .toString ());
479
+ builder .append (getResolvableType () .toString ());
498
480
return builder .toString ();
499
481
}
500
482
@@ -529,9 +511,9 @@ public static TypeDescriptor valueOf(Class<?> type) {
529
511
* @return the collection type descriptor
530
512
*/
531
513
public static TypeDescriptor collection (Class <?> collectionType , TypeDescriptor elementTypeDescriptor ) {
532
- Assert .notNull (collectionType , "collectionType must not be null" );
514
+ Assert .notNull (collectionType , "Collection type must not be null" );
533
515
if (!Collection .class .isAssignableFrom (collectionType )) {
534
- throw new IllegalArgumentException ("collectionType must be a java.util.Collection" );
516
+ throw new IllegalArgumentException ("Collection type must be a [ java.util.Collection] " );
535
517
}
536
518
ResolvableType element = (elementTypeDescriptor != null ? elementTypeDescriptor .resolvableType : null );
537
519
return new TypeDescriptor (ResolvableType .forClassWithGenerics (collectionType , element ), null , null );
@@ -552,8 +534,9 @@ public static TypeDescriptor collection(Class<?> collectionType, TypeDescriptor
552
534
* @return the map type descriptor
553
535
*/
554
536
public static TypeDescriptor map (Class <?> mapType , TypeDescriptor keyTypeDescriptor , TypeDescriptor valueTypeDescriptor ) {
537
+ Assert .notNull (mapType , "Map type must not be null" );
555
538
if (!Map .class .isAssignableFrom (mapType )) {
556
- throw new IllegalArgumentException ("mapType must be a java.util.Map" );
539
+ throw new IllegalArgumentException ("Map type must be a [ java.util.Map] " );
557
540
}
558
541
ResolvableType key = (keyTypeDescriptor != null ? keyTypeDescriptor .resolvableType : null );
559
542
ResolvableType value = (valueTypeDescriptor != null ? valueTypeDescriptor .resolvableType : null );
@@ -691,7 +674,60 @@ private static TypeDescriptor getRelatedIfResolvable(TypeDescriptor source, Reso
691
674
if (type .resolve () == null ) {
692
675
return null ;
693
676
}
694
- return new TypeDescriptor (type , null , source .annotations );
677
+ return new TypeDescriptor (type , null , source .getAnnotations ());
678
+ }
679
+
680
+
681
+ /**
682
+ * Adapter class for exposing a {@code TypeDescriptor}'s annotations as an
683
+ * {@link AnnotatedElement}, in particular to {@link AnnotatedElementUtils}.
684
+ * @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class)
685
+ * @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class)
686
+ */
687
+ private class AnnotatedElementAdapter implements AnnotatedElement , Serializable {
688
+
689
+ private final Annotation [] annotations ;
690
+
691
+ public AnnotatedElementAdapter (Annotation [] annotations ) {
692
+ this .annotations = annotations ;
693
+ }
694
+
695
+ @ Override
696
+ @ SuppressWarnings ("unchecked" )
697
+ public <T extends Annotation > T getAnnotation (Class <T > annotationClass ) {
698
+ for (Annotation annotation : getAnnotations ()) {
699
+ if (annotation .annotationType () == annotationClass ) {
700
+ return (T ) annotation ;
701
+ }
702
+ }
703
+ return null ;
704
+ }
705
+
706
+ @ Override
707
+ public Annotation [] getAnnotations () {
708
+ return (this .annotations != null ? this .annotations : EMPTY_ANNOTATION_ARRAY );
709
+ }
710
+
711
+ @ Override
712
+ public Annotation [] getDeclaredAnnotations () {
713
+ return getAnnotations ();
714
+ }
715
+
716
+ @ Override
717
+ public boolean equals (Object other ) {
718
+ return (this == other || (other instanceof AnnotatedElementAdapter &&
719
+ Arrays .equals (this .annotations , ((AnnotatedElementAdapter ) other ).annotations )));
720
+ }
721
+
722
+ @ Override
723
+ public int hashCode () {
724
+ return Arrays .hashCode (this .annotations );
725
+ }
726
+
727
+ @ Override
728
+ public String toString () {
729
+ return TypeDescriptor .this .toString ();
730
+ }
695
731
}
696
732
697
733
@@ -706,7 +742,7 @@ public static boolean isStream(Class<?> type) {
706
742
}
707
743
708
744
public static TypeDescriptor getStreamElementType (TypeDescriptor source ) {
709
- return getRelatedIfResolvable (source , source .resolvableType .as (Stream .class ).getGeneric (0 ));
745
+ return getRelatedIfResolvable (source , source .getResolvableType () .as (Stream .class ).getGeneric (0 ));
710
746
}
711
747
}
712
748
0 commit comments