001/* 002 * Licensed under the Apache License, Version 2.0 (the "License"); 003 * you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014package org.gbif.ws.util.spring; 015 016import java.lang.annotation.Annotation; 017import java.lang.reflect.Method; 018import java.util.Arrays; 019import java.util.HashMap; 020import java.util.Map; 021 022/** 023 * <p> 024 * General utility methods for working with annotations, handling bridge methods 025 * (which the compiler generates for generic declarations) as well as super 026 * methods (for optional "annotation inheritance"). Note that none of 027 * this is provided by the JDK's introspection facilities themselves. 028 * </p> 029 * <p> 030 * As a general rule for runtime-retained annotations (e.g. for transaction 031 * control, authorization or service exposure), always use the lookup methods on 032 * this class (e.g., {@link #findAnnotation(Method, Class)}, 033 * {@link #getAnnotation(Method, Class)}, and {@link #getAnnotations(Method)}) 034 * instead of the plain annotation lookup methods in the JDK. You can still 035 * explicitly choose between lookup on the given class level only ({@link #getAnnotation(Method, Class)}) 036 * and lookup in the entire inheritance hierarchy of the given method ({@link #findAnnotation(Method, Class)}). 037 * </p> 038 * 039 * @author Rod Johnson 040 * @author Rob Harrop 041 * @author Juergen Hoeller 042 * @author Sam Brannen 043 * @author Mark Fisher 044 * @see java.lang.reflect.Method#getAnnotations() 045 * @see java.lang.reflect.Method#getAnnotation(Class) 046 * @since 2.0 047 */ 048public abstract class AnnotationUtils { 049 050 /** 051 * The attribute name for annotations with a single element. 052 */ 053 static final String VALUE = "value"; 054 055 /** 056 * <p> 057 * Get all {@link Annotation Annotations} from the supplied {@link Method}. 058 * </p> 059 * <p> 060 * Correctly handles bridge {@link Method Methods} generated by the 061 * compiler. 062 * </p> 063 * 064 * @param method the method to look for annotations on 065 * 066 * @return the annotations found 067 */ 068 public static Annotation[] getAnnotations(final Method method) { 069 return BridgeMethodResolver.findBridgedMethod(method).getAnnotations(); 070 } 071 072 /** 073 * <p> 074 * Get a single {@link Annotation} of <code>annotationType</code> from the 075 * supplied {@link Method}. 076 * </p> 077 * <p> 078 * Correctly handles bridge {@link Method Methods} generated by the 079 * compiler. 080 * </p> 081 * 082 * @param method the method to look for annotations on 083 * @param annotationType the annotation class to look for 084 * 085 * @return the annotations found 086 */ 087 public static <A extends Annotation> A getAnnotation( 088 final Method method, final Class<A> annotationType) { 089 return BridgeMethodResolver.findBridgedMethod(method).getAnnotation(annotationType); 090 } 091 092 /** 093 * <p> 094 * Get a single {@link Annotation} of <code>annotationType</code> from the 095 * supplied {@link Method}, traversing its super methods if no annotation 096 * can be found on the given method itself. 097 * </p> 098 * <p> 099 * Annotations on methods are not inherited by default, so we need to handle 100 * this explicitly. 101 * </p> 102 * 103 * @param method the method to look for annotations on 104 * @param annotationType the annotation class to look for 105 * 106 * @return the annotation of the given type found, or <code>null</code> 107 */ 108 public static <A extends Annotation> A findAnnotation( 109 final Method method, final Class<A> annotationType) { 110 A annotation = getAnnotation(method, annotationType); 111 Class<?> cl = method.getDeclaringClass(); 112 while (annotation == null) { 113 cl = cl.getSuperclass(); 114 if (cl == null || cl.equals(Object.class)) { 115 break; 116 } 117 try { 118 Method equivalentMethod = 119 cl.getDeclaredMethod(method.getName(), method.getParameterTypes()); 120 annotation = getAnnotation(equivalentMethod, annotationType); 121 } catch (NoSuchMethodException ex) { 122 // We're done... 123 } 124 } 125 return annotation; 126 } 127 128 /** 129 * <p> 130 * Get a single {@link Annotation} of <code>annotationType</code> from the 131 * supplied {@link Class}, traversing its interfaces and super classes 132 * if no annotation can be found on the given class itself. 133 * </p> 134 * <p> 135 * This method explicitly handles class-level annotations which are not 136 * declared as {@link java.lang.annotation.Inherited inherited} as well as 137 * annotations on interfaces. 138 * </p> 139 * 140 * @param clazz the class to look for annotations on 141 * @param annotationType the annotation class to look for 142 * 143 * @return the annotation of the given type found, or <code>null</code> 144 */ 145 public static <A extends Annotation> A findAnnotation( 146 final Class<?> clazz, final Class<A> annotationType) { 147 Assert.notNull(clazz, "Class must not be null"); 148 A annotation = clazz.getAnnotation(annotationType); 149 if (annotation != null) { 150 return annotation; 151 } 152 for (Class<?> ifc : clazz.getInterfaces()) { 153 annotation = findAnnotation(ifc, annotationType); 154 if (annotation != null) { 155 return annotation; 156 } 157 } 158 if (clazz.getSuperclass() == null || Object.class.equals(clazz.getSuperclass())) { 159 return null; 160 } 161 return findAnnotation(clazz.getSuperclass(), annotationType); 162 } 163 164 /** 165 * <p> 166 * Finds the first {@link Class} in the inheritance hierarchy of the 167 * specified <code>clazz</code> (including the specified 168 * <code>clazz</code> itself) which declares an annotation for the 169 * specified <code>annotationType</code>, or <code>null</code> if not 170 * found. If the supplied <code>clazz</code> is <code>null</code>, 171 * <code>null</code> will be returned. 172 * </p> 173 * <p> 174 * If the supplied <code>clazz</code> is an interface, only the interface 175 * itself will be checked; the inheritance hierarchy for interfaces will not 176 * be traversed. 177 * </p> 178 * <p> 179 * The standard {@link Class} API does not provide a mechanism for 180 * determining which class in an inheritance hierarchy actually declares an 181 * {@link Annotation}, so we need to handle this explicitly. 182 * </p> 183 * 184 * @param annotationType the Class object corresponding to the annotation type 185 * @param clazz the Class object corresponding to the class on which to 186 * check for the annotation, or <code>null</code>. 187 * 188 * @return the first {@link Class} in the inheritance hierarchy of the 189 * specified <code>clazz</code> which declares an annotation for the specified 190 * <code>annotationType</code>, or <code>null</code> if not found. 191 * 192 * @see Class#isAnnotationPresent(Class) 193 * @see Class#getDeclaredAnnotations() 194 */ 195 public static Class<?> findAnnotationDeclaringClass( 196 final Class<? extends Annotation> annotationType, final Class<?> clazz) { 197 Assert.notNull(annotationType, "annotationType must not be null"); 198 if ((clazz == null) || clazz.equals(Object.class)) { 199 return null; 200 } 201 // else... 202 return (isAnnotationDeclaredLocally(annotationType, clazz)) 203 ? clazz 204 : findAnnotationDeclaringClass(annotationType, clazz.getSuperclass()); 205 } 206 207 /** 208 * <p> 209 * Returns <code>true</code> if an annotation for the specified 210 * <code>annotationType</code> is declared locally on the supplied 211 * <code>clazz</code>, else <code>false</code>. The supplied 212 * {@link Class} object may represent any type. 213 * </p> 214 * <p> 215 * Note: this method does <strong>not</strong> determine if the annotation 216 * is {@link java.lang.annotation.Inherited inherited}. For greater clarity 217 * regarding inherited annotations, consider using 218 * {@link #isAnnotationInherited(Class, Class)} instead. 219 * </p> 220 * 221 * @param annotationType the Class object corresponding to the annotation type 222 * @param clazz the Class object corresponding to the class on which to 223 * check for the annotation 224 * 225 * @return <code>true</code> if an annotation for the specified 226 * <code>annotationType</code> is declared locally on the supplied <code>clazz</code> 227 * 228 * @see Class#getDeclaredAnnotations() 229 * @see #isAnnotationInherited(Class, Class) 230 */ 231 public static boolean isAnnotationDeclaredLocally( 232 final Class<? extends Annotation> annotationType, final Class<?> clazz) { 233 Assert.notNull(annotationType, "annotationType must not be null"); 234 Assert.notNull(clazz, "clazz must not be null"); 235 boolean declaredLocally = false; 236 for (final Annotation annotation : Arrays.asList(clazz.getDeclaredAnnotations())) { 237 if (annotation.annotationType().equals(annotationType)) { 238 declaredLocally = true; 239 break; 240 } 241 } 242 return declaredLocally; 243 } 244 245 /** 246 * <p> 247 * Returns <code>true</code> if an annotation for the specified 248 * <code>annotationType</code> is present on the supplied 249 * <code>clazz</code> and is 250 * {@link java.lang.annotation.Inherited inherited} (i.e., not declared 251 * locally for the class), else <code>false</code>. 252 * </p> 253 * <p> 254 * If the supplied <code>clazz</code> is an interface, only the interface 255 * itself will be checked. In accord with standard meta-annotation 256 * semantics, the inheritance hierarchy for interfaces will not be 257 * traversed. See the {@link java.lang.annotation.Inherited JavaDoc} for the 258 * @Inherited meta-annotation for further details regarding annotation 259 * inheritance. 260 * </p> 261 * 262 * @param annotationType the Class object corresponding to the annotation type 263 * @param clazz the Class object corresponding to the class on which to 264 * check for the annotation 265 * 266 * @return <code>true</code> if an annotation for the specified 267 * <code>annotationType</code> is present on the supplied <code>clazz</code> 268 * and is {@link java.lang.annotation.Inherited inherited} 269 * 270 * @see Class#isAnnotationPresent(Class) 271 * @see #isAnnotationDeclaredLocally(Class, Class) 272 */ 273 public static boolean isAnnotationInherited( 274 final Class<? extends Annotation> annotationType, final Class<?> clazz) { 275 Assert.notNull(annotationType, "annotationType must not be null"); 276 Assert.notNull(clazz, "clazz must not be null"); 277 return (clazz.isAnnotationPresent(annotationType) 278 && !isAnnotationDeclaredLocally(annotationType, clazz)); 279 } 280 281 /** 282 * Retrieve the given annotation's attributes as a Map. 283 * 284 * @param annotation the annotation to retrieve the attributes for 285 * 286 * @return the Map of annotation attributes, with attribute names as keys 287 * and corresponding attribute values as values 288 */ 289 public static Map<String, Object> getAnnotationAttributes(final Annotation annotation) { 290 final Map<String, Object> attrs = new HashMap<String, Object>(); 291 final Method[] methods = annotation.annotationType().getDeclaredMethods(); 292 for (int j = 0; j < methods.length; j++) { 293 final Method method = methods[j]; 294 if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) { 295 try { 296 attrs.put(method.getName(), method.invoke(annotation)); 297 } catch (final Exception ex) { 298 throw new IllegalStateException("Could not obtain annotation attribute values", ex); 299 } 300 } 301 } 302 return attrs; 303 } 304 305 /** 306 * Retrieve the <em>value</em> of the <code>"value"</code> 307 * attribute of a single-element Annotation, given an annotation instance. 308 * 309 * @param annotation the annotation instance from which to retrieve the value 310 * 311 * @return the attribute value, or <code>null</code> if not found 312 * 313 * @see #getValue(Annotation, String) 314 */ 315 public static Object getValue(final Annotation annotation) { 316 return getValue(annotation, VALUE); 317 } 318 319 /** 320 * Retrieve the <em>value</em> of a named Annotation attribute, given an 321 * annotation instance. 322 * 323 * @param annotation the annotation instance from which to retrieve the value 324 * @param attributeName the name of the attribute value to retrieve 325 * 326 * @return the attribute value, or <code>null</code> if not found 327 * 328 * @see #getValue(Annotation) 329 */ 330 public static Object getValue(final Annotation annotation, final String attributeName) { 331 try { 332 final Method method = 333 annotation.annotationType().getDeclaredMethod(attributeName, new Class[0]); 334 return method.invoke(annotation); 335 } catch (final Exception ex) { 336 return null; 337 } 338 } 339 340 /** 341 * Retrieve the <em>default value</em> of the 342 * <code>"value"</code> attribute of a single-element 343 * Annotation, given an annotation instance. 344 * 345 * @param annotation the annotation instance from which to retrieve 346 * the default value 347 * 348 * @return the default value, or <code>null</code> if not found 349 * 350 * @see #getDefaultValue(Annotation, String) 351 */ 352 public static Object getDefaultValue(final Annotation annotation) { 353 return getDefaultValue(annotation, VALUE); 354 } 355 356 /** 357 * Retrieve the <em>default value</em> of a named Annotation attribute, 358 * given an annotation instance. 359 * 360 * @param annotation the annotation instance from which to retrieve 361 * the default value 362 * @param attributeName the name of the attribute value to retrieve 363 * 364 * @return the default value of the named attribute, or <code>null</code> 365 * if not found. 366 * 367 * @see #getDefaultValue(Class, String) 368 */ 369 public static Object getDefaultValue(final Annotation annotation, final String attributeName) { 370 return getDefaultValue(annotation.annotationType(), attributeName); 371 } 372 373 /** 374 * Retrieve the <em>default value</em> of the 375 * <code>"value"</code> attribute of a single-element 376 * Annotation, given the {@link Class annotation type}. 377 * 378 * @param annotationType the <em>annotation type</em> for which the 379 * default value should be retrieved 380 * 381 * @return the default value, or <code>null</code> if not found 382 * 383 * @see #getDefaultValue(Class, String) 384 */ 385 public static Object getDefaultValue(final Class<? extends Annotation> annotationType) { 386 return getDefaultValue(annotationType, VALUE); 387 } 388 389 /** 390 * Retrieve the <em>default value</em> of a named Annotation attribute, 391 * given the {@link Class annotation type}. 392 * 393 * @param annotationType the <em>annotation type</em> for which the 394 * default value should be retrieved 395 * @param attributeName the name of the attribute value to retrieve. 396 * 397 * @return the default value of the named attribute, or <code>null</code> 398 * if not found 399 * 400 * @see #getDefaultValue(Annotation, String) 401 */ 402 public static Object getDefaultValue( 403 final Class<? extends Annotation> annotationType, final String attributeName) { 404 try { 405 final Method method = annotationType.getDeclaredMethod(attributeName, new Class[0]); 406 return method.getDefaultValue(); 407 } catch (final Exception ex) { 408 return null; 409 } 410 } 411}