    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.lang3.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.lang3.ArrayUtils;
    024    import org.apache.commons.lang3.ClassUtils;
    025
    026    /**
    027     * <p> Utility reflection methods focused on constructors, modeled 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 privileges. If this call
    041     * fails then a warning will be logged and the method may fail.</p>
    042     *
    043     * @since 2.5
    044     * @version $Id: ConstructorUtils.java 1144010 2011-07-07 20:02:10Z joehni $
    045     */
    046    public class ConstructorUtils {
    047
    048        /**
    049         * <p>ConstructorUtils instances should NOT be constructed in standard
    050         * programming. Instead, the class should be used as
    051         * <code>ConstructorUtils.invokeConstructor(cls, args)</code>.</p>
    052         *
    053         * <p>This constructor is public to permit tools that require a JavaBean
    054         * instance to operate.</p>
    055         */
    056        public ConstructorUtils() {
    057            super();
    058        }
    059
    060        /**
    061         * <p>Returns a new instance of the specified class inferring the right constructor
    062         * from the types of the arguments.</p>
    063         *
    064         * <p>This locates and calls a constructor.
    065         * The constructor signature must match the argument types by assignment compatibility.</p>
    066         *
    067         * @param <T> the type to be constructed
    068         * @param cls  the class to be constructed, not null
    069         * @param args  the array of arguments, null treated as empty
    070         * @return new instance of <code>cls</code>, not null
    071         *
    072         * @throws NoSuchMethodException if a matching constructor cannot be found
    073         * @throws IllegalAccessException if invocation is not permitted by security
    074         * @throws InvocationTargetException if an error occurs on invocation
    075         * @throws InstantiationException if an error occurs on instantiation
    076         * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
    077         */
    078        public static <T> T invokeConstructor(Class<T> cls, Object... args)
    079                throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
    080                InstantiationException {
    081            if (args == null) {
    082                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
    083            }
    084            Class<?> parameterTypes[] = new Class[args.length];
    085            for (int i = 0; i < args.length; i++) {
    086                parameterTypes[i] = args[i].getClass();
    087            }
    088            return invokeConstructor(cls, args, parameterTypes);
    089        }
    090
    091        /**
    092         * <p>Returns a new instance of the specified class choosing the right constructor
    093         * from the list of parameter types.</p>
    094         *
    095         * <p>This locates and calls a constructor.
    096         * The constructor signature must match the parameter types by assignment compatibility.</p>
    097         *
    098         * @param <T> the type to be constructed
    099         * @param cls  the class to be constructed, not null
    100         * @param args  the array of arguments, null treated as empty
    101         * @param parameterTypes  the array of parameter types, null treated as empty
    102         * @return new instance of <code>cls</code>, not null
    103         *
    104         * @throws NoSuchMethodException if a matching constructor cannot be found
    105         * @throws IllegalAccessException if invocation is not permitted by security
    106         * @throws InvocationTargetException if an error occurs on invocation
    107         * @throws InstantiationException if an error occurs on instantiation
    108         * @see Constructor#newInstance
    109         */
    110        public static <T> T invokeConstructor(Class<T> cls, Object[] args, Class<?>[] parameterTypes)
    111                throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
    112                InstantiationException {
    113            if (parameterTypes == null) {
    114                parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
    115            }
    116            if (args == null) {
    117                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
    118            }
    119            Constructor<T> ctor = getMatchingAccessibleConstructor(cls, parameterTypes);
    120            if (ctor == null) {
    121                throw new NoSuchMethodException(
    122                    "No such accessible constructor on object: " + cls.getName());
    123            }
    124            return ctor.newInstance(args);
    125        }
    126
    127        /**
    128         * <p>Returns a new instance of the specified class inferring the right constructor
    129         * from the types of the arguments.</p>
    130         *
    131         * <p>This locates and calls a constructor.
    132         * The constructor signature must match the argument types exactly.</p>
    133         *
    134         * @param <T> the type to be constructed
    135         * @param cls  the class to be constructed, not null
    136         * @param args  the array of arguments, null treated as empty
    137         * @return new instance of <code>cls</code>, not null
    138         *
    139         * @throws NoSuchMethodException if a matching constructor cannot be found
    140         * @throws IllegalAccessException if invocation is not permitted by security
    141         * @throws InvocationTargetException if an error occurs on invocation
    142         * @throws InstantiationException if an error occurs on instantiation
    143         * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
    144         */
    145        public static <T> T invokeExactConstructor(Class<T> cls, Object... args)
    146                throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
    147                InstantiationException {
    148            if (args == null) {
    149                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
    150            }
    151            int arguments = args.length;
    152            Class<?> parameterTypes[] = new Class[arguments];
    153            for (int i = 0; i < arguments; i++) {
    154                parameterTypes[i] = args[i].getClass();
    155            }
    156            return invokeExactConstructor(cls, args, parameterTypes);
    157        }
    158
    159        /**
    160         * <p>Returns a new instance of the specified class choosing the right constructor
    161         * from the list of parameter types.</p>
    162         *
    163         * <p>This locates and calls a constructor.
    164         * The constructor signature must match the parameter types exactly.</p>
    165         *
    166         * @param <T> the type to be constructed
    167         * @param cls  the class to be constructed, not null
    168         * @param args  the array of arguments, null treated as empty
    169         * @param parameterTypes  the array of parameter types, null treated as empty
    170         * @return new instance of <code>cls</code>, not null
    171         *
    172         * @throws NoSuchMethodException if a matching constructor cannot be found
    173         * @throws IllegalAccessException if invocation is not permitted by security
    174         * @throws InvocationTargetException if an error occurs on invocation
    175         * @throws InstantiationException if an error occurs on instantiation
    176         * @see Constructor#newInstance
    177         */
    178        public static <T> T invokeExactConstructor(Class<T> cls, Object[] args,
    179                Class<?>[] parameterTypes) throws NoSuchMethodException, IllegalAccessException,
    180                InvocationTargetException, InstantiationException {
    181            if (args == null) {
    182                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
    183            }
    184            if (parameterTypes == null) {
    185                parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
    186            }
    187            Constructor<T> ctor = getAccessibleConstructor(cls, parameterTypes);
    188            if (ctor == null) {
    189                throw new NoSuchMethodException(
    190                    "No such accessible constructor on object: "+ cls.getName());
    191            }
    192            return ctor.newInstance(args);
    193        }
    194
    195        //-----------------------------------------------------------------------
    196        /**
    197         * <p>Finds a constructor given a class and signature, checking accessibility.</p>
    198         *
    199         * <p>This finds the constructor and ensures that it is accessible.
    200         * The constructor signature must match the parameter types exactly.</p>
    201         *
    202         * @param <T> the constructor type
    203         * @param cls  the class to find a constructor for, not null
    204         * @param parameterTypes  the array of parameter types, null treated as empty
    205         * @return the constructor, null if no matching accessible constructor found
    206         * @see Class#getConstructor
    207         * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
    208         */
    209        public static <T> Constructor<T> getAccessibleConstructor(Class<T> cls,
    210                Class<?>... parameterTypes) {
    211            try {
    212                return getAccessibleConstructor(cls.getConstructor(parameterTypes));
    213            } catch (NoSuchMethodException e) {
    214                return null;
    215            }
    216        }
    217
    218        /**
    219         * <p>Checks if the specified constructor is accessible.</p>
    220         *
    221         * <p>This simply ensures that the constructor is accessible.</p>
    222         *
    223         * @param <T> the constructor type
    224         * @param ctor  the prototype constructor object, not null
    225         * @return the constructor, null if no matching accessible constructor found
    226         * @see java.lang.SecurityManager
    227         */
    228        public static <T> Constructor<T> getAccessibleConstructor(Constructor<T> ctor) {
    229            return MemberUtils.isAccessible(ctor)
    230                    && Modifier.isPublic(ctor.getDeclaringClass().getModifiers()) ? ctor : null;
    231        }
    232
    233        /**
    234         * <p>Finds an accessible constructor with compatible parameters.</p>
    235         *
    236         * <p>This checks all the constructor and finds one with compatible parameters
    237         * This requires that every parameter is assignable from the given parameter types.
    238         * This is a more flexible search than the normal exact matching algorithm.</p>
    239         *
    240         * <p>First it checks if there is a constructor matching the exact signature.
    241         * If not then all the constructors of the class are checked to see if their
    242         * signatures are assignment compatible with the parameter types.
    243         * The first assignment compatible matching constructor is returned.</p>
    244         *
    245         * @param <T> the constructor type
    246         * @param cls  the class to find a constructor for, not null
    247         * @param parameterTypes find method with compatible parameters
    248         * @return the constructor, null if no matching accessible constructor found
    249         */
    250        public static <T> Constructor<T> getMatchingAccessibleConstructor(Class<T> cls,
    251                Class<?>... parameterTypes) {
    252            // see if we can find the constructor directly
    253            // most of the time this works and it's much faster
    254            try {
    255                Constructor<T> ctor = cls.getConstructor(parameterTypes);
    256                MemberUtils.setAccessibleWorkaround(ctor);
    257                return ctor;
    258            } catch (NoSuchMethodException e) { // NOPMD - Swallow
    259            }
    260            Constructor<T> result = null;
    261            /*
    262             * (1) Class.getConstructors() is documented to return Constructor<T> so as
    263             * long as the array is not subsequently modified, everything's fine.
    264             */
    265            Constructor<?>[] ctors = cls.getConstructors();
    266
    267            // return best match:
    268            for (Constructor<?> ctor : ctors) {
    269                // compare parameters
    270                if (ClassUtils.isAssignable(parameterTypes, ctor.getParameterTypes(), true)) {
    271                    // get accessible version of constructor
    272                    ctor = getAccessibleConstructor(ctor);
    273                    if (ctor != null) {
    274                        MemberUtils.setAccessibleWorkaround(ctor);
    275                        if (result == null
    276                                || MemberUtils.compareParameterTypes(ctor.getParameterTypes(), result
    277                                        .getParameterTypes(), parameterTypes) < 0) {
    278                            // temporary variable for annotation, see comment above (1)
    279                            @SuppressWarnings("unchecked")
    280                            Constructor<T> constructor = (Constructor<T>)ctor;
    281                            result = constructor;
    282                        }
    283                    }
    284                }
    285            }
    286            return result;
    287        }
    288
    289    }