1818
1919import java .io .Serializable ;
2020import java .lang .annotation .Annotation ;
21+ import java .lang .reflect .AnnotatedElement ;
2122import java .lang .reflect .Field ;
2223import java .lang .reflect .Type ;
24+ import java .util .Arrays ;
2325import java .util .Collection ;
2426import java .util .HashMap ;
2527import java .util .Map ;
2628import java .util .stream .Stream ;
2729
2830import org .springframework .core .MethodParameter ;
2931import org .springframework .core .ResolvableType ;
30- import org .springframework .core .annotation .AnnotationUtils ;
32+ import org .springframework .core .annotation .AnnotatedElementUtils ;
3133import org .springframework .lang .UsesJava8 ;
3234import org .springframework .util .Assert ;
3335import org .springframework .util .ClassUtils ;
@@ -70,7 +72,7 @@ public class TypeDescriptor implements Serializable {
7072
7173 private final ResolvableType resolvableType ;
7274
73- private final Annotation [] annotations ;
75+ private final AnnotatedElement annotatedElement ;
7476
7577
7678 /**
@@ -83,9 +85,8 @@ public TypeDescriptor(MethodParameter methodParameter) {
8385 Assert .notNull (methodParameter , "MethodParameter must not be null" );
8486 this .resolvableType = ResolvableType .forMethodParameter (methodParameter );
8587 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 ());
8990 }
9091
9192 /**
@@ -97,7 +98,7 @@ public TypeDescriptor(Field field) {
9798 Assert .notNull (field , "Field must not be null" );
9899 this .resolvableType = ResolvableType .forField (field );
99100 this .type = this .resolvableType .resolve (field .getType ());
100- this .annotations = nullSafeAnnotations (field .getAnnotations ());
101+ this .annotatedElement = new AnnotatedElementAdapter (field .getAnnotations ());
101102 }
102103
103104 /**
@@ -110,7 +111,7 @@ public TypeDescriptor(Property property) {
110111 Assert .notNull (property , "Property must not be null" );
111112 this .resolvableType = ResolvableType .forMethodParameter (property .getMethodParameter ());
112113 this .type = this .resolvableType .resolve (property .getType ());
113- this .annotations = nullSafeAnnotations (property .getAnnotations ());
114+ this .annotatedElement = new AnnotatedElementAdapter (property .getAnnotations ());
114115 }
115116
116117 /**
@@ -124,14 +125,10 @@ public TypeDescriptor(Property property) {
124125 protected TypeDescriptor (ResolvableType resolvableType , Class <?> type , Annotation [] annotations ) {
125126 this .resolvableType = resolvableType ;
126127 this .type = (type != null ? type : resolvableType .resolve (Object .class ));
127- this .annotations = nullSafeAnnotations (annotations );
128+ this .annotatedElement = new AnnotatedElementAdapter (annotations );
128129 }
129130
130131
131- private Annotation [] nullSafeAnnotations (Annotation [] annotations ) {
132- return (annotations != null ? annotations : EMPTY_ANNOTATION_ARRAY );
133- }
134-
135132 /**
136133 * Variation of {@link #getType()} that accounts for a primitive type by
137134 * returning its object wrapper type.
@@ -193,8 +190,8 @@ public TypeDescriptor narrow(Object value) {
193190 if (value == null ) {
194191 return this ;
195192 }
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 () );
198195 }
199196
200197 /**
@@ -210,7 +207,7 @@ public TypeDescriptor upcast(Class<?> superType) {
210207 return null ;
211208 }
212209 Assert .isAssignable (superType , getType ());
213- return new TypeDescriptor (this . resolvableType . as (superType ), superType , this . annotations );
210+ return new TypeDescriptor (getResolvableType (). as (superType ), superType , getAnnotations () );
214211 }
215212
216213 /**
@@ -232,7 +229,7 @@ public boolean isPrimitive() {
232229 * @return the annotations, or an empty array if none
233230 */
234231 public Annotation [] getAnnotations () {
235- return this .annotations ;
232+ return this .annotatedElement . getAnnotations () ;
236233 }
237234
238235 /**
@@ -243,7 +240,7 @@ public Annotation[] getAnnotations() {
243240 * @return <tt>true</tt> if the annotation is present
244241 */
245242 public boolean hasAnnotation (Class <? extends Annotation > annotationType ) {
246- return ( getAnnotation ( annotationType ) != null );
243+ return AnnotatedElementUtils . isAnnotated ( this . annotatedElement , annotationType );
247244 }
248245
249246 /**
@@ -254,22 +251,7 @@ public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
254251 */
255252 @ SuppressWarnings ("unchecked" )
256253 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 );
273255 }
274256
275257 /**
@@ -337,13 +319,13 @@ public boolean isArray() {
337319 * @throws IllegalStateException if this type is not a {@code java.util.Collection} or array type
338320 */
339321 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 () );
342324 }
343- if (streamAvailable && StreamDelegate .isStream (this . type )) {
325+ if (streamAvailable && StreamDelegate .isStream (getType () )) {
344326 return StreamDelegate .getStreamElementType (this );
345327 }
346- return getRelatedIfResolvable (this , this . resolvableType .asCollection ().getGeneric (0 ));
328+ return getRelatedIfResolvable (this , getResolvableType () .asCollection ().getGeneric (0 ));
347329 }
348330
349331 /**
@@ -384,8 +366,8 @@ public boolean isMap() {
384366 * @throws IllegalStateException if this type is not a {@code java.util.Map}
385367 */
386368 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 ));
389371 }
390372
391373 /**
@@ -419,8 +401,8 @@ public TypeDescriptor getMapKeyTypeDescriptor(Object mapKey) {
419401 * @throws IllegalStateException if this type is not a {@code java.util.Map}
420402 */
421403 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 ));
424406 }
425407
426408 /**
@@ -448,7 +430,7 @@ private TypeDescriptor narrow(Object value, TypeDescriptor typeDescriptor) {
448430 if (typeDescriptor != null ) {
449431 return typeDescriptor .narrow (value );
450432 }
451- return (value != null ? new TypeDescriptor (this . resolvableType , value .getClass (), this . annotations ) : null );
433+ return (value != null ? new TypeDescriptor (getResolvableType () , value .getClass (), getAnnotations () ) : null );
452434 }
453435
454436 @ Override
@@ -494,7 +476,7 @@ public String toString() {
494476 for (Annotation ann : getAnnotations ()) {
495477 builder .append ("@" ).append (ann .annotationType ().getName ()).append (' ' );
496478 }
497- builder .append (this . resolvableType .toString ());
479+ builder .append (getResolvableType () .toString ());
498480 return builder .toString ();
499481 }
500482
@@ -529,9 +511,9 @@ public static TypeDescriptor valueOf(Class<?> type) {
529511 * @return the collection type descriptor
530512 */
531513 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" );
533515 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] " );
535517 }
536518 ResolvableType element = (elementTypeDescriptor != null ? elementTypeDescriptor .resolvableType : null );
537519 return new TypeDescriptor (ResolvableType .forClassWithGenerics (collectionType , element ), null , null );
@@ -552,8 +534,9 @@ public static TypeDescriptor collection(Class<?> collectionType, TypeDescriptor
552534 * @return the map type descriptor
553535 */
554536 public static TypeDescriptor map (Class <?> mapType , TypeDescriptor keyTypeDescriptor , TypeDescriptor valueTypeDescriptor ) {
537+ Assert .notNull (mapType , "Map type must not be null" );
555538 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] " );
557540 }
558541 ResolvableType key = (keyTypeDescriptor != null ? keyTypeDescriptor .resolvableType : null );
559542 ResolvableType value = (valueTypeDescriptor != null ? valueTypeDescriptor .resolvableType : null );
@@ -691,7 +674,60 @@ private static TypeDescriptor getRelatedIfResolvable(TypeDescriptor source, Reso
691674 if (type .resolve () == null ) {
692675 return null ;
693676 }
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+ }
695731 }
696732
697733
@@ -706,7 +742,7 @@ public static boolean isStream(Class<?> type) {
706742 }
707743
708744 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 ));
710746 }
711747 }
712748
0 commit comments