1 package org.codehaus.plexus.util.introspection;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.lang.reflect.Method;
20 import java.util.ArrayList;
21 import java.util.Hashtable;
22 import java.util.Iterator;
23 import java.util.LinkedList;
24 import java.util.List;
25 import java.util.Map;
26
27
28
29
30
31
32
33
34
35 public class MethodMap {
36 private static final int MORE_SPECIFIC = 0;
37
38 private static final int LESS_SPECIFIC = 1;
39
40 private static final int INCOMPARABLE = 2;
41
42
43
44
45 Map<String, List<Method>> methodByNameMap = new Hashtable<String, List<Method>>();
46
47
48
49
50
51
52
53 public void add(Method method) {
54 String methodName = method.getName();
55
56 List<Method> l = get(methodName);
57
58 if (l == null) {
59 l = new ArrayList<Method>();
60 methodByNameMap.put(methodName, l);
61 }
62
63 l.add(method);
64 }
65
66
67
68
69
70
71
72 public List<Method> get(String key) {
73 return methodByNameMap.get(key);
74 }
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93 public Method find(String methodName, Object[] args) throws AmbiguousException {
94 List<Method> methodList = get(methodName);
95
96 if (methodList == null) {
97 return null;
98 }
99
100 int l = args.length;
101 Class[] classes = new Class[l];
102
103 for (int i = 0; i < l; ++i) {
104 Object arg = args[i];
105
106
107
108
109
110 classes[i] = arg == null ? null : arg.getClass();
111 }
112
113 return getMostSpecific(methodList, classes);
114 }
115
116
117
118
119 public static class AmbiguousException extends Exception {}
120
121 private static Method getMostSpecific(List<Method> methods, Class[] classes) throws AmbiguousException {
122 LinkedList<Method> applicables = getApplicables(methods, classes);
123
124 if (applicables.isEmpty()) {
125 return null;
126 }
127
128 if (applicables.size() == 1) {
129 return applicables.getFirst();
130 }
131
132
133
134
135
136
137 LinkedList<Method> maximals = new LinkedList<Method>();
138
139 for (Method app : applicables) {
140 Class[] appArgs = app.getParameterTypes();
141 boolean lessSpecific = false;
142
143 for (Iterator<Method> maximal = maximals.iterator(); !lessSpecific && maximal.hasNext(); ) {
144 Method max = maximal.next();
145
146 switch (moreSpecific(appArgs, max.getParameterTypes())) {
147 case MORE_SPECIFIC: {
148
149
150
151
152
153 maximal.remove();
154 break;
155 }
156
157 case LESS_SPECIFIC: {
158
159
160
161
162
163 lessSpecific = true;
164 break;
165 }
166 }
167 }
168
169 if (!lessSpecific) {
170 maximals.addLast(app);
171 }
172 }
173
174 if (maximals.size() > 1) {
175
176 throw new AmbiguousException();
177 }
178
179 return maximals.getFirst();
180 }
181
182
183
184
185
186
187
188
189
190
191 private static int moreSpecific(Class[] c1, Class[] c2) {
192 boolean c1MoreSpecific = false;
193 boolean c2MoreSpecific = false;
194
195 for (int i = 0; i < c1.length; ++i) {
196 if (c1[i] != c2[i]) {
197 c1MoreSpecific = c1MoreSpecific || isStrictMethodInvocationConvertible(c2[i], c1[i]);
198 c2MoreSpecific = c2MoreSpecific || isStrictMethodInvocationConvertible(c1[i], c2[i]);
199 }
200 }
201
202 if (c1MoreSpecific) {
203 if (c2MoreSpecific) {
204
205
206
207
208 return INCOMPARABLE;
209 }
210
211 return MORE_SPECIFIC;
212 }
213
214 if (c2MoreSpecific) {
215 return LESS_SPECIFIC;
216 }
217
218
219
220
221
222 return INCOMPARABLE;
223 }
224
225
226
227
228
229
230
231
232
233 private static LinkedList<Method> getApplicables(List<Method> methods, Class[] classes) {
234 LinkedList<Method> list = new LinkedList<Method>();
235
236 for (Object method1 : methods) {
237 Method method = (Method) method1;
238
239 if (isApplicable(method, classes)) {
240 list.add(method);
241 }
242 }
243 return list;
244 }
245
246
247
248
249
250
251
252
253 private static boolean isApplicable(Method method, Class[] classes) {
254 Class[] methodArgs = method.getParameterTypes();
255
256 if (methodArgs.length != classes.length) {
257 return false;
258 }
259
260 for (int i = 0; i < classes.length; ++i) {
261 if (!isMethodInvocationConvertible(methodArgs[i], classes[i])) {
262 return false;
263 }
264 }
265
266 return true;
267 }
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282 private static boolean isMethodInvocationConvertible(Class formal, Class actual) {
283
284
285
286 if (actual == null && !formal.isPrimitive()) {
287 return true;
288 }
289
290
291
292
293
294 if (actual != null && formal.isAssignableFrom(actual)) {
295 return true;
296 }
297
298
299
300
301
302 if (formal.isPrimitive()) {
303 if (formal == Boolean.TYPE && actual == Boolean.class) return true;
304 if (formal == Character.TYPE && actual == Character.class) return true;
305 if (formal == Byte.TYPE && actual == Byte.class) return true;
306 if (formal == Short.TYPE && (actual == Short.class || actual == Byte.class)) return true;
307 if (formal == Integer.TYPE && (actual == Integer.class || actual == Short.class || actual == Byte.class))
308 return true;
309 if (formal == Long.TYPE
310 && (actual == Long.class
311 || actual == Integer.class
312 || actual == Short.class
313 || actual == Byte.class)) return true;
314 if (formal == Float.TYPE
315 && (actual == Float.class
316 || actual == Long.class
317 || actual == Integer.class
318 || actual == Short.class
319 || actual == Byte.class)) return true;
320 if (formal == Double.TYPE
321 && (actual == Double.class
322 || actual == Float.class
323 || actual == Long.class
324 || actual == Integer.class
325 || actual == Short.class
326 || actual == Byte.class)) return true;
327 }
328
329 return false;
330 }
331
332
333
334
335
336
337
338
339
340
341
342 private static boolean isStrictMethodInvocationConvertible(Class formal, Class actual) {
343
344
345
346 if (actual == null && !formal.isPrimitive()) {
347 return true;
348 }
349
350
351
352
353
354 if (formal.isAssignableFrom(actual)) {
355 return true;
356 }
357
358
359
360
361
362 if (formal.isPrimitive()) {
363 if (formal == Short.TYPE && (actual == Byte.TYPE)) return true;
364 if (formal == Integer.TYPE && (actual == Short.TYPE || actual == Byte.TYPE)) return true;
365 if (formal == Long.TYPE && (actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE))
366 return true;
367 if (formal == Float.TYPE
368 && (actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE))
369 return true;
370 if (formal == Double.TYPE
371 && (actual == Float.TYPE
372 || actual == Long.TYPE
373 || actual == Integer.TYPE
374 || actual == Short.TYPE
375 || actual == Byte.TYPE)) return true;
376 }
377 return false;
378 }
379 }