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