1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.lang3.reflect;
18
19 import java.lang.reflect.Constructor;
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Modifier;
22
23 import org.apache.commons.lang3.ArrayUtils;
24 import org.apache.commons.lang3.ClassUtils;
25
26 /**
27 * <p> Utility reflection methods focused on constructors, modeled after
28 * {@link MethodUtils}. </p>
29 *
30 * <h3>Known Limitations</h3> <h4>Accessing Public Constructors In A Default
31 * Access Superclass</h4> <p>There is an issue when invoking public constructors
32 * contained in a default access superclass. Reflection locates these
33 * constructors fine and correctly assigns them as public. However, an
34 * <code>IllegalAccessException</code> is thrown if the constructors is
35 * invoked.</p>
36 *
37 * <p><code>ConstructorUtils</code> contains a workaround for this situation. It
38 * will attempt to call <code>setAccessible</code> on this constructor. If this
39 * call succeeds, then the method can be invoked as normal. This call will only
40 * succeed when the application has sufficient security privileges. If this call
41 * fails then a warning will be logged and the method may fail.</p>
42 *
43 * @since 2.5
44 * @version $Id: ConstructorUtils.java 1436770 2013-01-22 07:09:45Z ggregory $
45 */
46 public class ConstructorUtils {
47
48 /**
49 * <p>ConstructorUtils instances should NOT be constructed in standard
50 * programming. Instead, the class should be used as
51 * <code>ConstructorUtils.invokeConstructor(cls, args)</code>.</p>
52 *
53 * <p>This constructor is public to permit tools that require a JavaBean
54 * instance to operate.</p>
55 */
56 public ConstructorUtils() {
57 super();
58 }
59
60 /**
61 * <p>Returns a new instance of the specified class inferring the right constructor
62 * from the types of the arguments.</p>
63 *
64 * <p>This locates and calls a constructor.
65 * The constructor signature must match the argument types by assignment compatibility.</p>
66 *
67 * @param <T> the type to be constructed
68 * @param cls the class to be constructed, not null
69 * @param args the array of arguments, null treated as empty
70 * @return new instance of <code>cls</code>, not null
71 *
72 * @throws NoSuchMethodException if a matching constructor cannot be found
73 * @throws IllegalAccessException if invocation is not permitted by security
74 * @throws InvocationTargetException if an error occurs on invocation
75 * @throws InstantiationException if an error occurs on instantiation
76 * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
77 */
78 public static <T> T invokeConstructor(final Class<T> cls, Object... args)
79 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
80 InstantiationException {
81 if (args == null) {
82 args = ArrayUtils.EMPTY_OBJECT_ARRAY;
83 }
84 final Class<?> parameterTypes[] = ClassUtils.toClass(args);
85 return invokeConstructor(cls, args, parameterTypes);
86 }
87
88 /**
89 * <p>Returns a new instance of the specified class choosing the right constructor
90 * from the list of parameter types.</p>
91 *
92 * <p>This locates and calls a constructor.
93 * The constructor signature must match the parameter types by assignment compatibility.</p>
94 *
95 * @param <T> the type to be constructed
96 * @param cls the class to be constructed, not null
97 * @param args the array of arguments, null treated as empty
98 * @param parameterTypes the array of parameter types, null treated as empty
99 * @return new instance of <code>cls</code>, not null
100 *
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 if (parameterTypes == null) {
111 parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
112 }
113 if (args == null) {
114 args = ArrayUtils.EMPTY_OBJECT_ARRAY;
115 }
116 final Constructor<T> ctor = getMatchingAccessibleConstructor(cls, parameterTypes);
117 if (ctor == null) {
118 throw new NoSuchMethodException(
119 "No such accessible constructor on object: " + cls.getName());
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 null
133 * @param args the array of arguments, null treated as empty
134 * @return new instance of <code>cls</code>, not null
135 *
136 * @throws NoSuchMethodException if a matching constructor cannot be found
137 * @throws IllegalAccessException if invocation is not permitted by security
138 * @throws InvocationTargetException if an error occurs on invocation
139 * @throws InstantiationException if an error occurs on instantiation
140 * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
141 */
142 public static <T> T invokeExactConstructor(final Class<T> cls, Object... args)
143 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
144 InstantiationException {
145 if (args == null) {
146 args = ArrayUtils.EMPTY_OBJECT_ARRAY;
147 }
148 final Class<?> parameterTypes[] = ClassUtils.toClass(args);
149 return invokeExactConstructor(cls, args, parameterTypes);
150 }
151
152 /**
153 * <p>Returns a new instance of the specified class choosing the right constructor
154 * from the list of parameter types.</p>
155 *
156 * <p>This locates and calls a constructor.
157 * The constructor signature must match the parameter types exactly.</p>
158 *
159 * @param <T> the type to be constructed
160 * @param cls the class to be constructed, not null
161 * @param args the array of arguments, null treated as empty
162 * @param parameterTypes the array of parameter types, null treated as empty
163 * @return new instance of <code>cls</code>, not null
164 *
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 if (args == null) {
175 args = ArrayUtils.EMPTY_OBJECT_ARRAY;
176 }
177 if (parameterTypes == null) {
178 parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
179 }
180 final Constructor<T> ctor = getAccessibleConstructor(cls, parameterTypes);
181 if (ctor == null) {
182 throw new NoSuchMethodException(
183 "No such accessible constructor on object: "+ cls.getName());
184 }
185 return ctor.newInstance(args);
186 }
187
188 //-----------------------------------------------------------------------
189 /**
190 * <p>Finds a constructor given a class and signature, checking accessibility.</p>
191 *
192 * <p>This finds the constructor and ensures that it is accessible.
193 * The constructor signature must match the parameter types exactly.</p>
194 *
195 * @param <T> the constructor type
196 * @param cls the class to find a constructor for, not null
197 * @param parameterTypes the array of parameter types, null treated as empty
198 * @return the constructor, null if no matching accessible constructor found
199 * @see Class#getConstructor
200 * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
201 */
202 public static <T> Constructor<T> getAccessibleConstructor(final Class<T> cls,
203 final Class<?>... parameterTypes) {
204 try {
205 return getAccessibleConstructor(cls.getConstructor(parameterTypes));
206 } catch (final NoSuchMethodException e) {
207 return null;
208 }
209 }
210
211 /**
212 * <p>Checks if the specified constructor is accessible.</p>
213 *
214 * <p>This simply ensures that the constructor is accessible.</p>
215 *
216 * @param <T> the constructor type
217 * @param ctor the prototype constructor object, not null
218 * @return the constructor, null if no matching accessible constructor found
219 * @see java.lang.SecurityManager
220 */
221 public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) {
222 return MemberUtils.isAccessible(ctor)
223 && Modifier.isPublic(ctor.getDeclaringClass().getModifiers()) ? 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 null
240 * @param parameterTypes find method with compatible parameters
241 * @return the constructor, null if no matching accessible constructor found
242 */
243 public static <T> Constructor<T> getMatchingAccessibleConstructor(final Class<T> cls,
244 final Class<?>... parameterTypes) {
245 // see if we can find the constructor directly
246 // most of the time this works and it's much faster
247 try {
248 final Constructor<T> ctor = cls.getConstructor(parameterTypes);
249 MemberUtils.setAccessibleWorkaround(ctor);
250 return ctor;
251 } catch (final NoSuchMethodException e) { // NOPMD - Swallow
252 }
253 Constructor<T> result = null;
254 /*
255 * (1) Class.getConstructors() is documented to return Constructor<T> so as
256 * long as the array is not subsequently modified, everything's fine.
257 */
258 final Constructor<?>[] ctors = cls.getConstructors();
259
260 // return best match:
261 for (Constructor<?> ctor : ctors) {
262 // compare parameters
263 if (ClassUtils.isAssignable(parameterTypes, ctor.getParameterTypes(), true)) {
264 // get accessible version of constructor
265 ctor = getAccessibleConstructor(ctor);
266 if (ctor != null) {
267 MemberUtils.setAccessibleWorkaround(ctor);
268 if (result == null
269 || MemberUtils.compareParameterTypes(ctor.getParameterTypes(), result
270 .getParameterTypes(), 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 }