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 * https://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
18 package org.apache.commons.lang3.reflect;
19
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.InvocationTargetException;
22 import java.util.Objects;
23
24 import org.apache.commons.lang3.ArrayUtils;
25 import org.apache.commons.lang3.ClassUtils;
26
27 /**
28 * Utility reflection methods focused on constructors, modeled after {@link MethodUtils}.
29 *
30 * <h2>Known Limitations</h2>
31 * <h3>Accessing Constructors In A Non-Public Class</h3>
32 * <p>
33 * Constructors in non-public classes (such as package-private classes or classes enclosed in non-public classes) are
34 * not accessible. Methods such as {@link #getAccessibleConstructor(Class, Class[])} and
35 * {@link #getMatchingAccessibleConstructor(Class, Class[])} return {@code null} when invoked on non-public classes.
36 * Consequently, invocation methods such as {@link #invokeConstructor(Class, Object...)} and
37 * {@link #invokeExactConstructor(Class, Object...)} throw a {@link NoSuchMethodException}.
38 * </p>
39 *
40 * @since 2.5
41 */
42 public class ConstructorUtils {
43
44 /**
45 * Gets a constructor given a class and signature, checking accessibility.
46 *
47 * <p>
48 * This finds the constructor and ensures that it is accessible. The constructor signature must match the parameter types exactly.
49 * </p>
50 *
51 * @param <T> the constructor type.
52 * @param cls The class to find a constructor for, not {@code null}.
53 * @param parameterTypes The array of parameter types, {@code null} treated as empty.
54 * @return The constructor, {@code null} if no matching accessible constructor found.
55 * @throws NullPointerException Thrown if {@code cls} is {@code null}.
56 * @throws SecurityException Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class loader
57 * for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the
58 * class.
59 * @see Class#getConstructor
60 * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
61 */
62 public static <T> Constructor<T> getAccessibleConstructor(final Class<T> cls, final Class<?>... parameterTypes) {
63 Objects.requireNonNull(cls, "cls");
64 if (!isAccessible(cls)) {
65 return null;
66 }
67 try {
68 return getAccessibleConstructor(cls.getConstructor(parameterTypes));
69 } catch (final NoSuchMethodException e) {
70 return null;
71 }
72 }
73
74 /**
75 * Gets the specified constructor if it is accessible; otherwise, returns null.
76 *
77 * <p>
78 * This simply ensures that the constructor is accessible.
79 * </p>
80 *
81 * @param <T> the constructor type.
82 * @param ctor The prototype constructor object, not {@code null}.
83 * @return The constructor, {@code null} if no matching accessible constructor found.
84 * @see SecurityManager
85 * @throws NullPointerException Thrown if {@code ctor} is {@code null}.
86 * @throws SecurityException Thrown if a security manager is present and a caller's class loader is not the same as or an ancestor of the class loader
87 * for a class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the class.
88 */
89 public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) {
90 Objects.requireNonNull(ctor, "ctor");
91 return MemberUtils.isAccessible(ctor) && isAccessible(ctor.getDeclaringClass()) ? ctor : null;
92 }
93
94 /**
95 * Gets an accessible constructor with compatible parameters.
96 *
97 * <p>
98 * This checks all the constructor and finds one with compatible parameters This requires that every parameter is assignable from the given parameter types.
99 * This is a more flexible search than the normal exact matching algorithm.
100 * </p>
101 * <p>
102 * First it checks if there is a constructor matching the exact signature. If not then all the constructors of the class are checked to see if their
103 * signatures are assignment-compatible with the parameter types. The first assignment-compatible matching constructor is returned.
104 * </p>
105 *
106 * @param <T> the constructor type.
107 * @param cls The class to find a constructor for, not {@code null}.
108 * @param parameterTypes find method with compatible parameters.
109 * @return The constructor, null if no matching accessible constructor found.
110 * @throws NullPointerException Thrown if {@code cls} is {@code null}
111 * @throws SecurityException Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class loader for the
112 * class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the class.
113 * @see SecurityManager#checkPackageAccess(String)
114 */
115 public static <T> Constructor<T> getMatchingAccessibleConstructor(final Class<T> cls, final Class<?>... parameterTypes) {
116 Objects.requireNonNull(cls, "cls");
117 if (!isAccessible(cls)) {
118 return null;
119 }
120 // see if we can find the constructor directly
121 // most of the time this works and it's much faster
122 try {
123 final Constructor<T> ctor = getAccessibleConstructor(cls.getConstructor(parameterTypes));
124 if (ctor != null) {
125 return MemberUtils.setAccessibleWorkaround(ctor);
126 }
127 } catch (final NoSuchMethodException ignored) {
128 // ignore
129 }
130 Constructor<T> result = null;
131 /*
132 * (1) Class.getConstructors() is documented to return Constructor<T> so as long as the array is not subsequently modified, everything's fine.
133 */
134 final Constructor<?>[] ctors = cls.getConstructors();
135 // return best match:
136 for (Constructor<?> ctor : ctors) {
137 // compare parameters
138 if (MemberUtils.isMatchingConstructor(ctor, parameterTypes)) {
139 // get accessible version of constructor
140 ctor = getAccessibleConstructor(ctor);
141 if (ctor != null) {
142 MemberUtils.setAccessibleWorkaround(ctor);
143 if (result == null || MemberUtils.compareConstructorFit(ctor, result, parameterTypes) < 0) {
144 // temporary variable for annotation, see comment above (1)
145 @SuppressWarnings("unchecked")
146 final Constructor<T> constructor = (Constructor<T>) ctor;
147 result = constructor;
148 }
149 }
150 }
151 }
152 return result;
153 }
154
155 /**
156 * Returns a new instance of the specified class inferring the right constructor from the types of the arguments.
157 *
158 * <p>
159 * This locates and calls a constructor. The constructor signature must match the argument types by assignment compatibility.
160 * </p>
161 *
162 * @param <T> the type to be constructed.
163 * @param cls The class to be constructed, not {@code null}.
164 * @param args The array of arguments, {@code null} treated as empty.
165 * @return new instance of {@code cls}, not {@code null}.
166 * @throws NullPointerException Thrown if {@code cls} is {@code null}.
167 * @throws NoSuchMethodException Thrown if a matching constructor cannot be found.
168 * @throws IllegalAccessException Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is
169 * inaccessible.
170 * @throws IllegalArgumentException Thrown if:
171 * <ul>
172 * <li>the number of actual and formal parameters differ; or</li>
173 * <li>an unwrapping conversion for primitive arguments fails; or</li>
174 * <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a
175 * method invocation conversion; if this constructor pertains to an enum type.</li>
176 * </ul>
177 * @throws InstantiationException Thrown if the class that declares the underlying constructor represents an abstract class.
178 * @throws InvocationTargetException Thrown if the underlying constructor throws an exception.
179 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
180 * @throws SecurityException Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class
181 * loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the
182 * package of the class.
183 * @see #invokeConstructor(Class, Object[], Class[])
184 */
185 public static <T> T invokeConstructor(final Class<T> cls, final Object... args)
186 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
187 final Object[] actuals = ArrayUtils.nullToEmpty(args);
188 return invokeConstructor(cls, actuals, ClassUtils.toClass(actuals));
189 }
190
191 /**
192 * Returns a new instance of the specified class choosing the right constructor from the list of parameter types.
193 *
194 * <p>
195 * This locates and calls a constructor. The constructor signature must match the parameter types by assignment compatibility.
196 * </p>
197 *
198 * @param <T> the type to be constructed.
199 * @param cls The class to be constructed, not {@code null}.
200 * @param args The array of arguments, {@code null} treated as empty.
201 * @param parameterTypes The array of parameter types, {@code null} treated as empty.
202 * @return new instance of {@code cls}, not {@code null}
203 * @throws NullPointerException Thrown if {@code cls} is {@code null}.
204 * @throws NoSuchMethodException Thrown if a matching constructor cannot be found.
205 * @throws IllegalAccessException Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is
206 * inaccessible.
207 * @throws IllegalArgumentException Thrown if:
208 * <ul>
209 * <li>the number of actual and formal parameters differ; or</li>
210 * <li>an unwrapping conversion for primitive arguments fails; or</li>
211 * <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a
212 * method invocation conversion; if this constructor pertains to an enum type.</li>
213 * </ul>
214 * @throws InstantiationException Thrown if the class that declares the underlying constructor represents an abstract class.
215 * @throws InvocationTargetException Thrown if the underlying constructor throws an exception.
216 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
217 * @throws SecurityException Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class
218 * loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the
219 * package of the class.
220 * @see Constructor#newInstance(Object...)
221 * @see Constructor#newInstance
222 */
223 public static <T> T invokeConstructor(final Class<T> cls, final Object[] args, final Class<?>[] parameterTypes)
224 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
225 final Object[] actuals = ArrayUtils.nullToEmpty(args);
226 final Constructor<T> ctor = getMatchingAccessibleConstructor(cls, ArrayUtils.nullToEmpty(parameterTypes));
227 if (ctor == null) {
228 throw new NoSuchMethodException("No such accessible constructor on object: " + cls.getName());
229 }
230 return ctor.newInstance(MethodUtils.toVarArgs(ctor, actuals));
231 }
232
233 /**
234 * Returns a new instance of the specified class inferring the right constructor from the types of the arguments.
235 *
236 * <p>
237 * This locates and calls a constructor. The constructor signature must match the argument types exactly.
238 * </p>
239 *
240 * @param <T> the type to be constructed.
241 * @param cls The class to be constructed, not {@code null}.
242 * @param args The array of arguments, {@code null} treated as empty.
243 * @return new instance of {@code cls}, not {@code null}.
244 * @throws NullPointerException Thrown if {@code cls} is {@code null}.
245 * @throws NoSuchMethodException Thrown if a matching constructor cannot be found.
246 * @throws IllegalAccessException Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is
247 * inaccessible.
248 * @throws IllegalArgumentException Thrown if:
249 * <ul>
250 * <li>the number of actual and formal parameters differ; or</li>
251 * <li>an unwrapping conversion for primitive arguments fails; or</li>
252 * <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a
253 * method invocation conversion; if this constructor pertains to an enum type.</li>
254 * </ul>
255 * @throws InstantiationException Thrown if the class that declares the underlying constructor represents an abstract class.
256 * @throws InvocationTargetException Thrown if the underlying constructor throws an exception.
257 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
258 * @throws SecurityException Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class
259 * loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the
260 * package of the class.
261 * @see Constructor#newInstance(Object...)
262 * @see #invokeExactConstructor(Class, Object[], Class[])
263 */
264 public static <T> T invokeExactConstructor(final Class<T> cls, final Object... args)
265 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
266 final Object[] actuals = ArrayUtils.nullToEmpty(args);
267 return invokeExactConstructor(cls, actuals, ClassUtils.toClass(actuals));
268 }
269
270 /**
271 * Returns a new instance of the specified class choosing the right constructor from the list of parameter types.
272 *
273 * <p>
274 * This locates and calls a constructor. The constructor signature must match the parameter types exactly.
275 * </p>
276 *
277 * @param <T> the type to construct.
278 * @param cls The class to construct, not {@code null}.
279 * @param args The array of arguments, {@code null} treated as empty.
280 * @param parameterTypes The array of parameter types, {@code null} treated as empty.
281 * @return new instance of {@code cls}, not {@code null}.
282 * @throws NullPointerException Thrown if {@code cls} is {@code null}.
283 * @throws NoSuchMethodException Thrown if a matching constructor cannot be found.
284 * @throws IllegalAccessException Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is
285 * inaccessible.
286 * @throws IllegalArgumentException Thrown if:
287 * <ul>
288 * <li>the number of actual and formal parameters differ; or</li>
289 * <li>an unwrapping conversion for primitive arguments fails; or</li>
290 * <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a
291 * method invocation conversion; if this constructor pertains to an enum type.</li>
292 * </ul>
293 * @throws InstantiationException Thrown if the class that declares the underlying constructor represents an abstract class.
294 * @throws InvocationTargetException Thrown if the underlying constructor throws an exception.
295 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
296 * @throws SecurityException Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class
297 * loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the
298 * package of the class.
299 * @see Constructor#newInstance(Object...)
300 */
301 public static <T> T invokeExactConstructor(final Class<T> cls, final Object[] args, final Class<?>[] parameterTypes)
302 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
303 final Constructor<T> ctor = getAccessibleConstructor(cls, ArrayUtils.nullToEmpty(parameterTypes));
304 if (ctor == null) {
305 throw new NoSuchMethodException("No such accessible constructor on object: " + cls.getName());
306 }
307 return ctor.newInstance(ArrayUtils.nullToEmpty(args));
308 }
309
310 /**
311 * Tests whether the specified class is generally accessible, i.e. is declared in an entirely {@code public} manner.
312 *
313 * @param type to check.
314 * @return {@code true} if {@code type} and any enclosing classes are {@code public}.
315 * @throws SecurityException Thrown if a security manager is present and a caller's class loader is not the same as or an ancestor of the class loader for a
316 * class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the class.
317 */
318 private static boolean isAccessible(final Class<?> type) {
319 Class<?> cls = type;
320 while (cls != null) {
321 if (!ClassUtils.isPublic(cls)) {
322 return false;
323 }
324 cls = cls.getEnclosingClass();
325 }
326 return true;
327 }
328
329 /**
330 * ConstructorUtils instances should NOT be constructed in standard programming. Instead, the class should be used as
331 * {@code ConstructorUtils.invokeConstructor(cls, args)}.
332 *
333 * <p>
334 * This constructor is {@code public} to permit tools that require a JavaBean instance to operate.
335 * </p>
336 *
337 * @deprecated TODO Make private in 4.0.
338 */
339 @Deprecated
340 public ConstructorUtils() {
341 // empty
342 }
343 }