1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.beanutils;
19
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Modifier;
23
24 /***
25 * <p> Utility reflection methods focussed on constructors, modelled after {@link MethodUtils}. </p>
26 *
27 * <h3>Known Limitations</h3>
28 * <h4>Accessing Public Constructors In A Default Access Superclass</h4>
29 * <p>There is an issue when invoking public constructors contained in a default access superclass.
30 * Reflection locates these constructors fine and correctly assigns them as public.
31 * However, an <code>IllegalAccessException</code> is thrown if the constructors is invoked.</p>
32 *
33 * <p><code>ConstructorUtils</code> contains a workaround for this situation.
34 * It will attempt to call <code>setAccessible</code> on this constructor.
35 * If this call succeeds, then the method can be invoked as normal.
36 * This call will only succeed when the application has sufficient security privilages.
37 * If this call fails then a warning will be logged and the method may fail.</p>
38 *
39 * @author Craig R. McClanahan
40 * @author Ralph Schaer
41 * @author Chris Audley
42 * @author Rey Francois
43 * @author Gregor Rayman
44 * @author Jan Sorensen
45 * @author Robert Burrell Donkin
46 * @author Rodney Waldhoff
47 * @version $Revision: 555824 $ $Date: 2007-07-13 01:27:15 +0100 (Fri, 13 Jul 2007) $
48 */
49 public class ConstructorUtils {
50
51
52 /*** An empty class array */
53 private static final Class[] EMPTY_CLASS_PARAMETERS = new Class[0];
54 /*** An empty object array */
55 private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
56
57
58
59 /***
60 * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor.
61 * The formal parameter type is inferred from the actual values of <code>arg</code>.
62 * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
63 *
64 * <p>The signatures should be assignment compatible.</p>
65 *
66 * @param klass the class to be constructed.
67 * @param arg the actual argument
68 * @return new instance of <code>klazz</code>
69 *
70 * @throws NoSuchMethodException If the constructor cannot be found
71 * @throws IllegalAccessException If an error occurs accessing the constructor
72 * @throws InvocationTargetException If an error occurs invoking the constructor
73 * @throws InstantiationException If an error occurs instantiating the class
74 *
75 * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
76 */
77 public static Object invokeConstructor(Class klass, Object arg)
78 throws
79 NoSuchMethodException,
80 IllegalAccessException,
81 InvocationTargetException,
82 InstantiationException {
83
84 Object[] args = { arg };
85 return invokeConstructor(klass, args);
86
87 }
88
89 /***
90 * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
91 * The formal parameter types are inferred from the actual values of <code>args</code>.
92 * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
93 *
94 * <p>The signatures should be assignment compatible.</p>
95 *
96 * @param klass the class to be constructed.
97 * @param args actual argument array
98 * @return new instance of <code>klazz</code>
99 *
100 * @throws NoSuchMethodException If the constructor cannot be found
101 * @throws IllegalAccessException If an error occurs accessing the constructor
102 * @throws InvocationTargetException If an error occurs invoking the constructor
103 * @throws InstantiationException If an error occurs instantiating the class
104 *
105 * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
106 */
107 public static Object invokeConstructor(Class klass, Object[] args)
108 throws
109 NoSuchMethodException,
110 IllegalAccessException,
111 InvocationTargetException,
112 InstantiationException {
113
114 if (null == args) {
115 args = EMPTY_OBJECT_ARRAY;
116 }
117 int arguments = args.length;
118 Class parameterTypes[] = new Class[arguments];
119 for (int i = 0; i < arguments; i++) {
120 parameterTypes[i] = args[i].getClass();
121 }
122 return invokeConstructor(klass, args, parameterTypes);
123
124 }
125
126 /***
127 * <p>Returns new instance of <code>klazz</code> created using constructor
128 * with signature <code>parameterTypes</code> and actual arguments <code>args</code>.</p>
129 *
130 * <p>The signatures should be assignment compatible.</p>
131 *
132 * @param klass the class to be constructed.
133 * @param args actual argument array
134 * @param parameterTypes parameter types array
135 * @return new instance of <code>klazz</code>
136 *
137 * @throws NoSuchMethodException if matching constructor cannot be found
138 * @throws IllegalAccessException thrown on the constructor's invocation
139 * @throws InvocationTargetException thrown on the constructor's invocation
140 * @throws InstantiationException thrown on the constructor's invocation
141 * @see Constructor#newInstance
142 */
143 public static Object invokeConstructor(
144 Class klass,
145 Object[] args,
146 Class[] parameterTypes)
147 throws
148 NoSuchMethodException,
149 IllegalAccessException,
150 InvocationTargetException,
151 InstantiationException {
152
153 if (parameterTypes == null) {
154 parameterTypes = EMPTY_CLASS_PARAMETERS;
155 }
156 if (args == null) {
157 args = EMPTY_OBJECT_ARRAY;
158 }
159
160 Constructor ctor =
161 getMatchingAccessibleConstructor(klass, parameterTypes);
162 if (null == ctor) {
163 throw new NoSuchMethodException(
164 "No such accessible constructor on object: " + klass.getName());
165 }
166 return ctor.newInstance(args);
167 }
168
169
170 /***
171 * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor.
172 * The formal parameter type is inferred from the actual values of <code>arg</code>.
173 * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
174 *
175 * <p>The signatures should match exactly.</p>
176 *
177 * @param klass the class to be constructed.
178 * @param arg the actual argument
179 * @return new instance of <code>klazz</code>
180 *
181 * @throws NoSuchMethodException If the constructor cannot be found
182 * @throws IllegalAccessException If an error occurs accessing the constructor
183 * @throws InvocationTargetException If an error occurs invoking the constructor
184 * @throws InstantiationException If an error occurs instantiating the class
185 *
186 * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
187 */
188 public static Object invokeExactConstructor(Class klass, Object arg)
189 throws
190 NoSuchMethodException,
191 IllegalAccessException,
192 InvocationTargetException,
193 InstantiationException {
194
195 Object[] args = { arg };
196 return invokeExactConstructor(klass, args);
197
198 }
199
200 /***
201 * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
202 * The formal parameter types are inferred from the actual values of <code>args</code>.
203 * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
204 *
205 * <p>The signatures should match exactly.</p>
206 *
207 * @param klass the class to be constructed.
208 * @param args actual argument array
209 * @return new instance of <code>klazz</code>
210 *
211 * @throws NoSuchMethodException If the constructor cannot be found
212 * @throws IllegalAccessException If an error occurs accessing the constructor
213 * @throws InvocationTargetException If an error occurs invoking the constructor
214 * @throws InstantiationException If an error occurs instantiating the class
215 *
216 * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
217 */
218 public static Object invokeExactConstructor(Class klass, Object[] args)
219 throws
220 NoSuchMethodException,
221 IllegalAccessException,
222 InvocationTargetException,
223 InstantiationException {
224 if (null == args) {
225 args = EMPTY_OBJECT_ARRAY;
226 }
227 int arguments = args.length;
228 Class parameterTypes[] = new Class[arguments];
229 for (int i = 0; i < arguments; i++) {
230 parameterTypes[i] = args[i].getClass();
231 }
232 return invokeExactConstructor(klass, args, parameterTypes);
233
234 }
235
236 /***
237 * <p>Returns new instance of <code>klazz</code> created using constructor
238 * with signature <code>parameterTypes</code> and actual arguments
239 * <code>args</code>.</p>
240 *
241 * <p>The signatures should match exactly.</p>
242 *
243 * @param klass the class to be constructed.
244 * @param args actual argument array
245 * @param parameterTypes parameter types array
246 * @return new instance of <code>klazz</code>
247 *
248 * @throws NoSuchMethodException if matching constructor cannot be found
249 * @throws IllegalAccessException thrown on the constructor's invocation
250 * @throws InvocationTargetException thrown on the constructor's invocation
251 * @throws InstantiationException thrown on the constructor's invocation
252 * @see Constructor#newInstance
253 */
254 public static Object invokeExactConstructor(
255 Class klass,
256 Object[] args,
257 Class[] parameterTypes)
258 throws
259 NoSuchMethodException,
260 IllegalAccessException,
261 InvocationTargetException,
262 InstantiationException {
263
264 if (args == null) {
265 args = EMPTY_OBJECT_ARRAY;
266 }
267
268 if (parameterTypes == null) {
269 parameterTypes = EMPTY_CLASS_PARAMETERS;
270 }
271
272 Constructor ctor = getAccessibleConstructor(klass, parameterTypes);
273 if (null == ctor) {
274 throw new NoSuchMethodException(
275 "No such accessible constructor on object: " + klass.getName());
276 }
277 return ctor.newInstance(args);
278
279 }
280
281 /***
282 * Returns a constructor with single argument.
283 * @param klass the class to be constructed
284 * @param parameterType The constructor parameter type
285 * @return null if matching accessible constructor can not be found.
286 * @see Class#getConstructor
287 * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
288 */
289 public static Constructor getAccessibleConstructor(
290 Class klass,
291 Class parameterType) {
292
293 Class[] parameterTypes = { parameterType };
294 return getAccessibleConstructor(klass, parameterTypes);
295
296 }
297
298 /***
299 * Returns a constructor given a class and signature.
300 * @param klass the class to be constructed
301 * @param parameterTypes the parameter array
302 * @return null if matching accessible constructor can not be found
303 * @see Class#getConstructor
304 * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
305 */
306 public static Constructor getAccessibleConstructor(
307 Class klass,
308 Class[] parameterTypes) {
309
310 try {
311 return getAccessibleConstructor(
312 klass.getConstructor(parameterTypes));
313 } catch (NoSuchMethodException e) {
314 return (null);
315 }
316
317 }
318
319 /***
320 * Returns accessible version of the given constructor.
321 * @param ctor prototype constructor object.
322 * @return <code>null</code> if accessible constructor can not be found.
323 * @see java.lang.SecurityManager
324 */
325 public static Constructor getAccessibleConstructor(Constructor ctor) {
326
327
328 if (ctor == null) {
329 return (null);
330 }
331
332
333 if (!Modifier.isPublic(ctor.getModifiers())) {
334 return (null);
335 }
336
337
338 Class clazz = ctor.getDeclaringClass();
339 if (Modifier.isPublic(clazz.getModifiers())) {
340 return (ctor);
341 }
342
343
344 return null;
345
346 }
347
348
349 /***
350 * <p>Find an accessible constructor with compatible parameters.
351 * Compatible parameters mean that every method parameter is assignable from
352 * the given parameters. In other words, it finds constructor that will take
353 * the parameters given.</p>
354 *
355 * <p>First it checks if there is constructor matching the exact signature.
356 * If no such, all the constructors of the class are tested if their signatures
357 * are assignment compatible with the parameter types.
358 * The first matching constructor is returned.</p>
359 *
360 * @param clazz find constructor for this class
361 * @param parameterTypes find method with compatible parameters
362 * @return a valid Constructor object. If there's no matching constructor, returns <code>null</code>.
363 */
364 private static Constructor getMatchingAccessibleConstructor(
365 Class clazz,
366 Class[] parameterTypes) {
367
368
369 try {
370 Constructor ctor = clazz.getConstructor(parameterTypes);
371 try {
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388 ctor.setAccessible(true);
389 } catch (SecurityException se) {
390
391 }
392 return ctor;
393
394 } catch (NoSuchMethodException e) {
395 }
396
397
398 int paramSize = parameterTypes.length;
399 Constructor[] ctors = clazz.getConstructors();
400 for (int i = 0, size = ctors.length; i < size; i++) {
401
402 Class[] ctorParams = ctors[i].getParameterTypes();
403 int ctorParamSize = ctorParams.length;
404 if (ctorParamSize == paramSize) {
405 boolean match = true;
406 for (int n = 0; n < ctorParamSize; n++) {
407 if (!MethodUtils
408 .isAssignmentCompatible(
409 ctorParams[n],
410 parameterTypes[n])) {
411 match = false;
412 break;
413 }
414 }
415
416 if (match) {
417
418 Constructor ctor = getAccessibleConstructor(ctors[i]);
419 if (ctor != null) {
420 try {
421 ctor.setAccessible(true);
422 } catch (SecurityException se) {
423
424
425
426 }
427 return ctor;
428 }
429 }
430 }
431 }
432
433 return null;
434 }
435
436 }