    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    }