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 focused on constructors, modeled 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 privileges. If this call
041     * fails then a warning will be logged and the method may fail.</p>
042     *
043     * @since 2.5
044     * @version $Id: ConstructorUtils.java 1144010 2011-07-07 20:02:10Z joehni $
045     */
046    public 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)</code>.</p>
052         *
053         * <p>This constructor is 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 null
069         * @param args  the array of arguments, null treated as empty
070         * @return new instance of <code>cls</code>, not null
071         *
072         * @throws NoSuchMethodException if a matching constructor cannot be found
073         * @throws IllegalAccessException if invocation is not permitted by security
074         * @throws InvocationTargetException if an error occurs on invocation
075         * @throws InstantiationException if an error occurs on instantiation
076         * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
077         */
078        public static <T> T invokeConstructor(Class<T> cls, Object... args)
079                throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
080                InstantiationException {
081            if (args == null) {
082                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
083            }
084            Class<?> parameterTypes[] = new Class[args.length];
085            for (int i = 0; i < args.length; i++) {
086                parameterTypes[i] = args[i].getClass();
087            }
088            return invokeConstructor(cls, args, parameterTypes);
089        }
090    
091        /**
092         * <p>Returns a new instance of the specified class choosing the right constructor
093         * from the list of parameter types.</p>
094         * 
095         * <p>This locates and calls a constructor.
096         * The constructor signature must match the parameter types by assignment compatibility.</p>
097         *
098         * @param <T> the type to be constructed
099         * @param cls  the class to be constructed, not null
100         * @param args  the array of arguments, null treated as empty
101         * @param parameterTypes  the array of parameter types, null treated as empty
102         * @return new instance of <code>cls</code>, not null
103         *
104         * @throws NoSuchMethodException if a matching constructor cannot be found
105         * @throws IllegalAccessException if invocation is not permitted by security
106         * @throws InvocationTargetException if an error occurs on invocation
107         * @throws InstantiationException if an error occurs on instantiation
108         * @see Constructor#newInstance
109         */
110        public static <T> T invokeConstructor(Class<T> cls, Object[] args, Class<?>[] parameterTypes)
111                throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
112                InstantiationException {
113            if (parameterTypes == null) {
114                parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
115            }
116            if (args == null) {
117                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
118            }
119            Constructor<T> ctor = getMatchingAccessibleConstructor(cls, parameterTypes);
120            if (ctor == null) {
121                throw new NoSuchMethodException(
122                    "No such accessible constructor on object: " + cls.getName());
123            }
124            return ctor.newInstance(args);
125        }
126    
127        /**
128         * <p>Returns a new instance of the specified class inferring the right constructor
129         * from the types of the arguments.</p>
130         *
131         * <p>This locates and calls a constructor.
132         * The constructor signature must match the argument types exactly.</p>
133         *
134         * @param <T> the type to be constructed
135         * @param cls  the class to be constructed, not null
136         * @param args  the array of arguments, null treated as empty
137         * @return new instance of <code>cls</code>, not null
138         *
139         * @throws NoSuchMethodException if a matching constructor cannot be found
140         * @throws IllegalAccessException if invocation is not permitted by security
141         * @throws InvocationTargetException if an error occurs on invocation
142         * @throws InstantiationException if an error occurs on instantiation
143         * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
144         */
145        public static <T> T invokeExactConstructor(Class<T> cls, Object... args)
146                throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
147                InstantiationException {
148            if (args == null) {
149                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
150            }
151            int arguments = args.length;
152            Class<?> parameterTypes[] = new Class[arguments];
153            for (int i = 0; i < arguments; i++) {
154                parameterTypes[i] = args[i].getClass();
155            }
156            return invokeExactConstructor(cls, args, parameterTypes);
157        }
158    
159        /**
160         * <p>Returns a new instance of the specified class choosing the right constructor
161         * from the list of parameter types.</p>
162         *
163         * <p>This locates and calls a constructor.
164         * The constructor signature must match the parameter types exactly.</p>
165         *
166         * @param <T> the type to be constructed
167         * @param cls  the class to be constructed, not null
168         * @param args  the array of arguments, null treated as empty
169         * @param parameterTypes  the array of parameter types, null treated as empty
170         * @return new instance of <code>cls</code>, not null
171         *
172         * @throws NoSuchMethodException if a matching constructor cannot be found
173         * @throws IllegalAccessException if invocation is not permitted by security
174         * @throws InvocationTargetException if an error occurs on invocation
175         * @throws InstantiationException if an error occurs on instantiation
176         * @see Constructor#newInstance
177         */
178        public static <T> T invokeExactConstructor(Class<T> cls, Object[] args,
179                Class<?>[] parameterTypes) throws NoSuchMethodException, IllegalAccessException,
180                InvocationTargetException, InstantiationException {
181            if (args == null) {
182                args = ArrayUtils.EMPTY_OBJECT_ARRAY;
183            }
184            if (parameterTypes == null) {
185                parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
186            }
187            Constructor<T> ctor = getAccessibleConstructor(cls, parameterTypes);
188            if (ctor == null) {
189                throw new NoSuchMethodException(
190                    "No such accessible constructor on object: "+ cls.getName());
191            }
192            return ctor.newInstance(args);
193        }
194    
195        //-----------------------------------------------------------------------
196        /**
197         * <p>Finds a constructor given a class and signature, checking accessibility.</p>
198         * 
199         * <p>This finds the constructor and ensures that it is accessible.
200         * The constructor signature must match the parameter types exactly.</p>
201         *
202         * @param <T> the constructor type
203         * @param cls  the class to find a constructor for, not null
204         * @param parameterTypes  the array of parameter types, null treated as empty
205         * @return the constructor, null if no matching accessible constructor found
206         * @see Class#getConstructor
207         * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
208         */
209        public static <T> Constructor<T> getAccessibleConstructor(Class<T> cls,
210                Class<?>... parameterTypes) {
211            try {
212                return getAccessibleConstructor(cls.getConstructor(parameterTypes));
213            } catch (NoSuchMethodException e) {
214                return null;
215            }
216        }
217    
218        /**
219         * <p>Checks if the specified constructor is accessible.</p>
220         * 
221         * <p>This simply ensures that the constructor is accessible.</p>
222         *
223         * @param <T> the constructor type
224         * @param ctor  the prototype constructor object, not null
225         * @return the constructor, null if no matching accessible constructor found
226         * @see java.lang.SecurityManager
227         */
228        public static <T> Constructor<T> getAccessibleConstructor(Constructor<T> ctor) {
229            return MemberUtils.isAccessible(ctor)
230                    && Modifier.isPublic(ctor.getDeclaringClass().getModifiers()) ? ctor : null;
231        }
232    
233        /**
234         * <p>Finds an accessible constructor with compatible parameters.</p>
235         * 
236         * <p>This checks all the constructor and finds one with compatible parameters
237         * This requires that every parameter is assignable from the given parameter types.
238         * This is a more flexible search than the normal exact matching algorithm.</p>
239         *
240         * <p>First it checks if there is a constructor matching the exact signature.
241         * If not then all the constructors of the class are checked to see if their
242         * signatures are assignment compatible with the parameter types.
243         * The first assignment compatible matching constructor is returned.</p>
244         *
245         * @param <T> the constructor type
246         * @param cls  the class to find a constructor for, not null
247         * @param parameterTypes find method with compatible parameters
248         * @return the constructor, null if no matching accessible constructor found
249         */
250        public static <T> Constructor<T> getMatchingAccessibleConstructor(Class<T> cls,
251                Class<?>... parameterTypes) {
252            // see if we can find the constructor directly
253            // most of the time this works and it's much faster
254            try {
255                Constructor<T> ctor = cls.getConstructor(parameterTypes);
256                MemberUtils.setAccessibleWorkaround(ctor);
257                return ctor;
258            } catch (NoSuchMethodException e) { // NOPMD - Swallow
259            }
260            Constructor<T> result = null;
261            /*
262             * (1) Class.getConstructors() is documented to return Constructor<T> so as
263             * long as the array is not subsequently modified, everything's fine.
264             */
265            Constructor<?>[] ctors = cls.getConstructors();
266    
267            // return best match:
268            for (Constructor<?> ctor : ctors) {
269                // compare parameters
270                if (ClassUtils.isAssignable(parameterTypes, ctor.getParameterTypes(), true)) {
271                    // get accessible version of constructor
272                    ctor = getAccessibleConstructor(ctor);
273                    if (ctor != null) {
274                        MemberUtils.setAccessibleWorkaround(ctor);
275                        if (result == null
276                                || MemberUtils.compareParameterTypes(ctor.getParameterTypes(), result
277                                        .getParameterTypes(), parameterTypes) < 0) {
278                            // temporary variable for annotation, see comment above (1)
279                            @SuppressWarnings("unchecked")
280                            Constructor<T> constructor = (Constructor<T>)ctor;
281                            result = constructor;
282                        }
283                    }
284                }
285            }
286            return result;
287        }
288    
289    }