View Javadoc

1   package com.atlassian.plugins.rest.common.util;
2   
3   import com.google.common.collect.Lists;
4   import org.apache.commons.lang.StringUtils;
5   import org.slf4j.Logger;
6   import org.slf4j.LoggerFactory;
7   
8   import java.lang.annotation.Annotation;
9   import java.lang.reflect.AnnotatedElement;
10  import java.lang.reflect.Field;
11  import java.util.List;
12  import javax.annotation.Nonnull;
13  import javax.annotation.Nullable;
14  
15  import static com.google.common.base.Preconditions.checkNotNull;
16  import static java.util.Arrays.asList;
17  
18  /**
19   * A class to simplify some reflection calls.
20   */
21  public class ReflectionUtils {
22      private static final Logger log = LoggerFactory.getLogger(ReflectionUtils.class);
23  
24      private ReflectionUtils() {
25      }
26  
27      /**
28       * Gets the value of the {@link Field field} for the given object. It will change the accessibility of the field if necessary.
29       * Setting it back to its original value at the end of the method call.
30       *
31       * @param field  the field to read from
32       * @param object the object to read the field from
33       * @return the value of the field.
34       */
35      public static Object getFieldValue(Field field, Object object) {
36          final boolean accessible = field.isAccessible();
37          try {
38              if (!accessible) {
39                  field.setAccessible(true);
40              }
41              return field.get(object);
42          } catch (IllegalAccessException e) {
43              throw new RuntimeException("Could not access '" + field + "' from '" + object + "'", e);
44          } finally {
45              if (!accessible) {
46                  field.setAccessible(false);
47              }
48          }
49      }
50  
51      /**
52       * Sets the value of the {@link Field field} for the given object. It will change the accessibility of the field if necessary.
53       * Setting it back to its original value at the end of the method call.
54       *
55       * @param field  the field to set the value of
56       * @param object the object to for which to set the field value.
57       * @param value  the new value to be set to the field of object.
58       */
59      public static void setFieldValue(Field field, Object object, Object value) {
60          final boolean accessible = field.isAccessible();
61          try {
62              if (!accessible) {
63                  field.setAccessible(true);
64              }
65              field.set(object, value);
66          } catch (IllegalAccessException e) {
67              throw new RuntimeException("Could not access '" + field + "' from '" + object + "'", e);
68          } finally {
69              if (!accessible) {
70                  field.setAccessible(false);
71              }
72          }
73      }
74  
75      /**
76       * Returns the result of running {@link Class#getDeclaredFields()} on the
77       * supplied class, as well as all its super types. Fields are ordered in
78       * ascending hierarchy order (subclasses first).
79       *
80       * @param clazz
81       * @return all of the class's fields (including inherited fields).
82       * @since v1.0.4
83       */
84      public static List<Field> getDeclaredFields(Class clazz) {
85          if (clazz == null) {
86              return Lists.newArrayList();
87          } else {
88              final List<Field> superFields = getDeclaredFields(clazz.getSuperclass());
89              superFields.addAll(0, asList(clazz.getDeclaredFields()));
90              return superFields;
91          }
92      }
93  
94      /**
95       * Gets the provided annotation from the provided annotated element, if it exists.
96       * <p>
97       * Will only return a result if the found annotation is from the same class loader as the target type.
98       * <p>
99       * Will emit a warning into the log if the annotation is found but has been loaded
100      * from a different class loader.
101      *
102      * @param annotationType The annotation to search for
103      * @param element        The element to search
104      * @return the element's annotation for the specified annotation type if present, else <code>null</code>
105      * @since 3.0.10
106      */
107     public static <T extends Annotation> T getAnnotation(@Nonnull final Class<T> annotationType, @Nullable final AnnotatedElement element) {
108         checkNotNull(annotationType, "An annotation is required");
109 
110         if (element == null) {
111             return null;
112         }
113 
114         for (Annotation a : element.getAnnotations()) {
115             if (StringUtils.equals(a.annotationType().getCanonicalName(), annotationType.getCanonicalName())) {
116                 if (!a.annotationType().equals(annotationType)) {
117                     log.warn("Detected usage of the {} annotation loaded from elsewhere. {} != {}",
118                             annotationType.getCanonicalName(),
119                             annotationType.getClassLoader(),
120                             a.annotationType().getClassLoader());
121                     return null;
122                 }
123                 return (T) a;
124             }
125         }
126         return null;
127     }
128 }