| ReflectionUtil.java |
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 as published by
5 * the Free Software Foundation. This program is distributed in the hope
6 * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7 * 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 * Copyright: 2005
18 * The copyright to this program is held by it's authors.
19 *
20 * ID: $Id: ReflectionUtil.java 2090 2011-03-07 04:13:05Z dmsmith $
21 */
22 package org.crosswire.common.util;
23
24 import java.lang.reflect.Constructor;
25 import java.lang.reflect.InvocationTargetException;
26 import java.lang.reflect.Method;
27
28 /**
29 * Various utilities for calling constructors and methods via introspection.
30 *
31 * @see gnu.lgpl.License for license details.<br>
32 * The copyright to this program is held by it's authors.
33 * @author Joe Walker [joe at eireneh dot com]
34 * @author DM Smith [dmsmith555 at yahoo dot com]
35 */
36 public final class ReflectionUtil {
37 /**
38 * Prevent instantiation
39 */
40 private ReflectionUtil() {
41 }
42
43 /**
44 * Build an object using its default constructor. Note: a constructor that
45 * takes a boolean needs a type of boolean.class, but a parameter of type
46 * Boolean. Likewise for other primitives. If this is needed, do not call
47 * this method.
48 *
49 * @param className
50 * the full class name of the object
51 * @return the constructed object
52 * @throws ClassNotFoundException
53 * @throws IllegalAccessException
54 * @throws InstantiationException
55 */
56 public static <T> T construct(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
57 Class<T> clazz = (Class<T>) ClassUtil.forName(className);
58 return clazz.newInstance();
59 }
60
61 /**
62 * Build an object using the supplied parameters. Note: a constructor that
63 * takes a boolean needs a type of boolean.class, but a parameter of type
64 * Boolean. Likewise for other primitives.
65 *
66 * @param className
67 * the full class name of the object
68 * @param params
69 * the constructor's arguments
70 * @return the built object
71 * @throws ClassNotFoundException
72 * @throws NoSuchMethodException
73 * @throws IllegalAccessException
74 * @throws InvocationTargetException
75 * @throws InstantiationException
76 */
77 public static <T> T construct(String className, Object... params) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
78 InvocationTargetException, InstantiationException
79 {
80 Class<?>[] paramTypes = describeParameters(params);
81 Class<T> clazz = (Class<T>) ClassUtil.forName(className);
82 final Constructor<T> c = clazz.getConstructor(paramTypes);
83 return c.newInstance(params);
84 }
85
86 /**
87 * Build an object using the supplied parameters.
88 *
89 * @param className
90 * the full class name of the object
91 * @param params
92 * the constructor's arguments
93 * @param paramTypes
94 * the types of the parameters
95 * @return the built object
96 * @throws ClassNotFoundException
97 * @throws NoSuchMethodException
98 * @throws IllegalAccessException
99 * @throws InvocationTargetException
100 * @throws InstantiationException
101 */
102 public static <T> T construct(String className, Object[] params, Class<?>[] paramTypes) throws ClassNotFoundException, NoSuchMethodException,
103 IllegalAccessException, InvocationTargetException, InstantiationException
104 {
105 Class<?>[] calledTypes = paramTypes;
106 if (calledTypes == null) {
107 calledTypes = describeParameters(params);
108 }
109 Class<T> clazz = (Class<T>) ClassUtil.forName(className);
110 final Constructor<T> c = clazz.getConstructor(calledTypes);
111 return c.newInstance(params);
112 }
113
114 /**
115 * Call a method on a class given a sting
116 *
117 * @param base
118 * The object to invoke a method on
119 * @param methodName
120 * The text of the invocation, for example "getName"
121 * @param params
122 * For example new Object[] { ...}
123 */
124 public static Object invoke(Object base, String methodName, Object... params) throws NoSuchMethodException, IllegalAccessException,
125 InvocationTargetException
126 {
127 Class<?> clazz = base.getClass();
128 return invoke(clazz, base, methodName, params);
129 }
130
131 /**
132 * Call a static method on a class given a string
133 *
134 * @param call
135 * The text of the invocation, for example
136 * "java.lang.String.getName"
137 * @param params
138 * For example new Object[] { ...}
139 */
140 public static Object invoke(String call, Object... params) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
141 InvocationTargetException
142 {
143 // Split the call into class name and method name
144 int lastDot = call.lastIndexOf('.');
145 String className = call.substring(0, lastDot);
146 String methodName = call.substring(lastDot + 1);
147 Class<?> clazz = ClassUtil.forName(className);
148 return invoke(clazz, clazz, methodName, params);
149 }
150
151 /**
152 * Call a method on an object, or statically, with the supplied parameters.
153 *
154 * Note: a method that takes a boolean needs a type of boolean.class, but a
155 * parameter of type Boolean. Likewise for other primitives. If this is
156 * needed, do not call this method.
157 *
158 * @param clazz
159 * the class of the object
160 * @param obj
161 * the object having the method, or null to call a static method
162 * @param methodName
163 * the method to be called
164 * @param params
165 * the parameters
166 * @return whatever the method returns
167 * @throws NoSuchMethodException
168 * @throws IllegalAccessException
169 * @throws InvocationTargetException
170 */
171 public static <T> Object invoke(Class<T> clazz, Object obj, String methodName, Object... params) throws NoSuchMethodException, IllegalAccessException,
172 InvocationTargetException
173 {
174 return invoke(clazz, obj, methodName, params, null);
175 }
176
177 /**
178 * Call a method on an object, or statically, with the supplied parameters.
179 *
180 * Note: a method that takes a boolean needs a type of boolean.class, but a
181 * parameter of type Boolean. Likewise for other primitives.
182 *
183 * @param clazz
184 * the class of the object
185 * @param obj
186 * the object having the method, or null to call a static method
187 * @param methodName
188 * the method to be called
189 * @param params
190 * the parameters
191 * @param paramTypes
192 * the types of each of the parameters
193 * @return whatever the method returns
194 * @throws NoSuchMethodException
195 * @throws IllegalAccessException
196 * @throws InvocationTargetException
197 */
198 public static <T> Object invoke(Class<T> clazz, Object obj, String methodName, Object[] params, Class<?>[] paramTypes) throws NoSuchMethodException,
199 IllegalAccessException, InvocationTargetException
200 {
201 Class<?>[] calledTypes = paramTypes;
202 if (calledTypes == null) {
203 calledTypes = describeParameters(params);
204 }
205 return getMethod(clazz, methodName, calledTypes).invoke(obj, params);
206 }
207
208 /**
209 * Construct a parallel array of class objects for each element in params.
210 *
211 * @param params
212 * the types to describe
213 * @return the parallel array of class objects
214 */
215 private static Class<?>[] describeParameters(Object... params) {
216 Class<?>[] calledTypes = new Class[params.length];
217 for (int i = 0; i < params.length; i++) {
218 Class<?> clazz = params[i].getClass();
219 if (clazz.equals(Boolean.class)) {
220 clazz = boolean.class;
221 }
222 calledTypes[i] = clazz;
223 }
224 return calledTypes;
225 }
226
227 private static <T> Method getMethod(Class<T> clazz, String methodName, Class<?>[] calledTypes) throws NoSuchMethodException {
228 // The bad news is that we can't use something like:
229 // clazz.getMethod(methodNames, called_types);
230 // because it does not cope with inheritance (at least in the MVM)
231 // so we have to search ourselves...
232 Method[] testMethods = clazz.getMethods();
233 outer: for (int i = 0; i < testMethods.length; i++) {
234 // This this the right method name?
235 if (!testMethods[i].getName().equals(methodName)) {
236 continue outer;
237 }
238
239 // The right number of params
240 Class<?>[] testTypes = testMethods[i].getParameterTypes();
241 if (testTypes.length != calledTypes.length) {
242 continue;
243 }
244
245 // Of the right types?
246 for (int j = 0; j < testTypes.length; j++) {
247 if (!testTypes[j].isAssignableFrom(calledTypes[j])) {
248 continue outer;
249 }
250 }
251
252 // So this is a match
253 return testMethods[i];
254 }
255
256 throw new NoSuchMethodException(methodName);
257 }
258 }
259