0
|
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 } |