Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ReflectionUtil |
|
| 2.0;2 |
1 | /** | |
2 | * Distribution License: | |
3 | * JSword is free software; you can redistribute it and/or modify it under | |
4 | * the terms of the GNU Lesser General Public License, version 2.1 or later | |
5 | * as published by the Free Software Foundation. This program is distributed | |
6 | * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even | |
7 | * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
8 | * See the GNU Lesser General Public License for more details. | |
9 | * | |
10 | * The License is available on the internet at: | |
11 | * http://www.gnu.org/copyleft/lgpl.html | |
12 | * or by writing to: | |
13 | * Free Software Foundation, Inc. | |
14 | * 59 Temple Place - Suite 330 | |
15 | * Boston, MA 02111-1307, USA | |
16 | * | |
17 | * © CrossWire Bible Society, 2005 - 2016 | |
18 | * | |
19 | */ | |
20 | package org.crosswire.common.util; | |
21 | ||
22 | import java.lang.reflect.Constructor; | |
23 | import java.lang.reflect.InvocationTargetException; | |
24 | import java.lang.reflect.Method; | |
25 | ||
26 | /** | |
27 | * Various utilities for calling constructors and methods via introspection. | |
28 | * | |
29 | * @see gnu.lgpl.License The GNU Lesser General Public License for details. | |
30 | * @author Joe Walker | |
31 | * @author DM Smith | |
32 | */ | |
33 | public final class ReflectionUtil { | |
34 | /** | |
35 | * Prevent instantiation | |
36 | */ | |
37 | 0 | private ReflectionUtil() { |
38 | 0 | } |
39 | ||
40 | /** | |
41 | * Build an object using its default constructor. Note: a constructor that | |
42 | * takes a boolean needs a type of boolean.class, but a parameter of type | |
43 | * Boolean. Likewise for other primitives. If this is needed, do not call | |
44 | * this method. | |
45 | * | |
46 | * @param <T> the type of the object to construct | |
47 | * @param className | |
48 | * the full class name of the object | |
49 | * @return the constructed object | |
50 | * @throws ClassNotFoundException if the class is not found | |
51 | * @throws InstantiationException | |
52 | * if this {@code data} represents an abstract class, | |
53 | * an interface, an array class, a primitive type, or void; | |
54 | * or if the class has no nullary constructor; | |
55 | * or if the instantiation fails for some other reason. | |
56 | * @throws IllegalAccessException if the class or its nullary | |
57 | * constructor is not accessible. | |
58 | */ | |
59 | public static <T> T construct(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException { | |
60 | 0 | Class<T> clazz = (Class<T>) ClassUtil.forName(className); |
61 | 0 | return clazz.newInstance(); |
62 | } | |
63 | ||
64 | /** | |
65 | * Build an object using the supplied parameters. Note: a constructor that | |
66 | * takes a boolean needs a type of boolean.class, but a parameter of type | |
67 | * Boolean. Likewise for other primitives. | |
68 | * | |
69 | * @param <T> the type of the object to construct | |
70 | * @param className | |
71 | * the full class name of the object | |
72 | * @param params | |
73 | * the constructor's arguments | |
74 | * @return the built object | |
75 | * @throws ClassNotFoundException if the class is not found | |
76 | * @throws NoSuchMethodException | |
77 | * the method does not exist | |
78 | * @throws InstantiationException | |
79 | * if this {@code data} represents an abstract class, | |
80 | * an interface, an array class, a primitive type, or void; | |
81 | * or if the class has no nullary constructor; | |
82 | * or if the instantiation fails for some other reason. | |
83 | * @throws IllegalAccessException if the class or its nullary | |
84 | * constructor is not accessible. | |
85 | * @throws InvocationTargetException if the underlying constructor | |
86 | * throws an exception. | |
87 | * @throws InstantiationException | |
88 | * if the class that declares the | |
89 | * underlying constructor represents an abstract class. | |
90 | */ | |
91 | public static <T> T construct(String className, Object... params) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, | |
92 | InvocationTargetException, InstantiationException | |
93 | { | |
94 | 0 | Class<T> clazz = (Class<T>) ClassUtil.forName(className); |
95 | 0 | return construct(clazz, params); |
96 | } | |
97 | ||
98 | /** | |
99 | * Build an object using the supplied parameters. Note: a constructor that | |
100 | * takes a boolean needs a type of boolean.class, but a parameter of type | |
101 | * Boolean. Likewise for other primitives. | |
102 | * | |
103 | * @param <T> the type of the object to construct | |
104 | * @param clazz | |
105 | * the class of the object | |
106 | * @param params | |
107 | * the constructor's arguments | |
108 | * @return the built object | |
109 | * @throws NoSuchMethodException | |
110 | * the method does not exist | |
111 | * @throws InstantiationException | |
112 | * if the class that declares the | |
113 | * underlying constructor represents an abstract class. | |
114 | * @throws IllegalAccessException | |
115 | * if this {@code Constructor} object | |
116 | * is enforcing Java language access control and the underlying | |
117 | * constructor is inaccessible. | |
118 | * @throws InvocationTargetException if the underlying constructor | |
119 | * throws an exception. | |
120 | */ | |
121 | public static <T> T construct(final Class<T> clazz, final Object... params) throws NoSuchMethodException, InstantiationException, IllegalAccessException, | |
122 | InvocationTargetException | |
123 | { | |
124 | 0 | Class<?>[] paramTypes = describeParameters(params); |
125 | 0 | final Constructor<T> c = clazz.getConstructor(paramTypes); |
126 | 0 | return c.newInstance(params); |
127 | } | |
128 | ||
129 | /** | |
130 | * Build an object using the supplied parameters. | |
131 | * | |
132 | * @param <T> the type of the object to construct | |
133 | * @param className | |
134 | * the full class name of the object | |
135 | * @param params | |
136 | * the constructor's arguments | |
137 | * @param paramTypes | |
138 | * the types of the parameters | |
139 | * @return the built object | |
140 | * @throws ClassNotFoundException if the class is not found | |
141 | * @throws NoSuchMethodException | |
142 | * the method does not exist | |
143 | * @throws InstantiationException | |
144 | * if the class that declares the | |
145 | * underlying constructor represents an abstract class. | |
146 | * @throws IllegalAccessException | |
147 | * if this {@code Constructor} object | |
148 | * is enforcing Java language access control and the underlying | |
149 | * constructor is inaccessible. | |
150 | * @throws InvocationTargetException if the underlying constructor | |
151 | * throws an exception. | |
152 | */ | |
153 | public static <T> T construct(String className, Object[] params, Class<?>[] paramTypes) throws ClassNotFoundException, NoSuchMethodException, | |
154 | IllegalAccessException, InvocationTargetException, InstantiationException | |
155 | { | |
156 | 0 | Class<?>[] calledTypes = paramTypes; |
157 | 0 | if (calledTypes == null) { |
158 | 0 | calledTypes = describeParameters(params); |
159 | } | |
160 | 0 | Class<T> clazz = (Class<T>) ClassUtil.forName(className); |
161 | 0 | final Constructor<T> c = clazz.getConstructor(calledTypes); |
162 | 0 | return c.newInstance(params); |
163 | } | |
164 | ||
165 | /** | |
166 | * Call a method on a class given a sting | |
167 | * | |
168 | * @param base | |
169 | * The object to invoke a method on | |
170 | * @param methodName | |
171 | * The text of the invocation, for example "getName" | |
172 | * @param params | |
173 | * For example new Object[] { ...} | |
174 | * @return whatever the method returs | |
175 | * @throws NoSuchMethodException | |
176 | * the method does not exist | |
177 | * @throws IllegalAccessException | |
178 | * if this {@code Constructor} object | |
179 | * is enforcing Java language access control and the underlying | |
180 | * constructor is inaccessible. | |
181 | * @throws InvocationTargetException if the underlying constructor | |
182 | * throws an exception. | |
183 | */ | |
184 | public static Object invoke(Object base, String methodName, Object... params) throws NoSuchMethodException, IllegalAccessException, | |
185 | InvocationTargetException | |
186 | { | |
187 | 0 | Class<?> clazz = base.getClass(); |
188 | 0 | return invoke(clazz, base, methodName, params); |
189 | } | |
190 | ||
191 | /** | |
192 | * Call a static method on a class given a string | |
193 | * | |
194 | * @param call | |
195 | * The text of the invocation, for example | |
196 | * "java.lang.String.getName" | |
197 | * @param params | |
198 | * For example new Object[] { ...} | |
199 | * @return whatever the method returs | |
200 | * @throws ClassNotFoundException if the class is not found | |
201 | * @throws NoSuchMethodException | |
202 | * the method does not exist | |
203 | * @throws IllegalAccessException | |
204 | * if this {@code Constructor} object | |
205 | * is enforcing Java language access control and the underlying | |
206 | * constructor is inaccessible. | |
207 | * @throws InvocationTargetException if the underlying constructor | |
208 | * throws an exception. | |
209 | */ | |
210 | public static Object invoke(String call, Object... params) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, | |
211 | InvocationTargetException | |
212 | { | |
213 | // Split the call into class name and method name | |
214 | 0 | int lastDot = call.lastIndexOf('.'); |
215 | 0 | String className = call.substring(0, lastDot); |
216 | 0 | String methodName = call.substring(lastDot + 1); |
217 | 0 | Class<?> clazz = ClassUtil.forName(className); |
218 | 0 | return invoke(clazz, clazz, methodName, params); |
219 | } | |
220 | ||
221 | /** | |
222 | * Call a method on an object, or statically, with the supplied parameters. | |
223 | * | |
224 | * Note: a method that takes a boolean needs a type of boolean.class, but a | |
225 | * parameter of type Boolean. Likewise for other primitives. If this is | |
226 | * needed, do not call this method. | |
227 | * | |
228 | * @param <T> the type of the object to construct | |
229 | * @param clazz | |
230 | * the class of the object | |
231 | * @param obj | |
232 | * the object having the method, or null to call a static method | |
233 | * @param methodName | |
234 | * the method to be called | |
235 | * @param params | |
236 | * the parameters | |
237 | * @return whatever the method returns | |
238 | * @throws NoSuchMethodException | |
239 | * the method does not exist | |
240 | * @throws IllegalAccessException | |
241 | * if this {@code Constructor} object | |
242 | * is enforcing Java language access control and the underlying | |
243 | * constructor is inaccessible. | |
244 | * @throws InvocationTargetException if the underlying constructor | |
245 | * throws an exception. | |
246 | */ | |
247 | public static <T> Object invoke(Class<T> clazz, Object obj, String methodName, Object... params) throws NoSuchMethodException, IllegalAccessException, | |
248 | InvocationTargetException | |
249 | { | |
250 | 0 | return invoke(clazz, obj, methodName, params, null); |
251 | } | |
252 | ||
253 | /** | |
254 | * Call a method on an object, or statically, with the supplied parameters. | |
255 | * | |
256 | * Note: a method that takes a boolean needs a type of boolean.class, but a | |
257 | * parameter of type Boolean. Likewise for other primitives. | |
258 | * | |
259 | * @param <T> the type of the object to construct | |
260 | * @param clazz | |
261 | * the class of the object | |
262 | * @param obj | |
263 | * the object having the method, or null to call a static method | |
264 | * @param methodName | |
265 | * the method to be called | |
266 | * @param params | |
267 | * the parameters | |
268 | * @param paramTypes | |
269 | * the types of each of the parameters | |
270 | * @return whatever the method returns | |
271 | * @throws NoSuchMethodException | |
272 | * the method does not exist | |
273 | * @throws IllegalAccessException | |
274 | * if this {@code Constructor} object | |
275 | * is enforcing Java language access control and the underlying | |
276 | * constructor is inaccessible. | |
277 | * @throws InvocationTargetException if the underlying constructor | |
278 | * throws an exception. | |
279 | */ | |
280 | public static <T> Object invoke(Class<T> clazz, Object obj, String methodName, Object[] params, Class<?>[] paramTypes) throws NoSuchMethodException, | |
281 | IllegalAccessException, InvocationTargetException | |
282 | { | |
283 | 0 | Class<?>[] calledTypes = paramTypes; |
284 | 0 | if (calledTypes == null) { |
285 | 0 | calledTypes = describeParameters(params); |
286 | } | |
287 | 0 | return getMethod(clazz, methodName, calledTypes).invoke(obj, params); |
288 | } | |
289 | ||
290 | /** | |
291 | * Construct a parallel array of class objects for each element in params. | |
292 | * | |
293 | * @param params | |
294 | * the types to describe | |
295 | * @return the parallel array of class objects | |
296 | */ | |
297 | private static Class<?>[] describeParameters(Object... params) { | |
298 | 0 | Class<?>[] calledTypes = new Class[params.length]; |
299 | 0 | for (int i = 0; i < params.length; i++) { |
300 | 0 | Class<?> clazz = params[i].getClass(); |
301 | 0 | if (clazz.equals(Boolean.class)) { |
302 | 0 | clazz = boolean.class; |
303 | } | |
304 | 0 | calledTypes[i] = clazz; |
305 | } | |
306 | 0 | return calledTypes; |
307 | } | |
308 | ||
309 | private static <T> Method getMethod(Class<T> clazz, String methodName, Class<?>[] calledTypes) throws NoSuchMethodException { | |
310 | // The bad news is that we can't use something like: | |
311 | // clazz.getMethod(methodNames, called_types); | |
312 | // because it does not cope with inheritance (at least in the MVM) | |
313 | // so we have to search ourselves... | |
314 | 0 | Method[] testMethods = clazz.getMethods(); |
315 | 0 | outer: for (int i = 0; i < testMethods.length; i++) { |
316 | // This this the right method name? | |
317 | 0 | if (!testMethods[i].getName().equals(methodName)) { |
318 | 0 | continue outer; |
319 | } | |
320 | ||
321 | // The right number of params | |
322 | 0 | Class<?>[] testTypes = testMethods[i].getParameterTypes(); |
323 | 0 | if (testTypes.length != calledTypes.length) { |
324 | 0 | continue; |
325 | } | |
326 | ||
327 | // Of the right types? | |
328 | 0 | for (int j = 0; j < testTypes.length; j++) { |
329 | 0 | if (!testTypes[j].isAssignableFrom(calledTypes[j])) { |
330 | 0 | continue outer; |
331 | } | |
332 | } | |
333 | ||
334 | // So this is a match | |
335 | 0 | return testMethods[i]; |
336 | } | |
337 | ||
338 | 0 | throw new NoSuchMethodException(methodName); |
339 | } | |
340 | } |