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     */
017    package org.apache.commons.lang3.reflect;
018    
019    import java.lang.reflect.Constructor;
020    import java.lang.reflect.InvocationTargetException;
021    import java.lang.reflect.Modifier;
022    
023    import org.apache.commons.lang3.ArrayUtils;
024    import org.apache.commons.lang3.ClassUtils;
025    
026    /**
027     * <p> Utility reflection methods focussed on constructors, modelled after {@link MethodUtils}. </p>
028     *
029     * <h3>Known Limitations</h3>
030     * <h4>Accessing Public Constructors In A Default Access Superclass</h4>
031     * <p>There is an issue when invoking public constructors contained in a default access superclass.
032     * Reflection locates these constructors fine and correctly assigns them as public.
033     * However, an <code>IllegalAccessException</code> is thrown if the constructors is invoked.</p>
034     *
035     * <p><code>ConstructorUtils</code> contains a workaround for this situation.
036     * It will attempt to call <code>setAccessible</code> on this constructor.
037     * If this call succeeds, then the method can be invoked as normal.
038     * This call will only succeed when the application has sufficient security privilages.
039     * If this call fails then a warning will be logged and the method may fail.</p>
040     *
041     * @author Apache Software Foundation
042     * @author Craig R. McClanahan
043     * @author Ralph Schaer
044     * @author Chris Audley
045     * @author Rey Francois
046     * @author Gregor Rayman
047     * @author Jan Sorensen
048     * @author Robert Burrell Donkin
049     * @author Rodney Waldhoff
050     * @since 2.5
051     * @version $Id: ConstructorUtils.java 925970 2010-03-22 06:22:28Z bayard $
052     */
053    public class ConstructorUtils {
054    
055        /**
056         * <p>ConstructorUtils instances should NOT be constructed in standard programming.
057         * Instead, the class should be used as
058         * <code>ConstructorUtils.invokeConstructor(cls, args)</code>.</p>
059         *
060         * <p>This constructor is public to permit tools that require a JavaBean
061         * instance to operate.</p>
062         */
063        public ConstructorUtils() {
064            super();
065        }
066    
067        /**
068         * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
069         * The formal parameter types are inferred from the actual values of <code>args</code>.
070         * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
071         *
072         * <p>The signatures should be assignment compatible.</p>
073         *
074         * @param cls the class to be constructed.
075         * @param args actual argument array
076         * @return new instance of <code>klazz</code>
077         *
078         * @throws NoSuchMethodException If the constructor cannot be found
079         * @throws IllegalAccessException If an error occurs accessing the constructor
080         * @throws InvocationTargetException If an error occurs invoking the constructor
081         * @throws InstantiationException If an error occurs instantiating the class
082         *
083         * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
084         */
085        public static <T> T invokeConstructor(Class<T> cls, Object... args)
086                throws NoSuchMethodException, IllegalAccessException,
087                InvocationTargetException, InstantiationException {
088            if (null == args) {
089                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
090            }
091            Class<?> parameterTypes[] = new Class[args.length];
092            for (int i = 0; i < args.length; i++) {
093                parameterTypes[i] = args[i].getClass();
094            }
095            return invokeConstructor(cls, args, parameterTypes);
096        }
097    
098        /**
099         * <p>Returns new instance of <code>klazz</code> created using constructor
100         * with signature <code>parameterTypes</code> and actual arguments <code>args</code>.</p>
101         *
102         * <p>The signatures should be assignment compatible.</p>
103         *
104         * @param cls the class to be constructed.
105         * @param args actual argument array
106         * @param parameterTypes parameter types array
107         * @return new instance of <code>klazz</code>
108         *
109         * @throws NoSuchMethodException if matching constructor cannot be found
110         * @throws IllegalAccessException thrown on the constructor's invocation
111         * @throws InvocationTargetException thrown on the constructor's invocation
112         * @throws InstantiationException thrown on the constructor's invocation
113         * @see Constructor#newInstance
114         */
115        public static <T> T invokeConstructor(Class<T> cls, Object[] args,
116                Class<?>[] parameterTypes) throws NoSuchMethodException,
117                IllegalAccessException, InvocationTargetException,
118                InstantiationException {
119            if (parameterTypes == null) {
120                parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
121            }
122            if (args == null) {
123                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
124            }
125            Constructor<T> ctor = getMatchingAccessibleConstructor(cls, parameterTypes);
126            if (null == ctor) {
127                throw new NoSuchMethodException(
128                        "No such accessible constructor on object: "
129                                + cls.getName());
130            }
131            return ctor.newInstance(args);
132        }
133    
134        /**
135         * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
136         * The formal parameter types are inferred from the actual values of <code>args</code>.
137         * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
138         *
139         * <p>The signatures should match exactly.</p>
140         *
141         * @param cls the class to be constructed.
142         * @param args actual argument array
143         * @return new instance of <code>klazz</code>
144         *
145         * @throws NoSuchMethodException If the constructor cannot be found
146         * @throws IllegalAccessException If an error occurs accessing the constructor
147         * @throws InvocationTargetException If an error occurs invoking the constructor
148         * @throws InstantiationException If an error occurs instantiating the class
149         *
150         * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
151         */
152        public static <T> T invokeExactConstructor(Class<T> cls, Object... args)
153                throws NoSuchMethodException, IllegalAccessException,
154                InvocationTargetException, InstantiationException {
155            if (null == args) {
156                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
157            }
158            int arguments = args.length;
159            Class<?> parameterTypes[] = new Class[arguments];
160            for (int i = 0; i < arguments; i++) {
161                parameterTypes[i] = args[i].getClass();
162            }
163            return invokeExactConstructor(cls, args, parameterTypes);
164        }
165    
166        /**
167         * <p>Returns new instance of <code>klazz</code> created using constructor
168         * with signature <code>parameterTypes</code> and actual arguments
169         * <code>args</code>.</p>
170         *
171         * <p>The signatures should match exactly.</p>
172         *
173         * @param cls the class to be constructed.
174         * @param args actual argument array
175         * @param parameterTypes parameter types array
176         * @return new instance of <code>klazz</code>
177         *
178         * @throws NoSuchMethodException if matching constructor cannot be found
179         * @throws IllegalAccessException thrown on the constructor's invocation
180         * @throws InvocationTargetException thrown on the constructor's invocation
181         * @throws InstantiationException thrown on the constructor's invocation
182         * @see Constructor#newInstance
183         */
184        public static <T> T invokeExactConstructor(Class<T> cls, Object[] args,
185                Class<?>[] parameterTypes) throws NoSuchMethodException,
186                IllegalAccessException, InvocationTargetException,
187                InstantiationException {
188            if (args == null) {
189                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
190            }
191            if (parameterTypes == null) {
192                parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
193            }
194            Constructor<T> ctor = getAccessibleConstructor(cls, parameterTypes);
195            if (null == ctor) {
196                throw new NoSuchMethodException(
197                        "No such accessible constructor on object: "
198                                + cls.getName());
199            }
200            return ctor.newInstance(args);
201        }
202    
203        /**
204         * Returns a constructor given a class and signature.
205         * @param cls the class to be constructed
206         * @param parameterTypes the parameter array
207         * @return null if matching accessible constructor can not be found
208         * @see Class#getConstructor
209         * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
210         */
211        public static <T> Constructor<T> getAccessibleConstructor(Class<T> cls,
212                Class<?>... parameterTypes) {
213            try {
214                return getAccessibleConstructor(cls.getConstructor(parameterTypes));
215            } catch (NoSuchMethodException e) {
216                return (null);
217            }
218        }
219    
220        /**
221         * Returns accessible version of the given constructor.
222         * @param ctor prototype constructor object.
223         * @return <code>null</code> if accessible constructor can not be found.
224         * @see java.lang.SecurityManager
225         */
226        public static <T> Constructor<T> getAccessibleConstructor(Constructor<T> ctor) {
227            return MemberUtils.isAccessible(ctor)
228                    && Modifier.isPublic(ctor.getDeclaringClass().getModifiers()) ? ctor
229                    : null;
230        }
231    
232        /**
233         * <p>Find an accessible constructor with compatible parameters.
234         * Compatible parameters mean that every method parameter is assignable from
235         * the given parameters. In other words, it finds constructor that will take
236         * the parameters given.</p>
237         *
238         * <p>First it checks if there is constructor matching the exact signature.
239         * If no such, all the constructors of the class are tested if their signatures
240         * are assignment compatible with the parameter types.
241         * The first matching constructor is returned.</p>
242         *
243         * @param cls find constructor for this class
244         * @param parameterTypes find method with compatible parameters
245         * @return a valid Constructor object. If there's no matching constructor, returns <code>null</code>.
246         */
247        @SuppressWarnings("unchecked")
248        public static <T> Constructor<T> getMatchingAccessibleConstructor(Class<T> cls,
249                Class<?>... parameterTypes) {
250            // see if we can find the constructor directly
251            // most of the time this works and it's much faster
252            try {
253                Constructor<T> ctor = cls.getConstructor(parameterTypes);
254                MemberUtils.setAccessibleWorkaround(ctor);
255                return ctor;
256            } catch (NoSuchMethodException e) { /* SWALLOW */
257            }
258            Constructor<T> result = null;
259            // search through all constructors
260            Constructor<?>[] ctors = cls.getConstructors();
261            for (int i = 0; i < ctors.length; i++) {
262                // compare parameters
263                if (ClassUtils.isAssignable(parameterTypes, ctors[i]
264                        .getParameterTypes(), true)) {
265                    // get accessible version of method
266                    Constructor<T> ctor = getAccessibleConstructor((Constructor<T>) ctors[i]);
267                    if (ctor != null) {
268                        MemberUtils.setAccessibleWorkaround(ctor);
269                        if (result == null
270                                || MemberUtils.compareParameterTypes(ctor
271                                        .getParameterTypes(), result
272                                        .getParameterTypes(), parameterTypes) < 0) {
273                            result = ctor;
274                        }
275                    }
276                }
277            }
278            return result;
279        }
280    
281    }