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.jexl.util.introspection;
  17. import java.lang.reflect.Method;
  18. import java.util.ArrayList;
  19. import java.util.Hashtable;
  20. import java.util.Iterator;
  21. import java.util.LinkedList;
  22. import java.util.List;
  23. import java.util.Map;
  24. /**
  25. *
  26. * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
  27. * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
  28. * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
  29. * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
  30. * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
  31. * @version $Id: MethodMap.java,v 1.5 2004/08/19 17:15:59 dion Exp $
  32. */
  33. public class MethodMap
  34. {
  35. private static final int MORE_SPECIFIC = 0;
  36. private static final int LESS_SPECIFIC = 1;
  37. private static final int INCOMPARABLE = 2;
  38. /**
  39. * Keep track of all methods with the same name.
  40. */
  41. Map methodByNameMap = new Hashtable();
  42. /**
  43. * Add a method to a list of methods by name.
  44. * For a particular class we are keeping track
  45. * of all the methods with the same name.
  46. */
  47. public void add(Method method)
  48. {
  49. String methodName = method.getName();
  50. List l = get( methodName );
  51. if ( l == null)
  52. {
  53. l = new ArrayList();
  54. methodByNameMap.put(methodName, l);
  55. }
  56. l.add(method);
  57. }
  58. /**
  59. * Return a list of methods with the same name.
  60. *
  61. * @param String key
  62. * @return List list of methods
  63. */
  64. public List get(String key)
  65. {
  66. return (List) methodByNameMap.get(key);
  67. }
  68. /**
  69. * <p>
  70. * Find a method. Attempts to find the
  71. * most specific applicable method using the
  72. * algorithm described in the JLS section
  73. * 15.12.2 (with the exception that it can't
  74. * distinguish a primitive type argument from
  75. * an object type argument, since in reflection
  76. * primitive type arguments are represented by
  77. * their object counterparts, so for an argument of
  78. * type (say) java.lang.Integer, it will not be able
  79. * to decide between a method that takes int and a
  80. * method that takes java.lang.Integer as a parameter.
  81. * </p>
  82. *
  83. * <p>
  84. * This turns out to be a relatively rare case
  85. * where this is needed - however, functionality
  86. * like this is needed.
  87. * </p>
  88. *
  89. * @param methodName name of method
  90. * @param args the actual arguments with which the method is called
  91. * @return the most specific applicable method, or null if no
  92. * method is applicable.
  93. * @throws AmbiguousException if there is more than one maximally
  94. * specific applicable method
  95. */
  96. public Method find(String methodName, Object[] args)
  97. throws AmbiguousException
  98. {
  99. List methodList = get(methodName);
  100. if (methodList == null)
  101. {
  102. return null;
  103. }
  104. int l = args.length;
  105. Class[] classes = new Class[l];
  106. for(int i = 0; i < l; ++i)
  107. {
  108. Object arg = args[i];
  109. /*
  110. * if we are careful down below, a null argument goes in there
  111. * so we can know that the null was passed to the method
  112. */
  113. classes[i] =
  114. arg == null ? null : arg.getClass();
  115. }
  116. return getMostSpecific(methodList, classes);
  117. }
  118. /**
  119. * simple distinguishable exception, used when
  120. * we run across ambiguous overloading
  121. */
  122. public static class AmbiguousException extends Exception
  123. {
  124. }
  125. private static Method getMostSpecific(List methods, Class[] classes)
  126. throws AmbiguousException
  127. {
  128. LinkedList applicables = getApplicables(methods, classes);
  129. if(applicables.isEmpty())
  130. {
  131. return null;
  132. }
  133. if(applicables.size() == 1)
  134. {
  135. return (Method)applicables.getFirst();
  136. }
  137. /*
  138. * This list will contain the maximally specific methods. Hopefully at
  139. * the end of the below loop, the list will contain exactly one method,
  140. * (the most specific method) otherwise we have ambiguity.
  141. */
  142. LinkedList maximals = new LinkedList();
  143. for (Iterator applicable = applicables.iterator();
  144. applicable.hasNext();)
  145. {
  146. Method app = (Method) applicable.next();
  147. Class[] appArgs = app.getParameterTypes();
  148. boolean lessSpecific = false;
  149. for (Iterator maximal = maximals.iterator();
  150. !lessSpecific && maximal.hasNext();)
  151. {
  152. Method max = (Method) maximal.next();
  153. switch(moreSpecific(appArgs, max.getParameterTypes()))
  154. {
  155. case MORE_SPECIFIC:
  156. {
  157. /*
  158. * This method is more specific than the previously
  159. * known maximally specific, so remove the old maximum.
  160. */
  161. maximal.remove();
  162. break;
  163. }
  164. case LESS_SPECIFIC:
  165. {
  166. /*
  167. * This method is less specific than some of the
  168. * currently known maximally specific methods, so we
  169. * won't add it into the set of maximally specific
  170. * methods
  171. */
  172. lessSpecific = true;
  173. break;
  174. }
  175. }
  176. }
  177. if(!lessSpecific)
  178. {
  179. maximals.addLast(app);
  180. }
  181. }
  182. if(maximals.size() > 1)
  183. {
  184. // We have more than one maximally specific method
  185. throw new AmbiguousException();
  186. }
  187. return (Method)maximals.getFirst();
  188. }
  189. /**
  190. * Determines which method signature (represented by a class array) is more
  191. * specific. This defines a partial ordering on the method signatures.
  192. * @param c1 first signature to compare
  193. * @param c2 second signature to compare
  194. * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if
  195. * c1 is less specific than c2, INCOMPARABLE if they are incomparable.
  196. */
  197. private static int moreSpecific(Class[] c1, Class[] c2)
  198. {
  199. boolean c1MoreSpecific = false;
  200. boolean c2MoreSpecific = false;
  201. for(int i = 0; i < c1.length; ++i)
  202. {
  203. if(c1[i] != c2[i])
  204. {
  205. c1MoreSpecific =
  206. c1MoreSpecific ||
  207. isStrictMethodInvocationConvertible(c2[i], c1[i]);
  208. c2MoreSpecific =
  209. c2MoreSpecific ||
  210. isStrictMethodInvocationConvertible(c1[i], c2[i]);
  211. }
  212. }
  213. if(c1MoreSpecific)
  214. {
  215. if(c2MoreSpecific)
  216. {
  217. /*
  218. * Incomparable due to cross-assignable arguments (i.e.
  219. * foo(String, Object) vs. foo(Object, String))
  220. */
  221. return INCOMPARABLE;
  222. }
  223. return MORE_SPECIFIC;
  224. }
  225. if(c2MoreSpecific)
  226. {
  227. return LESS_SPECIFIC;
  228. }
  229. /*
  230. * Incomparable due to non-related arguments (i.e.
  231. * foo(Runnable) vs. foo(Serializable))
  232. */
  233. return INCOMPARABLE;
  234. }
  235. /**
  236. * Returns all methods that are applicable to actual argument types.
  237. * @param methods list of all candidate methods
  238. * @param classes the actual types of the arguments
  239. * @return a list that contains only applicable methods (number of
  240. * formal and actual arguments matches, and argument types are assignable
  241. * to formal types through a method invocation conversion).
  242. */
  243. private static LinkedList getApplicables(List methods, Class[] classes)
  244. {
  245. LinkedList list = new LinkedList();
  246. for (Iterator imethod = methods.iterator(); imethod.hasNext();)
  247. {
  248. Method method = (Method) imethod.next();
  249. if(isApplicable(method, classes))
  250. {
  251. list.add(method);
  252. }
  253. }
  254. return list;
  255. }
  256. /**
  257. * Returns true if the supplied method is applicable to actual
  258. * argument types.
  259. */
  260. private static boolean isApplicable(Method method, Class[] classes)
  261. {
  262. Class[] methodArgs = method.getParameterTypes();
  263. if(methodArgs.length != classes.length)
  264. {
  265. return false;
  266. }
  267. for(int i = 0; i < classes.length; ++i)
  268. {
  269. if(!isMethodInvocationConvertible(methodArgs[i], classes[i]))
  270. {
  271. return false;
  272. }
  273. }
  274. return true;
  275. }
  276. /**
  277. * Determines whether a type represented by a class object is
  278. * convertible to another type represented by a class object using a
  279. * method invocation conversion, treating object types of primitive
  280. * types as if they were primitive types (that is, a Boolean actual
  281. * parameter type matches boolean primitive formal type). This behavior
  282. * is because this method is used to determine applicable methods for
  283. * an actual parameter list, and primitive types are represented by
  284. * their object duals in reflective method calls.
  285. *
  286. * @param formal the formal parameter type to which the actual
  287. * parameter type should be convertible
  288. * @param actual the actual parameter type.
  289. * @return true if either formal type is assignable from actual type,
  290. * or formal is a primitive type and actual is its corresponding object
  291. * type or an object type of a primitive type that can be converted to
  292. * the formal type.
  293. */
  294. private static boolean isMethodInvocationConvertible(Class formal,
  295. Class actual)
  296. {
  297. /*
  298. * if it's a null, it means the arg was null
  299. */
  300. if (actual == null && !formal.isPrimitive())
  301. {
  302. return true;
  303. }
  304. /*
  305. * Check for identity or widening reference conversion
  306. */
  307. if (actual != null && formal.isAssignableFrom(actual))
  308. {
  309. return true;
  310. }
  311. /*
  312. * Check for boxing with widening primitive conversion. Note that
  313. * actual parameters are never primitives.
  314. */
  315. if (formal.isPrimitive())
  316. {
  317. if(formal == Boolean.TYPE && actual == Boolean.class)
  318. return true;
  319. if(formal == Character.TYPE && actual == Character.class)
  320. return true;
  321. if(formal == Byte.TYPE && actual == Byte.class)
  322. return true;
  323. if(formal == Short.TYPE &&
  324. (actual == Short.class || actual == Byte.class))
  325. return true;
  326. if(formal == Integer.TYPE &&
  327. (actual == Integer.class || actual == Short.class ||
  328. actual == Byte.class))
  329. return true;
  330. if(formal == Long.TYPE &&
  331. (actual == Long.class || actual == Integer.class ||
  332. actual == Short.class || actual == Byte.class))
  333. return true;
  334. if(formal == Float.TYPE &&
  335. (actual == Float.class || actual == Long.class ||
  336. actual == Integer.class || actual == Short.class ||
  337. actual == Byte.class))
  338. return true;
  339. if(formal == Double.TYPE &&
  340. (actual == Double.class || actual == Float.class ||
  341. actual == Long.class || actual == Integer.class ||
  342. actual == Short.class || actual == Byte.class))
  343. return true;
  344. }
  345. return false;
  346. }
  347. /**
  348. * Determines whether a type represented by a class object is
  349. * convertible to another type represented by a class object using a
  350. * method invocation conversion, without matching object and primitive
  351. * types. This method is used to determine the more specific type when
  352. * comparing signatures of methods.
  353. *
  354. * @param formal the formal parameter type to which the actual
  355. * parameter type should be convertible
  356. * @param actual the actual parameter type.
  357. * @return true if either formal type is assignable from actual type,
  358. * or formal and actual are both primitive types and actual can be
  359. * subject to widening conversion to formal.
  360. */
  361. private static boolean isStrictMethodInvocationConvertible(Class formal,
  362. Class actual)
  363. {
  364. /*
  365. * we shouldn't get a null into, but if so
  366. */
  367. if (actual == null && !formal.isPrimitive())
  368. {
  369. return true;
  370. }
  371. /*
  372. * Check for identity or widening reference conversion
  373. */
  374. if(formal.isAssignableFrom(actual))
  375. {
  376. return true;
  377. }
  378. /*
  379. * Check for widening primitive conversion.
  380. */
  381. if(formal.isPrimitive())
  382. {
  383. if(formal == Short.TYPE && (actual == Byte.TYPE))
  384. return true;
  385. if(formal == Integer.TYPE &&
  386. (actual == Short.TYPE || actual == Byte.TYPE))
  387. return true;
  388. if(formal == Long.TYPE &&
  389. (actual == Integer.TYPE || actual == Short.TYPE ||
  390. actual == Byte.TYPE))
  391. return true;
  392. if(formal == Float.TYPE &&
  393. (actual == Long.TYPE || actual == Integer.TYPE ||
  394. actual == Short.TYPE || actual == Byte.TYPE))
  395. return true;
  396. if(formal == Double.TYPE &&
  397. (actual == Float.TYPE || actual == Long.TYPE ||
  398. actual == Integer.TYPE || actual == Short.TYPE ||
  399. actual == Byte.TYPE))
  400. return true;
  401. }
  402. return false;
  403. }
  404. }