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 }