PageRenderTime 46ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/bsh/Reflect.java

#
Java | 968 lines | 609 code | 98 blank | 261 comment | 105 complexity | 21fc04cf5f27f7322efaef1914bec453 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*****************************************************************************
  2. * *
  3. * This file is part of the BeanShell Java Scripting distribution. *
  4. * Documentation and updates may be found at http://www.beanshell.org/ *
  5. * *
  6. * Sun Public License Notice: *
  7. * *
  8. * The contents of this file are subject to the Sun Public License Version *
  9. * 1.0 (the "License"); you may not use this file except in compliance with *
  10. * the License. A copy of the License is available at http://www.sun.com *
  11. * *
  12. * The Original Code is BeanShell. The Initial Developer of the Original *
  13. * Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright *
  14. * (C) 2000. All Rights Reserved. *
  15. * *
  16. * GNU Public License Notice: *
  17. * *
  18. * Alternatively, the contents of this file may be used under the terms of *
  19. * the GNU Lesser General Public License (the "LGPL"), in which case the *
  20. * provisions of LGPL are applicable instead of those above. If you wish to *
  21. * allow use of your version of this file only under the terms of the LGPL *
  22. * and not to allow others to use your version of this file under the SPL, *
  23. * indicate your decision by deleting the provisions above and replace *
  24. * them with the notice and other provisions required by the LGPL. If you *
  25. * do not delete the provisions above, a recipient may use your version of *
  26. * this file under either the SPL or the LGPL. *
  27. * *
  28. * Patrick Niemeyer (pat@pat.net) *
  29. * Author of Learning Java, O'Reilly & Associates *
  30. * http://www.pat.net/~pat/ *
  31. * *
  32. *****************************************************************************/
  33. package bsh;
  34. import java.lang.reflect.*;
  35. import java.io.*;
  36. import java.util.Vector;
  37. /**
  38. All of the reflection API code lies here. It is in the form
  39. of static utilities. Maybe this belongs in LHS.java or a generic object
  40. wrapper class.
  41. */
  42. /*
  43. Note: This class is messy. The method and field resolution need to be
  44. rewritten. Various methods in here catch NoSuchMethod or NoSuchField
  45. exceptions during their searches. These should be rewritten to avoid
  46. having to catch the exceptions. Method lookups are now cached at a high
  47. level so they are less important, however the logic is messy.
  48. */
  49. class Reflect
  50. {
  51. /**
  52. Invoke method on arbitrary object.
  53. invocation may be static (through the object instance) or dynamic.
  54. Object may be a bsh scripted object (This type).
  55. */
  56. public static Object invokeObjectMethod(
  57. Object object, String methodName, Object[] args,
  58. Interpreter interpreter, CallStack callstack, SimpleNode callerInfo )
  59. throws ReflectError, EvalError, InvocationTargetException
  60. {
  61. // Bsh scripted object
  62. if ( object instanceof This && !This.isExposedThisMethod( methodName) )
  63. return ((This)object).invokeMethod(
  64. methodName, args, interpreter, callstack, callerInfo,
  65. false/*delcaredOnly*/ );
  66. else
  67. // Java object
  68. {
  69. // find the java method
  70. try {
  71. BshClassManager bcm =
  72. interpreter == null ? null : interpreter.getClassManager();
  73. Class clas = object.getClass();
  74. Method method = resolveExpectedJavaMethod(
  75. bcm, clas, object, methodName, args, false );
  76. return invokeOnMethod( method, object, args );
  77. } catch ( UtilEvalError e ) {
  78. throw e.toEvalError( callerInfo, callstack );
  79. }
  80. }
  81. }
  82. /**
  83. Invoke a method known to be static.
  84. No object instance is needed and there is no possibility of the
  85. method being a bsh scripted method.
  86. */
  87. public static Object invokeStaticMethod(
  88. BshClassManager bcm, Class clas, String methodName, Object [] args )
  89. throws ReflectError, UtilEvalError, InvocationTargetException
  90. {
  91. Interpreter.debug("invoke static Method");
  92. Method method = resolveExpectedJavaMethod(
  93. bcm, clas, null, methodName, args, true );
  94. return invokeOnMethod( method, null, args );
  95. }
  96. /**
  97. Invoke the Java method on the specified object.
  98. @param args may be null
  99. */
  100. static Object invokeOnMethod(
  101. Method method, Object object, Object[] args )
  102. throws ReflectError, InvocationTargetException
  103. {
  104. if ( args == null )
  105. args = new Object[0];
  106. if ( Interpreter.DEBUG )
  107. {
  108. Interpreter.debug("Invoking method (entry): "
  109. +method+" with args:" );
  110. for(int i=0; i<args.length; i++)
  111. Interpreter.debug(
  112. "args["+i+"] = "+args[i]
  113. +" type = "+args[i].getClass() );
  114. }
  115. // Map types to assignable forms, need to keep this fast...
  116. 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.getAssignableForm( args[i], types[i] );
  121. } catch ( UtilEvalError e ) {
  122. throw new InterpreterError(
  123. "illegal argument type in method invocation: "+e );
  124. }
  125. // unwrap any primitives
  126. tmpArgs = Primitive.unwrap( tmpArgs );
  127. if ( Interpreter.DEBUG )
  128. {
  129. Interpreter.debug("Invoking method (after massaging values): "
  130. +method+" with tmpArgs:" );
  131. for(int i=0; i<tmpArgs.length; i++)
  132. Interpreter.debug(
  133. "tmpArgs["+i+"] = "+tmpArgs[i]
  134. +" type = "+tmpArgs[i].getClass() );
  135. }
  136. try {
  137. Object returnValue = method.invoke( object, tmpArgs );
  138. if ( returnValue == null )
  139. returnValue = Primitive.NULL;
  140. Class returnType = method.getReturnType();
  141. return Primitive.wrap( returnValue, returnType );
  142. } catch( IllegalAccessException e ) {
  143. throw new ReflectError( "Cannot access method "
  144. + StringUtil.methodString(
  145. method.getName(), method.getParameterTypes() )
  146. + " in '" + method.getDeclaringClass() + "' :" + e );
  147. }
  148. }
  149. public static Object getIndex(Object array, int index)
  150. throws ReflectError, UtilTargetError
  151. {
  152. if ( Interpreter.DEBUG )
  153. Interpreter.debug("getIndex: "+array+", index="+index);
  154. try {
  155. Object val = Array.get(array, index);
  156. return Primitive.wrap( val, array.getClass().getComponentType() );
  157. }
  158. catch( ArrayIndexOutOfBoundsException e1 ) {
  159. throw new UtilTargetError( e1 );
  160. } catch(Exception e) {
  161. throw new ReflectError("Array access:" + e);
  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. public static Object getStaticField(Class clas, String fieldName)
  181. throws UtilEvalError, ReflectError
  182. {
  183. return getFieldValue( clas, null, fieldName, true/*onlystatic*/);
  184. }
  185. /**
  186. */
  187. public static Object getObjectField( Object object, String fieldName )
  188. throws UtilEvalError, ReflectError
  189. {
  190. if ( object instanceof This )
  191. return ((This)object).namespace.getVariable( fieldName );
  192. else {
  193. try {
  194. return getFieldValue(
  195. object.getClass(), object, fieldName, false/*onlystatic*/);
  196. } catch ( ReflectError e ) {
  197. // no field, try property acces
  198. if ( hasObjectPropertyGetter( object.getClass(), fieldName ) )
  199. return getObjectProperty( object, fieldName );
  200. else
  201. throw e;
  202. }
  203. }
  204. }
  205. static LHS getLHSStaticField(Class clas, String fieldName)
  206. throws UtilEvalError, ReflectError
  207. {
  208. Field f = resolveExpectedJavaField(
  209. clas, fieldName, true/*onlystatic*/);
  210. return new LHS(f);
  211. }
  212. /**
  213. Get an LHS reference to an object field.
  214. This method also deals with the field style property access.
  215. In the field does not exist we check for a property setter.
  216. */
  217. static LHS getLHSObjectField( Object object, String fieldName )
  218. throws UtilEvalError, ReflectError
  219. {
  220. if ( object instanceof This )
  221. {
  222. // I guess this is when we pass it as an argument?
  223. // Setting locally
  224. boolean recurse = false;
  225. return new LHS( ((This)object).namespace, fieldName, recurse );
  226. }
  227. try {
  228. Field f = resolveExpectedJavaField(
  229. object.getClass(), fieldName, false/*onlyStatic*/ );
  230. return new LHS(object, f);
  231. } catch ( ReflectError e )
  232. {
  233. // not a field, try property access
  234. if ( hasObjectPropertySetter( object.getClass(), fieldName ) )
  235. return new LHS( object, fieldName );
  236. else
  237. throw e;
  238. }
  239. }
  240. private static Object getFieldValue(
  241. Class clas, Object object, String fieldName, boolean onlyStatic )
  242. throws UtilEvalError, ReflectError
  243. {
  244. try {
  245. Field f = resolveExpectedJavaField( clas, fieldName, onlyStatic );
  246. Object value = f.get(object);
  247. Class returnType = f.getType();
  248. return Primitive.wrap( value, returnType );
  249. } catch( NullPointerException e ) { // shouldn't happen
  250. throw new ReflectError(
  251. "???" + fieldName + " is not a static field.");
  252. } catch(IllegalAccessException e) {
  253. throw new ReflectError("Can't access field: " + fieldName);
  254. }
  255. }
  256. /**
  257. */
  258. /*
  259. Note: this method and resolveExpectedJavaField should be rewritten
  260. to invert this logic so that no exceptions need to be caught
  261. unecessarily. This is just a temporary impl.
  262. @return the field or null if not found
  263. */
  264. protected static Field resolveJavaField(
  265. Class clas, String fieldName, boolean onlyStatic )
  266. throws UtilEvalError
  267. {
  268. try {
  269. return resolveExpectedJavaField( clas, fieldName, onlyStatic );
  270. } catch ( ReflectError e ) {
  271. return null;
  272. }
  273. }
  274. /**
  275. @throws ReflectError if the field is not found.
  276. */
  277. /*
  278. Note: this should really just throw NoSuchFieldException... need
  279. to change related signatures and code.
  280. */
  281. protected static Field resolveExpectedJavaField(
  282. Class clas, String fieldName, boolean onlyStatic
  283. )
  284. throws UtilEvalError, ReflectError
  285. {
  286. Field f;
  287. try {
  288. if ( Capabilities.haveAccessibility() )
  289. f = findAccessibleField( clas, fieldName );
  290. else
  291. // this one only finds public (and in interfaces, etc.)
  292. f = clas.getField(fieldName);
  293. }
  294. catch( NoSuchFieldException e)
  295. {
  296. // try declaredField
  297. throw new ReflectError("No such field: " + fieldName );
  298. }
  299. if ( onlyStatic && !Modifier.isStatic( f.getModifiers() ) )
  300. throw new UtilEvalError(
  301. "Can't reach instance field: "+fieldName
  302. +" from static context: "+clas.getName() );
  303. return f;
  304. }
  305. /**
  306. Used when accessibility capability is available to locate an occurrance
  307. of the field in the most derived class or superclass and set its
  308. accessibility flag.
  309. Note that this method is not needed in the simple non accessible
  310. case because we don't have to hunt for fields.
  311. Note that classes may declare overlapping private fields, so the
  312. distinction about the most derived is important. Java doesn't normally
  313. allow this kind of access (super won't show private variables) so
  314. there is no real syntax for specifying which class scope to use...
  315. @return the Field or throws NoSuchFieldException
  316. @throws NoSuchFieldException if the field is not found
  317. */
  318. /*
  319. This method should be rewritten to use getFields() and avoid catching
  320. exceptions during the search.
  321. */
  322. private static Field findAccessibleField( Class clas, String fieldName )
  323. throws UtilEvalError, NoSuchFieldException
  324. {
  325. Field field;
  326. // Quick check catches public fields include those in interfaces
  327. try {
  328. field = clas.getField(fieldName);
  329. ReflectManager.RMSetAccessible( field );
  330. return field;
  331. } catch ( NoSuchFieldException e ) { }
  332. // Now, on with the hunt...
  333. while ( clas != null )
  334. {
  335. try {
  336. field = clas.getDeclaredField(fieldName);
  337. ReflectManager.RMSetAccessible( field );
  338. return field;
  339. // Not found, fall through to next class
  340. } catch(NoSuchFieldException e) { }
  341. clas = clas.getSuperclass();
  342. }
  343. throw new NoSuchFieldException( fieldName );
  344. }
  345. /**
  346. This method expects a non-null method from resolveJavaMethod().
  347. If the method is not found it throws a descriptive ReflectError.
  348. */
  349. protected static Method resolveExpectedJavaMethod(
  350. BshClassManager bcm, Class clas, Object object,
  351. String name, Object[] args, boolean onlyStatic )
  352. throws ReflectError, UtilEvalError
  353. {
  354. Method method = resolveJavaMethod(
  355. bcm, clas, object, name, args, onlyStatic );
  356. if ( method != null )
  357. return method;
  358. Class [] types = Types.getTypes(args);
  359. throw new ReflectError(
  360. ( onlyStatic ? "Static method " : "Method " )
  361. + StringUtil.methodString(name, types) +
  362. " not found in class'" + clas.getName() + "'");
  363. }
  364. /**
  365. The full blown resolver method. All other method invocation methods
  366. delegate to this. The method may be static or dynamic unless
  367. onlyStatic is set (in which case object may be null).
  368. If onlyStatic is set the only static methods will be located.
  369. <p/>
  370. This method performs Java method caching internally.
  371. @param onlyStatic
  372. The method located must be static, the object param may be null.
  373. @return the method or null if no matching method was found.
  374. @deprecated
  375. */
  376. /*
  377. Note: object is only used here for precondition... get rid of it?
  378. */
  379. protected static Method resolveJavaMethod(
  380. BshClassManager bcm, Class clas, Object object,
  381. String name, Object[] args, boolean onlyStatic )
  382. throws UtilEvalError
  383. {
  384. // Why is object in the args?
  385. if ( object == Primitive.NULL )
  386. throw new UtilTargetError( new NullPointerException(
  387. "Attempt to invoke method " +name+" on null value" ) );
  388. Class [] types = Types.getTypes(args);
  389. return resolveJavaMethod( bcm, clas, name, types, onlyStatic );
  390. }
  391. /*
  392. Notes:
  393. This is broken. It finds public but less specific methods over
  394. non-public but more specific ones.
  395. findMostSpecficMethod() needs to be rewritten to eliminate
  396. findAccessibleMethod. We should implement the findMostSpecificMethod
  397. that uses the publicOnly flag. FindMostSpecificMethod should also
  398. operate in two passes to give standard Java assignable matches priority
  399. over extended bsh type matches.
  400. */
  401. protected static Method resolveJavaMethod(
  402. BshClassManager bcm, Class clas, String name,
  403. Class [] types, boolean onlyStatic )
  404. throws UtilEvalError
  405. {
  406. if ( clas == null )
  407. throw new InterpreterError("null class");
  408. Method method = null;
  409. if ( bcm == null )
  410. Interpreter.debug("resolveJavaMethod UNOPTIMIZED lookup");
  411. else {
  412. method = bcm.getResolvedMethod( clas, name, types, onlyStatic );
  413. if ( method != null )
  414. return method;
  415. }
  416. if ( Interpreter.DEBUG )
  417. Interpreter.debug( "Searching for method: "+
  418. StringUtil.methodString(name, types)
  419. + " in '" + clas.getName() + "'" );
  420. /*
  421. First try for an accessible version of the exact match.
  422. This first lookup seems redundant with below, but is apparently
  423. needed. This whole thing is messy.
  424. */
  425. try {
  426. method = findAccessibleMethod( clas, name, types );
  427. } catch ( SecurityException e ) { }
  428. // If not found and there are arguments to match -
  429. // Look for an overloaded assignable match
  430. // (First find the method, then find accessible version of it)
  431. if ( method == null && types.length > 0 )
  432. {
  433. // Gather all of the methods of class and parents
  434. Vector mv = new Vector();
  435. Class c = clas;
  436. while( c != null )
  437. {
  438. Method [] m = c.getDeclaredMethods();
  439. for(int i=0; i<m.length; i++)
  440. mv.add( m[i] );
  441. c = c.getSuperclass();
  442. }
  443. Method [] methods = new Method [mv.size()];
  444. mv.copyInto( methods );
  445. boolean publicOnly = !Capabilities.haveAccessibility();
  446. method = findMostSpecificMethod( name, types, methods, publicOnly );
  447. if ( method != null && !Modifier.isPublic( method.getModifiers() ) )
  448. {
  449. try {
  450. ReflectManager.RMSetAccessible( method );
  451. } catch ( UtilEvalError e ) { /*ignore*/ }
  452. }
  453. /*
  454. // If found a method, make sure we have accessible version of it
  455. if ( method != null )
  456. {
  457. try {
  458. method = findAccessibleMethod(
  459. clas, method.getName(), method.getParameterTypes() );
  460. } catch ( SecurityException e ) { /leave null/ }
  461. if ( Interpreter.DEBUG && method == null )
  462. Interpreter.debug(
  463. "had a method, but it wasn't accessible");
  464. }
  465. */
  466. }
  467. if ( method != null
  468. && onlyStatic && !Modifier.isStatic( method.getModifiers() )
  469. )
  470. throw new UtilEvalError(
  471. "Cannot reach instance method: "
  472. + StringUtil.methodString(
  473. method.getName(), method.getParameterTypes() )
  474. + " from static context: "+ clas.getName() );
  475. // Succeeded. Cache the resolved method.
  476. if ( method != null && bcm != null )
  477. bcm.cacheResolvedMethod( clas, types, method );
  478. return method;
  479. }
  480. /**
  481. Locate a version of the method with the exact signature specified
  482. that is accessible via a public interface or through a public
  483. superclass or - if accessibility is on - through any interface or
  484. superclass.
  485. In the normal (non-accessible) case this still solves the problem that
  486. arises when a package private class or private inner class implements a
  487. public interface or derives from a public type.
  488. @param onlyStatic the method located must be static.
  489. @return null on not found
  490. */
  491. /*
  492. Notes: See notes on findMostSpecificMethod.
  493. This method should be rolled into findMostSpecificMethod.
  494. */
  495. static Method findAccessibleMethod(
  496. Class clas, String name, Class [] types )
  497. throws UtilEvalError
  498. {
  499. Method meth = null;
  500. Method inaccessibleVersion = null;
  501. Vector classQ = new Vector();
  502. classQ.addElement( clas );
  503. Method found = null;
  504. while ( classQ.size() > 0 )
  505. {
  506. Class c = (Class)classQ.firstElement();
  507. classQ.removeElementAt(0);
  508. // Is this it?
  509. // Is the class public or can we use accessibility?
  510. if ( Modifier.isPublic( c.getModifiers() )
  511. || ( Capabilities.haveAccessibility() ) )
  512. {
  513. try
  514. {
  515. meth = c.getDeclaredMethod( name, types );
  516. // Is the method public or are we in accessibility mode?
  517. if ( ( Modifier.isPublic( meth.getModifiers() )
  518. && Modifier.isPublic( c.getModifiers() ) )
  519. || ( Capabilities.haveAccessibility()
  520. && ReflectManager.RMSetAccessible( meth ) ) )
  521. {
  522. found = meth; // Yes, it is.
  523. break;
  524. }
  525. else
  526. {
  527. // Found at least one matching method but couldn't use
  528. inaccessibleVersion = meth;
  529. }
  530. } catch ( NoSuchMethodException e ) {
  531. // ignore and move on
  532. }
  533. }
  534. // No, it is not.
  535. // Is this a class?
  536. if ( !c.isInterface() ) {
  537. Class superclass = c.getSuperclass();
  538. if ( superclass != null )
  539. classQ.addElement((Object)superclass);
  540. }
  541. // search all of its interfaces breadth first
  542. Class [] intfs = c.getInterfaces();
  543. for( int i=0; i< intfs.length; i++ )
  544. classQ.addElement((Object)intfs[i]);
  545. }
  546. if ( found != null )
  547. return found;
  548. if ( inaccessibleVersion != null )
  549. throw new UtilEvalError("Found non-public method: "
  550. +inaccessibleVersion
  551. +". Use setAccessibility(true) to enable access to "
  552. +" private and protected members of classes." );
  553. return null;
  554. }
  555. /**
  556. Primary object constructor
  557. This method is simpler than those that must resolve general method
  558. invocation because constructors are not inherited.
  559. */
  560. static Object constructObject( Class clas, Object[] args )
  561. throws ReflectError, InvocationTargetException
  562. {
  563. if ( clas.isInterface() )
  564. throw new ReflectError(
  565. "Can't create instance of an interface: "+clas);
  566. Object obj = null;
  567. Class[] types = Types.getTypes(args);
  568. Constructor con = null;
  569. /*
  570. Find an appropriate constructor.
  571. use declared here to see package and private as well
  572. (there are no inherited constructors to worry about)
  573. */
  574. Constructor[] constructors = clas.getDeclaredConstructors();
  575. if ( Interpreter.DEBUG )
  576. Interpreter.debug("Looking for most specific constructor: "+clas);
  577. con = findMostSpecificConstructor(types, constructors);
  578. if ( con == null )
  579. {
  580. if ( types.length == 0 )
  581. throw new ReflectError(
  582. "Can't find default constructor for: "+clas);
  583. else
  584. throw new ReflectError(
  585. "Can't find constructor: "
  586. + StringUtil.methodString( clas.getName(), types )
  587. +" in class: "+ clas.getName() );
  588. }
  589. if ( !Modifier.isPublic( con.getModifiers() )
  590. && Capabilities.haveAccessibility() )
  591. try {
  592. ReflectManager.RMSetAccessible( con );
  593. } catch ( UtilEvalError e ) { /*ignore*/ }
  594. args=Primitive.unwrap( args );
  595. try {
  596. obj = con.newInstance( args );
  597. } catch(InstantiationException e) {
  598. throw new ReflectError("the class is abstract ");
  599. } catch(IllegalAccessException e) {
  600. throw new ReflectError(
  601. "We don't have permission to create an instance."
  602. +"Use setAccessibility(true) to enable access." );
  603. } catch(IllegalArgumentException e) {
  604. throw new ReflectError("the number of arguments was wrong");
  605. }
  606. if (obj == null)
  607. throw new ReflectError("couldn't construct the object");
  608. return obj;
  609. }
  610. /**
  611. Implement JLS 15.11.2 for method resolution
  612. @return null on no match
  613. */
  614. /*
  615. Notes:
  616. This is broken. It finds public but less specific methods over
  617. non-public but more specific ones.
  618. This method needs to be rewritten to eliminate findAccessibleMethod.
  619. We should implement the findMostSpecificMethod that uses the publicOnly
  620. flag. FindMostSpecificMethod should also operate in two passes to give
  621. standard Java assignable matches priority over extended bsh type
  622. matches.
  623. */
  624. static Method findMostSpecificMethod(
  625. String name, Class[] idealMatch, Method[] methods,
  626. boolean publicOnly )
  627. {
  628. // Pull out the method signatures with matching names
  629. Vector sigs = new Vector();
  630. Vector meths = new Vector();
  631. for(int i=0; i<methods.length; i++)
  632. {
  633. if ( publicOnly && !Modifier.isPublic( methods[i].getModifiers() ) )
  634. continue;
  635. // method matches name
  636. if ( methods[i].getName().equals( name ) )
  637. {
  638. meths.addElement( methods[i] );
  639. sigs.addElement( methods[i].getParameterTypes() );
  640. }
  641. }
  642. Class [][] candidates = new Class [ sigs.size() ][];
  643. sigs.copyInto( candidates );
  644. if ( Interpreter.DEBUG )
  645. Interpreter.debug("Looking for most specific method: "+name);
  646. int match = findMostSpecificSignature( idealMatch, candidates );
  647. if ( match == -1 )
  648. return null;
  649. else
  650. return (Method)meths.elementAt( match );
  651. }
  652. /*
  653. This method should parallel findMostSpecificMethod()
  654. */
  655. static Constructor findMostSpecificConstructor(
  656. Class[] idealMatch, Constructor[] constructors)
  657. {
  658. int match =
  659. findMostSpecificConstructorIndex( idealMatch, constructors );
  660. if ( match == -1 )
  661. return null;
  662. else
  663. return constructors[ match ];
  664. }
  665. static int findMostSpecificConstructorIndex(
  666. Class[] idealMatch, Constructor[] constructors)
  667. {
  668. Class [][] candidates = new Class [ constructors.length ] [];
  669. for(int i=0; i< candidates.length; i++ )
  670. candidates[i] = constructors[i].getParameterTypes();
  671. return findMostSpecificSignature( idealMatch, candidates );
  672. }
  673. /**
  674. Implement JLS 15.11.2
  675. Return the index of the most specific arguments match or -1 if no
  676. match is found.
  677. */
  678. static int findMostSpecificSignature(
  679. Class [] idealMatch, Class [][] candidates )
  680. {
  681. Class [] bestMatch = null;
  682. int bestMatchIndex = -1;
  683. for (int i=0; i < candidates.length; i++) {
  684. Class[] targetMatch = candidates[i];
  685. /*
  686. If idealMatch fits targetMatch and this is the first match
  687. or targetMatch is more specific than the best match, make it
  688. the new best match.
  689. */
  690. if ( Types.isSignatureAssignable(idealMatch, targetMatch ) &&
  691. ((bestMatch == null) ||
  692. Types.isSignatureAssignable( targetMatch, bestMatch )))
  693. {
  694. bestMatch = targetMatch;
  695. bestMatchIndex = i;
  696. }
  697. }
  698. if ( bestMatch != null )
  699. return bestMatchIndex;
  700. else
  701. return -1;
  702. }
  703. private static String accessorName( String getorset, String propName ) {
  704. return getorset
  705. + String.valueOf(Character.toUpperCase(propName.charAt(0)))
  706. + propName.substring(1);
  707. }
  708. public static boolean hasObjectPropertyGetter(
  709. Class clas, String propName )
  710. {
  711. String getterName = accessorName("get", propName );
  712. try {
  713. clas.getMethod( getterName, new Class [0] );
  714. return true;
  715. } catch ( NoSuchMethodException e ) { /* fall through */ }
  716. getterName = accessorName("is", propName );
  717. try {
  718. Method m = clas.getMethod( getterName, new Class [0] );
  719. return ( m.getReturnType() == Boolean.TYPE );
  720. } catch ( NoSuchMethodException e ) {
  721. return false;
  722. }
  723. }
  724. public static boolean hasObjectPropertySetter(
  725. Class clas, String propName )
  726. {
  727. String setterName = accessorName("set", propName );
  728. Class [] sig = new Class [] { clas };
  729. Method [] methods = clas.getMethods();
  730. // we don't know the right hand side of the assignment yet.
  731. // has at least one setter of the right name?
  732. for(int i=0; i<methods.length; i++)
  733. if ( methods[i].getName().equals( setterName ) )
  734. return true;
  735. return false;
  736. }
  737. public static Object getObjectProperty(
  738. Object obj, String propName )
  739. throws UtilEvalError, ReflectError
  740. {
  741. Object[] args = new Object[] { };
  742. Interpreter.debug("property access: ");
  743. Method method = null;
  744. Exception e1=null, e2=null;
  745. try {
  746. String accessorName = accessorName( "get", propName );
  747. method = resolveExpectedJavaMethod(
  748. null/*bcm*/, obj.getClass(), obj, accessorName, args, false );
  749. } catch ( Exception e ) {
  750. e1 = e;
  751. }
  752. if ( method == null )
  753. try {
  754. String accessorName = accessorName( "is", propName );
  755. method = resolveExpectedJavaMethod(
  756. null/*bcm*/, obj.getClass(), obj,
  757. accessorName, args, false );
  758. if ( method.getReturnType() != Boolean.TYPE )
  759. method = null;
  760. } catch ( Exception e ) {
  761. e2 = e;
  762. }
  763. if ( method == null )
  764. throw new ReflectError("Error in property getter: "
  765. +e1 + (e2!=null?" : "+e2:"") );
  766. try {
  767. return invokeOnMethod( method, obj, args );
  768. }
  769. catch(InvocationTargetException e)
  770. {
  771. throw new UtilEvalError("Property accessor threw exception: "
  772. +e.getTargetException() );
  773. }
  774. }
  775. public static void setObjectProperty(
  776. Object obj, String propName, Object value)
  777. throws ReflectError, UtilEvalError
  778. {
  779. String accessorName = accessorName( "set", propName );
  780. Object[] args = new Object[] { value };
  781. Interpreter.debug("property access: ");
  782. try {
  783. Method method = resolveExpectedJavaMethod(
  784. null/*bcm*/, obj.getClass(), obj, accessorName, args, false );
  785. invokeOnMethod( method, obj, args );
  786. }
  787. catch ( InvocationTargetException e )
  788. {
  789. throw new UtilEvalError("Property accessor threw exception: "
  790. +e.getTargetException() );
  791. }
  792. }
  793. /**
  794. Return a more human readable version of the type name.
  795. Specifically, array types are returned with postfix "[]" dimensions.
  796. e.g. return "int []" for integer array instead of "class [I" as
  797. would be returned by Class getName() in that case.
  798. */
  799. public static String normalizeClassName(Class type)
  800. {
  801. if ( !type.isArray() )
  802. return type.getName();
  803. StringBuffer className = new StringBuffer();
  804. try {
  805. className.append( getArrayBaseType(type).getName() +" ");
  806. for(int i = 0; i < getArrayDimensions(type); i++)
  807. className.append("[]");
  808. } catch( ReflectError e ) { /*shouldn't happen*/ }
  809. return className.toString();
  810. }
  811. /**
  812. returns the dimensionality of the Class
  813. returns 0 if the Class is not an array class
  814. */
  815. public static int getArrayDimensions(Class arrayClass)
  816. {
  817. if ( !arrayClass.isArray() )
  818. return 0;
  819. return arrayClass.getName().lastIndexOf('[') + 1; // why so cute?
  820. }
  821. /**
  822. Returns the base type of an array Class.
  823. throws ReflectError if the Class is not an array class.
  824. */
  825. public static Class getArrayBaseType(Class arrayClass) throws ReflectError
  826. {
  827. if ( !arrayClass.isArray() )
  828. throw new ReflectError("The class is not an array.");
  829. return arrayClass.getComponentType();
  830. }
  831. /**
  832. A command may be implemented as a compiled Java class containing one or
  833. more static invoke() methods of the correct signature. The invoke()
  834. methods must accept two additional leading arguments of the interpreter
  835. and callstack, respectively. e.g. invoke(interpreter, callstack, ... )
  836. This method adds the arguments and invokes the static method, returning
  837. the result.
  838. */
  839. public static Object invokeCompiledCommand(
  840. Class commandClass, Object [] args, Interpreter interpreter,
  841. CallStack callstack )
  842. throws UtilEvalError
  843. {
  844. // add interpereter and namespace to args list
  845. Object[] invokeArgs = new Object[args.length + 2];
  846. invokeArgs[0] = interpreter;
  847. invokeArgs[1] = callstack;
  848. System.arraycopy( args, 0, invokeArgs, 2, args.length );
  849. BshClassManager bcm = interpreter.getClassManager();
  850. try {
  851. return Reflect.invokeStaticMethod(
  852. bcm, commandClass, "invoke", invokeArgs );
  853. } catch ( InvocationTargetException e ) {
  854. throw new UtilEvalError(
  855. "Error in compiled command: "+e.getTargetException() );
  856. } catch ( ReflectError e ) {
  857. throw new UtilEvalError("Error invoking compiled command: "+e );
  858. }
  859. }
  860. }