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 * <h3>Known Limitations</h3> <h4>Accessing Public Constructors In A Default
032 * Access Superclass</h4> <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     * <p>ConstructorUtils instances should NOT be constructed in standard
050     * programming. Instead, the class should be used as
051     * {@code ConstructorUtils.invokeConstructor(cls, args)}.</p>
052     *
053     * <p>This constructor is {@code 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 {@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</code>, 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, "class cannot be null");
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, "constructor cannot be null");
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, "class cannot be null");
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}