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 018package org.apache.commons.beanutils2; 019 020import java.lang.reflect.Array; 021import java.lang.reflect.Constructor; 022import java.lang.reflect.InvocationTargetException; 023import java.lang.reflect.Modifier; 024 025/** 026 * <p> 027 * Utility reflection methods focused on constructors, modeled after {@link MethodUtils}. 028 * </p> 029 * 030 * <h2>Known Limitations</h2> 031 * <h3>Accessing Public Constructors In A Default Access Superclass</h3> 032 * <p> 033 * There is an issue when invoking public constructors contained in a default access superclass. Reflection locates these constructors fine and correctly 034 * assigns them as public. However, an {@code IllegalAccessException} is thrown if the constructors is invoked. 035 * </p> 036 * 037 * <p> 038 * {@code ConstructorUtils} contains a workaround for this situation. It will attempt to call {@code setAccessible} on this constructor. If this call succeeds, 039 * then the method can be invoked as normal. This call will only succeed when the application has sufficient security privileges. If this call fails then a 040 * warning will be logged and the method may fail. 041 * </p> 042 */ 043public class ConstructorUtils { 044 045 /** 046 * Returns a constructor with single argument. 047 * 048 * @param <T> the type of the constructor 049 * @param klass the class to be constructed 050 * @param parameterType The constructor parameter type 051 * @return null if matching accessible constructor cannot be found. 052 * @see Class#getConstructor 053 * @see #getAccessibleConstructor(java.lang.reflect.Constructor) 054 */ 055 public static <T> Constructor<T> getAccessibleConstructor(final Class<T> klass, final Class<?> parameterType) { 056 057 final Class<?>[] parameterTypes = { parameterType }; 058 return getAccessibleConstructor(klass, parameterTypes); 059 } 060 061 /** 062 * Returns a constructor given a class and signature. 063 * 064 * @param <T> the type to be constructed 065 * @param klass the class to be constructed 066 * @param parameterTypes the parameter array 067 * @return null if matching accessible constructor cannot be found 068 * @see Class#getConstructor 069 * @see #getAccessibleConstructor(java.lang.reflect.Constructor) 070 */ 071 public static <T> Constructor<T> getAccessibleConstructor(final Class<T> klass, final Class<?>[] parameterTypes) { 072 073 try { 074 return getAccessibleConstructor(klass.getConstructor(parameterTypes)); 075 } catch (final NoSuchMethodException e) { 076 return null; 077 } 078 } 079 080 /** 081 * Returns accessible version of the given constructor. 082 * 083 * @param <T> the type of the constructor 084 * @param ctor prototype constructor object. 085 * @return {@code null} if accessible constructor cannot be found. 086 * @see SecurityManager 087 */ 088 public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) { 089 090 // Make sure we have a method to check 091 if (ctor == null) { 092 return null; 093 } 094 095 // If the requested method is not public we cannot call it 096 if (!Modifier.isPublic(ctor.getModifiers())) { 097 return null; 098 } 099 100 // If the declaring class is public, we are done 101 final Class<T> clazz = ctor.getDeclaringClass(); 102 if (Modifier.isPublic(clazz.getModifiers())) { 103 return ctor; 104 } 105 106 // what else can we do? 107 return null; 108 } 109 110 /** 111 * <p> 112 * Find an accessible constructor with compatible parameters. Compatible parameters mean that every method parameter is assignable from the given 113 * parameters. In other words, it finds constructor that will take the parameters given. 114 * </p> 115 * 116 * <p> 117 * First it checks if there is constructor matching the exact signature. If no such, all the constructors of the class are tested if their signatures are 118 * assignment compatible with the parameter types. The first matching constructor is returned. 119 * </p> 120 * 121 * @param <T> the type of the class to be inspected 122 * @param clazz find constructor for this class 123 * @param parameterTypes find method with compatible parameters 124 * @return a valid Constructor object. If there's no matching constructor, returns {@code null}. 125 */ 126 private static <T> Constructor<T> getMatchingAccessibleConstructor(final Class<T> clazz, final Class<?>[] parameterTypes) { 127 // see if we can find the method directly 128 // most of the time this works and it's much faster 129 try { 130 final Constructor<T> ctor = clazz.getConstructor(parameterTypes); 131 try { 132 // 133 // XXX Default access superclass workaround 134 // 135 // When a public class has a default access superclass 136 // with public methods, these methods are accessible. 137 // Calling them from compiled code works fine. 138 // 139 // Unfortunately, using reflection to invoke these methods 140 // seems to (wrongly) to prevent access even when the method 141 // modifier is public. 142 // 143 // The following workaround solves the problem but will only 144 // work from sufficiently privileges code. 145 // 146 // Better workarounds would be gratefully accepted. 147 // 148 ctor.setAccessible(true); 149 } catch (final SecurityException se) { 150 /* SWALLOW, if workaround fails don't fret. */ 151 } 152 return ctor; 153 154 } catch (final NoSuchMethodException e) { /* SWALLOW */ 155 } 156 157 // search through all methods 158 final int paramSize = parameterTypes.length; 159 final Constructor<?>[] ctors = clazz.getConstructors(); 160 for (final Constructor<?> ctor2 : ctors) { 161 // compare parameters 162 final Class<?>[] ctorParams = ctor2.getParameterTypes(); 163 final int ctorParamSize = ctorParams.length; 164 if (ctorParamSize == paramSize) { 165 boolean match = true; 166 for (int n = 0; n < ctorParamSize; n++) { 167 if (!MethodUtils.isAssignmentCompatible(ctorParams[n], parameterTypes[n])) { 168 match = false; 169 break; 170 } 171 } 172 173 if (match) { 174 // get accessible version of method 175 final Constructor<?> ctor = getAccessibleConstructor(ctor2); 176 if (ctor != null) { 177 try { 178 ctor.setAccessible(true); 179 } catch (final SecurityException se) { 180 /* 181 * Swallow SecurityException TODO: Why? 182 */ 183 } 184 // Class.getConstructors() actually returns constructors 185 // of type T, so it is safe to cast. 186 return (Constructor<T>) ctor; 187 } 188 } 189 } 190 } 191 192 return null; 193 } 194 195 /** 196 * <p> 197 * Convenience method returning new instance of {@code klazz} using a single argument constructor. The formal parameter type is inferred from the actual 198 * values of {@code arg}. See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details. 199 * </p> 200 * 201 * <p> 202 * The signatures should be assignment compatible. 203 * </p> 204 * 205 * @param <T> the type of the object to be constructed 206 * @param klass the class to be constructed. 207 * @param arg the actual argument. May be null (this will result in calling the default constructor). 208 * @return new instance of {@code klazz} 209 * @throws NoSuchMethodException If the constructor cannot be found 210 * @throws IllegalAccessException If an error occurs accessing the constructor 211 * @throws InvocationTargetException If an error occurs invoking the constructor 212 * @throws InstantiationException If an error occurs instantiating the class 213 * @see #invokeConstructor(Class, Object[], Class[]) 214 */ 215 public static <T> T invokeConstructor(final Class<T> klass, final Object arg) 216 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 217 218 final Object[] args = toArray(arg); 219 return invokeConstructor(klass, args); 220 } 221 222 /** 223 * <p> 224 * Returns new instance of {@code klazz</code> created using the actual arguments <code>args}. The formal parameter types are inferred from the actual 225 * values of {@code args}. See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details. 226 * </p> 227 * 228 * <p> 229 * The signatures should be assignment compatible. 230 * </p> 231 * 232 * @param <T> the type of the object to be constructed 233 * @param klass the class to be constructed. 234 * @param args actual argument array. May be null (this will result in calling the default constructor). 235 * @return new instance of {@code klazz} 236 * @throws NoSuchMethodException If the constructor cannot be found 237 * @throws IllegalAccessException If an error occurs accessing the constructor 238 * @throws InvocationTargetException If an error occurs invoking the constructor 239 * @throws InstantiationException If an error occurs instantiating the class 240 * @see #invokeConstructor(Class, Object[], Class[]) 241 */ 242 public static <T> T invokeConstructor(final Class<T> klass, Object[] args) 243 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 244 245 if (null == args) { 246 args = BeanUtils.EMPTY_OBJECT_ARRAY; 247 } 248 final int arguments = args.length; 249 final Class<?>[] parameterTypes = new Class<?>[arguments]; 250 for (int i = 0; i < arguments; i++) { 251 parameterTypes[i] = args[i].getClass(); 252 } 253 return invokeConstructor(klass, args, parameterTypes); 254 } 255 256 /** 257 * <p> 258 * Returns new instance of {@code klazz} created using constructor with signature {@code parameterTypes</code> and actual arguments <code>args}. 259 * </p> 260 * 261 * <p> 262 * The signatures should be assignment compatible. 263 * </p> 264 * 265 * @param <T> the type of the object to be constructed 266 * @param klass the class to be constructed. 267 * @param args actual argument array. May be null (this will result in calling the default constructor). 268 * @param parameterTypes parameter types array 269 * @return new instance of {@code klazz} 270 * @throws NoSuchMethodException if matching constructor cannot be found 271 * @throws IllegalAccessException thrown on the constructor's invocation 272 * @throws InvocationTargetException thrown on the constructor's invocation 273 * @throws InstantiationException thrown on the constructor's invocation 274 * @see Constructor#newInstance 275 */ 276 public static <T> T invokeConstructor(final Class<T> klass, Object[] args, Class<?>[] parameterTypes) 277 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 278 279 if (parameterTypes == null) { 280 parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY; 281 } 282 if (args == null) { 283 args = BeanUtils.EMPTY_OBJECT_ARRAY; 284 } 285 286 final Constructor<T> ctor = getMatchingAccessibleConstructor(klass, parameterTypes); 287 if (null == ctor) { 288 throw new NoSuchMethodException("No such accessible constructor on object: " + klass.getName()); 289 } 290 return ctor.newInstance(args); 291 } 292 293 /** 294 * <p> 295 * Convenience method returning new instance of {@code klazz} using a single argument constructor. The formal parameter type is inferred from the actual 296 * values of {@code arg}. See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details. 297 * </p> 298 * 299 * <p> 300 * The signatures should match exactly. 301 * </p> 302 * 303 * @param <T> the type of the object to be constructed 304 * @param klass the class to be constructed. 305 * @param arg the actual argument. May be null (this will result in calling the default constructor). 306 * @return new instance of {@code klazz} 307 * @throws NoSuchMethodException If the constructor cannot be found 308 * @throws IllegalAccessException If an error occurs accessing the constructor 309 * @throws InvocationTargetException If an error occurs invoking the constructor 310 * @throws InstantiationException If an error occurs instantiating the class 311 * @see #invokeExactConstructor(Class, Object[], Class[]) 312 */ 313 public static <T> T invokeExactConstructor(final Class<T> klass, final Object arg) 314 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 315 316 final Object[] args = toArray(arg); 317 return invokeExactConstructor(klass, args); 318 } 319 320 /** 321 * <p> 322 * Returns new instance of {@code klazz</code> created using the actual arguments <code>args}. The formal parameter types are inferred from the actual 323 * values of {@code args}. See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details. 324 * </p> 325 * 326 * <p> 327 * The signatures should match exactly. 328 * </p> 329 * 330 * @param <T> the type of the object to be constructed 331 * @param klass the class to be constructed. 332 * @param args actual argument array. May be null (this will result in calling the default constructor). 333 * @return new instance of {@code klazz} 334 * @throws NoSuchMethodException If the constructor cannot be found 335 * @throws IllegalAccessException If an error occurs accessing the constructor 336 * @throws InvocationTargetException If an error occurs invoking the constructor 337 * @throws InstantiationException If an error occurs instantiating the class 338 * @see #invokeExactConstructor(Class, Object[], Class[]) 339 */ 340 public static <T> T invokeExactConstructor(final Class<T> klass, Object[] args) 341 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 342 343 if (null == args) { 344 args = BeanUtils.EMPTY_OBJECT_ARRAY; 345 } 346 final int arguments = args.length; 347 final Class<?>[] parameterTypes = new Class[arguments]; 348 for (int i = 0; i < arguments; i++) { 349 parameterTypes[i] = args[i].getClass(); 350 } 351 return invokeExactConstructor(klass, args, parameterTypes); 352 } 353 354 /** 355 * <p> 356 * Returns new instance of {@code klazz} created using constructor with signature {@code parameterTypes} and actual arguments {@code args}. 357 * </p> 358 * 359 * <p> 360 * The signatures should match exactly. 361 * </p> 362 * 363 * @param <T> the type of the object to be constructed 364 * @param klass the class to be constructed. 365 * @param args actual argument array. May be null (this will result in calling the default constructor). 366 * @param parameterTypes parameter types array 367 * @return new instance of {@code klazz} 368 * @throws NoSuchMethodException if matching constructor cannot be found 369 * @throws IllegalAccessException thrown on the constructor's invocation 370 * @throws InvocationTargetException thrown on the constructor's invocation 371 * @throws InstantiationException thrown on the constructor's invocation 372 * @see Constructor#newInstance 373 */ 374 public static <T> T invokeExactConstructor(final Class<T> klass, Object[] args, Class<?>[] parameterTypes) 375 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 376 377 if (args == null) { 378 args = BeanUtils.EMPTY_OBJECT_ARRAY; 379 } 380 381 if (parameterTypes == null) { 382 parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY; 383 } 384 385 final Constructor<T> ctor = getAccessibleConstructor(klass, parameterTypes); 386 if (null == ctor) { 387 throw new NoSuchMethodException("No such accessible constructor on object: " + klass.getName()); 388 } 389 return ctor.newInstance(args); 390 } 391 392 /** 393 * Delegates to {@link Array#newInstance(Class, int)}. 394 * 395 * @param <T> Component type. 396 * @param componentType See {@link Array#newInstance(Class, int)}. 397 * @param length See {@link Array#newInstance(Class, int)}. 398 * @return See {@link Array#newInstance(Class, int)}. 399 */ 400 @SuppressWarnings("unchecked") 401 public static <T> T[] newArray(final Class<T> componentType, final int length) { 402 return (T[]) Array.newInstance(componentType, length); 403 } 404 405 private static Object[] toArray(final Object arg) { 406 Object[] args = null; 407 if (arg != null) { 408 args = new Object[] { arg }; 409 } 410 return args; 411 } 412 413}