Mercurial > hg > Members > sugi > MessagePack-java
comparison src/main/java/org/msgpack/template/builder/beans/Statement.java @ 0:cb825acd883a
first commit
author | sugi |
---|---|
date | Sat, 18 Oct 2014 15:06:15 +0900 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:cb825acd883a |
---|---|
1 // MODIFIED FOR THE MSGPACK PROJECT | |
2 // Licensed to the Apache Software Foundation (ASF) under one or more | |
3 // contributor license agreements. See the NOTICE file distributed with | |
4 // this work for additional information regarding copyright ownership. | |
5 // The ASF licenses this file to You under the Apache License, Version 2.0 | |
6 // (the "License"); you may not use this file except in compliance with | |
7 // the License. You may obtain a copy of the License at | |
8 // | |
9 // http://www.apache.org/licenses/LICENSE-2.0 | |
10 // | |
11 // Unless required by applicable law or agreed to in writing, software | |
12 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
13 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
14 // License for the specific language governing permissions and limitations under | |
15 // the License. | |
16 // | |
17 | |
18 package org.msgpack.template.builder.beans; | |
19 | |
20 import java.lang.reflect.Array; | |
21 import java.lang.reflect.Constructor; | |
22 import java.lang.reflect.InvocationTargetException; | |
23 import java.lang.reflect.Method; | |
24 import java.lang.reflect.Modifier; | |
25 import java.security.PrivilegedAction; | |
26 import java.util.ArrayList; | |
27 import java.util.Arrays; | |
28 import java.util.Comparator; | |
29 import java.util.HashMap; | |
30 import java.util.Iterator; | |
31 import java.util.Map; | |
32 import java.util.WeakHashMap; | |
33 | |
34 import org.apache.harmony.beans.BeansUtils; | |
35 import org.apache.harmony.beans.internal.nls.Messages; | |
36 | |
37 public class Statement { | |
38 | |
39 private Object target; | |
40 | |
41 private String methodName; | |
42 | |
43 private Object[] arguments; | |
44 | |
45 // cache used methods of specified target class to accelerate method search | |
46 private static WeakHashMap<Class<?>, Method[]> classMethodsCache = new WeakHashMap<Class<?>, Method[]>(); | |
47 | |
48 public Statement(Object target, String methodName, Object[] arguments) { | |
49 this.target = target; | |
50 this.methodName = methodName; | |
51 this.arguments = arguments == null ? BeansUtils.EMPTY_OBJECT_ARRAY | |
52 : arguments; | |
53 } | |
54 | |
55 @Override | |
56 public String toString() { | |
57 StringBuilder sb = new StringBuilder(); | |
58 if (target == null) { | |
59 sb.append(BeansUtils.NULL); | |
60 } else { | |
61 Class<?> clazz = target.getClass(); | |
62 sb.append(clazz == String.class ? BeansUtils.QUOTE : BeansUtils | |
63 .idOfClass(clazz)); | |
64 } | |
65 sb.append('.' + methodName + '('); | |
66 if (arguments != null) { | |
67 Class<?> clazz; | |
68 for (int index = 0; index < arguments.length; index++) { | |
69 if (index > 0) { | |
70 sb.append(", "); //$NON-NLS-1$ | |
71 } | |
72 if (arguments[index] == null) { | |
73 sb.append(BeansUtils.NULL); | |
74 } else { | |
75 clazz = arguments[index].getClass(); | |
76 sb.append(clazz == String.class ? '"' + (String) arguments[index] + '"' | |
77 : BeansUtils.idOfClass(clazz)); | |
78 } | |
79 } | |
80 } | |
81 sb.append(')'); | |
82 sb.append(';'); | |
83 return sb.toString(); | |
84 } | |
85 | |
86 public String getMethodName() { | |
87 return methodName; | |
88 } | |
89 | |
90 public Object[] getArguments() { | |
91 return arguments; | |
92 } | |
93 | |
94 public Object getTarget() { | |
95 return target; | |
96 } | |
97 | |
98 public void execute() throws Exception { | |
99 invokeMethod(); | |
100 } | |
101 | |
102 Object invokeMethod() throws Exception { | |
103 Object result = null; | |
104 try { | |
105 Object target = getTarget(); | |
106 String methodName = getMethodName(); | |
107 Object[] arguments = getArguments(); | |
108 Class<?> targetClass = target.getClass(); | |
109 if (targetClass.isArray()) { | |
110 Method method = findArrayMethod(methodName, arguments); | |
111 Object[] copy = new Object[arguments.length + 1]; | |
112 copy[0] = target; | |
113 System.arraycopy(arguments, 0, copy, 1, arguments.length); | |
114 result = method.invoke(null, copy); | |
115 } else if (BeansUtils.NEWINSTANCE.equals(methodName) | |
116 && target == Array.class) { | |
117 result = Array.newInstance((Class<?>) arguments[0], | |
118 ((Integer) arguments[1]).intValue()); | |
119 } else if (BeansUtils.NEW.equals(methodName) | |
120 || BeansUtils.NEWINSTANCE.equals(methodName)) { | |
121 if (target instanceof Class<?>) { | |
122 Constructor<?> constructor = findConstructor( | |
123 (Class<?>) target, arguments); | |
124 result = constructor.newInstance(arguments); | |
125 } else { | |
126 if (BeansUtils.NEW.equals(methodName)) { | |
127 throw new NoSuchMethodException(this.toString()); | |
128 } | |
129 // target class declares a public named "newInstance" method | |
130 Method method = findMethod(targetClass, methodName, | |
131 arguments, false); | |
132 result = method.invoke(target, arguments); | |
133 } | |
134 } else if (methodName.equals(BeansUtils.NEWARRAY)) { | |
135 // create a new array instance without length attribute | |
136 Class<?> clazz = (Class<?>) target, argClass; | |
137 | |
138 // check the element types of array | |
139 for (int index = 0; index < arguments.length; index++) { | |
140 argClass = arguments[index] == null ? null | |
141 : arguments[index].getClass(); | |
142 if (argClass != null && !clazz.isAssignableFrom(argClass) | |
143 && !BeansUtils.isPrimitiveWrapper(argClass, clazz)) { | |
144 throw new IllegalArgumentException( | |
145 Messages.getString("custom.beans.63")); //$NON-NLS-1$ | |
146 } | |
147 } | |
148 result = Array.newInstance(clazz, arguments.length); | |
149 if (clazz.isPrimitive()) { | |
150 // Copy element according to primitive types | |
151 arrayCopy(clazz, arguments, result, arguments.length); | |
152 } else { | |
153 // Copy element of Objects | |
154 System.arraycopy(arguments, 0, result, 0, arguments.length); | |
155 } | |
156 return result; | |
157 } else if (target instanceof Class<?>) { | |
158 Method method = null; | |
159 try { | |
160 /* MODIFIED FOR THE MSGPACK PROJECT | |
161 * Try to look for a static method of class described by the | |
162 * given Class object at first process only if the class | |
163 * differs from Class itself | |
164 */ | |
165 if (target != Class.class) { | |
166 method = findMethod((Class<?>) target, methodName, | |
167 arguments, true); | |
168 result = method.invoke(null, arguments); | |
169 } | |
170 } catch (NoSuchMethodException e) { | |
171 // expected | |
172 } | |
173 if (method == null) { | |
174 // static method was not found | |
175 // try to invoke method of Class object | |
176 if (BeansUtils.FORNAME.equals(methodName) | |
177 && arguments.length == 1 | |
178 && arguments[0] instanceof String) { | |
179 // special handling of Class.forName(String) | |
180 try { | |
181 result = Class.forName((String) arguments[0]); | |
182 } catch (ClassNotFoundException e2) { | |
183 result = Class.forName((String) arguments[0], true, | |
184 Thread.currentThread() | |
185 .getContextClassLoader()); | |
186 } | |
187 } else { | |
188 method = findMethod(targetClass, methodName, arguments, | |
189 false); | |
190 result = method.invoke(target, arguments); | |
191 } | |
192 } | |
193 } else if (target instanceof Iterator<?>) { | |
194 final Iterator<?> iterator = (Iterator<?>) target; | |
195 final Method method = findMethod(targetClass, methodName, | |
196 arguments, false); | |
197 if (iterator.hasNext()) { | |
198 result = new PrivilegedAction<Object>() { | |
199 public Object run() { | |
200 try { | |
201 method.setAccessible(true); | |
202 return (method.invoke(iterator, new Object[0])); | |
203 } catch (Exception e) { | |
204 // ignore | |
205 } | |
206 return null; | |
207 } | |
208 | |
209 }.run(); | |
210 } | |
211 } else { | |
212 Method method = findMethod(targetClass, methodName, arguments, | |
213 false); | |
214 method.setAccessible(true); | |
215 result = method.invoke(target, arguments); | |
216 } | |
217 } catch (InvocationTargetException ite) { | |
218 Throwable t = ite.getCause(); | |
219 throw (t != null) && (t instanceof Exception) ? (Exception) t : ite; | |
220 } | |
221 return result; | |
222 } | |
223 | |
224 private void arrayCopy(Class<?> type, Object[] src, Object dest, int length) { | |
225 if (type == boolean.class) { | |
226 boolean[] destination = (boolean[]) dest; | |
227 for (int index = 0; index < length; index++) { | |
228 destination[index] = ((Boolean) src[index]).booleanValue(); | |
229 } | |
230 } else if (type == short.class) { | |
231 short[] destination = (short[]) dest; | |
232 for (int index = 0; index < length; index++) { | |
233 destination[index] = ((Short) src[index]).shortValue(); | |
234 } | |
235 } else if (type == byte.class) { | |
236 byte[] destination = (byte[]) dest; | |
237 for (int index = 0; index < length; index++) { | |
238 destination[index] = ((Byte) src[index]).byteValue(); | |
239 } | |
240 } else if (type == char.class) { | |
241 char[] destination = (char[]) dest; | |
242 for (int index = 0; index < length; index++) { | |
243 destination[index] = ((Character) src[index]).charValue(); | |
244 } | |
245 } else if (type == int.class) { | |
246 int[] destination = (int[]) dest; | |
247 for (int index = 0; index < length; index++) { | |
248 destination[index] = ((Integer) src[index]).intValue(); | |
249 } | |
250 } else if (type == long.class) { | |
251 long[] destination = (long[]) dest; | |
252 for (int index = 0; index < length; index++) { | |
253 destination[index] = ((Long) src[index]).longValue(); | |
254 } | |
255 } else if (type == float.class) { | |
256 float[] destination = (float[]) dest; | |
257 for (int index = 0; index < length; index++) { | |
258 destination[index] = ((Float) src[index]).floatValue(); | |
259 } | |
260 } else if (type == double.class) { | |
261 double[] destination = (double[]) dest; | |
262 for (int index = 0; index < length; index++) { | |
263 destination[index] = ((Double) src[index]).doubleValue(); | |
264 } | |
265 } | |
266 } | |
267 | |
268 private Method findArrayMethod(String methodName, Object[] args) | |
269 throws NoSuchMethodException { | |
270 // the code below reproduces exact RI exception throwing behavior | |
271 boolean isGet = BeansUtils.GET.equals(methodName); //$NON-NLS-1$ | |
272 boolean isSet = BeansUtils.SET.equals(methodName); //$NON-NLS-1$ | |
273 if (!isGet && !isSet) { | |
274 throw new NoSuchMethodException(Messages.getString("custom.beans.3C")); //$NON-NLS-1$ | |
275 } else if (args.length > 0 && args[0].getClass() != Integer.class) { | |
276 throw new ClassCastException(Messages.getString("custom.beans.3D")); //$NON-NLS-1$ | |
277 } else if (isGet && args.length != 1) { | |
278 throw new ArrayIndexOutOfBoundsException( | |
279 Messages.getString("custom.beans.3E")); //$NON-NLS-1$ | |
280 } else if (isSet && args.length != 2) { | |
281 throw new ArrayIndexOutOfBoundsException( | |
282 Messages.getString("custom.beans.3F")); //$NON-NLS-1$ | |
283 } | |
284 | |
285 Class<?>[] paraTypes = isGet ? new Class<?>[] { Object.class, int.class } | |
286 : new Class<?>[] { Object.class, int.class, Object.class }; | |
287 return Array.class.getMethod(methodName, paraTypes); | |
288 } | |
289 | |
290 private Constructor<?> findConstructor(Class<?> clazz, Object[] args) | |
291 throws NoSuchMethodException { | |
292 Class<?>[] argTypes = getTypes(args), paraTypes, resultParaTypes; | |
293 Constructor<?> result = null; | |
294 boolean isAssignable; | |
295 for (Constructor<?> constructor : clazz.getConstructors()) { | |
296 paraTypes = constructor.getParameterTypes(); | |
297 if (match(argTypes, paraTypes)) { | |
298 if (result == null) { | |
299 // first time, set constructor | |
300 result = constructor; | |
301 continue; | |
302 } | |
303 // find out more suitable constructor | |
304 resultParaTypes = result.getParameterTypes(); | |
305 isAssignable = true; | |
306 for (int index = 0; index < paraTypes.length; index++) { | |
307 if (argTypes[index] != null | |
308 && !(isAssignable &= resultParaTypes[index] | |
309 .isAssignableFrom(paraTypes[index]))) { | |
310 break; | |
311 } | |
312 if (argTypes[index] == null | |
313 && !(isAssignable &= paraTypes[index] | |
314 .isAssignableFrom(resultParaTypes[index]))) { | |
315 break; | |
316 } | |
317 } | |
318 if (isAssignable) { | |
319 result = constructor; | |
320 } | |
321 } | |
322 } | |
323 if (result == null) { | |
324 throw new NoSuchMethodException(Messages.getString( | |
325 "custom.beans.40", clazz.getName())); //$NON-NLS-1$ | |
326 } | |
327 return result; | |
328 } | |
329 | |
330 /** | |
331 * Searches for best matching method for given name and argument types. | |
332 */ | |
333 static Method findMethod(Class<?> clazz, String methodName, Object[] args, | |
334 boolean isStatic) throws NoSuchMethodException { | |
335 Class<?>[] argTypes = getTypes(args); | |
336 | |
337 Method[] methods = null; | |
338 if (classMethodsCache.containsKey(clazz)) { | |
339 methods = classMethodsCache.get(clazz); | |
340 } else { | |
341 methods = clazz.getMethods(); | |
342 classMethodsCache.put(clazz, methods); | |
343 } | |
344 | |
345 ArrayList<Method> fitMethods = new ArrayList<Method>(); | |
346 for (Method method : methods) { | |
347 if (methodName.equals(method.getName())) { | |
348 if (!isStatic || Modifier.isStatic(method.getModifiers())) { | |
349 if (match(argTypes, method.getParameterTypes())) { | |
350 fitMethods.add(method); | |
351 } | |
352 } | |
353 } | |
354 } | |
355 int fitSize = fitMethods.size(); | |
356 if (fitSize == 0) { | |
357 throw new NoSuchMethodException(Messages.getString( | |
358 "custom.beans.41", methodName)); //$NON-NLS-1$ | |
359 } | |
360 if (fitSize == 1) { | |
361 return fitMethods.get(0); | |
362 } | |
363 // find the most relevant one | |
364 MethodComparator comparator = new MethodComparator(methodName, argTypes); | |
365 Method[] fitMethodArray = fitMethods.toArray(new Method[fitSize]); | |
366 Method onlyMethod = fitMethodArray[0]; | |
367 Class<?> onlyReturnType, fitReturnType; | |
368 int difference; | |
369 for (int i = 1; i < fitMethodArray.length; i++) { | |
370 // if 2 methods have same relevance, check their return type | |
371 if ((difference = comparator.compare(onlyMethod, fitMethodArray[i])) == 0) { | |
372 // if 2 methods have the same signature, check their return type | |
373 onlyReturnType = onlyMethod.getReturnType(); | |
374 fitReturnType = fitMethodArray[i].getReturnType(); | |
375 if (onlyReturnType == fitReturnType) { | |
376 // if 2 methods have the same relevance and return type | |
377 throw new NoSuchMethodException(Messages.getString( | |
378 "custom.beans.62", methodName)); //$NON-NLS-1$ | |
379 } | |
380 | |
381 if (onlyReturnType.isAssignableFrom(fitReturnType)) { | |
382 // if onlyReturnType is super class or interface of | |
383 // fitReturnType, set onlyMethod to fitMethodArray[i] | |
384 onlyMethod = fitMethodArray[i]; | |
385 } | |
386 } | |
387 if (difference > 0) { | |
388 onlyMethod = fitMethodArray[i]; | |
389 } | |
390 } | |
391 return onlyMethod; | |
392 } | |
393 | |
394 private static boolean match(Class<?>[] argTypes, Class<?>[] paraTypes) { | |
395 if (paraTypes.length != argTypes.length) { | |
396 return false; | |
397 } | |
398 for (int index = 0; index < paraTypes.length; index++) { | |
399 if (argTypes[index] != null | |
400 && !paraTypes[index].isAssignableFrom(argTypes[index]) | |
401 && !BeansUtils.isPrimitiveWrapper(argTypes[index], | |
402 paraTypes[index])) { | |
403 return false; | |
404 } | |
405 } | |
406 return true; | |
407 } | |
408 | |
409 static boolean isStaticMethodCall(Statement stmt) { | |
410 Object target = stmt.getTarget(); | |
411 String methodName = stmt.getMethodName(); | |
412 if (!(target instanceof Class<?>)) { | |
413 return false; | |
414 } | |
415 try { | |
416 Statement.findMethod((Class<?>) target, methodName, | |
417 stmt.getArguments(), true); | |
418 return true; | |
419 } catch (NoSuchMethodException e) { | |
420 return false; | |
421 } | |
422 } | |
423 | |
424 /* MODIFIED FOR THE MSGPACK PROJECT | |
425 * The list of "method signatures" used by persistence delegates to create | |
426 * objects. Not necessary reflects to real methods. | |
427 */ | |
428 private static final String[][] pdConstructorSignatures = { | |
429 { "java.lang.Class", "new", "java.lang.Boolean", "", "", "" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ | |
430 { "java.lang.Class", "new", "java.lang.Byte", "", "", "" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ | |
431 { "java.lang.Class", "new", "java.lang.Character", "", "", "" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ | |
432 { "java.lang.Class", "new", "java.lang.Double", "", "", "" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ | |
433 { "java.lang.Class", "new", "java.lang.Float", "", "", "" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ | |
434 { "java.lang.Class", "new", "java.lang.Integer", "", "", "" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ | |
435 { "java.lang.Class", "new", "java.lang.Long", "", "", "" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ | |
436 { "java.lang.Class", "new", "java.lang.Short", "", "", "" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ | |
437 { "java.lang.Class", "new", "java.lang.String", "", "", "" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ | |
438 { "java.lang.Class", "forName", "java.lang.String", "", "", "" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ | |
439 { "java.lang.Class", "newInstance", "java.lang.Class", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |
440 "java.lang.Integer", "", "" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |
441 { "java.lang.reflect.Field", "get", "null", "", "", "" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ | |
442 { "java.lang.Class", "forName", "java.lang.String", "", "", "" } //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ | |
443 }; | |
444 | |
445 static boolean isPDConstructor(Statement stmt) { | |
446 Object target = stmt.getTarget(); | |
447 String methodName = stmt.getMethodName(); | |
448 Object[] args = stmt.getArguments(); | |
449 String[] sig = new String[pdConstructorSignatures[0].length]; | |
450 if (target == null || methodName == null || args == null | |
451 || args.length == 0) { | |
452 // not a constructor for sure | |
453 return false; | |
454 } | |
455 sig[0] = target.getClass().getName(); | |
456 sig[1] = methodName; | |
457 for (int i = 2; i < sig.length; i++) { | |
458 if (args.length > i - 2) { | |
459 sig[i] = args[i - 2] != null ? args[i - 2].getClass().getName() | |
460 : "null"; //$NON-NLS-1$ | |
461 } else { | |
462 sig[i] = ""; //$NON-NLS-1$ | |
463 } | |
464 } | |
465 for (String[] element : pdConstructorSignatures) { | |
466 if (Arrays.equals(sig, element)) { | |
467 return true; | |
468 } | |
469 } | |
470 return false; | |
471 } | |
472 | |
473 private static Class<?> getPrimitiveWrapper(Class<?> base) { | |
474 Class<?> res = null; | |
475 if (base == boolean.class) { | |
476 res = Boolean.class; | |
477 } else if (base == byte.class) { | |
478 res = Byte.class; | |
479 } else if (base == char.class) { | |
480 res = Character.class; | |
481 } else if (base == short.class) { | |
482 res = Short.class; | |
483 } else if (base == int.class) { | |
484 res = Integer.class; | |
485 } else if (base == long.class) { | |
486 res = Long.class; | |
487 } else if (base == float.class) { | |
488 res = Float.class; | |
489 } else if (base == double.class) { | |
490 res = Double.class; | |
491 } | |
492 return res; | |
493 } | |
494 | |
495 private static Class<?>[] getTypes(Object[] arguments) { | |
496 Class<?>[] types = new Class[arguments.length]; | |
497 for (int index = 0; index < arguments.length; ++index) { | |
498 types[index] = (arguments[index] == null) ? null : arguments[index] | |
499 .getClass(); | |
500 } | |
501 return types; | |
502 } | |
503 | |
504 /** | |
505 * Comparator to determine which of two methods is "closer" to the reference | |
506 * method. | |
507 */ | |
508 static class MethodComparator implements Comparator<Method> { | |
509 static int INFINITY = Integer.MAX_VALUE; | |
510 | |
511 private String referenceMethodName; | |
512 | |
513 private Class<?>[] referenceMethodArgumentTypes; | |
514 | |
515 private final Map<Method, Integer> cache; | |
516 | |
517 public MethodComparator(String refMethodName, | |
518 Class<?>[] refArgumentTypes) { | |
519 this.referenceMethodName = refMethodName; | |
520 this.referenceMethodArgumentTypes = refArgumentTypes; | |
521 cache = new HashMap<Method, Integer>(); | |
522 } | |
523 | |
524 public int compare(Method m1, Method m2) { | |
525 Integer norm1 = cache.get(m1); | |
526 Integer norm2 = cache.get(m2); | |
527 if (norm1 == null) { | |
528 norm1 = Integer.valueOf(getNorm(m1)); | |
529 cache.put(m1, norm1); | |
530 } | |
531 if (norm2 == null) { | |
532 norm2 = Integer.valueOf(getNorm(m2)); | |
533 cache.put(m2, norm2); | |
534 } | |
535 return (norm1.intValue() - norm2.intValue()); | |
536 } | |
537 | |
538 /** | |
539 * Returns the norm for given method. The norm is the "distance" from | |
540 * the reference method to the given method. | |
541 * | |
542 * @param m | |
543 * the method to calculate the norm for | |
544 * @return norm of given method | |
545 */ | |
546 private int getNorm(Method m) { | |
547 String methodName = m.getName(); | |
548 Class<?>[] argumentTypes = m.getParameterTypes(); | |
549 int totalNorm = 0; | |
550 if (!referenceMethodName.equals(methodName) | |
551 || referenceMethodArgumentTypes.length != argumentTypes.length) { | |
552 return INFINITY; | |
553 } | |
554 for (int i = 0; i < referenceMethodArgumentTypes.length; i++) { | |
555 if (referenceMethodArgumentTypes[i] == null) { | |
556 // doesn't affect the norm calculation if null | |
557 continue; | |
558 } | |
559 if (referenceMethodArgumentTypes[i].isPrimitive()) { | |
560 referenceMethodArgumentTypes[i] = getPrimitiveWrapper(referenceMethodArgumentTypes[i]); | |
561 } | |
562 if (argumentTypes[i].isPrimitive()) { | |
563 argumentTypes[i] = getPrimitiveWrapper(argumentTypes[i]); | |
564 } | |
565 totalNorm += getDistance(referenceMethodArgumentTypes[i], | |
566 argumentTypes[i]); | |
567 } | |
568 return totalNorm; | |
569 } | |
570 | |
571 /** | |
572 * Returns a "hierarchy distance" between two classes. | |
573 * | |
574 * @param clz1 | |
575 * @param clz2 | |
576 * should be superclass or superinterface of clz1 | |
577 * @return hierarchy distance from clz1 to clz2, Integer.MAX_VALUE if | |
578 * clz2 is not assignable from clz1. | |
579 */ | |
580 private static int getDistance(Class<?> clz1, Class<?> clz2) { | |
581 Class<?> superClz; | |
582 int superDist = INFINITY; | |
583 if (!clz2.isAssignableFrom(clz1)) { | |
584 return INFINITY; | |
585 } | |
586 if (clz1.getName().equals(clz2.getName())) { | |
587 return 0; | |
588 } | |
589 superClz = clz1.getSuperclass(); | |
590 if (superClz != null) { | |
591 superDist = getDistance(superClz, clz2); | |
592 } | |
593 if (clz2.isInterface()) { | |
594 Class<?>[] interfaces = clz1.getInterfaces(); | |
595 int bestDist = INFINITY; | |
596 for (Class<?> element : interfaces) { | |
597 int curDist = getDistance(element, clz2); | |
598 if (curDist < bestDist) { | |
599 bestDist = curDist; | |
600 } | |
601 } | |
602 if (superDist < bestDist) { | |
603 bestDist = superDist; | |
604 } | |
605 return (bestDist != INFINITY ? bestDist + 1 : INFINITY); | |
606 } | |
607 return (superDist != INFINITY ? superDist + 2 : INFINITY); | |
608 } | |
609 } | |
610 } |