1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.codehaus.groovy.runtime;
19
20 import groovy.lang.Closure;
21 import groovy.lang.GString;
22 import groovy.lang.GroovyRuntimeException;
23 import groovy.lang.MetaMethod;
24
25 import java.lang.reflect.Array;
26 import java.lang.reflect.Constructor;
27 import java.lang.reflect.InvocationHandler;
28 import java.lang.reflect.InvocationTargetException;
29 import java.lang.reflect.Method;
30 import java.lang.reflect.Modifier;
31 import java.lang.reflect.Proxy;
32 import java.math.BigDecimal;
33 import java.math.BigInteger;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.logging.Level;
37 import java.util.logging.Logger;
38
39 import org.codehaus.groovy.GroovyBugError;
40
41 /***
42 * @author John Wilson
43 * @author Jochen Theodorou
44 */
45 public class MetaClassHelper {
46
47 public static final Object[] EMPTY_ARRAY = {};
48 public static Class[] EMPTY_TYPE_ARRAY = {};
49 protected static final Object[] ARRAY_WITH_NULL = { null };
50 protected static final Logger log = Logger.getLogger(MetaClassHelper.class.getName());
51 private static final int MAX_ARG_LEN = 12;
52
53 public static boolean accessibleToConstructor(final Class at, final Constructor constructor) {
54 boolean accessible = false;
55 if (Modifier.isPublic(constructor.getModifiers())) {
56 accessible = true;
57 }
58 else if (Modifier.isPrivate(constructor.getModifiers())) {
59 accessible = at.getName().equals(constructor.getName());
60 }
61 else if ( Modifier.isProtected(constructor.getModifiers()) ) {
62 if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() == null ) {
63 accessible = true;
64 }
65 else if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() != null ) {
66 accessible = false;
67 }
68 else if ( at.getPackage() != null && constructor.getDeclaringClass().getPackage() == null ) {
69 accessible = false;
70 }
71 else if ( at.getPackage().equals(constructor.getDeclaringClass().getPackage()) ) {
72 accessible = true;
73 }
74 else {
75 boolean flag = false;
76 Class clazz = at;
77 while ( !flag && clazz != null ) {
78 if (clazz.equals(constructor.getDeclaringClass()) ) {
79 flag = true;
80 break;
81 }
82 if (clazz.equals(Object.class) ) {
83 break;
84 }
85 clazz = clazz.getSuperclass();
86 }
87 accessible = flag;
88 }
89 }
90 else {
91 if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() == null ) {
92 accessible = true;
93 }
94 else if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() != null ) {
95 accessible = false;
96 }
97 else if ( at.getPackage() != null && constructor.getDeclaringClass().getPackage() == null ) {
98 accessible = false;
99 }
100 else if ( at.getPackage().equals(constructor.getDeclaringClass().getPackage()) ) {
101 accessible = true;
102 }
103 }
104 return accessible;
105 }
106
107 public static Object[] asWrapperArray(Object parameters, Class componentType) {
108 Object[] ret=null;
109 if (componentType == boolean.class) {
110 boolean[] array = (boolean[]) parameters;
111 ret = new Object[array.length];
112 for (int i=0; i<array.length; i++) {
113 ret[i] = new Boolean(array[i]);
114 }
115 } else if (componentType == char.class) {
116 char[] array = (char[]) parameters;
117 ret = new Object[array.length];
118 for (int i=0; i<array.length; i++) {
119 ret[i] = new Character(array[i]);
120 }
121 } else if (componentType == byte.class) {
122 byte[] array = (byte[]) parameters;
123 ret = new Object[array.length];
124 for (int i=0; i<array.length; i++) {
125 ret[i] = new Byte(array[i]);
126 }
127 } else if (componentType == int.class) {
128 int[] array = (int[]) parameters;
129 ret = new Object[array.length];
130 for (int i=0; i<array.length; i++) {
131 ret[i] = new Integer(array[i]);
132 }
133 } else if (componentType == short.class) {
134 short[] array = (short[]) parameters;
135 ret = new Object[array.length];
136 for (int i=0; i<array.length; i++) {
137 ret[i] = new Short(array[i]);
138 }
139 } else if (componentType == long.class) {
140 long[] array = (long[]) parameters;
141 ret = new Object[array.length];
142 for (int i=0; i<array.length; i++) {
143 ret[i] = new Long(array[i]);
144 }
145 } else if (componentType == double.class) {
146 double[] array = (double[]) parameters;
147 ret = new Object[array.length];
148 for (int i=0; i<array.length; i++) {
149 ret[i] = new Double(array[i]);
150 }
151 } else if (componentType == float.class) {
152 float[] array = (float[]) parameters;
153 ret = new Object[array.length];
154 for (int i=0; i<array.length; i++) {
155 ret[i] = new Float(array[i]);
156 }
157 }
158
159 return ret;
160 }
161
162
163 /***
164 * @param list
165 * @param parameterType
166 * @return
167 */
168 public static Object asPrimitiveArray(List list, Class parameterType) {
169 Class arrayType = parameterType.getComponentType();
170 Object objArray = Array.newInstance(arrayType, list.size());
171 for (int i = 0; i < list.size(); i++) {
172 Object obj = list.get(i);
173 if (arrayType.isPrimitive()) {
174 if (obj instanceof Integer) {
175 Array.setInt(objArray, i, ((Integer) obj).intValue());
176 }
177 else if (obj instanceof Double) {
178 Array.setDouble(objArray, i, ((Double) obj).doubleValue());
179 }
180 else if (obj instanceof Boolean) {
181 Array.setBoolean(objArray, i, ((Boolean) obj).booleanValue());
182 }
183 else if (obj instanceof Long) {
184 Array.setLong(objArray, i, ((Long) obj).longValue());
185 }
186 else if (obj instanceof Float) {
187 Array.setFloat(objArray, i, ((Float) obj).floatValue());
188 }
189 else if (obj instanceof Character) {
190 Array.setChar(objArray, i, ((Character) obj).charValue());
191 }
192 else if (obj instanceof Byte) {
193 Array.setByte(objArray, i, ((Byte) obj).byteValue());
194 }
195 else if (obj instanceof Short) {
196 Array.setShort(objArray, i, ((Short) obj).shortValue());
197 }
198 }
199 else {
200 Array.set(objArray, i, obj);
201 }
202 }
203 return objArray;
204 }
205
206 protected static Class autoboxType(Class type) {
207 if (type.isPrimitive()) {
208 if (type == int.class) {
209 return Integer.class;
210 }
211 else if (type == double.class) {
212 return Double.class;
213 }
214 else if (type == long.class) {
215 return Long.class;
216 }
217 else if (type == boolean.class) {
218 return Boolean.class;
219 }
220 else if (type == float.class) {
221 return Float.class;
222 }
223 else if (type == char.class) {
224 return Character.class;
225 }
226 else if (type == byte.class) {
227 return Byte.class;
228 }
229 else if (type == short.class) {
230 return Short.class;
231 }
232 }
233 return type;
234 }
235
236 public static int calculateParameterDistance(Class[] arguments, Class[] parameters) {
237 int dist=0;
238 for (int i=0; i<arguments.length; i++) {
239 if (parameters[i]==arguments[i]) continue;
240
241 if (parameters[i].isInterface()) {
242 dist+=3;
243 continue;
244 }
245
246 if (arguments[i]!=null) {
247 if (autoboxType(parameters[i]) == autoboxType(arguments[i])){
248
249
250 dist +=1;
251 continue;
252 }
253 if (arguments[i].isPrimitive() || parameters[i].isPrimitive()) {
254
255
256 dist+=2;
257 continue;
258 }
259
260
261 dist++;
262 Class clazz = arguments[i];
263 while (clazz!=null) {
264 if (clazz==parameters[i]) break;
265 if (clazz==GString.class && parameters[i]==String.class) {
266 dist+=2;
267 break;
268 }
269 clazz = clazz.getSuperclass();
270 dist+=3;
271 }
272 } else {
273
274
275
276
277 dist--;
278 Class clazz = parameters[i];
279 if (clazz.isPrimitive()) {
280 dist+=2;
281 } else {
282 while (clazz!=Object.class) {
283 clazz = clazz.getSuperclass();
284 dist+=2;
285 }
286 }
287 }
288 }
289 return dist;
290 }
291
292 public static String capitalize(String property) {
293 return property.substring(0, 1).toUpperCase() + property.substring(1, property.length());
294 }
295
296 /***
297 * @return the method with 1 parameter which takes the most general type of
298 * object (e.g. Object)
299 */
300 public static Object chooseEmptyMethodParams(List methods) {
301 for (Iterator iter = methods.iterator(); iter.hasNext();) {
302 Object method = iter.next();
303 Class[] paramTypes = getParameterTypes(method);
304 int paramLength = paramTypes.length;
305 if (paramLength == 0) {
306 return method;
307 }
308 }
309 return null;
310 }
311
312 /***
313 * @return the method with 1 parameter which takes the most general type of
314 * object (e.g. Object) ignoring primitve types
315 */
316 public static Object chooseMostGeneralMethodWith1NullParam(List methods) {
317
318
319 Class closestClass = null;
320 Object answer = null;
321
322 for (Iterator iter = methods.iterator(); iter.hasNext();) {
323 Object method = iter.next();
324 Class[] paramTypes = getParameterTypes(method);
325 int paramLength = paramTypes.length;
326 if (paramLength == 1) {
327 Class theType = paramTypes[0];
328 if (theType.isPrimitive()) continue;
329 if (closestClass == null || isAssignableFrom(theType, closestClass)) {
330 closestClass = theType;
331 answer = method;
332 }
333 }
334 }
335 return answer;
336 }
337
338 /***
339 * Coerces a GString instance into String if needed
340 *
341 * @return the coerced argument
342 */
343 protected static Object coerceGString(Object argument, Class clazz) {
344 if (clazz!=String.class) return argument;
345 if (!(argument instanceof GString)) return argument;
346 return argument.toString();
347 }
348
349 protected static Object coerceNumber(Object argument, Class param) {
350 if ((Number.class.isAssignableFrom(param) || param.isPrimitive()) && argument instanceof Number) {
351 Object oldArgument = argument;
352 boolean wasDouble = false;
353 boolean wasFloat = false;
354 if (param == Byte.class || param == Byte.TYPE ) {
355 argument = new Byte(((Number)argument).byteValue());
356 } else if (param == Double.class || param == Double.TYPE) {
357 wasDouble = true;
358 argument = new Double(((Number)argument).doubleValue());
359 } else if (param == Float.class || param == Float.TYPE) {
360 wasFloat = true;
361 argument = new Float(((Number)argument).floatValue());
362 } else if (param == Integer.class || param == Integer.TYPE) {
363 argument = new Integer(((Number)argument).intValue());
364 } else if (param == Long.class || param == Long.TYPE) {
365 argument = new Long(((Number)argument).longValue());
366 } else if (param == Short.class || param == Short.TYPE) {
367 argument = new Short(((Number)argument).shortValue());
368 } else if (param == BigDecimal.class ) {
369 argument = new BigDecimal(String.valueOf((Number)argument));
370 } else if (param == BigInteger.class) {
371 argument = new BigInteger(String.valueOf((Number)argument));
372 }
373
374 if (oldArgument instanceof BigDecimal) {
375 BigDecimal oldbd = (BigDecimal) oldArgument;
376 boolean throwException = false;
377 if (wasDouble) {
378 Double d = (Double) argument;
379 if (d.isInfinite()) throwException = true;
380 } else if (wasFloat) {
381 Float f = (Float) argument;
382 if (f.isInfinite()) throwException = true;
383 } else {
384 BigDecimal newbd = new BigDecimal(String.valueOf((Number)argument));
385 throwException = !oldArgument.equals(newbd);
386 }
387
388 if (throwException) throw new IllegalArgumentException(param+" out of range while converting from BigDecimal");
389 }
390
391 }
392 return argument;
393 }
394
395 protected static Object coerceArray(Object argument, Class param) {
396 if (!param.isArray()) return argument;
397 Class argumentClass = argument.getClass();
398 if (!argumentClass.isArray()) return argument;
399
400 Class paramComponent = param.getComponentType();
401 if (paramComponent.isPrimitive()) {
402 if (paramComponent == boolean.class && argumentClass==Boolean[].class) {
403 argument = InvokerHelper.convertToBooleanArray(argument);
404 } else if (paramComponent == byte.class && argumentClass==Byte[].class) {
405 argument = InvokerHelper.convertToByteArray(argument);
406 } else if (paramComponent == char.class && argumentClass==Character[].class) {
407 argument = InvokerHelper.convertToCharArray(argument);
408 } else if (paramComponent == short.class && argumentClass==Short[].class) {
409 argument = InvokerHelper.convertToShortArray(argument);
410 } else if (paramComponent == int.class && argumentClass==Integer[].class) {
411 argument = InvokerHelper.convertToIntArray(argument);
412 } else if (paramComponent == long.class &&
413 (argumentClass == Long[].class || argumentClass == Integer[].class))
414 {
415 argument = InvokerHelper.convertToLongArray(argument);
416 } else if (paramComponent == float.class &&
417 (argumentClass == Float[].class || argumentClass == Integer[].class))
418 {
419 argument = InvokerHelper.convertToFloatArray(argument);
420 } else if (paramComponent == double.class &&
421 (argumentClass == Double[].class || argumentClass==Float[].class
422 || BigDecimal.class.isAssignableFrom(argumentClass)))
423 {
424 argument = InvokerHelper.convertToDoubleArray(argument);
425 }
426 } else if (paramComponent==String.class && argument instanceof GString[]) {
427 GString[] strings = (GString[]) argument;
428 String[] ret = new String[strings.length];
429 for (int i=0; i<strings.length; i++) {
430 ret[i] = strings[i].toString();
431 }
432 argument = ret;
433 }
434 return argument;
435 }
436
437 /***
438 * @return true if a method of the same matching prototype was found in the
439 * list
440 */
441 public static boolean containsMatchingMethod(List list, MetaMethod method) {
442 for (Iterator iter = list.iterator(); iter.hasNext();) {
443 MetaMethod aMethod = (MetaMethod) iter.next();
444 Class[] params1 = aMethod.getParameterTypes();
445 Class[] params2 = method.getParameterTypes();
446 if (params1.length == params2.length) {
447 boolean matches = true;
448 for (int i = 0; i < params1.length; i++) {
449 if (params1[i] != params2[i]) {
450 matches = false;
451 break;
452 }
453 }
454 if (matches) {
455 return true;
456 }
457 }
458 }
459 return false;
460 }
461
462 /***
463 * param instance array to the type array
464 * @param args
465 * @return
466 */
467 public static Class[] convertToTypeArray(Object[] args) {
468 if (args == null)
469 return null;
470 int s = args.length;
471 Class[] ans = new Class[s];
472 for (int i = 0; i < s; i++) {
473 Object o = args[i];
474 if (o != null) {
475 ans[i] = o.getClass();
476 } else {
477 ans[i] = null;
478 }
479 }
480 return ans;
481 }
482
483 /***
484 * @param listenerType
485 * the interface of the listener to proxy
486 * @param listenerMethodName
487 * the name of the method in the listener API to call the
488 * closure on
489 * @param closure
490 * the closure to invoke on the listenerMethodName method
491 * invocation
492 * @return a dynamic proxy which calls the given closure on the given
493 * method name
494 */
495 public static Object createListenerProxy(Class listenerType, final String listenerMethodName, final Closure closure) {
496 InvocationHandler handler = new ClosureListener(listenerMethodName, closure);
497 return Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[] { listenerType }, handler);
498 }
499
500 public static Object doConstructorInvoke(Constructor constructor, Object[] argumentArray) {
501 if (log.isLoggable(Level.FINER)){
502 logMethodCall(constructor.getDeclaringClass(), constructor.getName(), argumentArray);
503 }
504 argumentArray = coerceArgumentsToClasses(argumentArray,constructor.getParameterTypes());
505 try {
506 return constructor.newInstance(argumentArray);
507 } catch (InvocationTargetException e) {
508 throw new InvokerInvocationException(e);
509 } catch (IllegalArgumentException e) {
510 throw createExceptionText("failed to invoke constructor: ", constructor, argumentArray, e, false);
511 } catch (IllegalAccessException e) {
512 throw createExceptionText("could not access constructor: ", constructor, argumentArray, e, false);
513 } catch (Exception e) {
514 throw createExceptionText("failed to invoke constructor: ", constructor, argumentArray, e, true);
515 }
516 }
517
518 private static GroovyRuntimeException createExceptionText(String init, Constructor constructor, Object[] argumentArray, Throwable e, boolean setReason) {
519 throw new GroovyRuntimeException(
520 init
521 + constructor
522 + " with arguments: "
523 + InvokerHelper.toString(argumentArray)
524 + " reason: "
525 + e,
526 setReason?e:null);
527 }
528
529 public static Object[] coerceArgumentsToClasses(Object[] argumentArray, Class[] paramTypes) {
530
531 if (argumentArray == null) {
532 argumentArray = EMPTY_ARRAY;
533 } else if (paramTypes.length == 1 && argumentArray.length == 0) {
534 if (isVargsMethod(paramTypes,argumentArray))
535 argumentArray = new Object[]{Array.newInstance(paramTypes[0].getComponentType(),0)};
536 else
537 argumentArray = ARRAY_WITH_NULL;
538 } else if (isVargsMethod(paramTypes,argumentArray)) {
539 argumentArray = fitToVargs(argumentArray, paramTypes);
540 }
541
542
543 for (int i=0; i<argumentArray.length; i++) {
544 Object argument = argumentArray[i];
545 if (argument==null) continue;
546 Class parameterType = paramTypes[i];
547 if (parameterType.isInstance(argument)) continue;
548
549 argument = coerceGString(argument,parameterType);
550 argument = coerceNumber(argument,parameterType);
551 argument = coerceArray(argument,parameterType);
552 argumentArray[i] = argument;
553 }
554 return argumentArray;
555 }
556
557 private static Object makeCommonArray(Object[] arguments, int offset, Class fallback) {
558
559 Class baseClass = null;
560 for (int i = offset; i < arguments.length; i++) {
561 if (arguments[i]==null) continue;
562 Class argClass = arguments[i].getClass();
563 if (baseClass==null) {
564 baseClass = argClass;
565 } else {
566 for (;baseClass!=Object.class; baseClass=baseClass.getSuperclass()){
567 if (baseClass.isAssignableFrom(argClass)) break;
568 }
569 }
570 }
571 if (baseClass==null) {
572
573 baseClass = fallback;
574 }
575 Object result = makeArray(null,baseClass,arguments.length-offset);
576 System.arraycopy(arguments,offset,result,0,arguments.length-offset);
577 return result;
578 }
579
580 private static Object makeArray(Object obj, Class secondary, int length) {
581 Class baseClass = secondary;
582 if (obj!=null) {
583 baseClass = obj.getClass();
584 }
585
586
587
588 return Array.newInstance(baseClass,length);
589 }
590
591 /***
592 * this method is called when the number of arguments to a method is greater than 1
593 * and if the method is a vargs method. This method will then transform the given
594 * arguments to make the method callable
595 *
596 * @param argumentArray the arguments used to call the method
597 * @param paramTypes the types of the paramters the method takes
598 */
599 private static Object[] fitToVargs(Object[] argumentArray, Class[] paramTypes) {
600 Class vargsClass = autoboxType(paramTypes[paramTypes.length-1].getComponentType());
601
602 if (argumentArray.length == paramTypes.length-1) {
603
604 Object[] newArgs = new Object[paramTypes.length];
605 System.arraycopy(argumentArray,0,newArgs,0,argumentArray.length);
606 Object vargs = makeArray(null,vargsClass,0);
607 newArgs[newArgs.length-1] = vargs;
608 return newArgs;
609 } else if (argumentArray.length==paramTypes.length) {
610
611
612
613 Object lastArgument = argumentArray[argumentArray.length-1];
614 if (lastArgument!=null && !lastArgument.getClass().isArray()) {
615
616 Object vargs = makeArray(lastArgument,vargsClass,1);
617 System.arraycopy(argumentArray,argumentArray.length-1,vargs,0,1);
618 argumentArray[argumentArray.length-1]=vargs;
619 return argumentArray;
620 } else {
621
622 return argumentArray;
623 }
624 } else if (argumentArray.length>paramTypes.length) {
625
626
627 Object[] newArgs = new Object[paramTypes.length];
628
629 System.arraycopy(argumentArray,0,newArgs,0,paramTypes.length-1);
630
631 int numberOfVargs = argumentArray.length-paramTypes.length;
632 Object vargs = makeCommonArray(argumentArray,paramTypes.length-1,vargsClass);
633 newArgs[newArgs.length-1] = vargs;
634 return newArgs;
635 } else {
636 throw new GroovyBugError("trying to call a vargs method without enough arguments");
637 }
638 }
639
640 private static GroovyRuntimeException createExceptionText(String init, MetaMethod method, Object object, Object[] args, Throwable reason, boolean setReason) {
641 return new GroovyRuntimeException(
642 init
643 + method
644 + " on: "
645 + object
646 + " with arguments: "
647 + InvokerHelper.toString(args)
648 + " reason: "
649 + reason,
650 setReason?reason:null);
651 }
652
653 public static Object doMethodInvoke(Object object, MetaMethod method, Object[] argumentArray) {
654 Class[] paramTypes = method.getParameterTypes();
655 argumentArray = coerceArgumentsToClasses(argumentArray,paramTypes);
656 try {
657 return method.invoke(object, argumentArray);
658 } catch (InvocationTargetException e) {
659 throw new InvokerInvocationException(e);
660 } catch (IllegalAccessException e) {
661 throw createExceptionText("could not access method: ", method, object, argumentArray, e, true);
662 } catch (IllegalArgumentException e) {
663
664
665
666
667
668
669
670
671
672 boolean setReason = e.getClass() != IllegalArgumentException.class;
673 throw createExceptionText("failed to invoke method: ", method, object, argumentArray, e, setReason);
674 } catch (RuntimeException e) {
675 throw e;
676 } catch (Exception e) {
677 throw createExceptionText("failed to invoke method: ", method, object, argumentArray, e, true);
678 }
679 }
680
681 protected static String getClassName(Object object) {
682 return (object instanceof Class) ? ((Class)object).getName() : object.getClass().getName();
683 }
684
685 /***
686 * Returns a callable object for the given method name on the object.
687 * The object acts like a Closure in that it can be called, like a closure
688 * and passed around - though really its a method pointer, not a closure per se.
689 */
690 public static Closure getMethodPointer(Object object, String methodName) {
691 return new MethodClosure(object, methodName);
692 }
693
694 public static Class[] getParameterTypes(Object methodOrConstructor) {
695 if (methodOrConstructor instanceof MetaMethod) {
696 MetaMethod method = (MetaMethod) methodOrConstructor;
697 return method.getParameterTypes();
698 }
699 if (methodOrConstructor instanceof Method) {
700 Method method = (Method) methodOrConstructor;
701 return method.getParameterTypes();
702 }
703 if (methodOrConstructor instanceof Constructor) {
704 Constructor constructor = (Constructor) methodOrConstructor;
705 return constructor.getParameterTypes();
706 }
707 throw new IllegalArgumentException("Must be a Method or Constructor");
708 }
709
710 protected static boolean isAssignableFrom(Class classToTransformTo, Class classToTransformFrom) {
711 if (classToTransformFrom==null) return true;
712 classToTransformTo = autoboxType(classToTransformTo);
713 classToTransformFrom = autoboxType(classToTransformFrom);
714
715 if (classToTransformTo == classToTransformFrom) {
716 return true;
717 }
718
719 else if (classToTransformTo == Integer.class) {
720 if ( classToTransformFrom == Integer.class
721 || classToTransformFrom == Short.class
722 || classToTransformFrom == Byte.class
723 || classToTransformFrom == BigInteger.class)
724 return true;
725 }
726 else if (classToTransformTo == Double.class) {
727 if ( classToTransformFrom == Double.class
728 || classToTransformFrom == Integer.class
729 || classToTransformFrom == Long.class
730 || classToTransformFrom == Short.class
731 || classToTransformFrom == Byte.class
732 || classToTransformFrom == Float.class
733 || classToTransformFrom == BigDecimal.class
734 || classToTransformFrom == BigInteger.class)
735 return true;
736 }
737 else if (classToTransformTo == BigDecimal.class) {
738 if ( classToTransformFrom == Double.class
739 || classToTransformFrom == Integer.class
740 || classToTransformFrom == Long.class
741 || classToTransformFrom == Short.class
742 || classToTransformFrom == Byte.class
743 || classToTransformFrom == Float.class
744 || classToTransformFrom == BigDecimal.class
745 || classToTransformFrom == BigInteger.class)
746 return true;
747 }
748 else if (classToTransformTo == BigInteger.class) {
749 if ( classToTransformFrom == Integer.class
750 || classToTransformFrom == Long.class
751 || classToTransformFrom == Short.class
752 || classToTransformFrom == Byte.class
753 || classToTransformFrom == BigInteger.class)
754 return true;
755 }
756 else if (classToTransformTo == Long.class) {
757 if ( classToTransformFrom == Long.class
758 || classToTransformFrom == Integer.class
759 || classToTransformFrom == Short.class
760 || classToTransformFrom == Byte.class)
761 return true;
762 }
763 else if (classToTransformTo == Float.class) {
764 if ( classToTransformFrom == Float.class
765 || classToTransformFrom == Integer.class
766 || classToTransformFrom == Long.class
767 || classToTransformFrom == Short.class
768 || classToTransformFrom == Byte.class)
769 return true;
770 }
771 else if (classToTransformTo == Short.class) {
772 if ( classToTransformFrom == Short.class
773 || classToTransformFrom == Byte.class)
774 return true;
775 }
776 else if (classToTransformTo==String.class) {
777 if ( classToTransformFrom == String.class ||
778 GString.class.isAssignableFrom(classToTransformFrom))
779 return true;
780 }
781
782 boolean answer = classToTransformTo.isAssignableFrom(classToTransformFrom);
783 return answer;
784 }
785
786 public static boolean isGenericSetMethod(MetaMethod method) {
787 return (method.getName().equals("set"))
788 && method.getParameterTypes().length == 2;
789 }
790
791 protected static boolean isSuperclass(Class claszz, Class superclass) {
792 while (claszz!=null) {
793 if (claszz==superclass) return true;
794 claszz = claszz.getSuperclass();
795 }
796 return false;
797 }
798
799 public static boolean isValidMethod(Class[] paramTypes, Class[] arguments, boolean includeCoerce) {
800 if (arguments == null) {
801 return true;
802 }
803 int size = arguments.length;
804
805 if ( (size>=paramTypes.length || size==paramTypes.length-1)
806 && paramTypes.length>0
807 && paramTypes[paramTypes.length-1].isArray())
808 {
809
810 for (int i = 0; i < paramTypes.length-1; i++) {
811 if (isAssignableFrom(paramTypes[i], arguments[i])) continue;
812 return false;
813 }
814
815 Class clazz = paramTypes[paramTypes.length-1].getComponentType();
816 for (int i=paramTypes.length; i<size; i++) {
817 if (isAssignableFrom(clazz, arguments[i])) continue;
818 return false;
819 }
820 return true;
821 } else if (paramTypes.length == size) {
822
823 for (int i = 0; i < size; i++) {
824 if (isAssignableFrom(paramTypes[i], arguments[i])) continue;
825 return false;
826 }
827 return true;
828 } else if (paramTypes.length == 1 && size == 0) {
829 return true;
830 }
831 return false;
832
833 }
834
835 public static boolean isValidMethod(Object method, Class[] arguments, boolean includeCoerce) {
836 Class[] paramTypes = getParameterTypes(method);
837 return isValidMethod(paramTypes, arguments, includeCoerce);
838 }
839
840 public static boolean isVargsMethod(Class[] paramTypes, Object[] arguments) {
841 if (paramTypes.length==0) return false;
842 if (!paramTypes[paramTypes.length-1].isArray()) return false;
843
844 if (paramTypes.length-1==arguments.length) return true;
845 if (paramTypes.length-1>arguments.length) return false;
846 if (arguments.length>paramTypes.length) return true;
847
848
849 Object last = arguments[arguments.length-1];
850 if (last==null) return true;
851 Class clazz = last.getClass();
852 if (clazz.equals(paramTypes[paramTypes.length-1])) return false;
853
854 return true;
855 }
856
857 public static void logMethodCall(Object object, String methodName, Object[] arguments) {
858 String className = getClassName(object);
859 String logname = "methodCalls." + className + "." + methodName;
860 Logger objLog = Logger.getLogger(logname);
861 if (! objLog.isLoggable(Level.FINER)) return;
862 StringBuffer msg = new StringBuffer(methodName);
863 msg.append("(");
864 if (arguments != null){
865 for (int i = 0; i < arguments.length;) {
866 msg.append(normalizedValue(arguments[i]));
867 if (++i < arguments.length) { msg.append(","); }
868 }
869 }
870 msg.append(")");
871 objLog.logp(Level.FINER, className, msg.toString(), "called from MetaClass.invokeMethod");
872 }
873
874 protected static String normalizedValue(Object argument) {
875 String value;
876 try {
877 value = argument.toString();
878 if (value.length() > MAX_ARG_LEN){
879 value = value.substring(0,MAX_ARG_LEN-2) + "..";
880 }
881 if (argument instanceof String){
882 value = "\'"+value+"\'";
883 }
884 } catch (Exception e) {
885 value = shortName(argument);
886 }
887 return value;
888 }
889
890 public static boolean parametersAreCompatible(Class[] arguments, Class[] parameters) {
891 if (arguments.length!=parameters.length) return false;
892 for (int i=0; i<arguments.length; i++) {
893 if (!isAssignableFrom(parameters[i],arguments[i])) return false;
894 }
895 return true;
896 }
897
898 protected static String shortName(Object object) {
899 if (object == null || object.getClass()==null) return "unknownClass";
900 String name = getClassName(object);
901 if (name == null) return "unknownClassName";
902 int lastDotPos = name.lastIndexOf('.');
903 if (lastDotPos < 0 || lastDotPos >= name.length()-1) return name;
904 return name.substring(lastDotPos+1);
905 }
906
907 public static Class[] wrap(Class[] classes) {
908 Class[] wrappedArguments = new Class[classes.length];
909 for (int i = 0; i < wrappedArguments.length; i++) {
910 Class c = classes[i];
911 if (c==null) continue;
912 if (c.isPrimitive()) {
913 if (c==Integer.TYPE) {
914 c=Integer.class;
915 } else if (c==Byte.TYPE) {
916 c=Byte.class;
917 } else if (c==Long.TYPE) {
918 c=Long.class;
919 } else if (c==Double.TYPE) {
920 c=Double.class;
921 } else if (c==Float.TYPE) {
922 c=Float.class;
923 }
924 } else if (isSuperclass(c,GString.class)) {
925 c = String.class;
926 }
927 wrappedArguments[i]=c;
928 }
929 return wrappedArguments;
930 }
931 }