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.InvocationTargetException;
  18. import java.lang.reflect.Method;
  19. import java.lang.reflect.Modifier;
  20. import java.util.WeakHashMap;
  21. import org.apache.commons.logging.Log;
  22. import org.apache.commons.logging.LogFactory;
  23. /**
  24. * <p> Utility reflection methods focussed on methods in general rather than properties in particular. </p>
  25. *
  26. * <h3>Known Limitations</h3>
  27. * <h4>Accessing Public Methods In A Default Access Superclass</h4>
  28. * <p>There is an issue when invoking public methods contained in a default access superclass.
  29. * Reflection locates these methods fine and correctly assigns them as public.
  30. * However, an <code>IllegalAccessException</code> is thrown if the method is invoked.</p>
  31. *
  32. * <p><code>MethodUtils</code> contains a workaround for this situation.
  33. * It will attempt to call <code>setAccessible</code> on this method.
  34. * If this call succeeds, then the method can be invoked as normal.
  35. * This call will only succeed when the application has sufficient security privilages.
  36. * If this call fails then a warning will be logged and the method may fail.</p>
  37. *
  38. * @author Craig R. McClanahan
  39. * @author Ralph Schaer
  40. * @author Chris Audley
  41. * @author Rey François
  42. * @author Gregor Raıman
  43. * @author Jan Sorensen
  44. * @author Robert Burrell Donkin
  45. */
  46. public class MethodUtils {
  47. // --------------------------------------------------------- Private Methods
  48. /**
  49. * All logging goes through this logger
  50. */
  51. private static Log log = LogFactory.getLog(MethodUtils.class);
  52. /** Only log warning about accessibility work around once */
  53. private static boolean loggedAccessibleWarning = false;
  54. /** An empty class array */
  55. private static final Class[] emptyClassArray = new Class[0];
  56. /** An empty object array */
  57. private static final Object[] emptyObjectArray = new Object[0];
  58. /**
  59. * Stores a cache of Methods against MethodDescriptors, in a WeakHashMap.
  60. */
  61. private static WeakHashMap cache = new WeakHashMap();
  62. // --------------------------------------------------------- Public Methods
  63. /**
  64. * <p>Invoke a named method whose parameter type matches the object type.</p>
  65. *
  66. * <p>The behaviour of this method is less deterministic
  67. * than {@link #invokeExactMethod}.
  68. * It loops through all methods with names that match
  69. * and then executes the first it finds with compatable parameters.</p>
  70. *
  71. * <p>This method supports calls to methods taking primitive parameters
  72. * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
  73. * would match a <code>boolean</code> primitive.</p>
  74. *
  75. * <p> This is a convenient wrapper for
  76. * {@link #invokeMethod(Object object,String methodName,Object [] args)}.
  77. * </p>
  78. *
  79. * @param object invoke method on this object
  80. * @param methodName get method with this name
  81. * @param arg use this argument
  82. *
  83. * @throws NoSuchMethodException if there is no such accessible method
  84. * @throws InvocationTargetException wraps an exception thrown by the
  85. * method invoked
  86. * @throws IllegalAccessException if the requested method is not accessible
  87. * via reflection
  88. */
  89. public static Object invokeMethod(
  90. Object object,
  91. String methodName,
  92. Object arg)
  93. throws
  94. NoSuchMethodException,
  95. IllegalAccessException,
  96. InvocationTargetException {
  97. Object[] args = {arg};
  98. return invokeMethod(object, methodName, args);
  99. }
  100. /**
  101. * <p>Invoke a named method whose parameter type matches the object type.</p>
  102. *
  103. * <p>The behaviour of this method is less deterministic
  104. * than {@link #invokeExactMethod(Object object,String methodName,Object [] args)}.
  105. * It loops through all methods with names that match
  106. * and then executes the first it finds with compatable parameters.</p>
  107. *
  108. * <p>This method supports calls to methods taking primitive parameters
  109. * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
  110. * would match a <code>boolean</code> primitive.</p>
  111. *
  112. * <p> This is a convenient wrapper for
  113. * {@link #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
  114. * </p>
  115. *
  116. * @param object invoke method on this object
  117. * @param methodName get method with this name
  118. * @param args use these arguments - treat null as empty array
  119. *
  120. * @throws NoSuchMethodException if there is no such accessible method
  121. * @throws InvocationTargetException wraps an exception thrown by the
  122. * method invoked
  123. * @throws IllegalAccessException if the requested method is not accessible
  124. * via reflection
  125. */
  126. public static Object invokeMethod(
  127. Object object,
  128. String methodName,
  129. Object[] args)
  130. throws
  131. NoSuchMethodException,
  132. IllegalAccessException,
  133. InvocationTargetException {
  134. if (args == null) {
  135. args = emptyObjectArray;
  136. }
  137. int arguments = args.length;
  138. Class parameterTypes [] = new Class[arguments];
  139. for (int i = 0; i < arguments; i++) {
  140. parameterTypes[i] = args[i].getClass();
  141. }
  142. return invokeMethod(object, methodName, args, parameterTypes);
  143. }
  144. /**
  145. * <p>Invoke a named method whose parameter type matches the object type.</p>
  146. *
  147. * <p>The behaviour of this method is less deterministic
  148. * than {@link
  149. * #invokeExactMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
  150. * It loops through all methods with names that match
  151. * and then executes the first it finds with compatable parameters.</p>
  152. *
  153. * <p>This method supports calls to methods taking primitive parameters
  154. * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
  155. * would match a <code>boolean</code> primitive.</p>
  156. *
  157. *
  158. * @param object invoke method on this object
  159. * @param methodName get method with this name
  160. * @param args use these arguments - treat null as empty array
  161. * @param parameterTypes match these parameters - treat null as empty array
  162. *
  163. * @throws NoSuchMethodException if there is no such accessible method
  164. * @throws InvocationTargetException wraps an exception thrown by the
  165. * method invoked
  166. * @throws IllegalAccessException if the requested method is not accessible
  167. * via reflection
  168. */
  169. public static Object invokeMethod(
  170. Object object,
  171. String methodName,
  172. Object[] args,
  173. Class[] parameterTypes)
  174. throws
  175. NoSuchMethodException,
  176. IllegalAccessException,
  177. InvocationTargetException {
  178. if (parameterTypes == null) {
  179. parameterTypes = emptyClassArray;
  180. }
  181. if (args == null) {
  182. args = emptyObjectArray;
  183. }
  184. Method method = getMatchingAccessibleMethod(
  185. object.getClass(),
  186. methodName,
  187. parameterTypes);
  188. if (method == null)
  189. throw new NoSuchMethodException("No such accessible method: " +
  190. methodName + "() on object: " + object.getClass().getName());
  191. return method.invoke(object, args);
  192. }
  193. /**
  194. * <p>Invoke a method whose parameter type matches exactly the object
  195. * type.</p>
  196. *
  197. * <p> This is a convenient wrapper for
  198. * {@link #invokeExactMethod(Object object,String methodName,Object [] args)}.
  199. * </p>
  200. *
  201. * @param object invoke method on this object
  202. * @param methodName get method with this name
  203. * @param arg use this argument
  204. *
  205. * @throws NoSuchMethodException if there is no such accessible method
  206. * @throws InvocationTargetException wraps an exception thrown by the
  207. * method invoked
  208. * @throws IllegalAccessException if the requested method is not accessible
  209. * via reflection
  210. */
  211. public static Object invokeExactMethod(
  212. Object object,
  213. String methodName,
  214. Object arg)
  215. throws
  216. NoSuchMethodException,
  217. IllegalAccessException,
  218. InvocationTargetException {
  219. Object[] args = {arg};
  220. return invokeExactMethod(object, methodName, args);
  221. }
  222. /**
  223. * <p>Invoke a method whose parameter types match exactly the object
  224. * types.</p>
  225. *
  226. * <p> This uses reflection to invoke the method obtained from a call to
  227. * {@link #getAccessibleMethod}.</p>
  228. *
  229. * @param object invoke method on this object
  230. * @param methodName get method with this name
  231. * @param args use these arguments - treat null as empty array
  232. *
  233. * @throws NoSuchMethodException if there is no such accessible method
  234. * @throws InvocationTargetException wraps an exception thrown by the
  235. * method invoked
  236. * @throws IllegalAccessException if the requested method is not accessible
  237. * via reflection
  238. */
  239. public static Object invokeExactMethod(
  240. Object object,
  241. String methodName,
  242. Object[] args)
  243. throws
  244. NoSuchMethodException,
  245. IllegalAccessException,
  246. InvocationTargetException {
  247. if (args == null) {
  248. args = emptyObjectArray;
  249. }
  250. int arguments = args.length;
  251. Class parameterTypes [] = new Class[arguments];
  252. for (int i = 0; i < arguments; i++) {
  253. parameterTypes[i] = args[i].getClass();
  254. }
  255. return invokeExactMethod(object, methodName, args, parameterTypes);
  256. }
  257. /**
  258. * <p>Invoke a method whose parameter types match exactly the parameter
  259. * types given.</p>
  260. *
  261. * <p>This uses reflection to invoke the method obtained from a call to
  262. * {@link #getAccessibleMethod}.</p>
  263. *
  264. * @param object invoke method on this object
  265. * @param methodName get method with this name
  266. * @param args use these arguments - treat null as empty array
  267. * @param parameterTypes match these parameters - treat null as empty array
  268. *
  269. * @throws NoSuchMethodException if there is no such accessible method
  270. * @throws InvocationTargetException wraps an exception thrown by the
  271. * method invoked
  272. * @throws IllegalAccessException if the requested method is not accessible
  273. * via reflection
  274. */
  275. public static Object invokeExactMethod(
  276. Object object,
  277. String methodName,
  278. Object[] args,
  279. Class[] parameterTypes)
  280. throws
  281. NoSuchMethodException,
  282. IllegalAccessException,
  283. InvocationTargetException {
  284. if (args == null) {
  285. args = emptyObjectArray;
  286. }
  287. if (parameterTypes == null) {
  288. parameterTypes = emptyClassArray;
  289. }
  290. Method method = getAccessibleMethod(
  291. object.getClass(),
  292. methodName,
  293. parameterTypes);
  294. if (method == null)
  295. throw new NoSuchMethodException("No such accessible method: " +
  296. methodName + "() on object: " + object.getClass().getName());
  297. return method.invoke(object, args);
  298. }
  299. /**
  300. * <p>Return an accessible method (that is, one that can be invoked via
  301. * reflection) with given name and a single parameter. If no such method
  302. * can be found, return <code>null</code>.
  303. * Basically, a convenience wrapper that constructs a <code>Class</code>
  304. * array for you.</p>
  305. *
  306. * @param clazz get method from this class
  307. * @param methodName get method with this name
  308. * @param parameterType taking this type of parameter
  309. */
  310. public static Method getAccessibleMethod(
  311. Class clazz,
  312. String methodName,
  313. Class parameterType) {
  314. Class[] parameterTypes = {parameterType};
  315. return getAccessibleMethod(clazz, methodName, parameterTypes);
  316. }
  317. /**
  318. * <p>Return an accessible method (that is, one that can be invoked via
  319. * reflection) with given name and parameters. If no such method
  320. * can be found, return <code>null</code>.
  321. * This is just a convenient wrapper for
  322. * {@link #getAccessibleMethod(Method method)}.</p>
  323. *
  324. * @param clazz get method from this class
  325. * @param methodName get method with this name
  326. * @param parameterTypes with these parameters types
  327. */
  328. public static Method getAccessibleMethod(
  329. Class clazz,
  330. String methodName,
  331. Class[] parameterTypes) {
  332. try {
  333. MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, true);
  334. // Check the cache first
  335. Method method = (Method)cache.get(md);
  336. if (method != null) {
  337. return method;
  338. }
  339. method = getAccessibleMethod
  340. (clazz.getMethod(methodName, parameterTypes));
  341. cache.put(md, method);
  342. return method;
  343. } catch (NoSuchMethodException e) {
  344. return (null);
  345. }
  346. }
  347. /**
  348. * <p>Return an accessible method (that is, one that can be invoked via
  349. * reflection) that implements the specified Method. If no such method
  350. * can be found, return <code>null</code>.</p>
  351. *
  352. * @param method The method that we wish to call
  353. */
  354. public static Method getAccessibleMethod(Method method) {
  355. // Make sure we have a method to check
  356. if (method == null) {
  357. return (null);
  358. }
  359. // If the requested method is not public we cannot call it
  360. if (!Modifier.isPublic(method.getModifiers())) {
  361. return (null);
  362. }
  363. // If the declaring class is public, we are done
  364. Class clazz = method.getDeclaringClass();
  365. if (Modifier.isPublic(clazz.getModifiers())) {
  366. return (method);
  367. }
  368. // Check the implemented interfaces and subinterfaces
  369. method =
  370. getAccessibleMethodFromInterfaceNest(clazz,
  371. method.getName(),
  372. method.getParameterTypes());
  373. return (method);
  374. }
  375. // -------------------------------------------------------- Private Methods
  376. /**
  377. * <p>Return an accessible method (that is, one that can be invoked via
  378. * reflection) that implements the specified method, by scanning through
  379. * all implemented interfaces and subinterfaces. If no such method
  380. * can be found, return <code>null</code>.</p>
  381. *
  382. * <p> There isn't any good reason why this method must be private.
  383. * It is because there doesn't seem any reason why other classes should
  384. * call this rather than the higher level methods.</p>
  385. *
  386. * @param clazz Parent class for the interfaces to be checked
  387. * @param methodName Method name of the method we wish to call
  388. * @param parameterTypes The parameter type signatures
  389. */
  390. private static Method getAccessibleMethodFromInterfaceNest
  391. (Class clazz, String methodName, Class parameterTypes[]) {
  392. Method method = null;
  393. // Search up the superclass chain
  394. for (; clazz != null; clazz = clazz.getSuperclass()) {
  395. // Check the implemented interfaces of the parent class
  396. Class interfaces[] = clazz.getInterfaces();
  397. for (int i = 0; i < interfaces.length; i++) {
  398. // Is this interface public?
  399. if (!Modifier.isPublic(interfaces[i].getModifiers()))
  400. continue;
  401. // Does the method exist on this interface?
  402. try {
  403. method = interfaces[i].getDeclaredMethod(methodName,
  404. parameterTypes);
  405. } catch (NoSuchMethodException e) {
  406. ;
  407. }
  408. if (method != null)
  409. break;
  410. // Recursively check our parent interfaces
  411. method =
  412. getAccessibleMethodFromInterfaceNest(interfaces[i],
  413. methodName,
  414. parameterTypes);
  415. if (method != null)
  416. break;
  417. }
  418. }
  419. // If we found a method return it
  420. if (method != null)
  421. return (method);
  422. // We did not find anything
  423. return (null);
  424. }
  425. /**
  426. * <p>Find an accessible method that matches the given name and has compatible parameters.
  427. * Compatible parameters mean that every method parameter is assignable from
  428. * the given parameters.
  429. * In other words, it finds a method with the given name
  430. * that will take the parameters given.<p>
  431. *
  432. * <p>This method is slightly undeterminstic since it loops
  433. * through methods names and return the first matching method.</p>
  434. *
  435. * <p>This method is used by
  436. * {@link
  437. * #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
  438. *
  439. * <p>This method can match primitive parameter by passing in wrapper classes.
  440. * For example, a <code>Boolean</code> will match a primitive <code>boolean</code>
  441. * parameter.
  442. *
  443. * @param clazz find method in this class
  444. * @param methodName find method with this name
  445. * @param parameterTypes find method with compatible parameters
  446. */
  447. public static Method getMatchingAccessibleMethod(
  448. Class clazz,
  449. String methodName,
  450. Class[] parameterTypes) {
  451. // trace logging
  452. if (log.isTraceEnabled()) {
  453. log.trace("Matching name=" + methodName + " on " + clazz);
  454. }
  455. MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, false);
  456. // see if we can find the method directly
  457. // most of the time this works and it's much faster
  458. try {
  459. // Check the cache first
  460. Method method = (Method)cache.get(md);
  461. if (method != null) {
  462. return method;
  463. }
  464. method = clazz.getMethod(methodName, parameterTypes);
  465. if (log.isTraceEnabled()) {
  466. log.trace("Found straight match: " + method);
  467. log.trace("isPublic:" + Modifier.isPublic(method.getModifiers()));
  468. }
  469. try {
  470. //
  471. // XXX Default access superclass workaround
  472. //
  473. // When a public class has a default access superclass
  474. // with public methods, these methods are accessible.
  475. // Calling them from compiled code works fine.
  476. //
  477. // Unfortunately, using reflection to invoke these methods
  478. // seems to (wrongly) to prevent access even when the method
  479. // modifer is public.
  480. //
  481. // The following workaround solves the problem but will only
  482. // work from sufficiently privilages code.
  483. //
  484. // Better workarounds would be greatfully accepted.
  485. //
  486. method.setAccessible(true);
  487. } catch (SecurityException se) {
  488. // log but continue just in case the method.invoke works anyway
  489. if (!loggedAccessibleWarning) {
  490. boolean vunerableJVM = false;
  491. try {
  492. String specVersion = System.getProperty("java.specification.version");
  493. if (specVersion.charAt(0) == '1' &&
  494. (specVersion.charAt(0) == '0' ||
  495. specVersion.charAt(0) == '1' ||
  496. specVersion.charAt(0) == '2' ||
  497. specVersion.charAt(0) == '3')) {
  498. vunerableJVM = true;
  499. }
  500. } catch (SecurityException e) {
  501. // don't know - so display warning
  502. vunerableJVM = true;
  503. }
  504. if (vunerableJVM) {
  505. log.warn(
  506. "Current Security Manager restricts use of workarounds for reflection bugs "
  507. + " in pre-1.4 JVMs.");
  508. }
  509. loggedAccessibleWarning = true;
  510. }
  511. log.debug(
  512. "Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.",
  513. se);
  514. }
  515. cache.put(md, method);
  516. return method;
  517. } catch (NoSuchMethodException e) { /* SWALLOW */ }
  518. // search through all methods
  519. int paramSize = parameterTypes.length;
  520. Method[] methods = clazz.getMethods();
  521. for (int i = 0, size = methods.length; i < size ; i++) {
  522. if (methods[i].getName().equals(methodName)) {
  523. // log some trace information
  524. if (log.isTraceEnabled()) {
  525. log.trace("Found matching name:");
  526. log.trace(methods[i]);
  527. }
  528. // compare parameters
  529. Class[] methodsParams = methods[i].getParameterTypes();
  530. int methodParamSize = methodsParams.length;
  531. if (methodParamSize == paramSize) {
  532. boolean match = true;
  533. for (int n = 0 ; n < methodParamSize; n++) {
  534. if (log.isTraceEnabled()) {
  535. log.trace("Param=" + parameterTypes[n].getName());
  536. log.trace("Method=" + methodsParams[n].getName());
  537. }
  538. if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) {
  539. if (log.isTraceEnabled()) {
  540. log.trace(methodsParams[n] + " is not assignable from "
  541. + parameterTypes[n]);
  542. }
  543. match = false;
  544. break;
  545. }
  546. }
  547. if (match) {
  548. // get accessible version of method
  549. Method method = getAccessibleMethod(methods[i]);
  550. if (method != null) {
  551. if (log.isTraceEnabled()) {
  552. log.trace(method + " accessible version of "
  553. + methods[i]);
  554. }
  555. try {
  556. //
  557. // XXX Default access superclass workaround
  558. // (See above for more details.)
  559. //
  560. method.setAccessible(true);
  561. } catch (SecurityException se) {
  562. // log but continue just in case the method.invoke works anyway
  563. if (!loggedAccessibleWarning) {
  564. log.warn(
  565. "Cannot use JVM pre-1.4 access bug workaround due to restrictive security manager.");
  566. loggedAccessibleWarning = true;
  567. }
  568. log.debug(
  569. "Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.",
  570. se);
  571. }
  572. cache.put(md, method);
  573. return method;
  574. }
  575. log.trace("Couldn't find accessible method.");
  576. }
  577. }
  578. }
  579. }
  580. // didn't find a match
  581. log.trace("No match found.");
  582. return null;
  583. }
  584. /**
  585. * <p>Determine whether a type can be used as a parameter in a method invocation.
  586. * This method handles primitive conversions correctly.</p>
  587. *
  588. * <p>In order words, it will match a <code>Boolean</code> to a <code>boolean</code>,
  589. * a <code>Long</code> to a <code>long</code>,
  590. * a <code>Float</code> to a <code>float</code>,
  591. * a <code>Integer</code> to a <code>int</code>,
  592. * and a <code>Double</code> to a <code>double</code>.
  593. * Now logic widening matches are allowed.
  594. * For example, a <code>Long</code> will not match a <code>int</code>.
  595. *
  596. * @param parameterType the type of parameter accepted by the method
  597. * @param parameterization the type of parameter being tested
  598. *
  599. * @return true if the assignement is compatible.
  600. */
  601. public static final boolean isAssignmentCompatible(Class parameterType, Class parameterization) {
  602. // try plain assignment
  603. if (parameterType.isAssignableFrom(parameterization)) {
  604. return true;
  605. }
  606. if (parameterType.isPrimitive()) {
  607. // this method does *not* do widening - you must specify exactly
  608. // is this the right behaviour?
  609. Class parameterWrapperClazz = getPrimitiveWrapper(parameterType);
  610. if (parameterWrapperClazz != null) {
  611. return parameterWrapperClazz.equals(parameterization);
  612. }
  613. }
  614. return false;
  615. }
  616. /**
  617. * Gets the wrapper object class for the given primitive type class.
  618. * For example, passing <code>boolean.class</code> returns <code>Boolean.class</code>
  619. * @param primitiveType the primitive type class for which a match is to be found
  620. * @return the wrapper type associated with the given primitive
  621. * or null if no match is found
  622. */
  623. public static Class getPrimitiveWrapper(Class primitiveType) {
  624. // does anyone know a better strategy than comparing names?
  625. if (boolean.class.equals(primitiveType)) {
  626. return Boolean.class;
  627. } else if (float.class.equals(primitiveType)) {
  628. return Float.class;
  629. } else if (long.class.equals(primitiveType)) {
  630. return Long.class;
  631. } else if (int.class.equals(primitiveType)) {
  632. return Integer.class;
  633. } else if (short.class.equals(primitiveType)) {
  634. return Short.class;
  635. } else if (byte.class.equals(primitiveType)) {
  636. return Byte.class;
  637. } else if (double.class.equals(primitiveType)) {
  638. return Double.class;
  639. } else if (char.class.equals(primitiveType)) {
  640. return Character.class;
  641. } else {
  642. return null;
  643. }
  644. }
  645. /**
  646. * Gets the class for the primitive type corresponding to the primitive wrapper class given.
  647. * For example, an instance of <code>Boolean.class</code> returns a <code>boolean.class</code>.
  648. * @param wrapperType the
  649. * @return the primitive type class corresponding to the given wrapper class,
  650. * null if no match is found
  651. */
  652. public static Class getPrimitiveType(Class wrapperType) {
  653. // does anyone know a better strategy than comparing names?
  654. if (Boolean.class.equals(wrapperType)) {
  655. return boolean.class;
  656. } else if (Float.class.equals(wrapperType)) {
  657. return float.class;
  658. } else if (Long.class.equals(wrapperType)) {
  659. return long.class;
  660. } else if (Integer.class.equals(wrapperType)) {
  661. return int.class;
  662. } else if (Short.class.equals(wrapperType)) {
  663. return short.class;
  664. } else if (Byte.class.equals(wrapperType)) {
  665. return byte.class;
  666. } else if (Double.class.equals(wrapperType)) {
  667. return double.class;
  668. } else if (Character.class.equals(wrapperType)) {
  669. return char.class;
  670. } else {
  671. if (log.isDebugEnabled()) {
  672. log.debug("Not a known primitive wrapper class: " + wrapperType);
  673. }
  674. return null;
  675. }
  676. }
  677. /**
  678. * Find a non primitive representation for given primitive class.
  679. *
  680. * @param clazz the class to find a representation for, not null
  681. * @return the original class if it not a primitive. Otherwise the wrapper class. Not null
  682. */
  683. public static Class toNonPrimitiveClass(Class clazz) {
  684. if (clazz.isPrimitive()) {
  685. Class primitiveClazz = MethodUtils.getPrimitiveWrapper(clazz);
  686. // the above method returns
  687. if (primitiveClazz != null) {
  688. return primitiveClazz;
  689. } else {
  690. return clazz;
  691. }
  692. } else {
  693. return clazz;
  694. }
  695. }
  696. /**
  697. * Represents the key to looking up a Method by reflection.
  698. */
  699. private static class MethodDescriptor {
  700. private Class cls;
  701. private String methodName;
  702. private Class[] paramTypes;
  703. private boolean exact;
  704. private int hashCode;
  705. /**
  706. * The sole constructor.
  707. *
  708. * @param cls the class to reflect, must not be null
  709. * @param methodName the method name to obtain
  710. * @param paramTypes the array of classes representing the paramater types
  711. * @param exact whether the match has to be exact.
  712. */
  713. public MethodDescriptor(Class cls, String methodName, Class[] paramTypes, boolean exact) {
  714. if (cls == null) {
  715. throw new IllegalArgumentException("Class cannot be null");
  716. }
  717. if (methodName == null) {
  718. throw new IllegalArgumentException("Method Name cannot be null");
  719. }
  720. if (paramTypes == null) {
  721. paramTypes = emptyClassArray;
  722. }
  723. this.cls = cls;
  724. this.methodName = methodName;
  725. this.paramTypes = paramTypes;
  726. this.exact= exact;
  727. this.hashCode = methodName.length();
  728. }
  729. /**
  730. * Checks for equality.
  731. * @param obj object to be tested for equality
  732. * @return true, if the object describes the same Method.
  733. */
  734. public boolean equals(Object obj) {
  735. if (!(obj instanceof MethodDescriptor)) {
  736. return false;
  737. }
  738. MethodDescriptor md = (MethodDescriptor)obj;
  739. return (
  740. exact == md.exact &&
  741. methodName.equals(md.methodName) &&
  742. cls.equals(md.cls) &&
  743. java.util.Arrays.equals(paramTypes, md.paramTypes)
  744. );
  745. }
  746. /**
  747. * Returns the string length of method name. I.e. if the
  748. * hashcodes are different, the objects are different. If the
  749. * hashcodes are the same, need to use the equals method to
  750. * determine equality.
  751. * @return the string length of method name.
  752. */
  753. public int hashCode() {
  754. return hashCode;
  755. }
  756. }
  757. }