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 .util .Assert ;
3234import org .springframework .util .ClassUtils ;
3335import org .springframework .util .ObjectUtils ;
@@ -66,7 +68,7 @@ public class TypeDescriptor implements Serializable {
6668
6769 private final ResolvableType resolvableType ;
6870
69- private final Annotation [] annotations ;
71+ private final AnnotatedElement annotatedElement ;
7072
7173
7274 /**
@@ -79,9 +81,8 @@ public TypeDescriptor(MethodParameter methodParameter) {
7981 Assert .notNull (methodParameter , "MethodParameter must not be null" );
8082 this .resolvableType = ResolvableType .forMethodParameter (methodParameter );
8183 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 ());
8586 }
8687
8788 /**
@@ -93,7 +94,7 @@ public TypeDescriptor(Field field) {
9394 Assert .notNull (field , "Field must not be null" );
9495 this .resolvableType = ResolvableType .forField (field );
9596 this .type = this .resolvableType .resolve (field .getType ());
96- this .annotations = nullSafeAnnotations (field .getAnnotations ());
97+ this .annotatedElement = new AnnotatedElementAdapter (field .getAnnotations ());
9798 }
9899
99100 /**
@@ -106,7 +107,7 @@ public TypeDescriptor(Property property) {
106107 Assert .notNull (property , "Property must not be null" );
107108 this .resolvableType = ResolvableType .forMethodParameter (property .getMethodParameter ());
108109 this .type = this .resolvableType .resolve (property .getType ());
109- this .annotations = nullSafeAnnotations (property .getAnnotations ());
110+ this .annotatedElement = new AnnotatedElementAdapter (property .getAnnotations ());
110111 }
111112
112113 /**
@@ -120,14 +121,10 @@ public TypeDescriptor(Property property) {
120121 protected TypeDescriptor (ResolvableType resolvableType , Class <?> type , Annotation [] annotations ) {
121122 this .resolvableType = resolvableType ;
122123 this .type = (type != null ? type : resolvableType .resolve (Object .class ));
123- this .annotations = nullSafeAnnotations (annotations );
124+ this .annotatedElement = new AnnotatedElementAdapter (annotations );
124125 }
125126
126127
127- private Annotation [] nullSafeAnnotations (Annotation [] annotations ) {
128- return (annotations != null ? annotations : EMPTY_ANNOTATION_ARRAY );
129- }
130-
131128 /**
132129 * Variation of {@link #getType()} that accounts for a primitive type by
133130 * returning its object wrapper type.
@@ -189,8 +186,8 @@ public TypeDescriptor narrow(Object value) {
189186 if (value == null ) {
190187 return this ;
191188 }
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 () );
194191 }
195192
196193 /**
@@ -206,7 +203,7 @@ public TypeDescriptor upcast(Class<?> superType) {
206203 return null ;
207204 }
208205 Assert .isAssignable (superType , getType ());
209- return new TypeDescriptor (this . resolvableType . as (superType ), superType , this . annotations );
206+ return new TypeDescriptor (getResolvableType (). as (superType ), superType , getAnnotations () );
210207 }
211208
212209 /**
@@ -228,7 +225,7 @@ public boolean isPrimitive() {
228225 * @return the annotations, or an empty array if none
229226 */
230227 public Annotation [] getAnnotations () {
231- return this .annotations ;
228+ return this .annotatedElement . getAnnotations () ;
232229 }
233230
234231 /**
@@ -239,7 +236,7 @@ public Annotation[] getAnnotations() {
239236 * @return <tt>true</tt> if the annotation is present
240237 */
241238 public boolean hasAnnotation (Class <? extends Annotation > annotationType ) {
242- return ( getAnnotation ( annotationType ) != null );
239+ return AnnotatedElementUtils . isAnnotated ( this . annotatedElement , annotationType );
243240 }
244241
245242 /**
@@ -250,22 +247,7 @@ public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
250247 */
251248 @ SuppressWarnings ("unchecked" )
252249 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 );
269251 }
270252
271253 /**
@@ -333,13 +315,13 @@ public boolean isArray() {
333315 * @throws IllegalStateException if this type is not a {@code java.util.Collection} or array type
334316 */
335317 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 () );
338320 }
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 ));
341323 }
342- return getRelatedIfResolvable (this , this . resolvableType .asCollection ().getGeneric (0 ));
324+ return getRelatedIfResolvable (this , getResolvableType () .asCollection ().getGeneric (0 ));
343325 }
344326
345327 /**
@@ -380,8 +362,8 @@ public boolean isMap() {
380362 * @throws IllegalStateException if this type is not a {@code java.util.Map}
381363 */
382364 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 ));
385367 }
386368
387369 /**
@@ -415,8 +397,8 @@ public TypeDescriptor getMapKeyTypeDescriptor(Object mapKey) {
415397 * @throws IllegalStateException if this type is not a {@code java.util.Map}
416398 */
417399 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 ));
420402 }
421403
422404 /**
@@ -444,7 +426,7 @@ private TypeDescriptor narrow(Object value, TypeDescriptor typeDescriptor) {
444426 if (typeDescriptor != null ) {
445427 return typeDescriptor .narrow (value );
446428 }
447- return (value != null ? new TypeDescriptor (this . resolvableType , value .getClass (), this . annotations ) : null );
429+ return (value != null ? new TypeDescriptor (getResolvableType () , value .getClass (), getAnnotations () ) : null );
448430 }
449431
450432 @ Override
@@ -490,7 +472,7 @@ public String toString() {
490472 for (Annotation ann : getAnnotations ()) {
491473 builder .append ("@" ).append (ann .annotationType ().getName ()).append (' ' );
492474 }
493- builder .append (this . resolvableType .toString ());
475+ builder .append (getResolvableType () .toString ());
494476 return builder .toString ();
495477 }
496478
@@ -525,9 +507,9 @@ public static TypeDescriptor valueOf(Class<?> type) {
525507 * @return the collection type descriptor
526508 */
527509 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" );
529511 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] " );
531513 }
532514 ResolvableType element = (elementTypeDescriptor != null ? elementTypeDescriptor .resolvableType : null );
533515 return new TypeDescriptor (ResolvableType .forClassWithGenerics (collectionType , element ), null , null );
@@ -548,8 +530,9 @@ public static TypeDescriptor collection(Class<?> collectionType, TypeDescriptor
548530 * @return the map type descriptor
549531 */
550532 public static TypeDescriptor map (Class <?> mapType , TypeDescriptor keyTypeDescriptor , TypeDescriptor valueTypeDescriptor ) {
533+ Assert .notNull (mapType , "Map type must not be null" );
551534 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] " );
553536 }
554537 ResolvableType key = (keyTypeDescriptor != null ? keyTypeDescriptor .resolvableType : null );
555538 ResolvableType value = (valueTypeDescriptor != null ? valueTypeDescriptor .resolvableType : null );
@@ -687,7 +670,60 @@ private static TypeDescriptor getRelatedIfResolvable(TypeDescriptor source, Reso
687670 if (type .resolve () == null ) {
688671 return null ;
689672 }
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+ }
691727 }
692728
693729}
0 commit comments