PageRenderTime 58ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

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

#
Java | 1183 lines | 709 code | 132 blank | 342 comment | 197 complexity | 9ee11c7dbdd03c5ec344420f5d19bb43 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. Note: More work to do in here to fix up the extended signature matching.
  42. need to work in a search along with findMostSpecificSignature...
  43. <p>
  44. Note: there are lots of cases here where the Java reflection API makes
  45. us catch exceptions (e.g. NoSuchFieldException) in order to do basic
  46. searching. This has to be inefficient... I wish they would add a more
  47. normal Java API for locating fields.
  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 && !passThisMethod( methodName) )
  63. return ((This)object).invokeMethod(
  64. methodName, args, interpreter, callstack, callerInfo );
  65. else
  66. // Java object
  67. {
  68. // find the java method
  69. try {
  70. BshClassManager bcm = callstack.top().getClassManager();
  71. Class clas = object.getClass();
  72. Method method = resolveJavaMethod(
  73. bcm, clas, object, methodName, args, false );
  74. return invokeOnMethod( method, object, args );
  75. } catch ( UtilEvalError e ) {
  76. throw e.toEvalError( callerInfo, callstack );
  77. }
  78. }
  79. }
  80. /**
  81. Invoke a method known to be static.
  82. No object instance is needed and there is no possibility of the
  83. method being a bsh scripted method.
  84. */
  85. public static Object invokeStaticMethod(
  86. BshClassManager bcm, Class clas, String methodName, Object [] args )
  87. throws ReflectError, UtilEvalError, InvocationTargetException
  88. {
  89. Interpreter.debug("invoke static Method");
  90. Method method = resolveJavaMethod(
  91. bcm, clas, null, methodName, args, true );
  92. return invokeOnMethod( method, null, args );
  93. }
  94. /**
  95. */
  96. private static Object invokeOnMethod(
  97. Method method, Object object, Object[] args )
  98. throws ReflectError, InvocationTargetException
  99. {
  100. if ( Interpreter.DEBUG )
  101. {
  102. Interpreter.debug("Invoking method (entry): "
  103. +method+" with args:" );
  104. for(int i=0; i<args.length; i++)
  105. Interpreter.debug(
  106. "args["+i+"] = "+args[i]
  107. +" type = "+args[i].getClass() );
  108. }
  109. // Map types to assignable forms, need to keep this fast...
  110. Object [] tmpArgs = new Object [ args.length ];
  111. Class [] types = method.getParameterTypes();
  112. try {
  113. for (int i=0; i<args.length; i++)
  114. tmpArgs[i] = NameSpace.getAssignableForm( args[i], types[i] );
  115. } catch ( UtilEvalError e ) {
  116. throw new InterpreterError(
  117. "illegal argument type in method invocation: "+e );
  118. }
  119. // unwrap any primitives
  120. tmpArgs = unwrapPrimitives( tmpArgs );
  121. if ( Interpreter.DEBUG )
  122. {
  123. Interpreter.debug("Invoking method (after massaging values): "
  124. +method+" with tmpArgs:" );
  125. for(int i=0; i<tmpArgs.length; i++)
  126. Interpreter.debug(
  127. "tmpArgs["+i+"] = "+tmpArgs[i]
  128. +" type = "+tmpArgs[i].getClass() );
  129. }
  130. try
  131. {
  132. Object returnValue = method.invoke( object, tmpArgs );
  133. if ( returnValue == null )
  134. returnValue = Primitive.NULL;
  135. Class returnType = method.getReturnType();
  136. return wrapPrimitive( returnValue, returnType );
  137. } catch( IllegalAccessException e ) {
  138. throw new ReflectError( "Cannot access method "
  139. + StringUtil.methodString(
  140. method.getName(), method.getParameterTypes() )
  141. + " in '" + method.getDeclaringClass() + "' :" + e );
  142. }
  143. }
  144. /**
  145. Allow invocations of these method names on This type objects.
  146. Don't give bsh.This a chance to override their behavior.
  147. <p>
  148. If the method is passed here the invocation will actually happen on
  149. the bsh.This object via the regular reflective method invocation
  150. mechanism. If not, then the method is evaluated by bsh.This itself
  151. as a scripted method call.
  152. */
  153. private static boolean passThisMethod( String name )
  154. {
  155. return
  156. name.equals("getClass")
  157. || name.equals("invokeMethod")
  158. || name.equals("getInterface")
  159. // These are necessary to let us test synchronization from scripts
  160. || name.equals("wait")
  161. || name.equals("notify")
  162. || name.equals("notifyAll");
  163. }
  164. public static Object getIndex(Object array, int index)
  165. throws ReflectError, UtilTargetError
  166. {
  167. if ( Interpreter.DEBUG )
  168. Interpreter.debug("getIndex: "+array+", index="+index);
  169. try {
  170. Object val = Array.get(array, index);
  171. return wrapPrimitive(val, array.getClass().getComponentType());
  172. }
  173. catch( ArrayIndexOutOfBoundsException e1 ) {
  174. throw new UtilTargetError( e1 );
  175. } catch(Exception e) {
  176. throw new ReflectError("Array access:" + e);
  177. }
  178. }
  179. public static void setIndex(Object array, int index, Object val)
  180. throws ReflectError, UtilTargetError
  181. {
  182. try {
  183. val = Primitive.unwrap(val);
  184. Array.set(array, index, val);
  185. }
  186. catch( ArrayStoreException e2 ) {
  187. throw new UtilTargetError( e2 );
  188. } catch( IllegalArgumentException e1 ) {
  189. throw new UtilTargetError(
  190. new ArrayStoreException( e1.toString() ) );
  191. } catch(Exception e) {
  192. throw new ReflectError("Array access:" + e);
  193. }
  194. }
  195. public static Object getStaticField(Class clas, String fieldName)
  196. throws UtilEvalError, ReflectError
  197. {
  198. return getFieldValue(clas, null, fieldName);
  199. }
  200. public static Object getObjectField( Object object, String fieldName )
  201. throws UtilEvalError, ReflectError
  202. {
  203. if ( object instanceof This )
  204. return ((This)object).namespace.getVariable( fieldName );
  205. else {
  206. try {
  207. return getFieldValue(object.getClass(), object, fieldName);
  208. } catch ( ReflectError e ) {
  209. // no field, try property acces
  210. if ( hasObjectPropertyGetter( object.getClass(), fieldName ) )
  211. return getObjectProperty( object, fieldName );
  212. else
  213. throw e;
  214. }
  215. }
  216. }
  217. static LHS getLHSStaticField(Class clas, String fieldName)
  218. throws UtilEvalError, ReflectError
  219. {
  220. Field f = getField(clas, fieldName);
  221. return new LHS(f);
  222. }
  223. /**
  224. Get an LHS reference to an object field.
  225. This method also deals with the field style property access.
  226. In the field does not exist we check for a property setter.
  227. */
  228. static LHS getLHSObjectField( Object object, String fieldName )
  229. throws UtilEvalError, ReflectError
  230. {
  231. if ( object instanceof This )
  232. {
  233. // I guess this is when we pass it as an argument?
  234. // Setting locally
  235. boolean recurse = false;
  236. return new LHS( ((This)object).namespace, fieldName, recurse );
  237. }
  238. try {
  239. Field f = getField(object.getClass(), fieldName);
  240. return new LHS(object, f);
  241. } catch ( ReflectError e ) {
  242. // not a field, try property access
  243. if ( hasObjectPropertySetter( object.getClass(), fieldName ) )
  244. return new LHS( object, fieldName );
  245. else
  246. throw e;
  247. }
  248. }
  249. private static Object getFieldValue(
  250. Class clas, Object object, String fieldName)
  251. throws UtilEvalError, ReflectError
  252. {
  253. try {
  254. Field f = getField(clas, fieldName);
  255. if ( f == null )
  256. throw new ReflectError("internal: field not found:"+fieldName);
  257. Object value = f.get(object);
  258. Class returnType = f.getType();
  259. return wrapPrimitive(value, returnType);
  260. }
  261. catch(NullPointerException e) {
  262. throw new ReflectError(
  263. "???" + fieldName + " is not a static field.");
  264. }
  265. catch(IllegalAccessException e) {
  266. throw new ReflectError("Can't access field: " + fieldName);
  267. }
  268. }
  269. /**
  270. All field lookup should come through here.
  271. i.e. this method owns Class getField();
  272. */
  273. private static Field getField(Class clas, String fieldName)
  274. throws UtilEvalError, ReflectError
  275. {
  276. try
  277. {
  278. if ( Capabilities.haveAccessibility() )
  279. return findAccessibleField( clas, fieldName );
  280. else
  281. // this one only finds public (and in interfaces, etc.)
  282. return clas.getField(fieldName);
  283. }
  284. catch( NoSuchFieldException e)
  285. {
  286. // try declaredField
  287. throw new ReflectError("No such field: " + fieldName );
  288. }
  289. }
  290. /**
  291. Used when accessibility capability is available to locate an occurrance
  292. of the field in the most derived class or superclass and set its
  293. accessibility flag.
  294. Note that this method is not needed in the simple non accessible
  295. case because we don't have to hunt for fields.
  296. Note that classes may declare overlapping private fields, so the
  297. distinction about the most derived is important. Java doesn't normally
  298. allow this kind of access (super won't show private variables) so
  299. there is no real syntax for specifying which class scope to use...
  300. */
  301. private static Field findAccessibleField( Class clas, String fieldName )
  302. throws UtilEvalError, NoSuchFieldException
  303. {
  304. // Quick check catches public fields include those in interfaces
  305. try {
  306. return clas.getField(fieldName);
  307. } catch ( NoSuchFieldException e ) { }
  308. // Now, on with the hunt...
  309. while ( clas != null )
  310. {
  311. try {
  312. Field field = clas.getDeclaredField(fieldName);
  313. if ( ReflectManager.RMSetAccessible( field ) )
  314. return field;
  315. /*
  316. // Try interfaces of class for the field (has to be public)
  317. Class [] interfaces = clas.getInterfaces();
  318. for(int i=0; i<interfaces.length;i++) {
  319. try {
  320. return interfaces[i].getField( fieldName );
  321. } catch ( NoSuchFieldException e ) { }
  322. }
  323. */
  324. // Not found, fall through to next class
  325. } catch(NoSuchFieldException e) { }
  326. clas = clas.getSuperclass();
  327. }
  328. throw new NoSuchFieldException( fieldName );
  329. }
  330. /**
  331. The full blown resolver method. Everybody should come here.
  332. The method may be static or dynamic unless onlyStatic is set
  333. (in which case object may be null).
  334. @param onlyStatic
  335. The method located must be static, the object param may be null.
  336. @throws ReflectError if method is not found
  337. */
  338. /*
  339. Note: Method invocation could probably be speeded up if we eliminated
  340. the throwing of exceptions in the search for the proper method.
  341. After 1.3 we are caching method resolution anyway... shouldn't matter
  342. much.
  343. */
  344. static Method resolveJavaMethod (
  345. BshClassManager bcm, Class clas, Object object,
  346. String name, Object[] args, boolean onlyStatic
  347. )
  348. throws ReflectError, UtilEvalError
  349. {
  350. Method method = null;
  351. if ( bcm == null )
  352. Interpreter.debug("resolveJavaMethod UNOPTIMIZED lookup");
  353. else
  354. {
  355. method = bcm.getResolvedMethod( clas, name, args, onlyStatic );
  356. if ( method != null )
  357. return method;
  358. }
  359. // This should probably be handled higher up
  360. if ( object == Primitive.NULL )
  361. throw new UtilTargetError( new NullPointerException(
  362. "Attempt to invoke method " +name+" on null value" ) );
  363. Class [] types = getTypes(args);
  364. // this was unecessary and counterproductive
  365. //args=unwrapPrimitives(args);
  366. // First try for an accessible version of the exact match.
  367. if ( Interpreter.DEBUG )
  368. Interpreter.debug( "Searching for method: "+
  369. StringUtil.methodString(name, types)
  370. + " in '" + clas.getName() + "'" );
  371. // Why do we do this? Won't the overloaded resolution below find it
  372. // just as well -- try to merge these next
  373. try {
  374. method = findAccessibleMethod(clas, name, types, onlyStatic);
  375. } catch ( SecurityException e ) { }
  376. if ( Interpreter.DEBUG && method != null )
  377. Interpreter.debug("findAccessibleMethod found: "+ method );
  378. // Look for an overloaded / standard Java assignable match
  379. // (First find the method, then find accessible version of it)
  380. if ( method == null )
  381. {
  382. // If no args stop here, can't do better than exact match above
  383. if ( types.length == 0 )
  384. throw new ReflectError(
  385. "No args "+ ( onlyStatic ? "static " : "" )
  386. +"method " + StringUtil.methodString(name, types) +
  387. " not found in class'" + clas.getName() + "'");
  388. Method [] methods = clas.getMethods();
  389. if ( onlyStatic )
  390. methods = retainStaticMethods( methods );
  391. method = findMostSpecificMethod( name, types, methods );
  392. if ( Interpreter.DEBUG && method != null )
  393. Interpreter.debug("findMostSpecificMethod found: "+ method );
  394. // try to find an extended method
  395. if ( method == null )
  396. {
  397. method = findExtendedMethod( name, args, methods );
  398. if ( Interpreter.DEBUG && method != null )
  399. Interpreter.debug("findExtendedMethod found: "+ method );
  400. }
  401. // If we found an assignable or extended method, make sure we have
  402. // an accessible version of it
  403. if ( method != null )
  404. {
  405. try {
  406. method = findAccessibleMethod( clas, method.getName(),
  407. method.getParameterTypes(), onlyStatic);
  408. } catch ( SecurityException e ) { }
  409. if ( Interpreter.DEBUG && method == null )
  410. Interpreter.debug(
  411. "had a method, but it wasn't accessible");
  412. }
  413. }
  414. // If we didn't find anything throw error
  415. if ( method == null )
  416. throw new ReflectError(
  417. ( onlyStatic ? "Static method " : "Method " )
  418. + StringUtil.methodString(name, types) +
  419. " not found in class'" + clas.getName() + "'");
  420. // Succeeded. Cache the resolved method.
  421. if ( bcm != null )
  422. bcm.cacheResolvedMethod( clas, args, method );
  423. return method;
  424. }
  425. /**
  426. Return only the static methods
  427. */
  428. private static Method [] retainStaticMethods( Method [] methods ) {
  429. Vector v = new Vector();
  430. for(int i=0; i<methods.length; i++)
  431. if ( Modifier.isStatic( methods[i].getModifiers() ) )
  432. v.addElement( methods[i] );
  433. Method [] ma = new Method [ v.size() ];
  434. v.copyInto( ma );
  435. return ma;
  436. }
  437. /**
  438. Locate a version of the method with the exact signature specified
  439. that is accessible via a public interface or through a public
  440. superclass or - if accessibility is on - through any interface or
  441. superclass.
  442. In the normal (non-accessible) case this still solves the problem that
  443. arises when a package private class or private inner class implements a
  444. public interface or derives from a public type.
  445. @param onlyStatic the method located must be static.
  446. @return null on not found
  447. */
  448. static Method findAccessibleMethod(
  449. Class clas, String name, Class [] types, boolean onlyStatic )
  450. throws UtilEvalError
  451. {
  452. Method meth = null;
  453. Method inaccessibleVersion = null;
  454. Vector classQ = new Vector();
  455. classQ.addElement( clas );
  456. Method found = null;
  457. while ( classQ.size() > 0 )
  458. {
  459. Class c = (Class)classQ.firstElement();
  460. classQ.removeElementAt(0);
  461. // Is this it?
  462. // Is the class public or can we use accessibility?
  463. if ( Modifier.isPublic( c.getModifiers() )
  464. || ( Capabilities.haveAccessibility() ) )
  465. {
  466. try
  467. {
  468. meth = c.getDeclaredMethod( name, types );
  469. // Is the method public or are we in accessibility mode?
  470. if ( ( Modifier.isPublic( meth.getModifiers() )
  471. && Modifier.isPublic( c.getModifiers() ) )
  472. || ( Capabilities.haveAccessibility()
  473. && ReflectManager.RMSetAccessible( meth ) ) )
  474. {
  475. found = meth; // Yes, it is.
  476. break;
  477. }
  478. else
  479. {
  480. // Found at least one matching method but couldn't use
  481. inaccessibleVersion = meth;
  482. }
  483. } catch ( NoSuchMethodException e ) {
  484. // ignore and move on
  485. }
  486. }
  487. // No, it is not.
  488. // Is this a class?
  489. if ( !c.isInterface() ) {
  490. Class superclass = c.getSuperclass();
  491. if ( superclass != null )
  492. classQ.addElement((Object)superclass);
  493. }
  494. // search all of its interfaces breadth first
  495. Class [] intfs = c.getInterfaces();
  496. for( int i=0; i< intfs.length; i++ )
  497. classQ.addElement((Object)intfs[i]);
  498. }
  499. /*
  500. If we found one and it satisfies onlyStatic return it
  501. Note: I don't believe it is necessary to check for the static
  502. condition in the above search because the Java compiler will not
  503. let dynamic and static methods hide/override one another. So
  504. we simply check what is found, if any, at the end.
  505. */
  506. if ( found != null &&
  507. ( !onlyStatic || Modifier.isStatic( found.getModifiers() ) ) )
  508. return found;
  509. /*
  510. Not sure if this the best place to do this...
  511. */
  512. if ( inaccessibleVersion != null )
  513. throw new UtilEvalError("Found non-public method: "
  514. +inaccessibleVersion
  515. +". Use setAccessibility(true) to enable access to "
  516. +" private and protected members of classes." );
  517. return null;
  518. }
  519. private static Object wrapPrimitive(
  520. Object value, Class returnType) throws ReflectError
  521. {
  522. if(value == null)
  523. return Primitive.NULL;
  524. if(returnType == Void.TYPE)
  525. return Primitive.VOID;
  526. else
  527. if(returnType.isPrimitive())
  528. {
  529. if(value instanceof Number)
  530. return new Primitive((Number)value);
  531. if(value instanceof Boolean)
  532. return new Primitive((Boolean)value);
  533. if(value instanceof Character)
  534. return new Primitive((Character)value);
  535. throw new ReflectError("Something bad happened");
  536. }
  537. else
  538. return value;
  539. }
  540. public static Class[] getTypes( Object[] args )
  541. {
  542. if ( args == null )
  543. return new Class[0];
  544. Class[] types = new Class[ args.length ];
  545. for( int i=0; i<args.length; i++ )
  546. {
  547. if ( args[i] == null )
  548. types[i] = null;
  549. else if ( args[i] instanceof Primitive )
  550. types[i] = ((Primitive)args[i]).getType();
  551. else
  552. types[i] = args[i].getClass();
  553. }
  554. return types;
  555. }
  556. /*
  557. Unwrap Primitive wrappers to their java.lang wrapper values.
  558. e.g. Primitive(42) becomes Integer(42)
  559. */
  560. private static Object [] unwrapPrimitives( Object[] args )
  561. {
  562. Object [] oa = new Object[ args.length ];
  563. for(int i=0; i<args.length; i++)
  564. oa[i] = Primitive.unwrap( args[i] );
  565. return oa;
  566. }
  567. /*
  568. private static Object unwrapPrimitive( Object arg )
  569. {
  570. if ( arg instanceof Primitive )
  571. return((Primitive)arg).getValue();
  572. else
  573. return arg;
  574. }
  575. */
  576. /**
  577. Primary object constructor
  578. This method is simpler than those that must resolve general method
  579. invocation because constructors are not inherited.
  580. */
  581. static Object constructObject( Class clas, Object[] args )
  582. throws ReflectError, InvocationTargetException
  583. {
  584. if ( clas.isInterface() )
  585. throw new ReflectError(
  586. "Can't create instance of an interface: "+clas);
  587. Object obj = null;
  588. Class[] types = getTypes(args);
  589. // this wasn't necessary until we invoke it, moved...
  590. //args=unwrapPrimitives(args);
  591. Constructor con = null;
  592. /*
  593. Find an appropriate constructor
  594. use declared here to see package and private as well
  595. (there are no inherited constructors to worry about)
  596. */
  597. Constructor[] constructors = clas.getDeclaredConstructors();
  598. if ( Interpreter.DEBUG )
  599. Interpreter.debug("Looking for most specific constructor: "+clas);
  600. con = findMostSpecificConstructor(types, constructors);
  601. if ( con == null )
  602. if ( types.length == 0 )
  603. throw new ReflectError(
  604. "Can't find default constructor for: "+clas);
  605. else
  606. con = findExtendedConstructor(args, constructors);
  607. if ( con == null )
  608. throw new ReflectError(
  609. "Can't find constructor: "
  610. + StringUtil.methodString( clas.getName(), types )
  611. +" in class: "+ clas.getName() );;
  612. try {
  613. args=unwrapPrimitives( args );
  614. obj = con.newInstance( args );
  615. } catch(InstantiationException e) {
  616. throw new ReflectError("the class is abstract ");
  617. } catch(IllegalAccessException e) {
  618. throw new ReflectError(
  619. "we don't have permission to create an instance");
  620. } catch(IllegalArgumentException e) {
  621. throw new ReflectError("the number of arguments was wrong");
  622. }
  623. if (obj == null)
  624. throw new ReflectError("couldn't construct the object");
  625. return obj;
  626. }
  627. /**
  628. Implement JLS 15.11.2 for method resolution
  629. @return null on no match
  630. */
  631. static Method findMostSpecificMethod(
  632. String name, Class[] idealMatch, Method[] methods )
  633. {
  634. // Pull out the method signatures with matching names
  635. Vector sigs = new Vector();
  636. Vector meths = new Vector();
  637. for(int i=0; i<methods.length; i++)
  638. {
  639. // method matches name
  640. if ( methods[i].getName().equals( name ) )
  641. {
  642. meths.addElement( methods[i] );
  643. sigs.addElement( methods[i].getParameterTypes() );
  644. }
  645. }
  646. Class [][] candidates = new Class [ sigs.size() ][];
  647. sigs.copyInto( candidates );
  648. if ( Interpreter.DEBUG )
  649. Interpreter.debug("Looking for most specific method: "+name);
  650. int match = findMostSpecificSignature( idealMatch, candidates );
  651. if ( match == -1 )
  652. return null;
  653. else
  654. return (Method)meths.elementAt( match );
  655. }
  656. /*
  657. This method should parallel findMostSpecificMethod()
  658. */
  659. static Constructor findMostSpecificConstructor(
  660. Class[] idealMatch, Constructor[] constructors)
  661. {
  662. // We don't have to worry about the name of our constructors
  663. Class [][] candidates = new Class [ constructors.length ] [];
  664. for(int i=0; i< candidates.length; i++ )
  665. candidates[i] = constructors[i].getParameterTypes();
  666. int match = findMostSpecificSignature( idealMatch, candidates );
  667. if ( match == -1 )
  668. return null;
  669. else
  670. return constructors[ match ];
  671. }
  672. /**
  673. findExtendedMethod uses the NameSpace.getAssignableForm() method to
  674. determine compatability of arguments. This allows special (non
  675. standard Java) bsh widening operations.
  676. Note that this method examines the *arguments* themselves not the types.
  677. @param args the arguments
  678. @return null on not found
  679. */
  680. /*
  681. Note: shouldn't we use something analagous to findMostSpecificSignature
  682. on a result set, rather than choosing the first one we find?
  683. (findMostSpecificSignature doesn't know about extended types).
  684. */
  685. static Method findExtendedMethod(
  686. String name, Object[] args, Method[] methods )
  687. {
  688. for(int i = 0; i < methods.length; i++)
  689. {
  690. Method currentMethod = methods[i];
  691. Class[] parameterTypes = currentMethod.getParameterTypes();
  692. if ( name.equals( currentMethod.getName() )
  693. && argsAssignable( parameterTypes, args ) )
  694. return currentMethod;
  695. }
  696. return null;
  697. }
  698. /**
  699. This uses the NameSpace.getAssignableForm() method to determine
  700. compatability of args. This allows special (non standard Java) bsh
  701. widening operations...
  702. */
  703. static Constructor findExtendedConstructor(
  704. Object[] args, Constructor[] constructors )
  705. {
  706. for(int i = 0; i < constructors.length; i++)
  707. {
  708. Constructor currentConstructor = constructors[i];
  709. Class[] parameterTypes = currentConstructor.getParameterTypes();
  710. if ( argsAssignable( parameterTypes, args ) )
  711. return currentConstructor;
  712. }
  713. return null;
  714. }
  715. /**
  716. Arguments are assignable as defined by NameSpace.getAssignableForm()
  717. which takes into account special bsh conversions such as XThis and (ug)
  718. primitive wrapper promotion.
  719. */
  720. private static boolean argsAssignable( Class [] parameters, Object [] args )
  721. {
  722. if ( parameters.length != args.length )
  723. return false;
  724. try {
  725. for(int j = 0; j < parameters.length; j++)
  726. NameSpace.getAssignableForm( args[j], parameters[j]);
  727. } catch ( UtilEvalError e ) {
  728. return false;
  729. }
  730. return true;
  731. }
  732. /**
  733. Implement JLS 15.11.2
  734. Return the index of the most specific arguments match or -1 if no
  735. match is found.
  736. */
  737. static int findMostSpecificSignature(
  738. Class [] idealMatch, Class [][] candidates )
  739. {
  740. Class [] bestMatch = null;
  741. int bestMatchIndex = -1;
  742. for (int i=0; i < candidates.length; i++) {
  743. Class[] targetMatch = candidates[i];
  744. /*
  745. If idealMatch fits targetMatch and this is the first match
  746. or targetMatch is more specific than the best match, make it
  747. the new best match.
  748. */
  749. if ( isSignatureAssignable(idealMatch, targetMatch ) &&
  750. ((bestMatch == null) ||
  751. isSignatureAssignable( targetMatch, bestMatch )))
  752. {
  753. bestMatch = targetMatch;
  754. bestMatchIndex = i;
  755. }
  756. }
  757. if ( bestMatch != null ) {
  758. /*
  759. if ( Interpreter.DEBUG )
  760. Interpreter.debug("best match: "
  761. + StringUtil.methodString("args",bestMatch));
  762. */
  763. return bestMatchIndex;
  764. }
  765. else {
  766. Interpreter.debug("no match found");
  767. return -1;
  768. }
  769. }
  770. /**
  771. Is the 'from' signature (argument types) assignable to the 'to'
  772. signature (candidate method types) using isJavaAssignableFrom()?
  773. This method handles the special case of null values in 'to' types
  774. indicating a loose type and matching anything.
  775. */
  776. private static boolean isSignatureAssignable( Class[] from, Class[] to )
  777. {
  778. if ( from.length != to.length )
  779. return false;
  780. for(int i=0; i<from.length; i++)
  781. {
  782. // Null 'to' type indicates loose type. Match anything.
  783. if ( to[i] == null )
  784. continue;
  785. if ( !isJavaAssignableFrom( to[i], from[i] ) )
  786. return false;
  787. }
  788. return true;
  789. }
  790. /**
  791. Is a standard Java assignment legal from the rhs type to the lhs type
  792. in a normal assignment?
  793. <p/>
  794. For Java primitive TYPE classes this method takes primitive promotion
  795. into account. The ordinary Class.isAssignableFrom() does not take
  796. primitive promotion conversions into account. Note that Java allows
  797. additional assignments without a cast in combination with variable
  798. declarations. Those are handled elsewhere (maybe should be here with a
  799. flag?)
  800. <p/>
  801. This class accepts a null rhs type indicating that the rhs was the
  802. value Primitive.NULL and allows it to be assigned to any object lhs
  803. type (non primitive)
  804. <p/>
  805. Note that the getAssignableForm() method in NameSpace is the primary
  806. bsh method for checking assignability. It adds additional bsh
  807. conversions, etc. (need to clarify what)
  808. @param lhs assigning from rhs to lhs
  809. @param rhs assigning from rhs to lhs
  810. */
  811. static boolean isJavaAssignableFrom( Class lhs, Class rhs )
  812. {
  813. // null 'from' type corresponds to type of Primitive.NULL
  814. // assign to any object type
  815. if ( rhs == null )
  816. return !lhs.isPrimitive();
  817. if ( lhs.isPrimitive() && rhs.isPrimitive() )
  818. {
  819. if ( lhs == rhs )
  820. return true;
  821. // handle primitive widening conversions - JLS 5.1.2
  822. if ( (rhs == Byte.TYPE) &&
  823. (lhs == Short.TYPE || lhs == Integer.TYPE ||
  824. lhs == Long.TYPE || lhs == Float.TYPE || lhs == Double.TYPE))
  825. return true;
  826. if ( (rhs == Short.TYPE) &&
  827. (lhs == Integer.TYPE || lhs == Long.TYPE ||
  828. lhs == Float.TYPE || lhs == Double.TYPE))
  829. return true;
  830. if ((rhs == Character.TYPE) &&
  831. (lhs == Integer.TYPE || lhs == Long.TYPE ||
  832. lhs == Float.TYPE || lhs == Double.TYPE))
  833. return true;
  834. if ((rhs == Integer.TYPE) &&
  835. (lhs == Long.TYPE || lhs == Float.TYPE ||
  836. lhs == Double.TYPE))
  837. return true;
  838. if ((rhs == Long.TYPE) &&
  839. (lhs == Float.TYPE || lhs == Double.TYPE))
  840. return true;
  841. if ((rhs == Float.TYPE) && (lhs == Double.TYPE))
  842. return true;
  843. }
  844. else
  845. if ( lhs.isAssignableFrom(rhs) )
  846. return true;
  847. return false;
  848. }
  849. private static String accessorName( String getorset, String propName ) {
  850. return getorset
  851. + String.valueOf(Character.toUpperCase(propName.charAt(0)))
  852. + propName.substring(1);
  853. }
  854. public static boolean hasObjectPropertyGetter(
  855. Class clas, String propName )
  856. {
  857. String getterName = accessorName("get", propName );
  858. try {
  859. clas.getMethod( getterName, new Class [0] );
  860. return true;
  861. } catch ( NoSuchMethodException e ) { /* fall through */ }
  862. getterName = accessorName("is", propName );
  863. try {
  864. Method m = clas.getMethod( getterName, new Class [0] );
  865. return ( m.getReturnType() == Boolean.TYPE );
  866. } catch ( NoSuchMethodException e ) {
  867. return false;
  868. }
  869. }
  870. public static boolean hasObjectPropertySetter(
  871. Class clas, String propName )
  872. {
  873. String setterName = accessorName("set", propName );
  874. Class [] sig = new Class [] { clas };
  875. Method [] methods = clas.getMethods();
  876. // we don't know the right hand side of the assignment yet.
  877. // has at least one setter of the right name?
  878. for(int i=0; i<methods.length; i++)
  879. if ( methods[i].getName().equals( setterName ) )
  880. return true;
  881. return false;
  882. }
  883. public static Object getObjectProperty(
  884. Object obj, String propName )
  885. throws UtilEvalError, ReflectError
  886. {
  887. Object[] args = new Object[] { };
  888. Interpreter.debug("property access: ");
  889. Method method = null;
  890. Exception e1=null, e2=null;
  891. try {
  892. String accessorName = accessorName( "get", propName );
  893. method = resolveJavaMethod(
  894. null/*bcm*/, obj.getClass(), obj, accessorName, args, false );
  895. } catch ( Exception e ) {
  896. e1 = e;
  897. }
  898. if ( method == null )
  899. try {
  900. String accessorName = accessorName( "is", propName );
  901. method = resolveJavaMethod( null/*bcm*/, obj.getClass(), obj,
  902. accessorName, args, false );
  903. if ( method.getReturnType() != Boolean.TYPE )
  904. method = null;
  905. } catch ( Exception e ) {
  906. e2 = e;
  907. }
  908. if ( method == null )
  909. throw new ReflectError("Error in property getter: "
  910. +e1 + (e2!=null?" : "+e2:"") );
  911. try {
  912. return invokeOnMethod( method, obj, args );
  913. }
  914. catch(InvocationTargetException e)
  915. {
  916. throw new UtilEvalError("Property accessor threw exception: "
  917. +e.getTargetException() );
  918. }
  919. }
  920. public static void setObjectProperty(
  921. Object obj, String propName, Object value)
  922. throws ReflectError, UtilEvalError
  923. {
  924. String accessorName = accessorName( "set", propName );
  925. Object[] args = new Object[] { value };
  926. Interpreter.debug("property access: ");
  927. try {
  928. Method method = resolveJavaMethod(
  929. null/*bcm*/, obj.getClass(), obj, accessorName, args, false );
  930. invokeOnMethod( method, obj, args );
  931. }
  932. catch ( InvocationTargetException e )
  933. {
  934. throw new UtilEvalError("Property accessor threw exception: "
  935. +e.getTargetException() );
  936. }
  937. }
  938. /**
  939. This method is meant to convert a JVM-array class name to the correct
  940. 'fully-qualified name' for the array class - JLS 6.7
  941. */
  942. public static String normalizeClassName(Class type)
  943. {
  944. if(!type.isArray())
  945. return type.getName();
  946. StringBuffer className = new StringBuffer();
  947. try
  948. {
  949. className.append(getArrayBaseType(type).getName());
  950. for(int i = 0; i < getArrayDimensions(type); i++)
  951. className.append("[]");
  952. }
  953. catch(Exception e) { }
  954. return className.toString();
  955. }
  956. /**
  957. returns the dimensionality of the Class
  958. returns 0 if the Class is not an array class
  959. */
  960. public static int getArrayDimensions(Class arrayClass)
  961. {
  962. if(!arrayClass.isArray())
  963. return 0;
  964. return arrayClass.getName().lastIndexOf('[') + 1;
  965. }
  966. /**
  967. Returns the base type of an array Class.
  968. throws ReflectError if the Class is not an array class.
  969. */
  970. public static Class getArrayBaseType(Class arrayClass) throws ReflectError
  971. {
  972. if(!arrayClass.isArray())
  973. throw new ReflectError("The class is not an array.");
  974. return arrayClass.getComponentType();
  975. }
  976. /**
  977. A command may be implemented as a compiled Java class containing one or
  978. more static invoke() methods of the correct signature. The invoke()
  979. methods must accept two additional leading arguments of the interpreter
  980. and callstack, respectively. e.g. invoke(interpreter, callstack, ... )
  981. This method adds the arguments and invokes the static method, returning
  982. the result.
  983. */
  984. public static Object invokeCompiledCommand(
  985. Class commandClass, Object [] args, Interpreter interpreter,
  986. CallStack callstack )
  987. throws UtilEvalError
  988. {
  989. // add interpereter and namespace to args list
  990. Object[] invokeArgs = new Object[args.length + 2];
  991. invokeArgs[0] = interpreter;
  992. invokeArgs[1] = callstack;
  993. System.arraycopy( args, 0, invokeArgs, 2, args.length );
  994. BshClassManager bcm = interpreter.getClassManager();
  995. try {
  996. return Reflect.invokeStaticMethod(
  997. bcm, commandClass, "invoke", invokeArgs );
  998. } catch ( InvocationTargetException e ) {
  999. throw new UtilEvalError(
  1000. "Error in compiled command: "+e.getTargetException() );
  1001. } catch ( ReflectError e ) {
  1002. throw new UtilEvalError("Error invoking compiled command: "+e );
  1003. }
  1004. }
  1005. }
  1006. /*
  1007. Ok, I wrote this... should we use it in lieu of the pair of methods?
  1008. I guess not...
  1009. private static Object findExtendedMethodOrConstructor(
  1010. String name, Object[] args, Object [] methodsOrConstructors )
  1011. {
  1012. for(int i = 0; i < methodsOrConstructors.length; i++)
  1013. {
  1014. Object currentMethodOrConstructor = methodsOrConstructors[i];
  1015. Class[] parameterTypes =
  1016. getParameterTypes( currentMethodOrConstructor );
  1017. if ( currentMethodOrConstructor instanceof Method
  1018. && !name.equals(
  1019. ((Method)currentMethodOrConstructor).getName() ) )
  1020. continue;
  1021. if ( argsAssignable( parameterTypes, args ) )
  1022. return currentMethodOrConstructor;
  1023. }
  1024. return null;
  1025. }
  1026. private static Class [] getParameterTypes( Object methodOrConstructor )
  1027. {
  1028. if ( methodOrConstructor instanceof Method )
  1029. return ((Method)methodOrConstructor).getParameterTypes();
  1030. else
  1031. return ((Constructor)methodOrConstructor).getParameterTypes();
  1032. }
  1033. */