1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.beanutils;
  17. import java.lang.reflect.Constructor;
  18. import java.lang.reflect.InvocationTargetException;
  19. import java.lang.reflect.Modifier;
  20. /**
  21. * <p> Utility reflection methods focussed on constructors, modelled after {@link MethodUtils}. </p>
  22. *
  23. * <h3>Known Limitations</h3>
  24. * <h4>Accessing Public Constructors In A Default Access Superclass</h4>
  25. * <p>There is an issue when invoking public constructors contained in a default access superclass.
  26. * Reflection locates these constructors fine and correctly assigns them as public.
  27. * However, an <code>IllegalAccessException</code> is thrown if the constructors is invoked.</p>
  28. *
  29. * <p><code>ConstructorUtils</code> contains a workaround for this situation.
  30. * It will attempt to call <code>setAccessible</code> on this constructor.
  31. * If this call succeeds, then the method can be invoked as normal.
  32. * This call will only succeed when the application has sufficient security privilages.
  33. * If this call fails then a warning will be logged and the method may fail.</p>
  34. *
  35. * @author Craig R. McClanahan
  36. * @author Ralph Schaer
  37. * @author Chris Audley
  38. * @author Rey François
  39. * @author Gregor Raıman
  40. * @author Jan Sorensen
  41. * @author Robert Burrell Donkin
  42. * @author Rodney Waldhoff
  43. * @version $Revision: 1.7 $ $Date: 2004/02/28 13:18:33 $
  44. */
  45. public class ConstructorUtils {
  46. // --------------------------------------------------------- Private Members
  47. /** An empty class array */
  48. private static final Class[] emptyClassArray = new Class[0];
  49. /** An empty object array */
  50. private static final Object[] emptyObjectArray = new Object[0];
  51. // --------------------------------------------------------- Public Methods
  52. /**
  53. * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor.
  54. * The formal parameter type is inferred from the actual values of <code>arg</code>.
  55. * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
  56. *
  57. * <p>The signatures should be assignment compatible.</p>
  58. *
  59. * @param klass the class to be constructed.
  60. * @param arg the actual argument
  61. * @return new instance of <code>klazz</code>
  62. *
  63. * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
  64. */
  65. public static Object invokeConstructor(Class klass, Object arg)
  66. throws
  67. NoSuchMethodException,
  68. IllegalAccessException,
  69. InvocationTargetException,
  70. InstantiationException {
  71. Object[] args = { arg };
  72. return invokeConstructor(klass, args);
  73. }
  74. /**
  75. * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
  76. * The formal parameter types are inferred from the actual values of <code>args</code>.
  77. * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
  78. *
  79. * <p>The signatures should be assignment compatible.</p>
  80. *
  81. * @param klass the class to be constructed.
  82. * @param args actual argument array
  83. * @return new instance of <code>klazz</code>
  84. *
  85. * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
  86. */
  87. public static Object invokeConstructor(Class klass, Object[] args)
  88. throws
  89. NoSuchMethodException,
  90. IllegalAccessException,
  91. InvocationTargetException,
  92. InstantiationException {
  93. if (null == args) {
  94. args = emptyObjectArray;
  95. }
  96. int arguments = args.length;
  97. Class parameterTypes[] = new Class[arguments];
  98. for (int i = 0; i < arguments; i++) {
  99. parameterTypes[i] = args[i].getClass();
  100. }
  101. return invokeConstructor(klass, args, parameterTypes);
  102. }
  103. /**
  104. * <p>Returns new instance of <code>klazz</code> created using constructor
  105. * with signature <code>parameterTypes</code> and actual arguments <code>args</code>.</p>
  106. *
  107. * <p>The signatures should be assignment compatible.</p>
  108. *
  109. * @param klass the class to be constructed.
  110. * @param args actual argument array
  111. * @param parameterTypes parameter types array
  112. * @return new instance of <code>klazz</code>
  113. *
  114. * @throws NoSuchMethodException if matching constructor cannot be found
  115. * @throws IllegalAccessException thrown on the constructor's invocation
  116. * @throws InvocationTargetException thrown on the constructor's invocation
  117. * @throws InstantiationException thrown on the constructor's invocation
  118. * @see Constructor#newInstance
  119. */
  120. public static Object invokeConstructor(
  121. Class klass,
  122. Object[] args,
  123. Class[] parameterTypes)
  124. throws
  125. NoSuchMethodException,
  126. IllegalAccessException,
  127. InvocationTargetException,
  128. InstantiationException {
  129. if (parameterTypes == null) {
  130. parameterTypes = emptyClassArray;
  131. }
  132. if (args == null) {
  133. args = emptyObjectArray;
  134. }
  135. Constructor ctor =
  136. getMatchingAccessibleConstructor(klass, parameterTypes);
  137. if (null == ctor) {
  138. throw new NoSuchMethodException(
  139. "No such accessible constructor on object: " + klass.getName());
  140. }
  141. return ctor.newInstance(args);
  142. }
  143. /**
  144. * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor.
  145. * The formal parameter type is inferred from the actual values of <code>arg</code>.
  146. * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
  147. *
  148. * <p>The signatures should match exactly.</p>
  149. *
  150. * @param klass the class to be constructed.
  151. * @param arg the actual argument
  152. * @return new instance of <code>klazz</code>
  153. *
  154. * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
  155. */
  156. public static Object invokeExactConstructor(Class klass, Object arg)
  157. throws
  158. NoSuchMethodException,
  159. IllegalAccessException,
  160. InvocationTargetException,
  161. InstantiationException {
  162. Object[] args = { arg };
  163. return invokeExactConstructor(klass, args);
  164. }
  165. /**
  166. * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
  167. * The formal parameter types are inferred from the actual values of <code>args</code>.
  168. * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
  169. *
  170. * <p>The signatures should match exactly.</p>
  171. *
  172. * @param klass the class to be constructed.
  173. * @param args actual argument array
  174. * @return new instance of <code>klazz</code>
  175. *
  176. * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
  177. */
  178. public static Object invokeExactConstructor(Class klass, Object[] args)
  179. throws
  180. NoSuchMethodException,
  181. IllegalAccessException,
  182. InvocationTargetException,
  183. InstantiationException {
  184. if (null == args) {
  185. args = emptyObjectArray;
  186. }
  187. int arguments = args.length;
  188. Class parameterTypes[] = new Class[arguments];
  189. for (int i = 0; i < arguments; i++) {
  190. parameterTypes[i] = args[i].getClass();
  191. }
  192. return invokeExactConstructor(klass, args, parameterTypes);
  193. }
  194. /**
  195. * <p>Returns new instance of <code>klazz</code> created using constructor
  196. * with signature <code>parameterTypes</code> and actual arguments
  197. * <code>args</code>.</p>
  198. *
  199. * <p>The signatures should match exactly.</p>
  200. *
  201. * @param klass the class to be constructed.
  202. * @param args actual argument array
  203. * @param parameterTypes parameter types array
  204. * @return new instance of <code>klazz</code>
  205. *
  206. * @throws NoSuchMethodException if matching constructor cannot be found
  207. * @throws IllegalAccessException thrown on the constructor's invocation
  208. * @throws InvocationTargetException thrown on the constructor's invocation
  209. * @throws InstantiationException thrown on the constructor's invocation
  210. * @see Constructor#newInstance
  211. */
  212. public static Object invokeExactConstructor(
  213. Class klass,
  214. Object[] args,
  215. Class[] parameterTypes)
  216. throws
  217. NoSuchMethodException,
  218. IllegalAccessException,
  219. InvocationTargetException,
  220. InstantiationException {
  221. if (args == null) {
  222. args = emptyObjectArray;
  223. }
  224. if (parameterTypes == null) {
  225. parameterTypes = emptyClassArray;
  226. }
  227. Constructor ctor = getAccessibleConstructor(klass, parameterTypes);
  228. if (null == ctor) {
  229. throw new NoSuchMethodException(
  230. "No such accessible constructor on object: " + klass.getName());
  231. }
  232. return ctor.newInstance(args);
  233. }
  234. /**
  235. * Returns a constructor with single argument.
  236. * @param klass the class to be constructed
  237. * @return null if matching accessible constructor can not be found.
  238. * @see Class#getConstructor
  239. * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
  240. */
  241. public static Constructor getAccessibleConstructor(
  242. Class klass,
  243. Class parameterType) {
  244. Class[] parameterTypes = { parameterType };
  245. return getAccessibleConstructor(klass, parameterTypes);
  246. }
  247. /**
  248. * Returns a constructor given a class and signature.
  249. * @param klass the class to be constructed
  250. * @param parameterTypes the parameter array
  251. * @return null if matching accessible constructor can not be found
  252. * @see Class#getConstructor
  253. * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
  254. */
  255. public static Constructor getAccessibleConstructor(
  256. Class klass,
  257. Class[] parameterTypes) {
  258. try {
  259. return getAccessibleConstructor(
  260. klass.getConstructor(parameterTypes));
  261. } catch (NoSuchMethodException e) {
  262. return (null);
  263. }
  264. }
  265. /**
  266. * Returns accessible version of the given constructor.
  267. * @param ctor prototype constructor object.
  268. * @return <code>null</code> if accessible constructor can not be found.
  269. * @see java.lang.SecurityManager
  270. */
  271. public static Constructor getAccessibleConstructor(Constructor ctor) {
  272. // Make sure we have a method to check
  273. if (ctor == null) {
  274. return (null);
  275. }
  276. // If the requested method is not public we cannot call it
  277. if (!Modifier.isPublic(ctor.getModifiers())) {
  278. return (null);
  279. }
  280. // If the declaring class is public, we are done
  281. Class clazz = ctor.getDeclaringClass();
  282. if (Modifier.isPublic(clazz.getModifiers())) {
  283. return (ctor);
  284. }
  285. // what else can we do?
  286. return null;
  287. }
  288. // -------------------------------------------------------- Private Methods
  289. /**
  290. * <p>Find an accessible constructor with compatible parameters.
  291. * Compatible parameters mean that every method parameter is assignable from
  292. * the given parameters. In other words, it finds constructor that will take
  293. * the parameters given.</p>
  294. *
  295. * <p>First it checks if there is constructor matching the exact signature.
  296. * If no such, all the constructors of the class are tested if their signatures
  297. * are assignment compatible with the parameter types.
  298. * The first matching constructor is returned.</p>
  299. *
  300. * @param clazz find constructor for this class
  301. * @param parameterTypes find method with compatible parameters
  302. * @return a valid Constructor object. If there's no matching constructor, returns <code>null</code>.
  303. */
  304. private static Constructor getMatchingAccessibleConstructor(
  305. Class clazz,
  306. Class[] parameterTypes) {
  307. // see if we can find the method directly
  308. // most of the time this works and it's much faster
  309. try {
  310. Constructor ctor = clazz.getConstructor(parameterTypes);
  311. try {
  312. //
  313. // XXX Default access superclass workaround
  314. //
  315. // When a public class has a default access superclass
  316. // with public methods, these methods are accessible.
  317. // Calling them from compiled code works fine.
  318. //
  319. // Unfortunately, using reflection to invoke these methods
  320. // seems to (wrongly) to prevent access even when the method
  321. // modifer is public.
  322. //
  323. // The following workaround solves the problem but will only
  324. // work from sufficiently privilages code.
  325. //
  326. // Better workarounds would be greatfully accepted.
  327. //
  328. ctor.setAccessible(true);
  329. } catch (SecurityException se) {}
  330. return ctor;
  331. } catch (NoSuchMethodException e) { /* SWALLOW */
  332. }
  333. // search through all methods
  334. int paramSize = parameterTypes.length;
  335. Constructor[] ctors = clazz.getConstructors();
  336. for (int i = 0, size = ctors.length; i < size; i++) {
  337. // compare parameters
  338. Class[] ctorParams = ctors[i].getParameterTypes();
  339. int ctorParamSize = ctorParams.length;
  340. if (ctorParamSize == paramSize) {
  341. boolean match = true;
  342. for (int n = 0; n < ctorParamSize; n++) {
  343. if (!MethodUtils
  344. .isAssignmentCompatible(
  345. ctorParams[n],
  346. parameterTypes[n])) {
  347. match = false;
  348. break;
  349. }
  350. }
  351. if (match) {
  352. // get accessible version of method
  353. Constructor ctor = getAccessibleConstructor(ctors[i]);
  354. if (ctor != null) {
  355. try {
  356. ctor.setAccessible(true);
  357. } catch (SecurityException se) {}
  358. return ctor;
  359. }
  360. }
  361. }
  362. }
  363. return null;
  364. }
  365. }