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.lang.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.lang.ArrayUtils;
024    import org.apache.commons.lang.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 905636 2010-02-02 14:03:32Z niallp $
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>Convenience method returning new instance of <code>klazz</code> using a single argument constructor.
069         * The formal parameter type is inferred from the actual values of <code>arg</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 arg the actual argument
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 Object invokeConstructor(Class cls, Object arg)
086                throws NoSuchMethodException, IllegalAccessException,
087                InvocationTargetException, InstantiationException {
088            return invokeConstructor(cls, new Object[] { arg });
089        }
090    
091        /**
092         * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
093         * The formal parameter types are inferred from the actual values of <code>args</code>.
094         * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
095         *
096         * <p>The signatures should be assignment compatible.</p>
097         *
098         * @param cls the class to be constructed.
099         * @param args actual argument array
100         * @return new instance of <code>klazz</code>
101         *
102         * @throws NoSuchMethodException If the constructor cannot be found
103         * @throws IllegalAccessException If an error occurs accessing the constructor
104         * @throws InvocationTargetException If an error occurs invoking the constructor
105         * @throws InstantiationException If an error occurs instantiating the class
106         *
107         * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
108         */
109        public static Object invokeConstructor(Class cls, Object[] args)
110                throws NoSuchMethodException, IllegalAccessException,
111                InvocationTargetException, InstantiationException {
112            if (null == args) {
113                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
114            }
115            Class parameterTypes[] = new Class[args.length];
116            for (int i = 0; i < args.length; i++) {
117                parameterTypes[i] = args[i].getClass();
118            }
119            return invokeConstructor(cls, args, parameterTypes);
120        }
121    
122        /**
123         * <p>Returns new instance of <code>klazz</code> created using constructor
124         * with signature <code>parameterTypes</code> and actual arguments <code>args</code>.</p>
125         *
126         * <p>The signatures should be assignment compatible.</p>
127         *
128         * @param cls the class to be constructed.
129         * @param args actual argument array
130         * @param parameterTypes parameter types array
131         * @return new instance of <code>klazz</code>
132         *
133         * @throws NoSuchMethodException if matching constructor cannot be found
134         * @throws IllegalAccessException thrown on the constructor's invocation
135         * @throws InvocationTargetException thrown on the constructor's invocation
136         * @throws InstantiationException thrown on the constructor's invocation
137         * @see Constructor#newInstance
138         */
139        public static Object invokeConstructor(Class cls, Object[] args,
140                Class[] parameterTypes) throws NoSuchMethodException,
141                IllegalAccessException, InvocationTargetException,
142                InstantiationException {
143            if (parameterTypes == null) {
144                parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
145            }
146            if (args == null) {
147                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
148            }
149            Constructor ctor = getMatchingAccessibleConstructor(cls, parameterTypes);
150            if (null == ctor) {
151                throw new NoSuchMethodException(
152                        "No such accessible constructor on object: "
153                                + cls.getName());
154            }
155            return ctor.newInstance(args);
156        }
157    
158        /**
159         * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor.
160         * The formal parameter type is inferred from the actual values of <code>arg</code>.
161         * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
162         *
163         * <p>The signatures should match exactly.</p>
164         *
165         * @param cls the class to be constructed.
166         * @param arg the actual argument
167         * @return new instance of <code>klazz</code>
168         *
169         * @throws NoSuchMethodException If the constructor cannot be found
170         * @throws IllegalAccessException If an error occurs accessing the constructor
171         * @throws InvocationTargetException If an error occurs invoking the constructor
172         * @throws InstantiationException If an error occurs instantiating the class
173         *
174         * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
175         */
176        public static Object invokeExactConstructor(Class cls, Object arg)
177                throws NoSuchMethodException, IllegalAccessException,
178                InvocationTargetException, InstantiationException {
179            return invokeExactConstructor(cls, new Object[] { arg });
180        }
181    
182        /**
183         * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
184         * The formal parameter types are inferred from the actual values of <code>args</code>.
185         * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
186         *
187         * <p>The signatures should match exactly.</p>
188         *
189         * @param cls the class to be constructed.
190         * @param args actual argument array
191         * @return new instance of <code>klazz</code>
192         *
193         * @throws NoSuchMethodException If the constructor cannot be found
194         * @throws IllegalAccessException If an error occurs accessing the constructor
195         * @throws InvocationTargetException If an error occurs invoking the constructor
196         * @throws InstantiationException If an error occurs instantiating the class
197         *
198         * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
199         */
200        public static Object invokeExactConstructor(Class cls, Object[] args)
201                throws NoSuchMethodException, IllegalAccessException,
202                InvocationTargetException, InstantiationException {
203            if (null == args) {
204                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
205            }
206            int arguments = args.length;
207            Class parameterTypes[] = new Class[arguments];
208            for (int i = 0; i < arguments; i++) {
209                parameterTypes[i] = args[i].getClass();
210            }
211            return invokeExactConstructor(cls, args, parameterTypes);
212        }
213    
214        /**
215         * <p>Returns new instance of <code>klazz</code> created using constructor
216         * with signature <code>parameterTypes</code> and actual arguments
217         * <code>args</code>.</p>
218         *
219         * <p>The signatures should match exactly.</p>
220         *
221         * @param cls the class to be constructed.
222         * @param args actual argument array
223         * @param parameterTypes parameter types array
224         * @return new instance of <code>klazz</code>
225         *
226         * @throws NoSuchMethodException if matching constructor cannot be found
227         * @throws IllegalAccessException thrown on the constructor's invocation
228         * @throws InvocationTargetException thrown on the constructor's invocation
229         * @throws InstantiationException thrown on the constructor's invocation
230         * @see Constructor#newInstance
231         */
232        public static Object invokeExactConstructor(Class cls, Object[] args,
233                Class[] parameterTypes) throws NoSuchMethodException,
234                IllegalAccessException, InvocationTargetException,
235                InstantiationException {
236            if (args == null) {
237                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
238            }
239            if (parameterTypes == null) {
240                parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
241            }
242            Constructor ctor = getAccessibleConstructor(cls, parameterTypes);
243            if (null == ctor) {
244                throw new NoSuchMethodException(
245                        "No such accessible constructor on object: "
246                                + cls.getName());
247            }
248            return ctor.newInstance(args);
249        }
250    
251        /**
252         * Returns a constructor with single argument.
253         * @param cls the class to be constructed
254         * @param parameterType The constructor parameter type
255         * @return null if matching accessible constructor can not be found.
256         * @see Class#getConstructor
257         * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
258         */
259        public static Constructor getAccessibleConstructor(Class cls,
260                Class parameterType) {
261            return getAccessibleConstructor(cls, new Class[] { parameterType });
262        }
263    
264        /**
265         * Returns a constructor given a class and signature.
266         * @param cls the class to be constructed
267         * @param parameterTypes the parameter array
268         * @return null if matching accessible constructor can not be found
269         * @see Class#getConstructor
270         * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
271         */
272        public static Constructor getAccessibleConstructor(Class cls,
273                Class[] parameterTypes) {
274            try {
275                return getAccessibleConstructor(cls.getConstructor(parameterTypes));
276            } catch (NoSuchMethodException e) {
277                return (null);
278            }
279        }
280    
281        /**
282         * Returns accessible version of the given constructor.
283         * @param ctor prototype constructor object.
284         * @return <code>null</code> if accessible constructor can not be found.
285         * @see java.lang.SecurityManager
286         */
287        public static Constructor getAccessibleConstructor(Constructor ctor) {
288            return MemberUtils.isAccessible(ctor)
289                    && Modifier.isPublic(ctor.getDeclaringClass().getModifiers()) ? ctor
290                    : null;
291        }
292    
293        /**
294         * <p>Find an accessible constructor with compatible parameters.
295         * Compatible parameters mean that every method parameter is assignable from
296         * the given parameters. In other words, it finds constructor that will take
297         * the parameters given.</p>
298         *
299         * <p>First it checks if there is constructor matching the exact signature.
300         * If no such, all the constructors of the class are tested if their signatures
301         * are assignment compatible with the parameter types.
302         * The first matching constructor is returned.</p>
303         *
304         * @param cls find constructor for this class
305         * @param parameterTypes find method with compatible parameters
306         * @return a valid Constructor object. If there's no matching constructor, returns <code>null</code>.
307         */
308        public static Constructor getMatchingAccessibleConstructor(Class cls,
309                Class[] parameterTypes) {
310            // see if we can find the constructor directly
311            // most of the time this works and it's much faster
312            try {
313                Constructor ctor = cls.getConstructor(parameterTypes);
314                MemberUtils.setAccessibleWorkaround(ctor);
315                return ctor;
316            } catch (NoSuchMethodException e) { /* SWALLOW */
317            }
318            Constructor result = null;
319            // search through all constructors
320            Constructor[] ctors = cls.getConstructors();
321            for (int i = 0; i < ctors.length; i++) {
322                // compare parameters
323                if (ClassUtils.isAssignable(parameterTypes, ctors[i]
324                        .getParameterTypes(), true)) {
325                    // get accessible version of method
326                    Constructor ctor = getAccessibleConstructor(ctors[i]);
327                    if (ctor != null) {
328                        MemberUtils.setAccessibleWorkaround(ctor);
329                        if (result == null
330                                || MemberUtils.compareParameterTypes(ctor
331                                        .getParameterTypes(), result
332                                        .getParameterTypes(), parameterTypes) < 0) {
333                            result = ctor;
334                        }
335                    }
336                }
337            }
338            return result;
339        }
340    
341    }