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