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