    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 */
    017package org.apache.commons.lang3.reflect;
    018
    019import java.lang.reflect.Constructor;
    020import java.lang.reflect.InvocationTargetException;
    021import java.lang.reflect.Modifier;
    022
    023import org.apache.commons.lang3.ArrayUtils;
    024import org.apache.commons.lang3.ClassUtils;
    025import org.apache.commons.lang3.Validate;
    026
    027/**
    028 * <p> Utility reflection methods focused on constructors, modeled after
    029 * {@link MethodUtils}. </p>
    030 *
    031 * <h2>Known Limitations</h2>
    032 * <h3>Accessing Public Constructors In A Default Access Superclass</h3>
    033 * <p>There is an issue when invoking {@code public} constructors
    034 * contained in a default access superclass. Reflection correctly locates these
    035 * constructors and assigns them as {@code public}. However, an
    036 * {@link IllegalAccessException} is thrown if the constructor is
    037 * invoked.</p>
    038 *
    039 * <p>{@link ConstructorUtils} contains a workaround for this situation: it
    040 * will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this constructor. If this
    041 * call succeeds, then the method can be invoked as normal. This call will only
    042 * succeed when the application has sufficient security privileges. If this call
    043 * fails then a warning will be logged and the method may fail.</p>
    044 *
    045 * @since 2.5
    046 */
    047public class ConstructorUtils {
    048
    049    /**
    050     * <p>ConstructorUtils instances should NOT be constructed in standard
    051     * programming. Instead, the class should be used as
    052     * {@code ConstructorUtils.invokeConstructor(cls, args)}.</p>
    053     *
    054     * <p>This constructor is {@code public} to permit tools that require a JavaBean
    055     * instance to operate.</p>
    056     */
    057    public ConstructorUtils() {
    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 {@code null}
    069     * @param args  the array of arguments, {@code null} treated as empty
    070     * @return new instance of {@code cls}, not {@code null}
    071     *
    072     * @throws NullPointerException if {@code cls} is {@code null}
    073     * @throws NoSuchMethodException if a matching constructor cannot be found
    074     * @throws IllegalAccessException if invocation is not permitted by security
    075     * @throws InvocationTargetException if an error occurs on invocation
    076     * @throws InstantiationException if an error occurs on instantiation
    077     * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
    078     */
    079    public static <T> T invokeConstructor(final Class<T> cls, Object... args)
    080            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
    081            InstantiationException {
    082        args = ArrayUtils.nullToEmpty(args);
    083        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
    084        return invokeConstructor(cls, args, parameterTypes);
    085    }
    086
    087    /**
    088     * <p>Returns a new instance of the specified class choosing the right constructor
    089     * from the list of parameter types.</p>
    090     *
    091     * <p>This locates and calls a constructor.
    092     * The constructor signature must match the parameter types by assignment compatibility.</p>
    093     *
    094     * @param <T> the type to be constructed
    095     * @param cls  the class to be constructed, not {@code null}
    096     * @param args  the array of arguments, {@code null} treated as empty
    097     * @param parameterTypes  the array of parameter types, {@code null} treated as empty
    098     * @return new instance of {@code cls}, not {@code null}
    099     *
    100     * @throws NullPointerException if {@code cls} is {@code null}
    101     * @throws NoSuchMethodException if a matching constructor cannot be found
    102     * @throws IllegalAccessException if invocation is not permitted by security
    103     * @throws InvocationTargetException if an error occurs on invocation
    104     * @throws InstantiationException if an error occurs on instantiation
    105     * @see Constructor#newInstance
    106     */
    107    public static <T> T invokeConstructor(final Class<T> cls, Object[] args, Class<?>[] parameterTypes)
    108            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
    109            InstantiationException {
    110        args = ArrayUtils.nullToEmpty(args);
    111        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
    112        final Constructor<T> ctor = getMatchingAccessibleConstructor(cls, parameterTypes);
    113        if (ctor == null) {
    114            throw new NoSuchMethodException(
    115                "No such accessible constructor on object: " + cls.getName());
    116        }
    117        if (ctor.isVarArgs()) {
    118            final Class<?>[] methodParameterTypes = ctor.getParameterTypes();
    119            args = MethodUtils.getVarArgs(args, methodParameterTypes);
    120        }
    121        return ctor.newInstance(args);
    122    }
    123
    124    /**
    125     * <p>Returns a new instance of the specified class inferring the right constructor
    126     * from the types of the arguments.</p>
    127     *
    128     * <p>This locates and calls a constructor.
    129     * The constructor signature must match the argument types exactly.</p>
    130     *
    131     * @param <T> the type to be constructed
    132     * @param cls the class to be constructed, not {@code null}
    133     * @param args the array of arguments, {@code null} treated as empty
    134     * @return new instance of {@code cls}, not {@code null}
    135     *
    136     * @throws NullPointerException if {@code cls} is {@code null}
    137     * @throws NoSuchMethodException if a matching constructor cannot be found
    138     * @throws IllegalAccessException if invocation is not permitted by security
    139     * @throws InvocationTargetException if an error occurs on invocation
    140     * @throws InstantiationException if an error occurs on instantiation
    141     * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
    142     */
    143    public static <T> T invokeExactConstructor(final Class<T> cls, Object... args)
    144            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
    145            InstantiationException {
    146        args = ArrayUtils.nullToEmpty(args);
    147        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
    148        return invokeExactConstructor(cls, args, parameterTypes);
    149    }
    150
    151    /**
    152     * <p>Returns a new instance of the specified class choosing the right constructor
    153     * from the list of parameter types.</p>
    154     *
    155     * <p>This locates and calls a constructor.
    156     * The constructor signature must match the parameter types exactly.</p>
    157     *
    158     * @param <T> the type to be constructed
    159     * @param cls the class to be constructed, not {@code null}
    160     * @param args the array of arguments, {@code null} treated as empty
    161     * @param parameterTypes  the array of parameter types, {@code null} treated as empty
    162     * @return new instance of {@code cls}, not {@code null}
    163     *
    164     * @throws NullPointerException if {@code cls} is {@code null}
    165     * @throws NoSuchMethodException if a matching constructor cannot be found
    166     * @throws IllegalAccessException if invocation is not permitted by security
    167     * @throws InvocationTargetException if an error occurs on invocation
    168     * @throws InstantiationException if an error occurs on instantiation
    169     * @see Constructor#newInstance
    170     */
    171    public static <T> T invokeExactConstructor(final Class<T> cls, Object[] args,
    172            Class<?>[] parameterTypes) throws NoSuchMethodException, IllegalAccessException,
    173            InvocationTargetException, InstantiationException {
    174        args = ArrayUtils.nullToEmpty(args);
    175        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
    176        final Constructor<T> ctor = getAccessibleConstructor(cls, parameterTypes);
    177        if (ctor == null) {
    178            throw new NoSuchMethodException(
    179                "No such accessible constructor on object: "+ cls.getName());
    180        }
    181        return ctor.newInstance(args);
    182    }
    183
    184    //-----------------------------------------------------------------------
    185    /**
    186     * <p>Finds a constructor given a class and signature, checking accessibility.</p>
    187     *
    188     * <p>This finds the constructor and ensures that it is accessible.
    189     * The constructor signature must match the parameter types exactly.</p>
    190     *
    191     * @param <T> the constructor type
    192     * @param cls the class to find a constructor for, not {@code null}
    193     * @param parameterTypes the array of parameter types, {@code null} treated as empty
    194     * @return the constructor, {@code null} if no matching accessible constructor found
    195     * @see Class#getConstructor
    196     * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
    197     * @throws NullPointerException if {@code cls} is {@code null}
    198     */
    199    public static <T> Constructor<T> getAccessibleConstructor(final Class<T> cls,
    200            final Class<?>... parameterTypes) {
    201        Validate.notNull(cls, "cls");
    202        try {
    203            return getAccessibleConstructor(cls.getConstructor(parameterTypes));
    204        } catch (final NoSuchMethodException e) {
    205            return null;
    206        }
    207    }
    208
    209    /**
    210     * <p>Checks if the specified constructor is accessible.</p>
    211     *
    212     * <p>This simply ensures that the constructor is accessible.</p>
    213     *
    214     * @param <T> the constructor type
    215     * @param ctor  the prototype constructor object, not {@code null}
    216     * @return the constructor, {@code null} if no matching accessible constructor found
    217     * @see java.lang.SecurityManager
    218     * @throws NullPointerException if {@code ctor} is {@code null}
    219     */
    220    public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) {
    221        Validate.notNull(ctor, "ctor");
    222        return MemberUtils.isAccessible(ctor)
    223                && isAccessible(ctor.getDeclaringClass()) ? ctor : null;
    224    }
    225
    226    /**
    227     * <p>Finds an accessible constructor with compatible parameters.</p>
    228     *
    229     * <p>This checks all the constructor and finds one with compatible parameters
    230     * This requires that every parameter is assignable from the given parameter types.
    231     * This is a more flexible search than the normal exact matching algorithm.</p>
    232     *
    233     * <p>First it checks if there is a constructor matching the exact signature.
    234     * If not then all the constructors of the class are checked to see if their
    235     * signatures are assignment-compatible with the parameter types.
    236     * The first assignment-compatible matching constructor is returned.</p>
    237     *
    238     * @param <T> the constructor type
    239     * @param cls  the class to find a constructor for, not {@code null}
    240     * @param parameterTypes find method with compatible parameters
    241     * @return the constructor, null if no matching accessible constructor found
    242     * @throws NullPointerException if {@code cls} is {@code null}
    243     */
    244    public static <T> Constructor<T> getMatchingAccessibleConstructor(final Class<T> cls,
    245            final Class<?>... parameterTypes) {
    246        Validate.notNull(cls, "cls");
    247        // see if we can find the constructor directly
    248        // most of the time this works and it's much faster
    249        try {
    250            final Constructor<T> ctor = cls.getConstructor(parameterTypes);
    251            MemberUtils.setAccessibleWorkaround(ctor);
    252            return ctor;
    253        } catch (final NoSuchMethodException e) { // NOPMD - Swallow
    254        }
    255        Constructor<T> result = null;
    256        /*
    257         * (1) Class.getConstructors() is documented to return Constructor<T> so as
    258         * long as the array is not subsequently modified, everything's fine.
    259         */
    260        final Constructor<?>[] ctors = cls.getConstructors();
    261
    262        // return best match:
    263        for (Constructor<?> ctor : ctors) {
    264            // compare parameters
    265            if (MemberUtils.isMatchingConstructor(ctor, parameterTypes)) {
    266                // get accessible version of constructor
    267                ctor = getAccessibleConstructor(ctor);
    268                if (ctor != null) {
    269                    MemberUtils.setAccessibleWorkaround(ctor);
    270                    if (result == null || MemberUtils.compareConstructorFit(ctor, result, parameterTypes) < 0) {
    271                        // temporary variable for annotation, see comment above (1)
    272                        @SuppressWarnings("unchecked")
    273                        final
    274                        Constructor<T> constructor = (Constructor<T>) ctor;
    275                        result = constructor;
    276                    }
    277                }
    278            }
    279        }
    280        return result;
    281    }
    282
    283    /**
    284     * Learn whether the specified class is generally accessible, i.e. is
    285     * declared in an entirely {@code public} manner.
    286     * @param type to check
    287     * @return {@code true} if {@code type} and any enclosing classes are
    288     *         {@code public}.
    289     */
    290    private static boolean isAccessible(final Class<?> type) {
    291        Class<?> cls = type;
    292        while (cls != null) {
    293            if (!Modifier.isPublic(cls.getModifiers())) {
    294                return false;
    295            }
    296            cls = cls.getEnclosingClass();
    297        }
    298        return true;
    299    }
    300
    301}