PageRenderTime 84ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/jEdit/tags/jedit-4-0-pre3/bsh/Reflect.java

#
Java | 989 lines | 605 code | 129 blank | 255 comment | 176 complexity | 5c12a78b9a23f4a2cbfe4d08be7c479e 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. See the design note about object wrappers
  40. in LHS.java for lamentations regarding this.
  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. */
  44. class Reflect {
  45. /**
  46. Invoke method on object.
  47. invocation may be static (through the object instance) or dynamic.
  48. Object may be This type.
  49. The This handling is necessary here (previously thought it might
  50. not be).
  51. @param callerInfo will be passed along in the caes where the method
  52. is a bsh scripted method. It may be null to indicate no caller info.
  53. */
  54. /*
  55. In the case where this method calls a bsh scripted method the callstack
  56. is currently lost
  57. */
  58. public static Object invokeObjectMethod(
  59. Interpreter interpreter, Object object, String methodName,
  60. Object[] args, SimpleNode callerInfo
  61. )
  62. throws ReflectError, InvocationTargetException, EvalError
  63. {
  64. /*
  65. Interpreter.debug("invoke Method " + methodName + " on object "
  66. + object + " with args (");
  67. */
  68. if ( object instanceof This && !showThisMethod( methodName) ) {
  69. // This .invokeMethod() just calls the namespace invokeMethod
  70. return ((This)object).invokeMethod(
  71. methodName, args, interpreter, null, callerInfo );
  72. } else
  73. return invokeMethod(
  74. object.getClass(), object, methodName, args, false );
  75. }
  76. /**
  77. Allow invocations of these method names on This type objects.
  78. Don't give bsh.This a chance to override their behavior.
  79. */
  80. private static boolean showThisMethod( String name ) {
  81. return ( name.equals("getClass") || name.equals("invokeMethod") );
  82. }
  83. /**
  84. Invoke a static method. No object instance is provided.
  85. */
  86. public static Object invokeStaticMethod(
  87. Class clas, String methodName, Object [] args)
  88. throws ReflectError, InvocationTargetException, EvalError
  89. {
  90. Interpreter.debug("invoke static Method");
  91. return invokeMethod( clas, null, methodName, args, true );
  92. }
  93. public static Object getIndex(Object array, int index)
  94. throws ReflectError, TargetError
  95. {
  96. try {
  97. Object val = Array.get(array, index);
  98. return wrapPrimitive(val, array.getClass().getComponentType());
  99. }
  100. catch( ArrayIndexOutOfBoundsException e1 ) {
  101. throw new TargetError( "Array Index", e1 );
  102. } catch(Exception e) {
  103. throw new ReflectError("Array access:" + e);
  104. }
  105. }
  106. public static void setIndex(Object array, int index, Object val)
  107. throws ReflectError, TargetError
  108. {
  109. try {
  110. val = unwrapPrimitive(val);
  111. Array.set(array, index, val);
  112. }
  113. catch( ArrayStoreException e2 ) {
  114. throw new TargetError( "Array store exception", e2 );
  115. } catch( IllegalArgumentException e1 ) {
  116. throw new TargetError( "Illegal Argument",
  117. new ArrayStoreException( e1.toString() ) );
  118. } catch(Exception e) {
  119. throw new ReflectError("Array access:" + e);
  120. }
  121. }
  122. public static Object getStaticField(Class clas, String fieldName)
  123. throws ReflectError
  124. {
  125. return getFieldValue(clas, null, fieldName);
  126. }
  127. public static Object getObjectField(Object object, String fieldName)
  128. throws ReflectError
  129. {
  130. if ( object instanceof This )
  131. return ((This)object).namespace.getVariable( fieldName );
  132. else {
  133. try {
  134. return getFieldValue(object.getClass(), object, fieldName);
  135. } catch ( ReflectError e ) {
  136. // no field, try property acces
  137. if ( hasObjectPropertyGetter( object.getClass(), fieldName ) )
  138. return getObjectProperty( object, fieldName );
  139. else
  140. throw e;
  141. }
  142. }
  143. }
  144. static LHS getLHSStaticField(Class clas, String fieldName)
  145. throws ReflectError
  146. {
  147. Field f = getField(clas, fieldName);
  148. return new LHS(f);
  149. }
  150. /**
  151. Get an LHS reference to an object field.
  152. This method also deals with the field style property access.
  153. In the field does not exist we check for a property setter.
  154. */
  155. static LHS getLHSObjectField(Object object, String fieldName)
  156. throws ReflectError
  157. {
  158. if ( object instanceof This )
  159. return new LHS(((This)object).namespace, fieldName );
  160. try {
  161. Field f = getField(object.getClass(), fieldName);
  162. return new LHS(object, f);
  163. } catch ( ReflectError e ) {
  164. // not a field, try property access
  165. if ( hasObjectPropertySetter( object.getClass(), fieldName ) )
  166. return new LHS( object, fieldName );
  167. else
  168. throw e;
  169. }
  170. }
  171. private static Object getFieldValue(
  172. Class clas, Object object, String fieldName) throws ReflectError
  173. {
  174. try {
  175. Field f = getField(clas, fieldName);
  176. if ( f == null )
  177. throw new ReflectError("internal: field not found:"+fieldName);
  178. Object value = f.get(object);
  179. Class returnType = f.getType();
  180. return wrapPrimitive(value, returnType);
  181. }
  182. catch(NullPointerException e) {
  183. throw new ReflectError(
  184. "???" + fieldName + " is not a static field.");
  185. }
  186. catch(IllegalAccessException e) {
  187. throw new ReflectError("Can't access field: " + fieldName);
  188. }
  189. }
  190. /**
  191. All field lookup should come through here.
  192. i.e. this method owns Class getField();
  193. */
  194. private static Field getField(Class clas, String fieldName)
  195. throws ReflectError
  196. {
  197. try
  198. {
  199. if ( Capabilities.haveAccessibility() )
  200. return findAccessibleField( clas, fieldName );
  201. else
  202. // this one only finds public
  203. return clas.getField(fieldName);
  204. }
  205. catch(NoSuchFieldException e)
  206. {
  207. // try declaredField
  208. throw new ReflectError("No such field: " + fieldName );
  209. }
  210. }
  211. /**
  212. Used when accessibility capability is available to locate an occurrance
  213. of the field in the most derived class or superclass and set its
  214. accessibility flag.
  215. Note that this method is not needed in the simple non accessible
  216. case because we don't have to hunt for fields.
  217. Note that classes may declare overlapping private fields, so the
  218. distinction about the most derived is important. Java doesn't normally
  219. allow this kind of access (super won't show private variables) so
  220. there is no real syntax for specifying which class scope to use...
  221. Need to improve this to handle interfaces.
  222. */
  223. private static Field findAccessibleField( Class clas, String fieldName )
  224. throws NoSuchFieldException
  225. {
  226. while ( clas != null )
  227. {
  228. try {
  229. Field field = clas.getDeclaredField(fieldName);
  230. if ( ReflectManager.RMSetAccessible( field ) )
  231. return field;
  232. // else fall through
  233. }
  234. catch(NoSuchFieldException e) { }
  235. clas = clas.getSuperclass();
  236. }
  237. throw new NoSuchFieldException( fieldName );
  238. }
  239. /**
  240. The full blown invoke method. Everybody should come here.
  241. The invoked method may be static or dynamic unless onlyStatic is set
  242. (in which case object may be null).
  243. @param onlyStatic
  244. The method located must be static, the object param may be null.
  245. Note: Method invocation could probably be speeded up if we eliminated
  246. the throwing of exceptions in the search for the proper method.
  247. We could probably cache our knowledge of method structure as well.
  248. */
  249. private static Object invokeMethod(
  250. Class clas, Object object, String name, Object[] args,
  251. boolean onlyStatic
  252. )
  253. throws ReflectError, InvocationTargetException, EvalError
  254. {
  255. if ( object == Primitive.NULL )
  256. throw new TargetError("Attempt to invoke method "
  257. +name+" on null value", new NullPointerException() );
  258. if ( object == Primitive.VOID )
  259. throw new EvalError("Attempt to invoke method "
  260. +name+" on undefined variable or class name" );
  261. if (args == null)
  262. args = new Object[] { };
  263. // Simple sanity check for voids
  264. // (maybe this should have been caught further up?)
  265. for(int i=0; i<args.length; i++)
  266. if(args[i] == Primitive.VOID)
  267. throw new ReflectError("Attempt to pass void argument " +
  268. "(position " + i + ") to method: " + name);
  269. Class returnType = null;
  270. Object returnValue = null;
  271. Class[] types = getTypes(args);
  272. unwrapPrimitives(args);
  273. try
  274. {
  275. // Try the easy case: Look for an accessible version of the
  276. // direct match.
  277. Method m = null;
  278. try {
  279. m = findAccessibleMethod(clas, name, types, onlyStatic);
  280. } catch ( SecurityException e ) { }
  281. if ( m == null )
  282. Interpreter.debug("Exact method " +
  283. StringUtil.methodString(name, types) +
  284. " not found in '" + clas.getName() + "'" );
  285. // Next look for an assignable match
  286. if ( m == null ) {
  287. // If no args stop here
  288. if ( types.length == 0 )
  289. throw new ReflectError(
  290. "No args "+ ( onlyStatic ? "static " : "" )
  291. +"method " + StringUtil.methodString(name, types) +
  292. " not found in class'" + clas.getName() + "'");
  293. // try to find an assignable method
  294. Method[] methods = clas.getMethods();
  295. if ( onlyStatic )
  296. // only try the static methods
  297. methods = retainStaticMethods( methods );
  298. m = findMostSpecificMethod(name, types, methods);
  299. // try to find an extended method
  300. methods = clas.getMethods();
  301. if ( m == null )
  302. m = findExtendedMethod(name, args, methods);
  303. // If we found an assignable method, make sure it's accessible
  304. if ( m != null ) {
  305. try {
  306. m = findAccessibleMethod( clas, m.getName(),
  307. m.getParameterTypes(), onlyStatic);
  308. } catch ( SecurityException e ) { }
  309. }
  310. }
  311. // Found something?
  312. if (m == null )
  313. throw new ReflectError(
  314. ( onlyStatic ? "Static method " : "Method " )
  315. + StringUtil.methodString(name, types) +
  316. " not found in class'" + clas.getName() + "'");
  317. // Invoke it
  318. returnValue = m.invoke(object, args);
  319. if(returnValue == null)
  320. returnValue = Primitive.NULL;
  321. returnType = m.getReturnType();
  322. } catch(IllegalAccessException e) {
  323. throw new ReflectError(
  324. "Cannot access method " + StringUtil.methodString(name, types) +
  325. " in '" + clas.getName() + "' :" + e);
  326. }
  327. return wrapPrimitive(returnValue, returnType);
  328. }
  329. /**
  330. Return only the static methods
  331. */
  332. private static Method [] retainStaticMethods( Method [] methods ) {
  333. Vector v = new Vector();
  334. for(int i=0; i<methods.length; i++)
  335. if ( Modifier.isStatic( methods[i].getModifiers() ) )
  336. v.addElement( methods[i] );
  337. Method [] ma = new Method [ v.size() ];
  338. v.copyInto( ma );
  339. return ma;
  340. }
  341. /**
  342. Locate a version of the method with the exact signature specified
  343. that is accessible via a public interface or through a public
  344. superclass.
  345. This solves the problem that arises when a package private class
  346. or private inner class implements a public interface or derives from
  347. a public type.
  348. @param onlyStatic the method located must be static.
  349. @returns null on not found
  350. */
  351. static Method findAccessibleMethod(
  352. Class clas, String name, Class [] types, boolean onlyStatic )
  353. {
  354. Method meth = null;
  355. Vector classQ = new Vector();
  356. classQ.addElement( clas );
  357. Method found = null;
  358. while ( classQ.size() > 0 )
  359. {
  360. Class c = (Class)classQ.firstElement();
  361. classQ.removeElementAt(0);
  362. // Is this it?
  363. // Is the class public or can we use accessibility?
  364. if ( Modifier.isPublic( c.getModifiers() )
  365. || ( Capabilities.haveAccessibility()
  366. && ReflectManager.RMSetAccessible( c ) ) )
  367. {
  368. try {
  369. meth = c.getDeclaredMethod( name, types );
  370. // Is the method public or are we in accessibility mode?
  371. if ( Modifier.isPublic( meth.getModifiers() )
  372. || ( Capabilities.haveAccessibility()
  373. && ReflectManager.RMSetAccessible( meth ) ) )
  374. {
  375. found = meth; // Yes, it is.
  376. break;
  377. }
  378. } catch ( NoSuchMethodException e ) {
  379. // ignore and move on
  380. }
  381. }
  382. // No, it is not.
  383. // Is this a class?
  384. if ( !c.isInterface() ) {
  385. Class superclass = c.getSuperclass();
  386. if ( superclass != null )
  387. classQ.addElement((Object)superclass);
  388. }
  389. // search all of its interfaces breadth first
  390. Class [] intfs = c.getInterfaces();
  391. for( int i=0; i< intfs.length; i++ )
  392. classQ.addElement((Object)intfs[i]);
  393. }
  394. /*
  395. If we found one and it satisfies onlyStatic return it
  396. Note: I don't believe it is necessary to check for the static
  397. condition in the above search because the Java compiler will not
  398. let dynamic and static methods hide/override one another. So
  399. we simply check what is found, if any, at the end.
  400. */
  401. if ( found != null &&
  402. ( !onlyStatic || Modifier.isStatic( found.getModifiers() ) ) )
  403. return found;
  404. // Didn't find one
  405. /*
  406. Interpreter.debug(
  407. "Can't find publically accessible "+
  408. ( onlyStatic ? " static " : "" )
  409. +" version of method: "+
  410. StringUtil.methodString(name, types) +
  411. " in interfaces or class hierarchy of class "+clas.getName() );
  412. */
  413. return null;
  414. }
  415. private static Object wrapPrimitive(
  416. Object value, Class returnType) throws ReflectError
  417. {
  418. if(value == null)
  419. return Primitive.NULL;
  420. if(returnType == Void.TYPE)
  421. return Primitive.VOID;
  422. else
  423. if(returnType.isPrimitive())
  424. {
  425. if(value instanceof Number)
  426. return new Primitive((Number)value);
  427. if(value instanceof Boolean)
  428. return new Primitive((Boolean)value);
  429. if(value instanceof Character)
  430. return new Primitive((Character)value);
  431. throw new ReflectError("Something bad happened");
  432. }
  433. else
  434. return value;
  435. }
  436. public static Class[] getTypes( Object[] args)
  437. {
  438. if(args == null)
  439. return new Class[0];
  440. Class[] types = new Class[args.length];
  441. for(int i=0; i<args.length; i++)
  442. {
  443. if ( args[i] == null )
  444. throw new InterpreterError("Null arg in getTypes()");
  445. if(args[i] instanceof Primitive)
  446. types[i] = ((Primitive)args[i]).getType();
  447. else
  448. types[i] = args[i].getClass();
  449. }
  450. return types;
  451. }
  452. /*
  453. Replace Primitive wrappers with their java.lang wrapper values
  454. These barf if one of the args is void... maybe these should throw
  455. an exception on void arg to force the rest of the code to clean up.
  456. There are places where we don't check right now... (constructors, index)
  457. */
  458. private static void unwrapPrimitives(Object[] args)
  459. {
  460. for(int i=0; i<args.length; i++)
  461. args[i] = unwrapPrimitive(args[i]);
  462. }
  463. private static Object unwrapPrimitive(Object arg)
  464. {
  465. if(arg instanceof Primitive)
  466. return((Primitive)arg).getValue();
  467. else
  468. return arg;
  469. }
  470. static Object constructObject(String clas, Object[] args)
  471. throws ReflectError, InvocationTargetException
  472. {
  473. Class c = BshClassManager.classForName( clas );
  474. if ( c == null )
  475. throw new ReflectError("Class not found: "+clas);
  476. return constructObject( c, args );
  477. }
  478. /**
  479. Primary object constructor
  480. */
  481. static Object constructObject(Class clas, Object[] args)
  482. throws ReflectError, InvocationTargetException
  483. {
  484. // simple sanity check for arguments
  485. for(int i=0; i<args.length; i++)
  486. if(args[i] == Primitive.VOID)
  487. throw new ReflectError("Attempt to pass void argument " +
  488. "(position " + i + ") to constructor for: " + clas);
  489. if ( clas.isInterface() )
  490. throw new ReflectError(
  491. "Can't create instance of an interface: "+clas);
  492. Object obj = null;
  493. Class[] types = getTypes(args);
  494. unwrapPrimitives(args);
  495. Constructor con = null;
  496. /*
  497. Find an appropriate constructor
  498. use declared here to see package and private as well
  499. (there are no inherited constructors to worry about)
  500. */
  501. Constructor[] constructors = clas.getDeclaredConstructors();
  502. Interpreter.debug("Looking for most specific constructor: "+clas);
  503. con = findMostSpecificConstructor(types, constructors);
  504. if ( con == null )
  505. if ( types.length == 0 )
  506. throw new ReflectError(
  507. "Can't find default constructor for: "+clas);
  508. else
  509. con = findExtendedConstructor(args, constructors);
  510. if(con == null)
  511. throw new ReflectError("Can't find constructor: "
  512. + clas );
  513. try {
  514. obj = con.newInstance(args);
  515. } catch(InstantiationException e) {
  516. throw new ReflectError("the class is abstract ");
  517. } catch(IllegalAccessException e) {
  518. throw new ReflectError(
  519. "we don't have permission to create an instance");
  520. } catch(IllegalArgumentException e) {
  521. throw new ReflectError("the number of arguments was wrong");
  522. }
  523. if (obj == null)
  524. throw new ReflectError("couldn't construct the object");
  525. return obj;
  526. }
  527. /**
  528. Implement JLS 15.11.2 for method resolution
  529. @param onlyStatic only static methods will be considered.
  530. @returns null on no match
  531. */
  532. static Method findMostSpecificMethod(
  533. String name, Class[] idealMatch, Method[] methods )
  534. {
  535. // Pull out the method signatures whos name matches
  536. Vector sigs = new Vector();
  537. Vector meths = new Vector();
  538. for(int i=0; i<methods.length; i++)
  539. // method matches name
  540. if ( methods[i].getName().equals( name ) )
  541. {
  542. meths.addElement( methods[i] );
  543. sigs.addElement( methods[i].getParameterTypes() );
  544. }
  545. Class [][] candidates = new Class [ sigs.size() ][];
  546. sigs.copyInto( candidates );
  547. Interpreter.debug("Looking for most specific method: "+name);
  548. int match = findMostSpecificSignature( idealMatch, candidates );
  549. if ( match == -1 )
  550. return null;
  551. else
  552. return (Method)meths.elementAt( match );
  553. }
  554. /**
  555. This uses the NameSpace.getAssignableForm() method to determine
  556. compatability of args. This allows special (non standard Java) bsh
  557. widening operations...
  558. @returns null on not found
  559. */
  560. static Method findExtendedMethod(
  561. String name, Object[] args, Method[] methods)
  562. {
  563. Method bestMatch = null;
  564. Object[] tempArgs = new Object[args.length];
  565. for(int i = 0; i < methods.length; i++) {
  566. Method currentMethod = methods[i];
  567. if ( name.equals( currentMethod.getName() )) {
  568. Class[] parameters = currentMethod.getParameterTypes();
  569. if ( parameters.length != args.length )
  570. continue;
  571. try {
  572. for(int j = 0; j < parameters.length; j++)
  573. tempArgs[j] = NameSpace.getAssignableForm(
  574. args[j], parameters[j]);
  575. // if you get here, all the arguments were assignable
  576. System.arraycopy(tempArgs, 0, args, 0, args.length);
  577. return currentMethod;
  578. } catch(EvalError e) {
  579. // do nothing (exception breaks you out of the for loop).
  580. }
  581. }
  582. }
  583. return null;
  584. }
  585. /*
  586. This method should exactly parallel findMostSpecificMethod()
  587. */
  588. static Constructor findMostSpecificConstructor(Class[] idealMatch,
  589. Constructor[] constructors)
  590. {
  591. Class [][] candidates = new Class [ constructors.length ] [];
  592. for(int i=0; i< candidates.length; i++ )
  593. candidates[i] = constructors[i].getParameterTypes();
  594. int match = findMostSpecificSignature( idealMatch, candidates );
  595. if ( match == -1 )
  596. return null;
  597. else
  598. return constructors[ match ];
  599. }
  600. /**
  601. This uses the NameSpace.getAssignableForm() method to determine
  602. compatability of args. This allows special (non standard Java) bsh
  603. widening operations...
  604. */
  605. static Constructor findExtendedConstructor(
  606. Object[] args, Constructor[] constructors )
  607. {
  608. Constructor bestMatch = null;
  609. Object[] tempArgs = new Object[args.length];
  610. for(int i = 0; i < constructors.length; i++)
  611. {
  612. Constructor currentConstructor = constructors[i];
  613. Class[] parameters = currentConstructor.getParameterTypes();
  614. if ( parameters.length != args.length )
  615. continue;
  616. try {
  617. for(int j = 0; j < parameters.length; j++)
  618. tempArgs[j] =
  619. NameSpace.getAssignableForm(args[j], parameters[j]);
  620. // if you get here, all the arguments were assignable
  621. System.arraycopy(tempArgs, 0, args, 0, args.length);
  622. return currentConstructor;
  623. }
  624. catch(EvalError e)
  625. {
  626. // do nothing (exception breaks you out of the for loop).
  627. }
  628. }
  629. return null;
  630. }
  631. /**
  632. Implement JLS 15.11.2
  633. Return the index of the most specific arguments match or -1 if no
  634. match is found.
  635. */
  636. static int findMostSpecificSignature(
  637. Class [] idealMatch, Class [][] candidates )
  638. {
  639. Class [] bestMatch = null;
  640. int bestMatchIndex = -1;
  641. for (int i=0; i < candidates.length; i++) {
  642. Class[] targetMatch = candidates[i];
  643. /*
  644. If idealMatch fits targetMatch and this is the first match
  645. or targetMatch is more specific than the best match, make it
  646. the new best match.
  647. */
  648. if ( isAssignable(idealMatch, targetMatch ) &&
  649. ((bestMatch == null) ||
  650. isAssignable( targetMatch, bestMatch )))
  651. {
  652. bestMatch = targetMatch;
  653. bestMatchIndex = i;
  654. }
  655. }
  656. if ( bestMatch != null ) {
  657. /*
  658. Interpreter.debug("best match: "
  659. + StringUtil.methodString("args",bestMatch));
  660. */
  661. return bestMatchIndex;
  662. }
  663. else {
  664. Interpreter.debug("no match found");
  665. return -1;
  666. }
  667. }
  668. /**
  669. Determine if the 'from' signature is assignable to the 'to' signature
  670. 'from' arg types, 'to' candidate types
  671. null value in 'to' type parameter indicates loose type.
  672. null value in either arg is considered empty array
  673. */
  674. static boolean isAssignable(Class[] from, Class[] to)
  675. {
  676. if ( from == null )
  677. from = new Class[0];
  678. if ( to == null )
  679. to = new Class[0];
  680. if (from.length != to.length)
  681. return false;
  682. for(int i=0; i<from.length; i++)
  683. {
  684. // Null type indicates loose type. Match anything.
  685. if ( to[i] == null )
  686. continue;
  687. // Let null arg type match any reference type
  688. if (from[i] == null) {
  689. if (!(to[i].isPrimitive()))
  690. continue;
  691. else
  692. return false;
  693. }
  694. if(!isAssignableFrom(to[i], from[i]))
  695. return false;
  696. }
  697. return true;
  698. }
  699. /**
  700. This base method is meant to address a deficiency of
  701. Class.isAssignableFrom() which does not take primitive widening
  702. conversions into account.
  703. Note that the getAssigbableForm() method in NameSpace is the primary
  704. bsh method for checking assignability. It adds extended bsh
  705. conversions, etc.
  706. @param lhs assigning from rhs to lhs
  707. @param rhs assigning from rhs to lsh
  708. */
  709. static boolean isAssignableFrom(Class lhs, Class rhs)
  710. {
  711. if(lhs.isPrimitive() && rhs.isPrimitive())
  712. {
  713. if(lhs == rhs)
  714. return true;
  715. // handle primitive widening conversions - JLS 5.1.2
  716. if((rhs == Byte.TYPE) && (lhs == Short.TYPE || lhs == Integer.TYPE ||
  717. lhs == Long.TYPE || lhs == Float.TYPE || lhs == Double.TYPE))
  718. return true;
  719. if((rhs == Short.TYPE) && (lhs == Integer.TYPE || lhs == Long.TYPE ||
  720. lhs == Float.TYPE || lhs == Double.TYPE))
  721. return true;
  722. if((rhs == Character.TYPE) && (lhs == Integer.TYPE || lhs == Long.TYPE ||
  723. lhs == Float.TYPE || lhs == Double.TYPE))
  724. return true;
  725. if((rhs == Integer.TYPE) && (lhs == Long.TYPE || lhs == Float.TYPE ||
  726. lhs == Double.TYPE))
  727. return true;
  728. if((rhs == Long.TYPE) && (lhs == Float.TYPE || lhs == Double.TYPE))
  729. return true;
  730. if((rhs == Float.TYPE) && (lhs == Double.TYPE))
  731. return true;
  732. }
  733. else
  734. if(lhs.isAssignableFrom(rhs))
  735. return true;
  736. return false;
  737. }
  738. private static String accessorName( String getorset, String propName ) {
  739. return getorset
  740. + String.valueOf(Character.toUpperCase(propName.charAt(0)))
  741. + propName.substring(1);
  742. }
  743. public static boolean hasObjectPropertyGetter(
  744. Class clas, String propName )
  745. {
  746. String getterName = accessorName("get", propName );
  747. try {
  748. clas.getMethod( getterName, new Class [0] );
  749. return true;
  750. } catch ( NoSuchMethodException e ) {
  751. return false;
  752. }
  753. }
  754. public static boolean hasObjectPropertySetter(
  755. Class clas, String propName )
  756. {
  757. String setterName = accessorName("set", propName );
  758. Class [] sig = new Class [] { clas };
  759. Method [] methods = clas.getMethods();
  760. // we don't know the right hand side of the assignment yet.
  761. // has at least one setter of the right name?
  762. for(int i=0; i<methods.length; i++)
  763. if ( methods[i].getName().equals( setterName ) )
  764. return true;
  765. return false;
  766. }
  767. public static Object getObjectProperty(
  768. Object obj, String propName)
  769. throws ReflectError
  770. {
  771. String accessorName = accessorName( "get", propName );
  772. Object[] args = new Object[] { };
  773. Interpreter.debug("property access: ");
  774. try {
  775. try {
  776. // null interpreter, accessor doesn't need to know
  777. // null callerInfo
  778. return invokeObjectMethod(null, obj, accessorName, args, null);
  779. } catch ( EvalError e ) {
  780. // what does this mean?
  781. throw new ReflectError("getter: "+e);
  782. }
  783. }
  784. catch(InvocationTargetException e)
  785. {
  786. throw new ReflectError(
  787. "Property accessor threw exception:" + e );
  788. }
  789. }
  790. public static void setObjectProperty(
  791. Object obj, String propName, Object value)
  792. throws ReflectError, EvalError
  793. {
  794. String accessorName = accessorName( "set", propName );
  795. Object[] args = new Object[] { value };
  796. Interpreter.debug("property access: ");
  797. try {
  798. // null interpreter, accessor doesn't need to know
  799. // null callerInfo
  800. invokeObjectMethod(null, obj, accessorName, args, null);
  801. }
  802. catch(InvocationTargetException e)
  803. {
  804. throw new EvalError("Property accessor threw exception!");
  805. }
  806. }
  807. /**
  808. This method is meant to convert a JVM-array class name to the correct
  809. 'fully-qualified name' for the array class - JLS 6.7
  810. */
  811. public static String normalizeClassName(Class type)
  812. {
  813. if(!type.isArray())
  814. return type.getName();
  815. StringBuffer className = new StringBuffer();
  816. try
  817. {
  818. className.append(getArrayBaseType(type).getName());
  819. for(int i = 0; i < getArrayDimensions(type); i++)
  820. className.append("[]");
  821. }
  822. catch(Exception e) { }
  823. return className.toString();
  824. }
  825. /**[
  826. returns the dimensionality of the Class
  827. returns 0 if the Class is not an array class
  828. */
  829. public static int getArrayDimensions(Class arrayClass)
  830. {
  831. if(!arrayClass.isArray())
  832. return 0;
  833. return arrayClass.getName().lastIndexOf('[') + 1;
  834. }
  835. /**
  836. Returns the base type of an array Class.
  837. throws ReflectError if the Class is not an array class.
  838. */
  839. public static Class getArrayBaseType(Class arrayClass) throws ReflectError
  840. {
  841. if(!arrayClass.isArray())
  842. throw new ReflectError("The class is not an array.");
  843. return arrayClass.getComponentType();
  844. }
  845. }