001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.test.util; 018 019import java.lang.annotation.Annotation; 020import java.util.HashSet; 021import java.util.Set; 022 023import org.springframework.core.annotation.AnnotatedElementUtils; 024import org.springframework.core.annotation.AnnotationAttributes; 025import org.springframework.core.annotation.AnnotationUtils; 026import org.springframework.core.style.ToStringCreator; 027import org.springframework.util.Assert; 028import org.springframework.util.ObjectUtils; 029 030/** 031 * {@code MetaAnnotationUtils} is a collection of utility methods that complements 032 * the standard support already available in {@link AnnotationUtils}. 033 * 034 * <p>Whereas {@code AnnotationUtils} provides utilities for <em>getting</em> or 035 * <em>finding</em> an annotation, {@code MetaAnnotationUtils} goes a step further 036 * by providing support for determining the <em>root class</em> on which an 037 * annotation is declared, either directly or indirectly via a <em>composed 038 * annotation</em>. This additional information is encapsulated in an 039 * {@link AnnotationDescriptor}. 040 * 041 * <p>The additional information provided by an {@code AnnotationDescriptor} is 042 * required by the <em>Spring TestContext Framework</em> in order to be able to 043 * support class hierarchy traversals for annotations such as 044 * {@link org.springframework.test.context.ContextConfiguration @ContextConfiguration}, 045 * {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}, 046 * and {@link org.springframework.test.context.ActiveProfiles @ActiveProfiles} 047 * which offer support for merging and overriding various <em>inherited</em> 048 * annotation attributes (e.g. 049 * {@link org.springframework.test.context.ContextConfiguration#inheritLocations}). 050 * 051 * @author Sam Brannen 052 * @since 4.0 053 * @see AnnotationUtils 054 * @see AnnotationDescriptor 055 */ 056public abstract class MetaAnnotationUtils { 057 058 /** 059 * Find the {@link AnnotationDescriptor} for the supplied {@code annotationType} 060 * on the supplied {@link Class}, traversing its annotations, interfaces, and 061 * superclasses if no annotation can be found on the given class itself. 062 * <p>This method explicitly handles class-level annotations which are not 063 * declared as {@linkplain java.lang.annotation.Inherited inherited} <em>as 064 * well as meta-annotations</em>. 065 * <p>The algorithm operates as follows: 066 * <ol> 067 * <li>Search for the annotation on the given class and return a corresponding 068 * {@code AnnotationDescriptor} if found. 069 * <li>Recursively search through all annotations that the given class declares. 070 * <li>Recursively search through all interfaces implemented by the given class. 071 * <li>Recursively search through the superclass hierarchy of the given class. 072 * </ol> 073 * <p>In this context, the term <em>recursively</em> means that the search 074 * process continues by returning to step #1 with the current annotation, 075 * interface, or superclass as the class to look for annotations on. 076 * @param clazz the class to look for annotations on 077 * @param annotationType the type of annotation to look for 078 * @return the corresponding annotation descriptor if the annotation was found; 079 * otherwise {@code null} 080 * @see AnnotationUtils#findAnnotationDeclaringClass(Class, Class) 081 * @see #findAnnotationDescriptorForTypes(Class, Class...) 082 */ 083 public static <T extends Annotation> AnnotationDescriptor<T> findAnnotationDescriptor( 084 Class<?> clazz, Class<T> annotationType) { 085 086 return findAnnotationDescriptor(clazz, new HashSet<Annotation>(), annotationType); 087 } 088 089 /** 090 * Perform the search algorithm for {@link #findAnnotationDescriptor(Class, Class)}, 091 * avoiding endless recursion by tracking which annotations have already been 092 * <em>visited</em>. 093 * @param clazz the class to look for annotations on 094 * @param visited the set of annotations that have already been visited 095 * @param annotationType the type of annotation to look for 096 * @return the corresponding annotation descriptor if the annotation was found; 097 * otherwise {@code null} 098 */ 099 private static <T extends Annotation> AnnotationDescriptor<T> findAnnotationDescriptor( 100 Class<?> clazz, Set<Annotation> visited, Class<T> annotationType) { 101 102 Assert.notNull(annotationType, "Annotation type must not be null"); 103 if (clazz == null || Object.class == clazz) { 104 return null; 105 } 106 107 // Declared locally? 108 if (AnnotationUtils.isAnnotationDeclaredLocally(annotationType, clazz)) { 109 return new AnnotationDescriptor<T>(clazz, clazz.getAnnotation(annotationType)); 110 } 111 112 // Declared on a composed annotation (i.e., as a meta-annotation)? 113 for (Annotation composedAnn : clazz.getDeclaredAnnotations()) { 114 Class<? extends Annotation> composedType = composedAnn.annotationType(); 115 if (!AnnotationUtils.isInJavaLangAnnotationPackage(composedType.getName()) && visited.add(composedAnn)) { 116 AnnotationDescriptor<T> descriptor = findAnnotationDescriptor(composedType, visited, annotationType); 117 if (descriptor != null) { 118 return new AnnotationDescriptor<T>( 119 clazz, descriptor.getDeclaringClass(), composedAnn, descriptor.getAnnotation()); 120 } 121 } 122 } 123 124 // Declared on interface? 125 for (Class<?> ifc : clazz.getInterfaces()) { 126 AnnotationDescriptor<T> descriptor = findAnnotationDescriptor(ifc, visited, annotationType); 127 if (descriptor != null) { 128 return new AnnotationDescriptor<T>(clazz, descriptor.getDeclaringClass(), 129 descriptor.getComposedAnnotation(), descriptor.getAnnotation()); 130 } 131 } 132 133 // Declared on a superclass? 134 return findAnnotationDescriptor(clazz.getSuperclass(), visited, annotationType); 135 } 136 137 /** 138 * Find the {@link UntypedAnnotationDescriptor} for the first {@link Class} 139 * in the inheritance hierarchy of the specified {@code clazz} (including 140 * the specified {@code clazz} itself) which declares at least one of the 141 * specified {@code annotationTypes}. 142 * <p>This method traverses the annotations, interfaces, and superclasses 143 * of the specified {@code clazz} if no annotation can be found on the given 144 * class itself. 145 * <p>This method explicitly handles class-level annotations which are not 146 * declared as {@linkplain java.lang.annotation.Inherited inherited} <em>as 147 * well as meta-annotations</em>. 148 * <p>The algorithm operates as follows: 149 * <ol> 150 * <li>Search for a local declaration of one of the annotation types on 151 * the given class and return a corresponding {@code UntypedAnnotationDescriptor} 152 * if found. 153 * <li>Recursively search through all annotations that the given class declares. 154 * <li>Recursively search through all interfaces implemented by the given class. 155 * <li>Recursively search through the superclass hierarchy of the given class. 156 * </ol> 157 * <p>In this context, the term <em>recursively</em> means that the search 158 * process continues by returning to step #1 with the current annotation, 159 * interface, or superclass as the class to look for annotations on. 160 * @param clazz the class to look for annotations on 161 * @param annotationTypes the types of annotations to look for 162 * @return the corresponding annotation descriptor if one of the annotations 163 * was found; otherwise {@code null} 164 * @see AnnotationUtils#findAnnotationDeclaringClassForTypes(java.util.List, Class) 165 * @see #findAnnotationDescriptor(Class, Class) 166 */ 167 @SuppressWarnings("unchecked") 168 public static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes( 169 Class<?> clazz, Class<? extends Annotation>... annotationTypes) { 170 171 return findAnnotationDescriptorForTypes(clazz, new HashSet<Annotation>(), annotationTypes); 172 } 173 174 /** 175 * Perform the search algorithm for {@link #findAnnotationDescriptorForTypes(Class, Class...)}, 176 * avoiding endless recursion by tracking which annotations have already been 177 * <em>visited</em>. 178 * @param clazz the class to look for annotations on 179 * @param visited the set of annotations that have already been visited 180 * @param annotationTypes the types of annotations to look for 181 * @return the corresponding annotation descriptor if one of the annotations 182 * was found; otherwise {@code null} 183 */ 184 @SuppressWarnings("unchecked") 185 private static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes(Class<?> clazz, 186 Set<Annotation> visited, Class<? extends Annotation>... annotationTypes) { 187 188 assertNonEmptyAnnotationTypeArray(annotationTypes, "The list of annotation types must not be empty"); 189 if (clazz == null || Object.class == clazz) { 190 return null; 191 } 192 193 // Declared locally? 194 for (Class<? extends Annotation> annotationType : annotationTypes) { 195 if (AnnotationUtils.isAnnotationDeclaredLocally(annotationType, clazz)) { 196 return new UntypedAnnotationDescriptor(clazz, clazz.getAnnotation(annotationType)); 197 } 198 } 199 200 // Declared on a composed annotation (i.e., as a meta-annotation)? 201 for (Annotation composedAnnotation : clazz.getDeclaredAnnotations()) { 202 if (!AnnotationUtils.isInJavaLangAnnotationPackage(composedAnnotation) && visited.add(composedAnnotation)) { 203 UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes( 204 composedAnnotation.annotationType(), visited, annotationTypes); 205 if (descriptor != null) { 206 return new UntypedAnnotationDescriptor(clazz, descriptor.getDeclaringClass(), 207 composedAnnotation, descriptor.getAnnotation()); 208 } 209 } 210 } 211 212 // Declared on interface? 213 for (Class<?> ifc : clazz.getInterfaces()) { 214 UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(ifc, visited, annotationTypes); 215 if (descriptor != null) { 216 return new UntypedAnnotationDescriptor(clazz, descriptor.getDeclaringClass(), 217 descriptor.getComposedAnnotation(), descriptor.getAnnotation()); 218 } 219 } 220 221 // Declared on a superclass? 222 return findAnnotationDescriptorForTypes(clazz.getSuperclass(), visited, annotationTypes); 223 } 224 225 private static void assertNonEmptyAnnotationTypeArray(Class<?>[] annotationTypes, String message) { 226 if (ObjectUtils.isEmpty(annotationTypes)) { 227 throw new IllegalArgumentException(message); 228 } 229 for (Class<?> clazz : annotationTypes) { 230 if (!Annotation.class.isAssignableFrom(clazz)) { 231 throw new IllegalArgumentException("Array elements must be of type Annotation"); 232 } 233 } 234 } 235 236 237 /** 238 * Descriptor for an {@link Annotation}, including the {@linkplain 239 * #getDeclaringClass() class} on which the annotation is <em>declared</em> 240 * as well as the actual {@linkplain #getAnnotation() annotation} instance. 241 * <p>If the annotation is used as a meta-annotation, the descriptor also includes 242 * the {@linkplain #getComposedAnnotation() composed annotation} on which the 243 * annotation is present. In such cases, the <em>root declaring class</em> is 244 * not directly annotated with the annotation but rather indirectly via the 245 * composed annotation. 246 * <p>Given the following example, if we are searching for the {@code @Transactional} 247 * annotation <em>on</em> the {@code TransactionalTests} class, then the 248 * properties of the {@code AnnotationDescriptor} would be as follows. 249 * <ul> 250 * <li>rootDeclaringClass: {@code TransactionalTests} class object</li> 251 * <li>declaringClass: {@code TransactionalTests} class object</li> 252 * <li>composedAnnotation: {@code null}</li> 253 * <li>annotation: instance of the {@code Transactional} annotation</li> 254 * </ul> 255 * <pre style="code"> 256 * @Transactional 257 * @ContextConfiguration({"/test-datasource.xml", "/repository-config.xml"}) 258 * public class TransactionalTests { } 259 * </pre> 260 * <p>Given the following example, if we are searching for the {@code @Transactional} 261 * annotation <em>on</em> the {@code UserRepositoryTests} class, then the 262 * properties of the {@code AnnotationDescriptor} would be as follows. 263 * <ul> 264 * <li>rootDeclaringClass: {@code UserRepositoryTests} class object</li> 265 * <li>declaringClass: {@code RepositoryTests} class object</li> 266 * <li>composedAnnotation: instance of the {@code RepositoryTests} annotation</li> 267 * <li>annotation: instance of the {@code Transactional} annotation</li> 268 * </ul> 269 * <pre style="code"> 270 * @Transactional 271 * @ContextConfiguration({"/test-datasource.xml", "/repository-config.xml"}) 272 * @Retention(RetentionPolicy.RUNTIME) 273 * public @interface RepositoryTests { } 274 * 275 * @RepositoryTests 276 * public class UserRepositoryTests { } 277 * </pre> 278 */ 279 public static class AnnotationDescriptor<T extends Annotation> { 280 281 private final Class<?> rootDeclaringClass; 282 283 private final Class<?> declaringClass; 284 285 private final Annotation composedAnnotation; 286 287 private final T annotation; 288 289 private final AnnotationAttributes annotationAttributes; 290 291 public AnnotationDescriptor(Class<?> rootDeclaringClass, T annotation) { 292 this(rootDeclaringClass, rootDeclaringClass, null, annotation); 293 } 294 295 public AnnotationDescriptor(Class<?> rootDeclaringClass, Class<?> declaringClass, 296 Annotation composedAnnotation, T annotation) { 297 298 Assert.notNull(rootDeclaringClass, "'rootDeclaringClass' must not be null"); 299 Assert.notNull(annotation, "Annotation must not be null"); 300 this.rootDeclaringClass = rootDeclaringClass; 301 this.declaringClass = declaringClass; 302 this.composedAnnotation = composedAnnotation; 303 this.annotation = annotation; 304 this.annotationAttributes = AnnotatedElementUtils.findMergedAnnotationAttributes( 305 rootDeclaringClass, annotation.annotationType().getName(), false, false); 306 } 307 308 public Class<?> getRootDeclaringClass() { 309 return this.rootDeclaringClass; 310 } 311 312 public Class<?> getDeclaringClass() { 313 return this.declaringClass; 314 } 315 316 public T getAnnotation() { 317 return this.annotation; 318 } 319 320 /** 321 * Synthesize the merged {@link #getAnnotationAttributes AnnotationAttributes} 322 * in this descriptor back into an annotation of the target 323 * {@linkplain #getAnnotationType annotation type}. 324 * @since 4.2 325 * @see #getAnnotationAttributes() 326 * @see #getAnnotationType() 327 * @see AnnotationUtils#synthesizeAnnotation(java.util.Map, Class, java.lang.reflect.AnnotatedElement) 328 */ 329 @SuppressWarnings("unchecked") 330 public T synthesizeAnnotation() { 331 return AnnotationUtils.synthesizeAnnotation( 332 getAnnotationAttributes(), (Class<T>) getAnnotationType(), getRootDeclaringClass()); 333 } 334 335 public Class<? extends Annotation> getAnnotationType() { 336 return this.annotation.annotationType(); 337 } 338 339 public AnnotationAttributes getAnnotationAttributes() { 340 return this.annotationAttributes; 341 } 342 343 public Annotation getComposedAnnotation() { 344 return this.composedAnnotation; 345 } 346 347 public Class<? extends Annotation> getComposedAnnotationType() { 348 return (this.composedAnnotation != null ? this.composedAnnotation.annotationType() : null); 349 } 350 351 /** 352 * Provide a textual representation of this {@code AnnotationDescriptor}. 353 */ 354 @Override 355 public String toString() { 356 return new ToStringCreator(this) 357 .append("rootDeclaringClass", this.rootDeclaringClass) 358 .append("declaringClass", this.declaringClass) 359 .append("composedAnnotation", this.composedAnnotation) 360 .append("annotation", this.annotation) 361 .toString(); 362 } 363 } 364 365 366 /** 367 * <em>Untyped</em> extension of {@code AnnotationDescriptor} that is used 368 * to describe the declaration of one of several candidate annotation types 369 * where the actual annotation type cannot be predetermined. 370 */ 371 public static class UntypedAnnotationDescriptor extends AnnotationDescriptor<Annotation> { 372 373 public UntypedAnnotationDescriptor(Class<?> rootDeclaringClass, Annotation annotation) { 374 this(rootDeclaringClass, rootDeclaringClass, null, annotation); 375 } 376 377 public UntypedAnnotationDescriptor(Class<?> rootDeclaringClass, Class<?> declaringClass, 378 Annotation composedAnnotation, Annotation annotation) { 379 380 super(rootDeclaringClass, declaringClass, composedAnnotation, annotation); 381 } 382 383 /** 384 * Throws an {@link UnsupportedOperationException} since the type of annotation 385 * represented by the {@link #getAnnotationAttributes AnnotationAttributes} in 386 * an {@code UntypedAnnotationDescriptor} is unknown. 387 * @since 4.2 388 */ 389 @Override 390 public Annotation synthesizeAnnotation() { 391 throw new UnsupportedOperationException( 392 "getMergedAnnotation() is unsupported in UntypedAnnotationDescriptor"); 393 } 394 } 395 396}