KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > beanutils > ConstructorUtils


1 /*
2  * Copyright 2001-2004 The Apache Software Foundation.
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.apache.commons.beanutils;
18
19 import java.lang.reflect.Constructor JavaDoc;
20 import java.lang.reflect.InvocationTargetException JavaDoc;
21 import java.lang.reflect.Modifier JavaDoc;
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     // --------------------------------------------------------- Private Members
51
/** An empty class array */
52     private static final Class JavaDoc[] emptyClassArray = new Class JavaDoc[0];
53     /** An empty object array */
54     private static final Object JavaDoc[] emptyObjectArray = new Object JavaDoc[0];
55
56     // --------------------------------------------------------- Public Methods
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 JavaDoc invokeConstructor(Class JavaDoc klass, Object JavaDoc arg)
72         throws
73             NoSuchMethodException JavaDoc,
74             IllegalAccessException JavaDoc,
75             InvocationTargetException JavaDoc,
76             InstantiationException JavaDoc {
77
78         Object JavaDoc[] 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 JavaDoc invokeConstructor(Class JavaDoc klass, Object JavaDoc[] args)
97         throws
98             NoSuchMethodException JavaDoc,
99             IllegalAccessException JavaDoc,
100             InvocationTargetException JavaDoc,
101             InstantiationException JavaDoc {
102
103         if (null == args) {
104             args = emptyObjectArray;
105         }
106         int arguments = args.length;
107         Class JavaDoc parameterTypes[] = new Class JavaDoc[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 JavaDoc invokeConstructor(
133         Class JavaDoc klass,
134         Object JavaDoc[] args,
135         Class JavaDoc[] parameterTypes)
136         throws
137             NoSuchMethodException JavaDoc,
138             IllegalAccessException JavaDoc,
139             InvocationTargetException JavaDoc,
140             InstantiationException JavaDoc {
141
142         if (parameterTypes == null) {
143             parameterTypes = emptyClassArray;
144         }
145         if (args == null) {
146             args = emptyObjectArray;
147         }
148
149         Constructor JavaDoc ctor =
150             getMatchingAccessibleConstructor(klass, parameterTypes);
151         if (null == ctor) {
152             throw new NoSuchMethodException JavaDoc(
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 JavaDoc invokeExactConstructor(Class JavaDoc klass, Object JavaDoc arg)
173         throws
174             NoSuchMethodException JavaDoc,
175             IllegalAccessException JavaDoc,
176             InvocationTargetException JavaDoc,
177             InstantiationException JavaDoc {
178
179         Object JavaDoc[] 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 JavaDoc invokeExactConstructor(Class JavaDoc klass, Object JavaDoc[] args)
198         throws
199             NoSuchMethodException JavaDoc,
200             IllegalAccessException JavaDoc,
201             InvocationTargetException JavaDoc,
202             InstantiationException JavaDoc {
203         if (null == args) {
204             args = emptyObjectArray;
205         }
206         int arguments = args.length;
207         Class JavaDoc parameterTypes[] = new Class JavaDoc[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 JavaDoc invokeExactConstructor(
234         Class JavaDoc klass,
235         Object JavaDoc[] args,
236         Class JavaDoc[] parameterTypes)
237         throws
238             NoSuchMethodException JavaDoc,
239             IllegalAccessException JavaDoc,
240             InvocationTargetException JavaDoc,
241             InstantiationException JavaDoc {
242
243         if (args == null) {
244             args = emptyObjectArray;
245         }
246
247         if (parameterTypes == null) {
248             parameterTypes = emptyClassArray;
249         }
250
251         Constructor JavaDoc ctor = getAccessibleConstructor(klass, parameterTypes);
252         if (null == ctor) {
253             throw new NoSuchMethodException JavaDoc(
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 JavaDoc getAccessibleConstructor(
268         Class JavaDoc klass,
269         Class JavaDoc parameterType) {
270
271         Class JavaDoc[] 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 JavaDoc getAccessibleConstructor(
285         Class JavaDoc klass,
286         Class JavaDoc[] parameterTypes) {
287
288         try {
289             return getAccessibleConstructor(
290                 klass.getConstructor(parameterTypes));
291         } catch (NoSuchMethodException JavaDoc 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 JavaDoc getAccessibleConstructor(Constructor JavaDoc ctor) {
304
305         // Make sure we have a method to check
306
if (ctor == null) {
307             return (null);
308         }
309
310         // If the requested method is not public we cannot call it
311
if (!Modifier.isPublic(ctor.getModifiers())) {
312             return (null);
313         }
314
315         // If the declaring class is public, we are done
316
Class JavaDoc clazz = ctor.getDeclaringClass();
317         if (Modifier.isPublic(clazz.getModifiers())) {
318             return (ctor);
319         }
320
321         // what else can we do?
322
return null;
323
324     }
325
326     // -------------------------------------------------------- Private Methods
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 JavaDoc getMatchingAccessibleConstructor(
343         Class JavaDoc clazz,
344         Class JavaDoc[] parameterTypes) {
345         // see if we can find the method directly
346
// most of the time this works and it's much faster
347
try {
348             Constructor JavaDoc ctor = clazz.getConstructor(parameterTypes);
349             try {
350                 //
351
// XXX Default access superclass workaround
352
//
353
// When a public class has a default access superclass
354
// with public methods, these methods are accessible.
355
// Calling them from compiled code works fine.
356
//
357
// Unfortunately, using reflection to invoke these methods
358
// seems to (wrongly) to prevent access even when the method
359
// modifer is public.
360
//
361
// The following workaround solves the problem but will only
362
// work from sufficiently privilages code.
363
//
364
// Better workarounds would be greatfully accepted.
365
//
366
ctor.setAccessible(true);
367             } catch (SecurityException JavaDoc se) {}
368             return ctor;
369
370         } catch (NoSuchMethodException JavaDoc e) { /* SWALLOW */
371         }
372
373         // search through all methods
374
int paramSize = parameterTypes.length;
375         Constructor JavaDoc[] ctors = clazz.getConstructors();
376         for (int i = 0, size = ctors.length; i < size; i++) {
377             // compare parameters
378
Class JavaDoc[] 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                     // get accessible version of method
394
Constructor JavaDoc ctor = getAccessibleConstructor(ctors[i]);
395                     if (ctor != null) {
396                         try {
397                             ctor.setAccessible(true);
398                         } catch (SecurityException JavaDoc se) {}
399                         return ctor;
400                     }
401                 }
402             }
403         }
404
405         return null;
406     }
407
408 }
409
Popular Tags