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