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