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 &quot;annotation inheritance&quot;). 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   * &#064;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>&quot;value&quot;</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>&quot;value&quot;</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>&quot;value&quot;</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}