001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.lang.reflect;
018
019 import java.lang.reflect.Constructor;
020 import java.lang.reflect.InvocationTargetException;
021 import java.lang.reflect.Modifier;
022
023 import org.apache.commons.lang.ArrayUtils;
024 import org.apache.commons.lang.ClassUtils;
025
026 /**
027 * <p> Utility reflection methods focussed on constructors, modelled after
028 * {@link MethodUtils}. </p>
029 *
030 * <h3>Known Limitations</h3> <h4>Accessing Public Constructors In A Default
031 * Access Superclass</h4> <p>There is an issue when invoking public constructors
032 * contained in a default access superclass. Reflection locates these
033 * constructors fine and correctly assigns them as public. However, an
034 * <code>IllegalAccessException</code> is thrown if the constructors is
035 * invoked.</p>
036 *
037 * <p><code>ConstructorUtils</code> contains a workaround for this situation. It
038 * will attempt to call <code>setAccessible</code> on this constructor. If this
039 * call succeeds, then the method can be invoked as normal. This call will only
040 * succeed when the application has sufficient security privilages. If this call
041 * fails then a warning will be logged and the method may fail.</p>
042 *
043 * @author Apache Software Foundation
044 * @author Craig R. McClanahan
045 * @author Ralph Schaer
046 * @author Chris Audley
047 * @author Rey Francois
048 * @author Gregor Rayman
049 * @author Jan Sorensen
050 * @author Robert Burrell Donkin
051 * @author Rodney Waldhoff
052 * @since 2.5
053 * @version $Id: ConstructorUtils.java 1056863 2011-01-09 02:00:25Z niallp $
054 */
055 public class ConstructorUtils {
056
057 /**
058 * <p>ConstructorUtils instances should NOT be constructed in standard
059 * programming. Instead, the class should be used as
060 * <code>ConstructorUtils.invokeConstructor(cls, args)</code>.</p>
061 *
062 * <p>This constructor is public to permit tools that require a JavaBean
063 * instance to operate.</p>
064 */
065 public ConstructorUtils() {
066 super();
067 }
068
069 /**
070 * <p>Returns new instance of <code>klazz</code> created using the actual
071 * arguments <code>args</code>. The formal parameter types are inferred from
072 * the actual values of <code>args</code>. See
073 * {@link #invokeExactConstructor(Class, Object[], Class[])} for more
074 * details.</p>
075 *
076 * <p>The signatures should be assignment compatible.</p>
077 *
078 * @param cls the class to be constructed.
079 * @param arg the actual argument
080 * @return new instance of <code>klazz</code>
081 *
082 * @throws NoSuchMethodException If the constructor cannot be found
083 * @throws IllegalAccessException If an error occurs accessing the constructor
084 * @throws InvocationTargetException If an error occurs invoking the constructor
085 * @throws InstantiationException If an error occurs instantiating the class
086 *
087 * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
088 */
089 public static Object invokeConstructor(Class cls, Object arg)
090 throws NoSuchMethodException, IllegalAccessException,
091 InvocationTargetException, InstantiationException {
092 return invokeConstructor(cls, new Object[] { arg });
093 }
094
095 /**
096 * <p>Returns new instance of <code>klazz</code> created using the actual
097 * arguments <code>args</code>. The formal parameter types are inferred from
098 * the actual values of <code>args</code>. See
099 * {@link #invokeExactConstructor(Class, Object[], Class[])} for more
100 * details.</p>
101 * <p>The signatures should be assignment compatible.</p>
102 *
103 * @param cls the class to be constructed.
104 * @param args actual argument array
105 * @return new instance of <code>klazz</code>
106 *
107 * @throws NoSuchMethodException If the constructor cannot be found
108 * @throws IllegalAccessException If an error occurs accessing the
109 * constructor
110 * @throws InvocationTargetException If an error occurs invoking the
111 * constructor
112 * @throws InstantiationException If an error occurs instantiating the class
113 *
114 * @see #invokeConstructor(java.lang.Class, java.lang.Object[],
115 * java.lang.Class[])
116 */
117 public static Object invokeConstructor(Class cls, Object[] args)
118 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
119 InstantiationException {
120 if (null == args) {
121 args = ArrayUtils.EMPTY_OBJECT_ARRAY;
122 }
123 Class parameterTypes[] = new Class[args.length];
124 for (int i = 0; i < args.length; i++) {
125 parameterTypes[i] = args[i].getClass();
126 }
127 return invokeConstructor(cls, args, parameterTypes);
128 }
129
130 /**
131 * <p>Returns new instance of <code>klazz</code> created using constructor
132 * with signature <code>parameterTypes</code> and actual arguments
133 * <code>args</code>.</p>
134 *
135 * <p>The signatures should be assignment compatible.</p>
136 *
137 * @param cls the class to be constructed.
138 * @param args actual argument array
139 * @param parameterTypes parameter types array
140 * @return new instance of <code>klazz</code>
141 *
142 * @throws NoSuchMethodException if matching constructor cannot be found
143 * @throws IllegalAccessException thrown on the constructor's invocation
144 * @throws InvocationTargetException thrown on the constructor's invocation
145 * @throws InstantiationException thrown on the constructor's invocation
146 * @see Constructor#newInstance
147 */
148 public static Object invokeConstructor(Class cls, Object[] args, Class[] parameterTypes)
149 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
150 InstantiationException {
151 if (parameterTypes == null) {
152 parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
153 }
154 if (args == null) {
155 args = ArrayUtils.EMPTY_OBJECT_ARRAY;
156 }
157 Constructor ctor = getMatchingAccessibleConstructor(cls, parameterTypes);
158 if (null == ctor) {
159 throw new NoSuchMethodException("No such accessible constructor on object: "
160 + cls.getName());
161 }
162 return ctor.newInstance(args);
163 }
164
165 /**
166 * <p>Returns new instance of <code>klazz</code> created using the actual
167 * arguments <code>args</code>. The formal parameter types are inferred from
168 * the actual values of <code>args</code>. See
169 * {@link #invokeExactConstructor(Class, Object[], Class[])} for more
170 * details.</p>
171 *
172 * <p>The signatures should match exactly.</p>
173 *
174 * @param cls the class to be constructed.
175 * @param arg the actual argument
176 * @return new instance of <code>klazz</code>
177 *
178 * @throws NoSuchMethodException If the constructor cannot be found
179 * @throws IllegalAccessException If an error occurs accessing the constructor
180 * @throws InvocationTargetException If an error occurs invoking the constructor
181 * @throws InstantiationException If an error occurs instantiating the class
182 *
183 * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
184 */
185 public static Object invokeExactConstructor(Class cls, Object arg)
186 throws NoSuchMethodException, IllegalAccessException,
187 InvocationTargetException, InstantiationException {
188 return invokeExactConstructor(cls, new Object[] { arg });
189 }
190
191 /**
192 * <p>Returns new instance of <code>klazz</code> created using the actual
193 * arguments <code>args</code>. The formal parameter types are inferred from
194 * the actual values of <code>args</code>. See
195 * {@link #invokeExactConstructor(Class, Object[], Class[])} for more
196 * details.</p>
197 *
198 * <p>The signatures should match exactly.</p>
199 *
200 * @param cls the class to be constructed.
201 * @param args actual argument array
202 * @return new instance of <code>klazz</code>
203 *
204 * @throws NoSuchMethodException If the constructor cannot be found
205 * @throws IllegalAccessException If an error occurs accessing the
206 * constructor
207 * @throws InvocationTargetException If an error occurs invoking the
208 * constructor
209 * @throws InstantiationException If an error occurs instantiating the class
210 *
211 * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[],
212 * java.lang.Class[])
213 */
214 public static Object invokeExactConstructor(Class cls, Object[] args)
215 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
216 InstantiationException {
217 if (null == args) {
218 args = ArrayUtils.EMPTY_OBJECT_ARRAY;
219 }
220 int arguments = args.length;
221 Class parameterTypes[] = new Class[arguments];
222 for (int i = 0; i < arguments; i++) {
223 parameterTypes[i] = args[i].getClass();
224 }
225 return invokeExactConstructor(cls, args, parameterTypes);
226 }
227
228 /**
229 * <p>Returns new instance of <code>klazz</code> created using constructor
230 * with signature <code>parameterTypes</code> and actual arguments
231 * <code>args</code>.</p>
232 *
233 * <p>The signatures should match exactly.</p>
234 *
235 * @param cls the class to be constructed.
236 * @param args actual argument array
237 * @param parameterTypes parameter types array
238 * @return new instance of <code>klazz</code>
239 *
240 * @throws NoSuchMethodException if matching constructor cannot be found
241 * @throws IllegalAccessException thrown on the constructor's invocation
242 * @throws InvocationTargetException thrown on the constructor's invocation
243 * @throws InstantiationException thrown on the constructor's invocation
244 * @see Constructor#newInstance
245 */
246 public static Object invokeExactConstructor(Class cls, Object[] args,
247 Class[] parameterTypes) throws NoSuchMethodException, IllegalAccessException,
248 InvocationTargetException, InstantiationException {
249 if (args == null) {
250 args = ArrayUtils.EMPTY_OBJECT_ARRAY;
251 }
252 if (parameterTypes == null) {
253 parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
254 }
255 Constructor ctor = getAccessibleConstructor(cls, parameterTypes);
256 if (null == ctor) {
257 throw new NoSuchMethodException("No such accessible constructor on object: "
258 + cls.getName());
259 }
260 return ctor.newInstance(args);
261 }
262
263 /**
264 * Returns a constructor with single argument.
265 * @param cls the class to be constructed
266 * @param parameterType The constructor parameter type
267 * @return null if matching accessible constructor can not be found.
268 * @see Class#getConstructor
269 * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
270 */
271 public static Constructor getAccessibleConstructor(Class cls,
272 Class parameterType) {
273 return getAccessibleConstructor(cls, new Class[] { parameterType });
274 }
275
276 /**
277 * Returns a constructor given a class and signature.
278 * @param cls 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(Class cls,
285 Class[] parameterTypes) {
286 try {
287 return getAccessibleConstructor(cls.getConstructor(parameterTypes));
288 } catch (NoSuchMethodException e) {
289 return (null);
290 }
291 }
292
293 /**
294 * Returns accessible version of the given constructor.
295 * @param ctor prototype constructor object.
296 * @return <code>null</code> if accessible constructor can not be found.
297 * @see java.lang.SecurityManager
298 */
299 public static Constructor getAccessibleConstructor(Constructor ctor) {
300 return MemberUtils.isAccessible(ctor)
301 && Modifier.isPublic(ctor.getDeclaringClass().getModifiers()) ? ctor : null;
302 }
303
304 /**
305 * <p>Find an accessible constructor with compatible parameters. Compatible
306 * parameters mean that every method parameter is assignable from the given
307 * parameters. In other words, it finds constructor that will take the
308 * parameters given.</p>
309 *
310 * <p>First it checks if there is constructor matching the exact signature.
311 * If no such, all the constructors of the class are tested if their
312 * signatures are assignment compatible with the parameter types. The first
313 * matching constructor is returned.</p>
314 *
315 * @param cls find constructor for this class
316 * @param parameterTypes find method with compatible parameters
317 * @return a valid Constructor object. If there's no matching constructor,
318 * returns <code>null</code>.
319 */
320 public static Constructor getMatchingAccessibleConstructor(Class cls,
321 Class[] parameterTypes) {
322 // see if we can find the constructor directly
323 // most of the time this works and it's much faster
324 try {
325 Constructor ctor = cls.getConstructor(parameterTypes);
326 MemberUtils.setAccessibleWorkaround(ctor);
327 return ctor;
328 } catch (NoSuchMethodException e) { /* SWALLOW */
329 }
330 Constructor result = null;
331 // search through all constructors
332 Constructor[] ctors = cls.getConstructors();
333 // return best match:
334 for (int i = 0; i < ctors.length; i++) {
335
336 // compare parameters
337 if (ClassUtils.isAssignable(parameterTypes, ctors[i].getParameterTypes(), true)) {
338 // get accessible version of constructor
339 Constructor ctor = getAccessibleConstructor(ctors[i]);
340 if (ctor != null) {
341 MemberUtils.setAccessibleWorkaround(ctor);
342 if (result == null
343 || MemberUtils.compareParameterTypes(ctor.getParameterTypes(), result
344 .getParameterTypes(), parameterTypes) < 0) {
345 result = ctor;
346 }
347 }
348 }
349 }
350 return result;
351 }
352
353 }