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}