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