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