1 33 34 package bsh; 35 36 import java.lang.reflect.*; 37 import java.util.Vector ; 38 39 44 51 class Reflect 52 { 53 59 public static Object invokeObjectMethod( 60 Object object, String methodName, Object [] args, 61 Interpreter interpreter, CallStack callstack, SimpleNode callerInfo ) 62 throws ReflectError, EvalError, InvocationTargetException 63 { 64 if ( object instanceof This && !This.isExposedThisMethod(methodName) ) 66 return ((This)object).invokeMethod( 67 methodName, args, interpreter, callstack, callerInfo, 68 false 69 ); 70 71 try { 73 BshClassManager bcm = 74 interpreter == null ? null : interpreter.getClassManager(); 75 Class clas = object.getClass(); 76 77 Method method = resolveExpectedJavaMethod( 78 bcm, clas, object, methodName, args, false ); 79 80 return invokeMethod( method, object, args ); 81 } catch ( UtilEvalError e ) { 82 throw e.toEvalError( callerInfo, callstack ); 83 } 84 } 85 86 91 public static Object invokeStaticMethod( 92 BshClassManager bcm, Class clas, String methodName, Object [] args ) 93 throws ReflectError, UtilEvalError, InvocationTargetException 94 { 95 Interpreter.debug("invoke static Method"); 96 Method method = resolveExpectedJavaMethod( 97 bcm, clas, null, methodName, args, true ); 98 return invokeMethod( method, null, args ); 99 } 100 101 106 static Object invokeMethod( 107 Method method, Object object, Object [] args ) 108 throws ReflectError, InvocationTargetException 109 { 110 if ( args == null ) 111 args = new Object [0]; 112 113 logInvokeMethod( "Invoking method (entry): ", method, args ); 114 115 Object [] tmpArgs = new Object [ args.length ]; 117 Class [] types = method.getParameterTypes(); 118 try { 119 for (int i=0; i<args.length; i++) 120 tmpArgs[i] = Types.castObject( 121 args[i], types[i], Types.ASSIGNMENT ); 122 } catch ( UtilEvalError e ) { 123 throw new InterpreterError( 124 "illegal argument type in method invocation: "+e ); 125 } 126 127 tmpArgs = Primitive.unwrap( tmpArgs ); 129 130 logInvokeMethod( "Invoking method (after massaging values): ", 131 method, tmpArgs ); 132 133 try { 134 Object returnValue = method.invoke( object, tmpArgs ); 135 if ( returnValue == null ) 136 returnValue = Primitive.NULL; 137 Class returnType = method.getReturnType(); 138 139 return Primitive.wrap( returnValue, returnType ); 140 } catch( IllegalAccessException e ) { 141 throw new ReflectError( "Cannot access method " 142 + StringUtil.methodString( 143 method.getName(), method.getParameterTypes() ) 144 + " in '" + method.getDeclaringClass() + "' :" + e ); 145 } 146 } 147 148 public static Object getIndex(Object array, int index) 149 throws ReflectError, UtilTargetError 150 { 151 if ( Interpreter.DEBUG ) 152 Interpreter.debug("getIndex: "+array+", index="+index); 153 try { 154 Object val = Array.get(array, index); 155 return Primitive.wrap( val, array.getClass().getComponentType() ); 156 } 157 catch( ArrayIndexOutOfBoundsException e1 ) { 158 throw new UtilTargetError( e1 ); 159 } catch(Exception e) { 160 throw new ReflectError("Array access:" + e); 161 } 162 } 163 164 public static void setIndex(Object array, int index, Object val) 165 throws ReflectError, UtilTargetError 166 { 167 try { 168 val = Primitive.unwrap(val); 169 Array.set(array, index, val); 170 } 171 catch( ArrayStoreException e2 ) { 172 throw new UtilTargetError( e2 ); 173 } catch( IllegalArgumentException e1 ) { 174 throw new UtilTargetError( 175 new ArrayStoreException ( e1.toString() ) ); 176 } catch(Exception e) { 177 throw new ReflectError("Array access:" + e); 178 } 179 } 180 181 public static Object getStaticFieldValue(Class clas, String fieldName) 182 throws UtilEvalError, ReflectError 183 { 184 return getFieldValue( clas, null, fieldName, true); 185 } 186 187 189 public static Object getObjectFieldValue( Object object, String fieldName ) 190 throws UtilEvalError, ReflectError 191 { 192 if ( object instanceof This ) 193 return ((This)object).namespace.getVariable( fieldName ); 194 else { 195 try { 196 return getFieldValue( 197 object.getClass(), object, fieldName, false); 198 } catch ( ReflectError e ) { 199 201 if ( hasObjectPropertyGetter( object.getClass(), fieldName ) ) 202 return getObjectProperty( object, fieldName ); 203 else 204 throw e; 205 } 206 } 207 } 208 209 static LHS getLHSStaticField(Class clas, String fieldName) 210 throws UtilEvalError, ReflectError 211 { 212 Field f = resolveExpectedJavaField( 213 clas, fieldName, true); 214 return new LHS(f); 215 } 216 217 223 static LHS getLHSObjectField( Object object, String fieldName ) 224 throws UtilEvalError, ReflectError 225 { 226 if ( object instanceof This ) 227 { 228 boolean recurse = false; 231 return new LHS( ((This)object).namespace, fieldName, recurse ); 232 } 233 234 try { 235 Field f = resolveExpectedJavaField( 236 object.getClass(), fieldName, false ); 237 return new LHS(object, f); 238 } catch ( ReflectError e ) 239 { 240 if ( hasObjectPropertySetter( object.getClass(), fieldName ) ) 242 return new LHS( object, fieldName ); 243 else 244 throw e; 245 } 246 } 247 248 private static Object getFieldValue( 249 Class clas, Object object, String fieldName, boolean staticOnly ) 250 throws UtilEvalError, ReflectError 251 { 252 try { 253 Field f = resolveExpectedJavaField( clas, fieldName, staticOnly ); 254 255 Object value = f.get(object); 256 Class returnType = f.getType(); 257 return Primitive.wrap( value, returnType ); 258 259 } catch( NullPointerException e ) { throw new ReflectError( 261 "???" + fieldName + " is not a static field."); 262 } catch(IllegalAccessException e) { 263 throw new ReflectError("Can't access field: " + fieldName); 264 } 265 } 266 267 273 protected static Field resolveJavaField( 274 Class clas, String fieldName, boolean staticOnly ) 275 throws UtilEvalError 276 { 277 try { 278 return resolveExpectedJavaField( clas, fieldName, staticOnly ); 279 } catch ( ReflectError e ) { 280 return null; 281 } 282 } 283 284 287 291 protected static Field resolveExpectedJavaField( 292 Class clas, String fieldName, boolean staticOnly 293 ) 294 throws UtilEvalError, ReflectError 295 { 296 Field field; 297 try { 298 if ( Capabilities.haveAccessibility() ) 299 field = findAccessibleField( clas, fieldName ); 300 else 301 field = clas.getField(fieldName); 303 } 304 catch( NoSuchFieldException e) { 305 throw new ReflectError("No such field: " + fieldName ); 306 } catch ( SecurityException e ) { 307 throw new UtilTargetError( 308 "Security Exception while searching fields of: "+clas, 309 e ); 310 } 311 312 if ( staticOnly && !Modifier.isStatic( field.getModifiers() ) ) 313 throw new UtilEvalError( 314 "Can't reach instance field: "+fieldName 315 +" from static context: "+clas.getName() ); 316 317 return field; 318 } 319 320 334 338 private static Field findAccessibleField( Class clas, String fieldName ) 339 throws UtilEvalError, NoSuchFieldException 340 { 341 Field field; 342 343 try { 345 field = clas.getField(fieldName); 346 ReflectManager.RMSetAccessible( field ); 347 return field; 348 } catch ( NoSuchFieldException e ) { } 349 350 while ( clas != null ) 352 { 353 try { 354 field = clas.getDeclaredField(fieldName); 355 ReflectManager.RMSetAccessible( field ); 356 return field; 357 358 360 } catch(NoSuchFieldException e) { } 361 362 clas = clas.getSuperclass(); 363 } 364 throw new NoSuchFieldException ( fieldName ); 365 } 366 367 371 protected static Method resolveExpectedJavaMethod( 372 BshClassManager bcm, Class clas, Object object, 373 String name, Object [] args, boolean staticOnly ) 374 throws ReflectError, UtilEvalError 375 { 376 if ( object == Primitive.NULL ) 377 throw new UtilTargetError( new NullPointerException ( 378 "Attempt to invoke method " +name+" on null value" ) ); 379 380 Class [] types = Types.getTypes(args); 381 Method method = resolveJavaMethod( bcm, clas, name, types, staticOnly ); 382 383 if ( method == null ) 384 throw new ReflectError( 385 ( staticOnly ? "Static method " : "Method " ) 386 + StringUtil.methodString(name, types) + 387 " not found in class'" + clas.getName() + "'"); 388 389 return method; 390 } 391 392 419 protected static Method resolveJavaMethod( 420 BshClassManager bcm, Class clas, String name, 421 Class [] types, boolean staticOnly ) 422 throws UtilEvalError 423 { 424 if ( clas == null ) 425 throw new InterpreterError("null class"); 426 427 Method method = null; 429 if ( bcm == null ) 430 Interpreter.debug("resolveJavaMethod UNOPTIMIZED lookup"); 431 else 432 method = bcm.getResolvedMethod( clas, name, types, staticOnly ); 433 434 if ( method == null ) 435 { 436 boolean publicOnly = !Capabilities.haveAccessibility(); 437 try { 439 method = findOverloadedMethod( clas, name, types, publicOnly ); 440 } catch ( SecurityException e ) { 441 throw new UtilTargetError( 442 "Security Exception while searching methods of: "+clas, 443 e ); 444 } 445 446 checkFoundStaticMethod( method, staticOnly, clas ); 447 448 if ( method != null && !publicOnly ) { 452 try { 453 ReflectManager.RMSetAccessible( method ); 454 } catch ( UtilEvalError e ) { } 455 } 456 457 if ( method != null && bcm != null ) 459 bcm.cacheResolvedMethod( clas, types, method ); 460 } 461 462 return method; 463 } 464 465 470 private static Method findOverloadedMethod( 471 Class baseClass, String methodName, Class [] types, boolean publicOnly ) 472 { 473 if ( Interpreter.DEBUG ) 474 Interpreter.debug( "Searching for method: "+ 475 StringUtil.methodString(methodName, types) 476 + " in '" + baseClass.getName() + "'" ); 477 478 Method [] methods = getCandidateMethods( 479 baseClass, methodName, types.length, publicOnly ); 480 481 if ( Interpreter.DEBUG ) 482 Interpreter.debug("Looking for most specific method: "+methodName); 483 Method method = findMostSpecificMethod( types, methods ); 484 485 return method; 486 } 487 488 501 static Method[] getCandidateMethods( 502 Class baseClass, String methodName, int numArgs, 503 boolean publicOnly ) 504 { 505 Vector candidates = gatherMethodsRecursive( 506 baseClass, methodName, numArgs, publicOnly, null); 507 508 Method [] ma = new Method[ candidates.size() ]; 510 candidates.copyInto( ma ); 511 return ma; 512 } 513 514 530 private static Vector gatherMethodsRecursive( 531 Class baseClass, String methodName, int numArgs, 532 boolean publicOnly, Vector candidates ) 533 { 534 if ( candidates == null ) 535 candidates = new Vector (); 536 537 if ( publicOnly ) { 542 if ( isPublic(baseClass) ) 543 addCandidates( baseClass.getMethods(), 544 methodName, numArgs, publicOnly, candidates ); 545 } else 546 addCandidates( baseClass.getDeclaredMethods(), 547 methodName, numArgs, publicOnly, candidates ); 548 549 Class [] intfs = baseClass.getInterfaces(); 551 for( int i=0; i< intfs.length; i++ ) 552 gatherMethodsRecursive( intfs[i], 553 methodName, numArgs, publicOnly, candidates ); 554 555 Class superclass = baseClass.getSuperclass(); 557 if ( superclass != null ) 558 gatherMethodsRecursive( superclass, 559 methodName, numArgs, publicOnly, candidates ); 560 561 return candidates; 562 } 563 564 private static Vector addCandidates( 565 Method [] methods, String methodName, 566 int numArgs, boolean publicOnly, Vector candidates ) 567 { 568 for ( int i = 0; i < methods.length; i++ ) 569 { 570 Method m = methods[i]; 571 if ( m.getName().equals( methodName ) 572 && ( m.getParameterTypes().length == numArgs ) 573 && ( !publicOnly || isPublic( m ) ) 574 ) 575 candidates.add( m ); 576 } 577 return candidates; 578 } 579 580 590 static Object constructObject( Class clas, Object [] args ) 591 throws ReflectError, InvocationTargetException 592 { 593 if ( clas.isInterface() ) 594 throw new ReflectError( 595 "Can't create instance of an interface: "+clas); 596 597 Object obj = null; 598 Class [] types = Types.getTypes(args); 599 Constructor con = null; 600 601 Constructor[] constructors = 604 Capabilities.haveAccessibility() ? 605 clas.getDeclaredConstructors() : clas.getConstructors() ; 606 607 if ( Interpreter.DEBUG ) 608 Interpreter.debug("Looking for most specific constructor: "+clas); 609 con = findMostSpecificConstructor(types, constructors); 610 if ( con == null ) 611 throw cantFindConstructor( clas, types ); 612 613 if ( !isPublic( con ) ) 614 try { 615 ReflectManager.RMSetAccessible( con ); 616 } catch ( UtilEvalError e ) { } 617 618 args=Primitive.unwrap( args ); 619 try { 620 obj = con.newInstance( args ); 621 } catch(InstantiationException e) { 622 throw new ReflectError("The class "+clas+" is abstract "); 623 } catch(IllegalAccessException e) { 624 throw new ReflectError( 625 "We don't have permission to create an instance." 626 +"Use setAccessibility(true) to enable access." ); 627 } catch(IllegalArgumentException e) { 628 throw new ReflectError("The number of arguments was wrong"); 629 } 630 if (obj == null) 631 throw new ReflectError("Couldn't construct the object"); 632 633 return obj; 634 } 635 636 641 static Constructor findMostSpecificConstructor( 642 Class [] idealMatch, Constructor[] constructors) 643 { 644 int match = findMostSpecificConstructorIndex(idealMatch, constructors ); 645 return ( match == -1 ) ? null : constructors[ match ]; 646 } 647 648 static int findMostSpecificConstructorIndex( 649 Class [] idealMatch, Constructor[] constructors) 650 { 651 Class [][] candidates = new Class [ constructors.length ] []; 652 for(int i=0; i< candidates.length; i++ ) 653 candidates[i] = constructors[i].getParameterTypes(); 654 655 return findMostSpecificSignature( idealMatch, candidates ); 656 } 657 658 670 static Method findMostSpecificMethod( 671 Class [] idealMatch, Method[] methods ) 672 { 673 Class [][] candidateSigs = new Class [ methods.length ][]; 675 for(int i=0; i<methods.length; i++) 676 candidateSigs[i] = methods[i].getParameterTypes(); 677 678 int match = findMostSpecificSignature( idealMatch, candidateSigs ); 679 return match == -1 ? null : methods[match]; 680 } 681 682 692 703 static int findMostSpecificSignature( 704 Class [] idealMatch, Class [][] candidates ) 705 { 706 for ( int round = Types.FIRST_ROUND_ASSIGNABLE; 707 round <= Types.LAST_ROUND_ASSIGNABLE; round++ ) 708 { 709 Class [] bestMatch = null; 710 int bestMatchIndex = -1; 711 712 for (int i=0; i < candidates.length; i++) 713 { 714 Class [] targetMatch = candidates[i]; 715 716 if ( Types.isSignatureAssignable( 720 idealMatch, targetMatch, round ) 721 && ( (bestMatch == null) || 722 Types.isSignatureAssignable( targetMatch, bestMatch, 723 Types.JAVA_BASE_ASSIGNABLE ) 724 ) 725 ) 726 { 727 bestMatch = targetMatch; 728 bestMatchIndex = i; 729 } 730 } 731 732 if ( bestMatch != null ) 733 return bestMatchIndex; 734 } 735 736 return -1; 737 } 738 739 private static String accessorName( String getorset, String propName ) { 740 return getorset 741 + String.valueOf(Character.toUpperCase(propName.charAt(0))) 742 + propName.substring(1); 743 } 744 745 public static boolean hasObjectPropertyGetter( 746 Class clas, String propName ) 747 { 748 String getterName = accessorName("get", propName ); 749 try { 750 clas.getMethod( getterName, new Class [0] ); 751 return true; 752 } catch ( NoSuchMethodException e ) { } 753 getterName = accessorName("is", propName ); 754 try { 755 Method m = clas.getMethod( getterName, new Class [0] ); 756 return ( m.getReturnType() == Boolean.TYPE ); 757 } catch ( NoSuchMethodException e ) { 758 return false; 759 } 760 } 761 762 public static boolean hasObjectPropertySetter( 763 Class clas, String propName ) 764 { 765 String setterName = accessorName("set", propName ); 766 Method [] methods = clas.getMethods(); 767 768 for(int i=0; i<methods.length; i++) 771 if ( methods[i].getName().equals( setterName ) ) 772 return true; 773 return false; 774 } 775 776 public static Object getObjectProperty( 777 Object obj, String propName ) 778 throws UtilEvalError, ReflectError 779 { 780 Object [] args = new Object [] { }; 781 782 Interpreter.debug("property access: "); 783 Method method = null; 784 785 Exception e1=null, e2=null; 786 try { 787 String accessorName = accessorName( "get", propName ); 788 method = resolveExpectedJavaMethod( 789 null, obj.getClass(), obj, accessorName, args, false ); 790 } catch ( Exception e ) { 791 e1 = e; 792 } 793 if ( method == null ) 794 try { 795 String accessorName = accessorName( "is", propName ); 796 method = resolveExpectedJavaMethod( 797 null, obj.getClass(), obj, 798 accessorName, args, false ); 799 if ( method.getReturnType() != Boolean.TYPE ) 800 method = null; 801 } catch ( Exception e ) { 802 e2 = e; 803 } 804 if ( method == null ) 805 throw new ReflectError("Error in property getter: " 806 +e1 + (e2!=null?" : "+e2:"") ); 807 808 try { 809 return invokeMethod( method, obj, args ); 810 } 811 catch(InvocationTargetException e) 812 { 813 throw new UtilEvalError("Property accessor threw exception: " 814 +e.getTargetException() ); 815 } 816 } 817 818 public static void setObjectProperty( 819 Object obj, String propName, Object value) 820 throws ReflectError, UtilEvalError 821 { 822 String accessorName = accessorName( "set", propName ); 823 Object [] args = new Object [] { value }; 824 825 Interpreter.debug("property access: "); 826 try { 827 Method method = resolveExpectedJavaMethod( 828 null, obj.getClass(), obj, accessorName, args, false ); 829 invokeMethod( method, obj, args ); 830 } 831 catch ( InvocationTargetException e ) 832 { 833 throw new UtilEvalError("Property accessor threw exception: " 834 +e.getTargetException() ); 835 } 836 } 837 838 844 public static String normalizeClassName(Class type) 845 { 846 if ( !type.isArray() ) 847 return type.getName(); 848 849 StringBuffer className = new StringBuffer (); 850 try { 851 className.append( getArrayBaseType(type).getName() +" "); 852 for(int i = 0; i < getArrayDimensions(type); i++) 853 className.append("[]"); 854 } catch( ReflectError e ) { } 855 856 return className.toString(); 857 } 858 859 863 public static int getArrayDimensions(Class arrayClass) 864 { 865 if ( !arrayClass.isArray() ) 866 return 0; 867 868 return arrayClass.getName().lastIndexOf('[') + 1; } 870 871 876 public static Class getArrayBaseType(Class arrayClass) throws ReflectError 877 { 878 if ( !arrayClass.isArray() ) 879 throw new ReflectError("The class is not an array."); 880 881 return arrayClass.getComponentType(); 882 883 } 884 885 893 public static Object invokeCompiledCommand( 894 Class commandClass, Object [] args, Interpreter interpreter, 895 CallStack callstack ) 896 throws UtilEvalError 897 { 898 Object [] invokeArgs = new Object [args.length + 2]; 900 invokeArgs[0] = interpreter; 901 invokeArgs[1] = callstack; 902 System.arraycopy( args, 0, invokeArgs, 2, args.length ); 903 BshClassManager bcm = interpreter.getClassManager(); 904 try { 905 return Reflect.invokeStaticMethod( 906 bcm, commandClass, "invoke", invokeArgs ); 907 } catch ( InvocationTargetException e ) { 908 throw new UtilEvalError( 909 "Error in compiled command: "+e.getTargetException() ); 910 } catch ( ReflectError e ) { 911 throw new UtilEvalError("Error invoking compiled command: "+e ); 912 } 913 } 914 915 private static void logInvokeMethod( 916 String msg, Method method, Object [] args ) 917 { 918 if ( Interpreter.DEBUG ) 919 { 920 Interpreter.debug( msg +method+" with args:" ); 921 for(int i=0; i<args.length; i++) 922 Interpreter.debug( 923 "args["+i+"] = "+args[i] 924 +" type = "+args[i].getClass() ); 925 } 926 } 927 928 private static void checkFoundStaticMethod( 929 Method method, boolean staticOnly, Class clas ) 930 throws UtilEvalError 931 { 932 if ( method != null && staticOnly && !isStatic( method ) ) 934 throw new UtilEvalError( 935 "Cannot reach instance method: " 936 + StringUtil.methodString( 937 method.getName(), method.getParameterTypes() ) 938 + " from static context: "+ clas.getName() ); 939 } 940 941 private static ReflectError cantFindConstructor( 942 Class clas, Class [] types ) 943 { 944 if ( types.length == 0 ) 945 return new ReflectError( 946 "Can't find default constructor for: "+clas); 947 else 948 return new ReflectError( 949 "Can't find constructor: " 950 + StringUtil.methodString( clas.getName(), types ) 951 +" in class: "+ clas.getName() ); 952 } 953 954 private static boolean isPublic( Class c ) { 955 return Modifier.isPublic( c.getModifiers() ); 956 } 957 private static boolean isPublic( Method m ) { 958 return Modifier.isPublic( m.getModifiers() ); 959 } 960 private static boolean isPublic( Constructor c ) { 961 return Modifier.isPublic( c.getModifiers() ); 962 } 963 private static boolean isStatic( Method m ) { 964 return Modifier.isStatic( m.getModifiers() ); 965 } 966 } 967 968 | Popular Tags |