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