    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        super();
    059    }
    060
    061    /**
    062     * <p>Returns a new instance of the specified class inferring the right constructor
    063     * from the types of the arguments.</p>
    064     *
    065     * <p>This locates and calls a constructor.
    066     * The constructor signature must match the argument types by assignment compatibility.</p>
    067     *
    068     * @param <T> the type to be constructed
    069     * @param cls  the class to be constructed, not {@code null}
    070     * @param args  the array of arguments, {@code null} treated as empty
    071     * @return new instance of {@code cls}, not {@code null}
    072     *
    073     * @throws NullPointerException if {@code cls} is {@code null}
    074     * @throws NoSuchMethodException if a matching constructor cannot be found
    075     * @throws IllegalAccessException if invocation is not permitted by security
    076     * @throws InvocationTargetException if an error occurs on invocation
    077     * @throws InstantiationException if an error occurs on instantiation
    078     * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
    079     */
    080    public static <T> T invokeConstructor(final Class<T> cls, Object... args)
    081            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
    082            InstantiationException {
    083        args = ArrayUtils.nullToEmpty(args);
    084        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
    085        return invokeConstructor(cls, args, parameterTypes);
    086    }
    087
    088    /**
    089     * <p>Returns a new instance of the specified class choosing the right constructor
    090     * from the list of parameter types.</p>
    091     *
    092     * <p>This locates and calls a constructor.
    093     * The constructor signature must match the parameter types by assignment compatibility.</p>
    094     *
    095     * @param <T> the type to be constructed
    096     * @param cls  the class to be constructed, not {@code null}
    097     * @param args  the array of arguments, {@code null} treated as empty
    098     * @param parameterTypes  the array of parameter types, {@code null} treated as empty
    099     * @return new instance of {@code cls}, not {@code null}
    100     *
    101     * @throws NullPointerException if {@code cls} is {@code null}
    102     * @throws NoSuchMethodException if a matching constructor cannot be found
    103     * @throws IllegalAccessException if invocation is not permitted by security
    104     * @throws InvocationTargetException if an error occurs on invocation
    105     * @throws InstantiationException if an error occurs on instantiation
    106     * @see Constructor#newInstance
    107     */
    108    public static <T> T invokeConstructor(final Class<T> cls, Object[] args, Class<?>[] parameterTypes)
    109            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
    110            InstantiationException {
    111        args = ArrayUtils.nullToEmpty(args);
    112        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
    113        final Constructor<T> ctor = getMatchingAccessibleConstructor(cls, parameterTypes);
    114        if (ctor == null) {
    115            throw new NoSuchMethodException(
    116                "No such accessible constructor on object: " + cls.getName());
    117        }
    118        if (ctor.isVarArgs()) {
    119            final Class<?>[] methodParameterTypes = ctor.getParameterTypes();
    120            args = MethodUtils.getVarArgs(args, methodParameterTypes);
    121        }
    122        return ctor.newInstance(args);
    123    }
    124
    125    /**
    126     * <p>Returns a new instance of the specified class inferring the right constructor
    127     * from the types of the arguments.</p>
    128     *
    129     * <p>This locates and calls a constructor.
    130     * The constructor signature must match the argument types exactly.</p>
    131     *
    132     * @param <T> the type to be constructed
    133     * @param cls the class to be constructed, not {@code null}
    134     * @param args the array of arguments, {@code null} treated as empty
    135     * @return new instance of {@code cls}, not {@code null}
    136     *
    137     * @throws NullPointerException if {@code cls} is {@code null}
    138     * @throws NoSuchMethodException if a matching constructor cannot be found
    139     * @throws IllegalAccessException if invocation is not permitted by security
    140     * @throws InvocationTargetException if an error occurs on invocation
    141     * @throws InstantiationException if an error occurs on instantiation
    142     * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
    143     */
    144    public static <T> T invokeExactConstructor(final Class<T> cls, Object... args)
    145            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
    146            InstantiationException {
    147        args = ArrayUtils.nullToEmpty(args);
    148        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
    149        return invokeExactConstructor(cls, args, parameterTypes);
    150    }
    151
    152    /**
    153     * <p>Returns a new instance of the specified class choosing the right constructor
    154     * from the list of parameter types.</p>
    155     *
    156     * <p>This locates and calls a constructor.
    157     * The constructor signature must match the parameter types exactly.</p>
    158     *
    159     * @param <T> the type to be constructed
    160     * @param cls the class to be constructed, not {@code null}
    161     * @param args the array of arguments, {@code null} treated as empty
    162     * @param parameterTypes  the array of parameter types, {@code null} treated as empty
    163     * @return new instance of {@code cls}, not {@code null}
    164     *
    165     * @throws NullPointerException if {@code cls} is {@code null}
    166     * @throws NoSuchMethodException if a matching constructor cannot be found
    167     * @throws IllegalAccessException if invocation is not permitted by security
    168     * @throws InvocationTargetException if an error occurs on invocation
    169     * @throws InstantiationException if an error occurs on instantiation
    170     * @see Constructor#newInstance
    171     */
    172    public static <T> T invokeExactConstructor(final Class<T> cls, Object[] args,
    173            Class<?>[] parameterTypes) throws NoSuchMethodException, IllegalAccessException,
    174            InvocationTargetException, InstantiationException {
    175        args = ArrayUtils.nullToEmpty(args);
    176        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
    177        final Constructor<T> ctor = getAccessibleConstructor(cls, parameterTypes);
    178        if (ctor == null) {
    179            throw new NoSuchMethodException(
    180                "No such accessible constructor on object: "+ cls.getName());
    181        }
    182        return ctor.newInstance(args);
    183    }
    184
    185    //-----------------------------------------------------------------------
    186    /**
    187     * <p>Finds a constructor given a class and signature, checking accessibility.</p>
    188     *
    189     * <p>This finds the constructor and ensures that it is accessible.
    190     * The constructor signature must match the parameter types exactly.</p>
    191     *
    192     * @param <T> the constructor type
    193     * @param cls the class to find a constructor for, not {@code null}
    194     * @param parameterTypes the array of parameter types, {@code null} treated as empty
    195     * @return the constructor, {@code null} if no matching accessible constructor found
    196     * @see Class#getConstructor
    197     * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
    198     * @throws NullPointerException if {@code cls} is {@code null}
    199     */
    200    public static <T> Constructor<T> getAccessibleConstructor(final Class<T> cls,
    201            final Class<?>... parameterTypes) {
    202        Validate.notNull(cls, "class cannot be null");
    203        try {
    204            return getAccessibleConstructor(cls.getConstructor(parameterTypes));
    205        } catch (final NoSuchMethodException e) {
    206            return null;
    207        }
    208    }
    209
    210    /**
    211     * <p>Checks if the specified constructor is accessible.</p>
    212     *
    213     * <p>This simply ensures that the constructor is accessible.</p>
    214     *
    215     * @param <T> the constructor type
    216     * @param ctor  the prototype constructor object, not {@code null}
    217     * @return the constructor, {@code null} if no matching accessible constructor found
    218     * @see java.lang.SecurityManager
    219     * @throws NullPointerException if {@code ctor} is {@code null}
    220     */
    221    public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) {
    222        Validate.notNull(ctor, "constructor cannot be null");
    223        return MemberUtils.isAccessible(ctor)
    224                && isAccessible(ctor.getDeclaringClass()) ? ctor : null;
    225    }
    226
    227    /**
    228     * <p>Finds an accessible constructor with compatible parameters.</p>
    229     *
    230     * <p>This checks all the constructor and finds one with compatible parameters
    231     * This requires that every parameter is assignable from the given parameter types.
    232     * This is a more flexible search than the normal exact matching algorithm.</p>
    233     *
    234     * <p>First it checks if there is a constructor matching the exact signature.
    235     * If not then all the constructors of the class are checked to see if their
    236     * signatures are assignment-compatible with the parameter types.
    237     * The first assignment-compatible matching constructor is returned.</p>
    238     *
    239     * @param <T> the constructor type
    240     * @param cls  the class to find a constructor for, not {@code null}
    241     * @param parameterTypes find method with compatible parameters
    242     * @return the constructor, null if no matching accessible constructor found
    243     * @throws NullPointerException if {@code cls} is {@code null}
    244     */
    245    public static <T> Constructor<T> getMatchingAccessibleConstructor(final Class<T> cls,
    246            final Class<?>... parameterTypes) {
    247        Validate.notNull(cls, "class cannot be null");
    248        // see if we can find the constructor directly
    249        // most of the time this works and it's much faster
    250        try {
    251            final Constructor<T> ctor = cls.getConstructor(parameterTypes);
    252            MemberUtils.setAccessibleWorkaround(ctor);
    253            return ctor;
    254        } catch (final NoSuchMethodException e) { // NOPMD - Swallow
    255        }
    256        Constructor<T> result = null;
    257        /*
    258         * (1) Class.getConstructors() is documented to return Constructor<T> so as
    259         * long as the array is not subsequently modified, everything's fine.
    260         */
    261        final Constructor<?>[] ctors = cls.getConstructors();
    262
    263        // return best match:
    264        for (Constructor<?> ctor : ctors) {
    265            // compare parameters
    266            if (MemberUtils.isMatchingConstructor(ctor, parameterTypes)) {
    267                // get accessible version of constructor
    268                ctor = getAccessibleConstructor(ctor);
    269                if (ctor != null) {
    270                    MemberUtils.setAccessibleWorkaround(ctor);
    271                    if (result == null || MemberUtils.compareConstructorFit(ctor, result, parameterTypes) < 0) {
    272                        // temporary variable for annotation, see comment above (1)
    273                        @SuppressWarnings("unchecked")
    274                        final
    275                        Constructor<T> constructor = (Constructor<T>) ctor;
    276                        result = constructor;
    277                    }
    278                }
    279            }
    280        }
    281        return result;
    282    }
    283
    284    /**
    285     * Learn whether the specified class is generally accessible, i.e. is
    286     * declared in an entirely {@code public} manner.
    287     * @param type to check
    288     * @return {@code true} if {@code type} and any enclosing classes are
    289     *         {@code public}.
    290     */
    291    private static boolean isAccessible(final Class<?> type) {
    292        Class<?> cls = type;
    293        while (cls != null) {
    294            if (!Modifier.isPublic(cls.getModifiers())) {
    295                return false;
    296            }
    297            cls = cls.getEnclosingClass();
    298        }
    299        return true;
    300    }
    301
    302}