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