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