1. /*
  2. * Copyright 1999-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.jxpath.util;
  17. import java.lang.reflect.Constructor;
  18. import java.lang.reflect.Method;
  19. import java.lang.reflect.Modifier;
  20. import java.util.Arrays;
  21. import org.apache.commons.jxpath.ExpressionContext;
  22. import org.apache.commons.jxpath.JXPathException;
  23. /**
  24. * Method lookup utilities, which find static and non-static methods as well
  25. * as constructors based on a name and list of parameters.
  26. *
  27. * @author Dmitri Plotnikov
  28. * @version $Revision: 1.7 $ $Date: 2004/02/29 14:17:43 $
  29. */
  30. public class MethodLookupUtils {
  31. private static final int NO_MATCH = 0;
  32. private static final int APPROXIMATE_MATCH = 1;
  33. private static final int EXACT_MATCH = 2;
  34. private static final Object[] EMPTY_ARRAY = new Object[0];
  35. public static Constructor lookupConstructor(
  36. Class targetClass,
  37. Object[] parameters)
  38. {
  39. boolean tryExact = true;
  40. int count = parameters == null ? 0 : parameters.length;
  41. Class types[] = new Class[count];
  42. for (int i = 0; i < count; i++) {
  43. Object param = parameters[i];
  44. if (param != null) {
  45. types[i] = param.getClass();
  46. }
  47. else {
  48. types[i] = null;
  49. tryExact = false;
  50. }
  51. }
  52. Constructor constructor = null;
  53. if (tryExact) {
  54. // First - without type conversion
  55. try {
  56. constructor = targetClass.getConstructor(types);
  57. if (constructor != null) {
  58. return constructor;
  59. }
  60. }
  61. catch (NoSuchMethodException ex) {
  62. // Ignore
  63. }
  64. }
  65. int currentMatch = 0;
  66. boolean ambiguous = false;
  67. // Then - with type conversion
  68. Constructor[] constructors = targetClass.getConstructors();
  69. for (int i = 0; i < constructors.length; i++) {
  70. int match =
  71. matchParameterTypes(
  72. constructors[i].getParameterTypes(),
  73. parameters);
  74. if (match != NO_MATCH) {
  75. if (match > currentMatch) {
  76. constructor = constructors[i];
  77. currentMatch = match;
  78. ambiguous = false;
  79. }
  80. else if (match == currentMatch) {
  81. ambiguous = true;
  82. }
  83. }
  84. }
  85. if (ambiguous) {
  86. throw new JXPathException(
  87. "Ambigous constructor " + Arrays.asList(parameters));
  88. }
  89. return constructor;
  90. }
  91. public static Method lookupStaticMethod(
  92. Class targetClass,
  93. String name,
  94. Object[] parameters)
  95. {
  96. boolean tryExact = true;
  97. int count = parameters == null ? 0 : parameters.length;
  98. Class types[] = new Class[count];
  99. for (int i = 0; i < count; i++) {
  100. Object param = parameters[i];
  101. if (param != null) {
  102. types[i] = param.getClass();
  103. }
  104. else {
  105. types[i] = null;
  106. tryExact = false;
  107. }
  108. }
  109. Method method = null;
  110. if (tryExact) {
  111. // First - without type conversion
  112. try {
  113. method = targetClass.getMethod(name, types);
  114. if (method != null
  115. && Modifier.isStatic(method.getModifiers())) {
  116. return method;
  117. }
  118. }
  119. catch (NoSuchMethodException ex) {
  120. // Ignore
  121. }
  122. }
  123. int currentMatch = 0;
  124. boolean ambiguous = false;
  125. // Then - with type conversion
  126. Method[] methods = targetClass.getMethods();
  127. for (int i = 0; i < methods.length; i++) {
  128. if (Modifier.isStatic(methods[i].getModifiers())
  129. && methods[i].getName().equals(name)) {
  130. int match =
  131. matchParameterTypes(
  132. methods[i].getParameterTypes(),
  133. parameters);
  134. if (match != NO_MATCH) {
  135. if (match > currentMatch) {
  136. method = methods[i];
  137. currentMatch = match;
  138. ambiguous = false;
  139. }
  140. else if (match == currentMatch) {
  141. ambiguous = true;
  142. }
  143. }
  144. }
  145. }
  146. if (ambiguous) {
  147. throw new JXPathException("Ambigous method call: " + name);
  148. }
  149. return method;
  150. }
  151. public static Method lookupMethod(
  152. Class targetClass,
  153. String name,
  154. Object[] parameters)
  155. {
  156. if (parameters == null
  157. || parameters.length < 1
  158. || parameters[0] == null) {
  159. return null;
  160. }
  161. if (matchType(targetClass, parameters[0]) == NO_MATCH) {
  162. return null;
  163. }
  164. targetClass = TypeUtils.convert(parameters[0], targetClass).getClass();
  165. boolean tryExact = true;
  166. int count = parameters.length - 1;
  167. Class types[] = new Class[count];
  168. Object arguments[] = new Object[count];
  169. for (int i = 0; i < count; i++) {
  170. Object param = parameters[i + 1];
  171. arguments[i] = param;
  172. if (param != null) {
  173. types[i] = param.getClass();
  174. }
  175. else {
  176. types[i] = null;
  177. tryExact = false;
  178. }
  179. }
  180. Method method = null;
  181. if (tryExact) {
  182. // First - without type conversion
  183. try {
  184. method = targetClass.getMethod(name, types);
  185. if (method != null
  186. && !Modifier.isStatic(method.getModifiers())) {
  187. return method;
  188. }
  189. }
  190. catch (NoSuchMethodException ex) {
  191. // Ignore
  192. }
  193. }
  194. int currentMatch = 0;
  195. boolean ambiguous = false;
  196. // Then - with type conversion
  197. Method[] methods = targetClass.getMethods();
  198. for (int i = 0; i < methods.length; i++) {
  199. if (!Modifier.isStatic(methods[i].getModifiers())
  200. && methods[i].getName().equals(name)) {
  201. int match =
  202. matchParameterTypes(
  203. methods[i].getParameterTypes(),
  204. arguments);
  205. if (match != NO_MATCH) {
  206. if (match > currentMatch) {
  207. method = methods[i];
  208. currentMatch = match;
  209. ambiguous = false;
  210. }
  211. else if (match == currentMatch) {
  212. ambiguous = true;
  213. }
  214. }
  215. }
  216. }
  217. if (ambiguous) {
  218. throw new JXPathException("Ambigous method call: " + name);
  219. }
  220. return method;
  221. }
  222. private static int matchParameterTypes(
  223. Class types[],
  224. Object parameters[])
  225. {
  226. int pi = 0;
  227. if (types.length >= 1
  228. && ExpressionContext.class.isAssignableFrom(types[0])) {
  229. pi++;
  230. }
  231. int length = parameters == null ? 0 : parameters.length;
  232. if (types.length != length + pi) {
  233. return NO_MATCH;
  234. }
  235. int totalMatch = EXACT_MATCH;
  236. for (int i = 0; i < length; i++) {
  237. int match = matchType(types[i + pi], parameters[i]);
  238. if (match == NO_MATCH) {
  239. return NO_MATCH;
  240. }
  241. if (match < totalMatch) {
  242. totalMatch = match;
  243. }
  244. }
  245. return totalMatch;
  246. }
  247. private static int matchType(Class expected, Object object) {
  248. if (object == null) {
  249. return APPROXIMATE_MATCH;
  250. }
  251. Class actual = object.getClass();
  252. if (expected.equals(actual)) {
  253. return EXACT_MATCH;
  254. }
  255. if (expected.isAssignableFrom(actual)) {
  256. return EXACT_MATCH;
  257. }
  258. if (TypeUtils.canConvert(object, expected)) {
  259. return APPROXIMATE_MATCH;
  260. }
  261. return NO_MATCH;
  262. }
  263. }