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 }