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.lang.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.lang.ArrayUtils; 024 import org.apache.commons.lang.ClassUtils; 025 026 /** 027 * <p> Utility reflection methods focussed on constructors, modelled 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 privilages. If this call 041 * fails then a warning will be logged and the method may fail.</p> 042 * 043 * @author Apache Software Foundation 044 * @author Craig R. McClanahan 045 * @author Ralph Schaer 046 * @author Chris Audley 047 * @author Rey Francois 048 * @author Gregor Rayman 049 * @author Jan Sorensen 050 * @author Robert Burrell Donkin 051 * @author Rodney Waldhoff 052 * @since 2.5 053 * @version $Id: ConstructorUtils.java 1056863 2011-01-09 02:00:25Z niallp $ 054 */ 055 public class ConstructorUtils { 056 057 /** 058 * <p>ConstructorUtils instances should NOT be constructed in standard 059 * programming. Instead, the class should be used as 060 * <code>ConstructorUtils.invokeConstructor(cls, args)</code>.</p> 061 * 062 * <p>This constructor is public to permit tools that require a JavaBean 063 * instance to operate.</p> 064 */ 065 public ConstructorUtils() { 066 super(); 067 } 068 069 /** 070 * <p>Returns new instance of <code>klazz</code> created using the actual 071 * arguments <code>args</code>. The formal parameter types are inferred from 072 * the actual values of <code>args</code>. See 073 * {@link #invokeExactConstructor(Class, Object[], Class[])} for more 074 * details.</p> 075 * 076 * <p>The signatures should be assignment compatible.</p> 077 * 078 * @param cls the class to be constructed. 079 * @param arg the actual argument 080 * @return new instance of <code>klazz</code> 081 * 082 * @throws NoSuchMethodException If the constructor cannot be found 083 * @throws IllegalAccessException If an error occurs accessing the constructor 084 * @throws InvocationTargetException If an error occurs invoking the constructor 085 * @throws InstantiationException If an error occurs instantiating the class 086 * 087 * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) 088 */ 089 public static Object invokeConstructor(Class cls, Object arg) 090 throws NoSuchMethodException, IllegalAccessException, 091 InvocationTargetException, InstantiationException { 092 return invokeConstructor(cls, new Object[] { arg }); 093 } 094 095 /** 096 * <p>Returns new instance of <code>klazz</code> created using the actual 097 * arguments <code>args</code>. The formal parameter types are inferred from 098 * the actual values of <code>args</code>. See 099 * {@link #invokeExactConstructor(Class, Object[], Class[])} for more 100 * details.</p> 101 * <p>The signatures should be assignment compatible.</p> 102 * 103 * @param cls the class to be constructed. 104 * @param args actual argument array 105 * @return new instance of <code>klazz</code> 106 * 107 * @throws NoSuchMethodException If the constructor cannot be found 108 * @throws IllegalAccessException If an error occurs accessing the 109 * constructor 110 * @throws InvocationTargetException If an error occurs invoking the 111 * constructor 112 * @throws InstantiationException If an error occurs instantiating the class 113 * 114 * @see #invokeConstructor(java.lang.Class, java.lang.Object[], 115 * java.lang.Class[]) 116 */ 117 public static Object invokeConstructor(Class cls, Object[] args) 118 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, 119 InstantiationException { 120 if (null == args) { 121 args = ArrayUtils.EMPTY_OBJECT_ARRAY; 122 } 123 Class parameterTypes[] = new Class[args.length]; 124 for (int i = 0; i < args.length; i++) { 125 parameterTypes[i] = args[i].getClass(); 126 } 127 return invokeConstructor(cls, args, parameterTypes); 128 } 129 130 /** 131 * <p>Returns new instance of <code>klazz</code> created using constructor 132 * with signature <code>parameterTypes</code> and actual arguments 133 * <code>args</code>.</p> 134 * 135 * <p>The signatures should be assignment compatible.</p> 136 * 137 * @param cls the class to be constructed. 138 * @param args actual argument array 139 * @param parameterTypes parameter types array 140 * @return new instance of <code>klazz</code> 141 * 142 * @throws NoSuchMethodException if matching constructor cannot be found 143 * @throws IllegalAccessException thrown on the constructor's invocation 144 * @throws InvocationTargetException thrown on the constructor's invocation 145 * @throws InstantiationException thrown on the constructor's invocation 146 * @see Constructor#newInstance 147 */ 148 public static Object invokeConstructor(Class cls, Object[] args, Class[] parameterTypes) 149 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, 150 InstantiationException { 151 if (parameterTypes == null) { 152 parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; 153 } 154 if (args == null) { 155 args = ArrayUtils.EMPTY_OBJECT_ARRAY; 156 } 157 Constructor ctor = getMatchingAccessibleConstructor(cls, parameterTypes); 158 if (null == ctor) { 159 throw new NoSuchMethodException("No such accessible constructor on object: " 160 + cls.getName()); 161 } 162 return ctor.newInstance(args); 163 } 164 165 /** 166 * <p>Returns new instance of <code>klazz</code> created using the actual 167 * arguments <code>args</code>. The formal parameter types are inferred from 168 * the actual values of <code>args</code>. See 169 * {@link #invokeExactConstructor(Class, Object[], Class[])} for more 170 * details.</p> 171 * 172 * <p>The signatures should match exactly.</p> 173 * 174 * @param cls the class to be constructed. 175 * @param arg the actual argument 176 * @return new instance of <code>klazz</code> 177 * 178 * @throws NoSuchMethodException If the constructor cannot be found 179 * @throws IllegalAccessException If an error occurs accessing the constructor 180 * @throws InvocationTargetException If an error occurs invoking the constructor 181 * @throws InstantiationException If an error occurs instantiating the class 182 * 183 * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) 184 */ 185 public static Object invokeExactConstructor(Class cls, Object arg) 186 throws NoSuchMethodException, IllegalAccessException, 187 InvocationTargetException, InstantiationException { 188 return invokeExactConstructor(cls, new Object[] { arg }); 189 } 190 191 /** 192 * <p>Returns new instance of <code>klazz</code> created using the actual 193 * arguments <code>args</code>. The formal parameter types are inferred from 194 * the actual values of <code>args</code>. See 195 * {@link #invokeExactConstructor(Class, Object[], Class[])} for more 196 * details.</p> 197 * 198 * <p>The signatures should match exactly.</p> 199 * 200 * @param cls the class to be constructed. 201 * @param args actual argument array 202 * @return new instance of <code>klazz</code> 203 * 204 * @throws NoSuchMethodException If the constructor cannot be found 205 * @throws IllegalAccessException If an error occurs accessing the 206 * constructor 207 * @throws InvocationTargetException If an error occurs invoking the 208 * constructor 209 * @throws InstantiationException If an error occurs instantiating the class 210 * 211 * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], 212 * java.lang.Class[]) 213 */ 214 public static Object invokeExactConstructor(Class cls, Object[] args) 215 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, 216 InstantiationException { 217 if (null == args) { 218 args = ArrayUtils.EMPTY_OBJECT_ARRAY; 219 } 220 int arguments = args.length; 221 Class parameterTypes[] = new Class[arguments]; 222 for (int i = 0; i < arguments; i++) { 223 parameterTypes[i] = args[i].getClass(); 224 } 225 return invokeExactConstructor(cls, args, parameterTypes); 226 } 227 228 /** 229 * <p>Returns new instance of <code>klazz</code> created using constructor 230 * with signature <code>parameterTypes</code> and actual arguments 231 * <code>args</code>.</p> 232 * 233 * <p>The signatures should match exactly.</p> 234 * 235 * @param cls the class to be constructed. 236 * @param args actual argument array 237 * @param parameterTypes parameter types array 238 * @return new instance of <code>klazz</code> 239 * 240 * @throws NoSuchMethodException if matching constructor cannot be found 241 * @throws IllegalAccessException thrown on the constructor's invocation 242 * @throws InvocationTargetException thrown on the constructor's invocation 243 * @throws InstantiationException thrown on the constructor's invocation 244 * @see Constructor#newInstance 245 */ 246 public static Object invokeExactConstructor(Class cls, Object[] args, 247 Class[] parameterTypes) throws NoSuchMethodException, IllegalAccessException, 248 InvocationTargetException, InstantiationException { 249 if (args == null) { 250 args = ArrayUtils.EMPTY_OBJECT_ARRAY; 251 } 252 if (parameterTypes == null) { 253 parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; 254 } 255 Constructor ctor = getAccessibleConstructor(cls, parameterTypes); 256 if (null == ctor) { 257 throw new NoSuchMethodException("No such accessible constructor on object: " 258 + cls.getName()); 259 } 260 return ctor.newInstance(args); 261 } 262 263 /** 264 * Returns a constructor with single argument. 265 * @param cls the class to be constructed 266 * @param parameterType The constructor parameter type 267 * @return null if matching accessible constructor can not be found. 268 * @see Class#getConstructor 269 * @see #getAccessibleConstructor(java.lang.reflect.Constructor) 270 */ 271 public static Constructor getAccessibleConstructor(Class cls, 272 Class parameterType) { 273 return getAccessibleConstructor(cls, new Class[] { parameterType }); 274 } 275 276 /** 277 * Returns a constructor given a class and signature. 278 * @param cls the class to be constructed 279 * @param parameterTypes the parameter array 280 * @return null if matching accessible constructor can not be found 281 * @see Class#getConstructor 282 * @see #getAccessibleConstructor(java.lang.reflect.Constructor) 283 */ 284 public static Constructor getAccessibleConstructor(Class cls, 285 Class[] parameterTypes) { 286 try { 287 return getAccessibleConstructor(cls.getConstructor(parameterTypes)); 288 } catch (NoSuchMethodException e) { 289 return (null); 290 } 291 } 292 293 /** 294 * Returns accessible version of the given constructor. 295 * @param ctor prototype constructor object. 296 * @return <code>null</code> if accessible constructor can not be found. 297 * @see java.lang.SecurityManager 298 */ 299 public static Constructor getAccessibleConstructor(Constructor ctor) { 300 return MemberUtils.isAccessible(ctor) 301 && Modifier.isPublic(ctor.getDeclaringClass().getModifiers()) ? ctor : null; 302 } 303 304 /** 305 * <p>Find an accessible constructor with compatible parameters. Compatible 306 * parameters mean that every method parameter is assignable from the given 307 * parameters. In other words, it finds constructor that will take the 308 * parameters given.</p> 309 * 310 * <p>First it checks if there is constructor matching the exact signature. 311 * If no such, all the constructors of the class are tested if their 312 * signatures are assignment compatible with the parameter types. The first 313 * matching constructor is returned.</p> 314 * 315 * @param cls find constructor for this class 316 * @param parameterTypes find method with compatible parameters 317 * @return a valid Constructor object. If there's no matching constructor, 318 * returns <code>null</code>. 319 */ 320 public static Constructor getMatchingAccessibleConstructor(Class cls, 321 Class[] parameterTypes) { 322 // see if we can find the constructor directly 323 // most of the time this works and it's much faster 324 try { 325 Constructor ctor = cls.getConstructor(parameterTypes); 326 MemberUtils.setAccessibleWorkaround(ctor); 327 return ctor; 328 } catch (NoSuchMethodException e) { /* SWALLOW */ 329 } 330 Constructor result = null; 331 // search through all constructors 332 Constructor[] ctors = cls.getConstructors(); 333 // return best match: 334 for (int i = 0; i < ctors.length; i++) { 335 336 // compare parameters 337 if (ClassUtils.isAssignable(parameterTypes, ctors[i].getParameterTypes(), true)) { 338 // get accessible version of constructor 339 Constructor ctor = getAccessibleConstructor(ctors[i]); 340 if (ctor != null) { 341 MemberUtils.setAccessibleWorkaround(ctor); 342 if (result == null 343 || MemberUtils.compareParameterTypes(ctor.getParameterTypes(), result 344 .getParameterTypes(), parameterTypes) < 0) { 345 result = ctor; 346 } 347 } 348 } 349 } 350 return result; 351 } 352 353 }