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
018package org.apache.commons.beanutils;
019
020import java.lang.reflect.Constructor;
021import java.lang.reflect.InvocationTargetException;
022import java.lang.reflect.Modifier;
023
024/**
025 * <p>Utility reflection methods focused on constructors, modeled after {@link MethodUtils}.</p>
026 *
027 * <strong>Known Limitations: Accessing Public Constructors In A Default Access Superclass</strong>
028 * <p>There is an issue when invoking public constructors contained in a default access superclass.
029 * Reflection locates these constructors fine and correctly assigns them as public.
030 * However, an <code>IllegalAccessException</code> is thrown if the constructors is invoked.</p>
031 *
032 * <p><code>ConstructorUtils</code> contains a workaround for this situation.
033 * It will attempt to call <code>setAccessible</code> on this constructor.
034 * If this call succeeds, then the method can be invoked as normal.
035 * This call will only succeed when the application has sufficient security privileges.
036 * If this call fails then a warning will be logged and the method may fail.</p>
037 */
038public class ConstructorUtils {
039
040    /** An empty class array */
041    private static final Class<?>[] EMPTY_CLASS_PARAMETERS = {};
042    /** An empty object array */
043    private static final Object[] EMPTY_OBJECT_ARRAY = {};
044
045    /**
046     * Returns a constructor with single argument.
047     * @param <T> the type of the constructor
048     * @param klass the class to be constructed
049     * @param parameterType The constructor parameter type
050     * @return null if matching accessible constructor cannot be found.
051     * @see Class#getConstructor
052     * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
053     */
054    public static <T> Constructor<T> getAccessibleConstructor(
055        final Class<T> klass,
056        final Class<?> parameterType) {
057
058        final Class<?>[] parameterTypes = { parameterType };
059        return getAccessibleConstructor(klass, parameterTypes);
060    }
061
062    /**
063     * Returns a constructor given a class and signature.
064     * @param <T> the type to be constructed
065     * @param klass the class to be constructed
066     * @param parameterTypes the parameter array
067     * @return null if matching accessible constructor cannot be found
068     * @see Class#getConstructor
069     * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
070     */
071    public static <T> Constructor<T> getAccessibleConstructor(
072        final Class<T> klass,
073        final Class<?>[] parameterTypes) {
074
075        try {
076            return getAccessibleConstructor(
077                klass.getConstructor(parameterTypes));
078        } catch (final NoSuchMethodException e) {
079            return null;
080        }
081    }
082
083    /**
084     * Returns accessible version of the given constructor.
085     * @param <T> the type of the constructor
086     * @param ctor prototype constructor object.
087     * @return <code>null</code> if accessible constructor cannot be found.
088     * @see java.lang.SecurityManager
089     */
090    public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) {
091
092        // Make sure we have a method to check
093        // If the requested method is not public we cannot call it
094        if (ctor == null || !Modifier.isPublic(ctor.getModifiers())) {
095            return null;
096        }
097
098        // If the declaring class is public, we are done
099        final Class<T> clazz = ctor.getDeclaringClass();
100        if (Modifier.isPublic(clazz.getModifiers())) {
101            return ctor;
102        }
103
104        // what else can we do?
105        return null;
106    }
107
108    /**
109     * <p>Find an accessible constructor with compatible parameters.
110     * Compatible parameters mean that every method parameter is assignable from
111     * the given parameters. In other words, it finds constructor that will take
112     * the parameters given.</p>
113     *
114     * <p>First it checks if there is constructor matching the exact signature.
115     * If no such, all the constructors of the class are tested if their signatures
116     * are assignment compatible with the parameter types.
117     * The first matching constructor is returned.</p>
118     *
119     * @param <T> the type of the class to be inspected
120     * @param clazz find constructor for this class
121     * @param parameterTypes find method with compatible parameters
122     * @return a valid Constructor object. If there's no matching constructor, returns <code>null</code>.
123     */
124    private static <T> Constructor<T> getMatchingAccessibleConstructor(
125        final Class<T> clazz,
126        final Class<?>[] parameterTypes) {
127        // see if we can find the method directly
128        // most of the time this works and it's much faster
129        try {
130            final Constructor<T> ctor = clazz.getConstructor(parameterTypes);
131            try {
132                //
133                // XXX Default access superclass workaround
134                //
135                // When a public class has a default access superclass
136                // with public methods, these methods are accessible.
137                // Calling them from compiled code works fine.
138                //
139                // Unfortunately, using reflection to invoke these methods
140                // seems to (wrongly) to prevent access even when the method
141                // modifer is public.
142                //
143                // The following workaround solves the problem but will only
144                // work from sufficiently privileges code.
145                //
146                // Better workarounds would be greatfully accepted.
147                //
148                ctor.setAccessible(true);
149            } catch (final SecurityException se) {
150                /* SWALLOW, if workaround fails don't fret. */
151            }
152            return ctor;
153
154        } catch (final NoSuchMethodException e) { /* SWALLOW */
155        }
156
157        // search through all methods
158        final int paramSize = parameterTypes.length;
159        final Constructor<?>[] ctors = clazz.getConstructors();
160        for (final Constructor<?> ctor2 : ctors) {
161            // compare parameters
162            final Class<?>[] ctorParams = ctor2.getParameterTypes();
163            final int ctorParamSize = ctorParams.length;
164            if (ctorParamSize == paramSize) {
165                boolean match = true;
166                for (int n = 0; n < ctorParamSize; n++) {
167                    if (!MethodUtils
168                        .isAssignmentCompatible(
169                            ctorParams[n],
170                            parameterTypes[n])) {
171                        match = false;
172                        break;
173                    }
174                }
175
176                if (match) {
177                    // get accessible version of method
178                    final Constructor<?> ctor = getAccessibleConstructor(ctor2);
179                    if (ctor != null) {
180                        try {
181                            ctor.setAccessible(true);
182                        } catch (final SecurityException se) {
183                            /* Swallow SecurityException
184                             * TODO: Why?
185                             */
186                        }
187                        @SuppressWarnings("unchecked")
188                        final
189                        // Class.getConstructors() actually returns constructors
190                        // of type T, so it is safe to cast.
191                        Constructor<T> typedCtor = (Constructor<T>) ctor;
192                        return typedCtor;
193                    }
194                }
195            }
196        }
197
198        return null;
199    }
200
201    /**
202     * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor.
203     * The formal parameter type is inferred from the actual values of <code>arg</code>.
204     * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
205     *
206     * <p>The signatures should be assignment compatible.</p>
207     *
208     * @param <T> the type of the object to be constructed
209     * @param klass the class to be constructed.
210     * @param arg the actual argument. May be null (this will result in calling the default constructor).
211     * @return new instance of <code>klazz</code>
212     * @throws NoSuchMethodException If the constructor cannot be found
213     * @throws IllegalAccessException If an error occurs accessing the constructor
214     * @throws InvocationTargetException If an error occurs invoking the constructor
215     * @throws InstantiationException If an error occurs instantiating the class
216     * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
217     */
218    public static <T> T invokeConstructor(final Class<T> klass, final Object arg)
219        throws
220            NoSuchMethodException,
221            IllegalAccessException,
222            InvocationTargetException,
223            InstantiationException {
224
225        final Object[] args = toArray(arg);
226        return invokeConstructor(klass, args);
227    }
228
229    /**
230     * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
231     * The formal parameter types are inferred from the actual values of <code>args</code>.
232     * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
233     *
234     * <p>The signatures should be assignment compatible.</p>
235     *
236     * @param <T> the type of the object to be constructed
237     * @param klass the class to be constructed.
238     * @param args actual argument array. May be null (this will result in calling the default constructor).
239     * @return new instance of <code>klazz</code>
240     * @throws NoSuchMethodException If the constructor cannot be found
241     * @throws IllegalAccessException If an error occurs accessing the constructor
242     * @throws InvocationTargetException If an error occurs invoking the constructor
243     * @throws InstantiationException If an error occurs instantiating the class
244     * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
245     */
246    public static <T> T invokeConstructor(final Class<T> klass, Object[] args)
247        throws
248            NoSuchMethodException,
249            IllegalAccessException,
250            InvocationTargetException,
251            InstantiationException {
252
253        if (null == args) {
254            args = EMPTY_OBJECT_ARRAY;
255        }
256        final int arguments = args.length;
257        final Class<?> parameterTypes[] = new Class<?>[arguments];
258        for (int i = 0; i < arguments; i++) {
259            parameterTypes[i] = args[i].getClass();
260        }
261        return invokeConstructor(klass, args, parameterTypes);
262    }
263
264    /**
265     * <p>Returns new instance of <code>klazz</code> created using constructor
266     * with signature <code>parameterTypes</code> and actual arguments <code>args</code>.</p>
267     *
268     * <p>The signatures should be assignment compatible.</p>
269     *
270     * @param <T> the type of the object to be constructed
271     * @param klass the class to be constructed.
272     * @param args actual argument array. May be null (this will result in calling the default constructor).
273     * @param parameterTypes parameter types array
274     * @return new instance of <code>klazz</code>
275     * @throws NoSuchMethodException if matching constructor cannot be found
276     * @throws IllegalAccessException thrown on the constructor's invocation
277     * @throws InvocationTargetException thrown on the constructor's invocation
278     * @throws InstantiationException thrown on the constructor's invocation
279     * @see Constructor#newInstance
280     */
281    public static <T> T invokeConstructor(
282        final Class<T> klass,
283        Object[] args,
284        Class<?>[] parameterTypes)
285        throws
286            NoSuchMethodException,
287            IllegalAccessException,
288            InvocationTargetException,
289            InstantiationException {
290
291        if (parameterTypes == null) {
292            parameterTypes = EMPTY_CLASS_PARAMETERS;
293        }
294        if (args == null) {
295            args = EMPTY_OBJECT_ARRAY;
296        }
297
298        final Constructor<T> ctor =
299            getMatchingAccessibleConstructor(klass, parameterTypes);
300        if (null == ctor) {
301            throw new NoSuchMethodException(
302                "No such accessible constructor on object: " + klass.getName());
303        }
304        return ctor.newInstance(args);
305    }
306
307    /**
308     * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor.
309     * The formal parameter type is inferred from the actual values of <code>arg</code>.
310     * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
311     *
312     * <p>The signatures should match exactly.</p>
313     *
314     * @param <T> the type of the object to be constructed
315     * @param klass the class to be constructed.
316     * @param arg the actual argument. May be null (this will result in calling the default constructor).
317     * @return new instance of <code>klazz</code>
318     * @throws NoSuchMethodException If the constructor cannot be found
319     * @throws IllegalAccessException If an error occurs accessing the constructor
320     * @throws InvocationTargetException If an error occurs invoking the constructor
321     * @throws InstantiationException If an error occurs instantiating the class
322     * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
323     */
324    public static <T> T invokeExactConstructor(final Class<T> klass, final Object arg)
325        throws
326            NoSuchMethodException,
327            IllegalAccessException,
328            InvocationTargetException,
329            InstantiationException {
330
331        final Object[] args = toArray(arg);
332        return invokeExactConstructor(klass, args);
333    }
334
335    /**
336     * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
337     * The formal parameter types are inferred from the actual values of <code>args</code>.
338     * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
339     *
340     * <p>The signatures should match exactly.</p>
341     *
342     * @param <T> the type of the object to be constructed
343     * @param klass the class to be constructed.
344     * @param args actual argument array. May be null (this will result in calling the default constructor).
345     * @return new instance of <code>klazz</code>
346     * @throws NoSuchMethodException If the constructor cannot be found
347     * @throws IllegalAccessException If an error occurs accessing the constructor
348     * @throws InvocationTargetException If an error occurs invoking the constructor
349     * @throws InstantiationException If an error occurs instantiating the class
350     * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
351     */
352    public static <T> T invokeExactConstructor(final Class<T> klass, Object[] args)
353        throws
354            NoSuchMethodException,
355            IllegalAccessException,
356            InvocationTargetException,
357            InstantiationException {
358
359        if (null == args) {
360            args = EMPTY_OBJECT_ARRAY;
361        }
362        final int arguments = args.length;
363        final Class<?> parameterTypes[] = new Class[arguments];
364        for (int i = 0; i < arguments; i++) {
365            parameterTypes[i] = args[i].getClass();
366        }
367        return invokeExactConstructor(klass, args, parameterTypes);
368    }
369
370    /**
371     * <p>Returns new instance of <code>klazz</code> created using constructor
372     * with signature <code>parameterTypes</code> and actual arguments
373     * <code>args</code>.</p>
374     *
375     * <p>The signatures should match exactly.</p>
376     *
377     * @param <T> the type of the object to be constructed
378     * @param klass the class to be constructed.
379     * @param args actual argument array. May be null (this will result in calling the default constructor).
380     * @param parameterTypes parameter types array
381     * @return new instance of <code>klazz</code>
382     * @throws NoSuchMethodException if matching constructor cannot be found
383     * @throws IllegalAccessException thrown on the constructor's invocation
384     * @throws InvocationTargetException thrown on the constructor's invocation
385     * @throws InstantiationException thrown on the constructor's invocation
386     * @see Constructor#newInstance
387     */
388    public static <T> T invokeExactConstructor(
389        final Class<T> klass,
390        Object[] args,
391        Class<?>[] parameterTypes)
392        throws
393            NoSuchMethodException,
394            IllegalAccessException,
395            InvocationTargetException,
396            InstantiationException {
397
398        if (args == null) {
399            args = EMPTY_OBJECT_ARRAY;
400        }
401
402        if (parameterTypes == null) {
403            parameterTypes = EMPTY_CLASS_PARAMETERS;
404        }
405
406        final Constructor<T> ctor = getAccessibleConstructor(klass, parameterTypes);
407        if (null == ctor) {
408            throw new NoSuchMethodException(
409                "No such accessible constructor on object: " + klass.getName());
410        }
411        return ctor.newInstance(args);
412    }
413
414    private static Object[] toArray(final Object arg) {
415        Object[] args = null;
416        if (arg != null) {
417            args = new Object[] { arg };
418        }
419        return args;
420    }
421
422}