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 }