View Javadoc
1   /*
2    * Copyright (C) 2015 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.codehaus.gmavenplus.util;
18  
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.Field;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.lang.reflect.Modifier;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.List;
27  
28  
29  /**
30   * Inspired heavily by Spring's <a href="https://github.com/SpringSource/spring-framework/blob/master/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java">ReflectionUtils</a>.
31   *
32   * @author Juergen Hoeller
33   * @author Rob Harrop
34   * @author Rod Johnson
35   * @author Costin Leau
36   * @author Sam Brannen
37   * @author Chris Beams
38   * @author Keegan Witt
39   */
40  public class ReflectionUtils {
41  
42      private ReflectionUtils() {
43      }
44  
45      /**
46       * Attempt to find a {@link Constructor} on the supplied class with the supplied parameter types.
47       * Searches all superclasses up to <code>Object</code>.
48       *
49       * @param clazz      The class to introspect
50       * @param paramTypes The parameter types of the method (may be <code>null</code> to indicate any signature)
51       * @return The Constructor object
52       */
53      public static Constructor<?> findConstructor(final Class<?> clazz, final Class<?>... paramTypes) {
54          if (clazz == null) {
55              throw new IllegalArgumentException("Class must not be null.");
56          }
57          Class<?> searchType = clazz;
58          while (searchType != null) {
59              Constructor<?>[] constructors = searchType.isInterface() ? clazz.getConstructors() : clazz.getDeclaredConstructors();
60              for (Constructor<?> constructor : constructors) {
61                  if (paramTypes == null || Arrays.equals(paramTypes, constructor.getParameterTypes())) {
62                      return constructor;
63                  }
64              }
65              searchType = searchType.getSuperclass();
66          }
67          throw new IllegalArgumentException("Unable to find constructor " + clazz.getName() + "(" + Arrays.toString(paramTypes).replaceAll("^\\[", "").replaceAll("]$", "").replaceAll("class ", "") + ").");
68      }
69  
70      /**
71       * Attempt to find a {@link Field field} on the supplied {@link Class} with the supplied <code>name</code> and/or {@link Class type}.
72       * Searches all superclasses up to {@link Object}.
73       *
74       * @param clazz The class to introspect
75       * @param name  The name of the field (may be <code>null</code> if type is specified)
76       * @param type  The type of the field (may be <code>null</code> if name is specified)
77       * @return The corresponding Field object
78       */
79      public static Field findField(final Class<?> clazz, final String name, final Class<?> type) {
80          if (clazz == null) {
81              throw new IllegalArgumentException("Class must not be null");
82          }
83          if (name == null && type == null) {
84              throw new IllegalArgumentException("Either name or type of the field must be specified.");
85          }
86          Class<?> searchType = clazz;
87          while (Object.class != searchType && searchType != null) {
88              Field[] fields = searchType.getDeclaredFields();
89              for (Field field : fields) {
90                  if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) {
91                      return field;
92                  }
93              }
94              searchType = searchType.getSuperclass();
95          }
96          throw new IllegalArgumentException("Unable to find " + (type != null ? type.getName() : "") + " " + (name != null ? name : "") + ".");
97      }
98  
99      /**
100      * Attempt to find a {@link Method} on the supplied class with the supplied name and parameter types.
101      * Searches all superclasses up to <code>Object</code>.
102      *
103      * @param clazz      The class to introspect
104      * @param name       The name of the method
105      * @param paramTypes The parameter types of the method
106      *                   (may be <code>null</code> to indicate any signature)
107      * @return The Method object
108      */
109     public static Method findMethod(final Class<?> clazz, final String name, final Class<?>... paramTypes) {
110         if (clazz == null) {
111             throw new IllegalArgumentException("Class must not be null.");
112         }
113         if (name == null) {
114             throw new IllegalArgumentException("Method name must not be null.");
115         }
116         Class<?> searchType = clazz;
117         while (searchType != null) {
118             Method[] methods = (searchType.isInterface() ? searchType.getMethods() : getDeclaredMethods(searchType));
119             for (Method method : methods) {
120                 if (name.equals(method.getName()) && (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) {
121                     return method;
122                 }
123             }
124             searchType = searchType.getSuperclass();
125         }
126         throw new IllegalArgumentException("Unable to find method " + clazz.getName() + "." + name + "(" + Arrays.toString(paramTypes).replaceAll("^\\[", "").replaceAll("]$", "").replaceAll("class ", "") + ").");
127     }
128 
129     /**
130      * Find and return the specified value from the specified enum class.
131      *
132      * @param clazz     The enum class to introspect
133      * @param valueName The name of the enum value to get
134      * @return The enum value
135      */
136     public static Object getEnumValue(final Class<?> clazz, final String valueName) {
137         if (clazz.isEnum()) {
138             for (Object o : clazz.getEnumConstants()) {
139                 if (o.toString().equals(valueName)) {
140                     return o;
141                 }
142             }
143             throw new IllegalArgumentException("Unable to get an enum constant with that name.");
144         } else {
145             throw new IllegalArgumentException(clazz + " must be an enum.");
146         }
147     }
148 
149     /**
150      * Get the field represented by the supplied {@link Field field object} on the specified {@link Object target object}.
151      * In accordance with {@link Field#get(Object)} semantics, the returned value is automatically wrapped if the underlying field has a primitive type.
152      *
153      * @param field  The field to get
154      * @param target The target object from which to get the field
155      * @return The field's current value
156      * @throws IllegalAccessException when unable to access the specified field because access modifiers prevent it
157      */
158     public static Object getField(final Field field, final Object target) throws IllegalAccessException {
159         field.setAccessible(true);
160         return field.get(target);
161     }
162 
163     /**
164      * Get the field represented by the supplied {@link Field field object} on the specified {@link Object target object}.
165      * In accordance with {@link Field#get(Object)} semantics, the returned value is automatically wrapped if the underlying field has a primitive type.
166      *
167      * @param field The field to get
168      * @return The field's current value
169      * @throws IllegalAccessException when unable to access the specified field because access modifiers prevent it
170      */
171     public static Object getStaticField(final Field field) throws IllegalAccessException {
172         if (!Modifier.isStatic(field.getModifiers())) {
173             throw new IllegalArgumentException("Field must be static.");
174         }
175         return getField(field, null);
176     }
177 
178     /**
179      * Invoke the specified {@link Constructor}  with the supplied arguments.
180      *
181      * @param constructor The method to invoke
182      * @param args        The invocation arguments (may be <code>null</code>)
183      * @return The invocation result, if any
184      * @throws IllegalAccessException                      when unable to access the specified constructor because access modifiers prevent it
185      * @throws java.lang.reflect.InvocationTargetException when a reflection invocation fails
186      * @throws InstantiationException                      when an instantiation fails
187      */
188     public static Object invokeConstructor(final Constructor<?> constructor, final Object... args) throws InvocationTargetException, IllegalAccessException, InstantiationException {
189         if (constructor == null) {
190             throw new IllegalArgumentException("Constructor must not be null.");
191         }
192         constructor.setAccessible(true);
193         return constructor.newInstance(args);
194     }
195 
196     /**
197      * Invoke the specified {@link Method} against the supplied target object with the supplied arguments.
198      * The target object can be <code>null</code> when invoking a static {@link Method}.
199      *
200      * @param method The method to invoke
201      * @param target The target object to invoke the method on
202      * @param args   The invocation arguments (may be <code>null</code>)
203      * @return The invocation result, if any
204      * @throws IllegalAccessException                      when unable to access the specified method because access modifiers prevent it
205      * @throws java.lang.reflect.InvocationTargetException when a reflection invocation fails
206      */
207     public static Object invokeMethod(final Method method, final Object target, final Object... args) throws InvocationTargetException, IllegalAccessException {
208         if (method == null) {
209             throw new IllegalArgumentException("Method must not be null.");
210         }
211         if (target == null) {
212             throw new IllegalArgumentException("Object must not be null.");
213         }
214         method.setAccessible(true);
215         return method.invoke(target, args);
216     }
217 
218     /**
219      * Invoke the specified static {@link Method} with the supplied arguments.
220      *
221      * @param method The method to invoke
222      * @param args   The invocation arguments (may be <code>null</code>)
223      * @return The invocation result, if any
224      * @throws IllegalAccessException                      when unable to access the specified method because access modifiers prevent it
225      * @throws java.lang.reflect.InvocationTargetException when a reflection invocation fails
226      */
227     public static Object invokeStaticMethod(final Method method, final Object... args) throws InvocationTargetException, IllegalAccessException {
228         if (method == null) {
229             throw new IllegalArgumentException("Method must not be null.");
230         }
231         if (!Modifier.isStatic(method.getModifiers())) {
232             throw new IllegalArgumentException("Method must be static.");
233         }
234         method.setAccessible(true);
235         return method.invoke(null, args);
236     }
237 
238     /**
239      * This variant retrieves {@link Class#getDeclaredMethods()} from a local cache in order to avoid the JVM's SecurityManager check and defensive array copying.
240      * In addition, it also includes Java 8 default methods from locally implemented interfaces, since those are effectively to be treated just like declared methods.
241      *
242      * @param clazz the class to introspect
243      * @return the cached array of methods
244      * @see Class#getDeclaredMethods()
245      */
246     private static Method[] getDeclaredMethods(Class<?> clazz) {
247         Method[] result;
248         Method[] declaredMethods = clazz.getDeclaredMethods();
249         List<Method> defaultMethods = findConcreteMethodsOnInterfaces(clazz);
250         if (defaultMethods != null) {
251             result = new Method[declaredMethods.length + defaultMethods.size()];
252             System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length);
253             int index = declaredMethods.length;
254             for (Method defaultMethod : defaultMethods) {
255                 result[index] = defaultMethod;
256                 index++;
257             }
258         } else {
259             result = declaredMethods;
260         }
261         return result;
262     }
263 
264     private static List<Method> findConcreteMethodsOnInterfaces(Class<?> clazz) {
265         List<Method> result = null;
266         for (Class<?> ifc : clazz.getInterfaces()) {
267             for (Method ifcMethod : ifc.getMethods()) {
268                 if (!Modifier.isAbstract(ifcMethod.getModifiers())) {
269                     if (result == null) {
270                         result = new ArrayList<>();
271                     }
272                     result.add(ifcMethod);
273                 }
274             }
275         }
276         return result;
277     }
278 
279 }