001 /* 002 * Copyright 2005 John G. Wilson 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 */ 017 018 package org.codehaus.groovy.runtime; 019 020 import groovy.lang.Closure; 021 import groovy.lang.GString; 022 import groovy.lang.GroovyRuntimeException; 023 import groovy.lang.MetaMethod; 024 025 import java.lang.reflect.Array; 026 import java.lang.reflect.Constructor; 027 import java.lang.reflect.InvocationHandler; 028 import java.lang.reflect.InvocationTargetException; 029 import java.lang.reflect.Method; 030 import java.lang.reflect.Modifier; 031 import java.lang.reflect.Proxy; 032 import java.math.BigDecimal; 033 import java.math.BigInteger; 034 import java.util.Arrays; 035 import java.util.Iterator; 036 import java.util.List; 037 import java.util.logging.Level; 038 import java.util.logging.Logger; 039 040 /** 041 * @author John Wilson 042 * 043 */ 044 045 public class MetaClassHelper { 046 047 public static final Object[] EMPTY_ARRAY = {}; 048 public static Class[] EMPTY_TYPE_ARRAY = {}; 049 protected static final Object[] ARRAY_WITH_EMPTY_ARRAY = { EMPTY_ARRAY }; 050 protected static final Object[] ARRAY_WITH_NULL = { null }; 051 protected static final Logger log = Logger.getLogger(MetaClassHelper.class.getName()); 052 private static final int MAX_ARG_LEN = 12; 053 054 public static boolean accessibleToConstructor(final Class at, final Constructor constructor) { 055 boolean accessible = false; 056 if (Modifier.isPublic(constructor.getModifiers())) { 057 accessible = true; 058 } 059 else if (Modifier.isPrivate(constructor.getModifiers())) { 060 accessible = at.getName().equals(constructor.getName()); 061 } 062 else if ( Modifier.isProtected(constructor.getModifiers()) ) { 063 if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() == null ) { 064 accessible = true; 065 } 066 else if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() != null ) { 067 accessible = false; 068 } 069 else if ( at.getPackage() != null && constructor.getDeclaringClass().getPackage() == null ) { 070 accessible = false; 071 } 072 else if ( at.getPackage().equals(constructor.getDeclaringClass().getPackage()) ) { 073 accessible = true; 074 } 075 else { 076 boolean flag = false; 077 Class clazz = at; 078 while ( !flag && clazz != null ) { 079 if (clazz.equals(constructor.getDeclaringClass()) ) { 080 flag = true; 081 break; 082 } 083 if (clazz.equals(Object.class) ) { 084 break; 085 } 086 clazz = clazz.getSuperclass(); 087 } 088 accessible = flag; 089 } 090 } 091 else { 092 if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() == null ) { 093 accessible = true; 094 } 095 else if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() != null ) { 096 accessible = false; 097 } 098 else if ( at.getPackage() != null && constructor.getDeclaringClass().getPackage() == null ) { 099 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 // type is not equal, increase distance by one to reflect 194 // the change in type 195 dist++; 196 continue; 197 } 198 199 // add one to dist to be sure interfaces are prefered 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 // choose the distance to Object if a parameter is null 213 // this will mean that Object is prefered over a more 214 // specific type 215 // remove one to dist to be sure Object is prefered 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 // lets look for methods with 1 argument which matches the type of the 279 // arguments 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; // to indicate that at least one param is coerced 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) { // Number types 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 // the following patch was provided by Mori Kouhei to fix JIRA 435 493 /* but it opens the ctor up to everyone, so it is no longer private! 494 final Constructor ctor = constructor; 495 AccessController.doPrivileged(new PrivilegedAction() { 496 public Object run() { 497 ctor.setAccessible(ctor.getDeclaringClass().equals(theClass)); 498 return null; 499 } 500 }); 501 */ 502 // end of patch 503 504 return constructor.newInstance(argumentArray); 505 } 506 catch (InvocationTargetException e) { 507 /*Throwable t = e.getTargetException(); 508 if (t instanceof Error) { 509 Error error = (Error) t; 510 throw error; 511 } 512 if (t instanceof RuntimeException) { 513 RuntimeException runtimeEx = (RuntimeException) t; 514 throw runtimeEx; 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 // allow fall through 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 //System.out.println("Evaluating method: " + method); 558 //System.out.println("on object: " + object + " with arguments: " + 559 // InvokerHelper.toString(argumentArray)); 560 //System.out.println(this.theClass); 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 // vargs 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 // allow fall through 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 /*Throwable t = e.getTargetException(); 608 if (t instanceof Error) { 609 Error error = (Error) t; 610 throw error; 611 } 612 if (t instanceof RuntimeException) { 613 RuntimeException runtimeEx = (RuntimeException) t; 614 throw runtimeEx; 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 // allow fall through 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 // allow fall through 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 // let's handle primitives 713 if (mostSpecificType.isPrimitive() && type.isPrimitive()) { 714 if (mostSpecificType == type) { 715 return true; 716 } 717 else { // note: there is not coercion for boolean and char. Range matters, precision doesn't 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); // this might have taken care of primitive types, rendering part of the following code unnecessary 772 if (!answer) { 773 if (type.isPrimitive()) { 774 if (type == int.class) { 775 return value == Integer.class;// || value == BigDecimal.class; //br added BigDecimal 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; // || value == BigDecimal.class;//br added BigDecimal 785 } 786 else if (type == float.class) { 787 return value == Float.class || value == Integer.class; // || value == BigDecimal.class;//br added BigDecimal 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 //if (type == String.class && value == GString.class) { 803 if (type == String.class && GString.class.isAssignableFrom(value)) { 804 return true; 805 } 806 else if (value == Number.class) { 807 // lets allow numbers to be coerced downwards? 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 // lets allow numbers to be coerced downwards? 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 // first check normal number of parameters 884 for (int i = 0; i < paramTypes.length-1; i++) { 885 if (isCompatibleClass(paramTypes[i], arguments[i], includeCoerce)) continue; 886 return false; 887 } 888 // check varged 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 // lets check the parameter types match 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 // -1 because the varg part is optional 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 // only case left is arguments.length==paramTypes.length 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"; // *very* defensive... 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 }