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 }