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 {@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 905636 2010-02-02 14:03:32Z niallp $ 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>Convenience method returning new instance of <code>klazz</code> using a single argument constructor. 069 * The formal parameter type is inferred from the actual values of <code>arg</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 arg the actual argument 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 Object invokeConstructor(Class cls, Object arg) 086 throws NoSuchMethodException, IllegalAccessException, 087 InvocationTargetException, InstantiationException { 088 return invokeConstructor(cls, new Object[] { arg }); 089 } 090 091 /** 092 * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>. 093 * The formal parameter types are inferred from the actual values of <code>args</code>. 094 * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> 095 * 096 * <p>The signatures should be assignment compatible.</p> 097 * 098 * @param cls the class to be constructed. 099 * @param args actual argument array 100 * @return new instance of <code>klazz</code> 101 * 102 * @throws NoSuchMethodException If the constructor cannot be found 103 * @throws IllegalAccessException If an error occurs accessing the constructor 104 * @throws InvocationTargetException If an error occurs invoking the constructor 105 * @throws InstantiationException If an error occurs instantiating the class 106 * 107 * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) 108 */ 109 public static Object invokeConstructor(Class cls, Object[] args) 110 throws NoSuchMethodException, IllegalAccessException, 111 InvocationTargetException, InstantiationException { 112 if (null == args) { 113 args = ArrayUtils.EMPTY_OBJECT_ARRAY; 114 } 115 Class parameterTypes[] = new Class[args.length]; 116 for (int i = 0; i < args.length; i++) { 117 parameterTypes[i] = args[i].getClass(); 118 } 119 return invokeConstructor(cls, args, parameterTypes); 120 } 121 122 /** 123 * <p>Returns new instance of <code>klazz</code> created using constructor 124 * with signature <code>parameterTypes</code> and actual arguments <code>args</code>.</p> 125 * 126 * <p>The signatures should be assignment compatible.</p> 127 * 128 * @param cls the class to be constructed. 129 * @param args actual argument array 130 * @param parameterTypes parameter types array 131 * @return new instance of <code>klazz</code> 132 * 133 * @throws NoSuchMethodException if matching constructor cannot be found 134 * @throws IllegalAccessException thrown on the constructor's invocation 135 * @throws InvocationTargetException thrown on the constructor's invocation 136 * @throws InstantiationException thrown on the constructor's invocation 137 * @see Constructor#newInstance 138 */ 139 public static Object invokeConstructor(Class cls, Object[] args, 140 Class[] parameterTypes) throws NoSuchMethodException, 141 IllegalAccessException, InvocationTargetException, 142 InstantiationException { 143 if (parameterTypes == null) { 144 parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; 145 } 146 if (args == null) { 147 args = ArrayUtils.EMPTY_OBJECT_ARRAY; 148 } 149 Constructor ctor = getMatchingAccessibleConstructor(cls, parameterTypes); 150 if (null == ctor) { 151 throw new NoSuchMethodException( 152 "No such accessible constructor on object: " 153 + cls.getName()); 154 } 155 return ctor.newInstance(args); 156 } 157 158 /** 159 * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor. 160 * The formal parameter type is inferred from the actual values of <code>arg</code>. 161 * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> 162 * 163 * <p>The signatures should match exactly.</p> 164 * 165 * @param cls the class to be constructed. 166 * @param arg the actual argument 167 * @return new instance of <code>klazz</code> 168 * 169 * @throws NoSuchMethodException If the constructor cannot be found 170 * @throws IllegalAccessException If an error occurs accessing the constructor 171 * @throws InvocationTargetException If an error occurs invoking the constructor 172 * @throws InstantiationException If an error occurs instantiating the class 173 * 174 * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) 175 */ 176 public static Object invokeExactConstructor(Class cls, Object arg) 177 throws NoSuchMethodException, IllegalAccessException, 178 InvocationTargetException, InstantiationException { 179 return invokeExactConstructor(cls, new Object[] { arg }); 180 } 181 182 /** 183 * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>. 184 * The formal parameter types are inferred from the actual values of <code>args</code>. 185 * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> 186 * 187 * <p>The signatures should match exactly.</p> 188 * 189 * @param cls the class to be constructed. 190 * @param args actual argument array 191 * @return new instance of <code>klazz</code> 192 * 193 * @throws NoSuchMethodException If the constructor cannot be found 194 * @throws IllegalAccessException If an error occurs accessing the constructor 195 * @throws InvocationTargetException If an error occurs invoking the constructor 196 * @throws InstantiationException If an error occurs instantiating the class 197 * 198 * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) 199 */ 200 public static Object invokeExactConstructor(Class cls, Object[] args) 201 throws NoSuchMethodException, IllegalAccessException, 202 InvocationTargetException, InstantiationException { 203 if (null == args) { 204 args = ArrayUtils.EMPTY_OBJECT_ARRAY; 205 } 206 int arguments = args.length; 207 Class parameterTypes[] = new Class[arguments]; 208 for (int i = 0; i < arguments; i++) { 209 parameterTypes[i] = args[i].getClass(); 210 } 211 return invokeExactConstructor(cls, args, parameterTypes); 212 } 213 214 /** 215 * <p>Returns new instance of <code>klazz</code> created using constructor 216 * with signature <code>parameterTypes</code> and actual arguments 217 * <code>args</code>.</p> 218 * 219 * <p>The signatures should match exactly.</p> 220 * 221 * @param cls the class to be constructed. 222 * @param args actual argument array 223 * @param parameterTypes parameter types array 224 * @return new instance of <code>klazz</code> 225 * 226 * @throws NoSuchMethodException if matching constructor cannot be found 227 * @throws IllegalAccessException thrown on the constructor's invocation 228 * @throws InvocationTargetException thrown on the constructor's invocation 229 * @throws InstantiationException thrown on the constructor's invocation 230 * @see Constructor#newInstance 231 */ 232 public static Object invokeExactConstructor(Class cls, Object[] args, 233 Class[] parameterTypes) throws NoSuchMethodException, 234 IllegalAccessException, InvocationTargetException, 235 InstantiationException { 236 if (args == null) { 237 args = ArrayUtils.EMPTY_OBJECT_ARRAY; 238 } 239 if (parameterTypes == null) { 240 parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; 241 } 242 Constructor ctor = getAccessibleConstructor(cls, parameterTypes); 243 if (null == ctor) { 244 throw new NoSuchMethodException( 245 "No such accessible constructor on object: " 246 + cls.getName()); 247 } 248 return ctor.newInstance(args); 249 } 250 251 /** 252 * Returns a constructor with single argument. 253 * @param cls the class to be constructed 254 * @param parameterType The constructor parameter type 255 * @return null if matching accessible constructor can not be found. 256 * @see Class#getConstructor 257 * @see #getAccessibleConstructor(java.lang.reflect.Constructor) 258 */ 259 public static Constructor getAccessibleConstructor(Class cls, 260 Class parameterType) { 261 return getAccessibleConstructor(cls, new Class[] { parameterType }); 262 } 263 264 /** 265 * Returns a constructor given a class and signature. 266 * @param cls the class to be constructed 267 * @param parameterTypes the parameter array 268 * @return null if matching accessible constructor can not be found 269 * @see Class#getConstructor 270 * @see #getAccessibleConstructor(java.lang.reflect.Constructor) 271 */ 272 public static Constructor getAccessibleConstructor(Class cls, 273 Class[] parameterTypes) { 274 try { 275 return getAccessibleConstructor(cls.getConstructor(parameterTypes)); 276 } catch (NoSuchMethodException e) { 277 return (null); 278 } 279 } 280 281 /** 282 * Returns accessible version of the given constructor. 283 * @param ctor prototype constructor object. 284 * @return <code>null</code> if accessible constructor can not be found. 285 * @see java.lang.SecurityManager 286 */ 287 public static Constructor getAccessibleConstructor(Constructor ctor) { 288 return MemberUtils.isAccessible(ctor) 289 && Modifier.isPublic(ctor.getDeclaringClass().getModifiers()) ? ctor 290 : null; 291 } 292 293 /** 294 * <p>Find an accessible constructor with compatible parameters. 295 * Compatible parameters mean that every method parameter is assignable from 296 * the given parameters. In other words, it finds constructor that will take 297 * the parameters given.</p> 298 * 299 * <p>First it checks if there is constructor matching the exact signature. 300 * If no such, all the constructors of the class are tested if their signatures 301 * are assignment compatible with the parameter types. 302 * The first matching constructor is returned.</p> 303 * 304 * @param cls find constructor for this class 305 * @param parameterTypes find method with compatible parameters 306 * @return a valid Constructor object. If there's no matching constructor, returns <code>null</code>. 307 */ 308 public static Constructor getMatchingAccessibleConstructor(Class cls, 309 Class[] parameterTypes) { 310 // see if we can find the constructor directly 311 // most of the time this works and it's much faster 312 try { 313 Constructor ctor = cls.getConstructor(parameterTypes); 314 MemberUtils.setAccessibleWorkaround(ctor); 315 return ctor; 316 } catch (NoSuchMethodException e) { /* SWALLOW */ 317 } 318 Constructor result = null; 319 // search through all constructors 320 Constructor[] ctors = cls.getConstructors(); 321 for (int i = 0; i < ctors.length; i++) { 322 // compare parameters 323 if (ClassUtils.isAssignable(parameterTypes, ctors[i] 324 .getParameterTypes(), true)) { 325 // get accessible version of method 326 Constructor ctor = getAccessibleConstructor(ctors[i]); 327 if (ctor != null) { 328 MemberUtils.setAccessibleWorkaround(ctor); 329 if (result == null 330 || MemberUtils.compareParameterTypes(ctor 331 .getParameterTypes(), result 332 .getParameterTypes(), parameterTypes) < 0) { 333 result = ctor; 334 } 335 } 336 } 337 } 338 return result; 339 } 340 341 }