PageRenderTime 150ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 995 lines | 493 code | 135 blank | 367 comment | 158 complexity | cdd29515331cf85597de9b2aab6a45de 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.Array;
  35. import java.util.Hashtable;
  36. import java.io.*;
  37. import java.lang.reflect.InvocationTargetException;
  38. /**
  39. What's in a name? I'll tell you...
  40. Name() is a somewhat ambiguous thing in the grammar and so is this.
  41. <p>
  42. This class is a name resolver. It holds a possibly ambiguous dot
  43. separated name and reference to a namespace in which it allegedly lives.
  44. It provides methods that attempt to resolve the name to various types of
  45. entities: e.g. an Object, a Class, a declared scripted BeanShell method.
  46. <p>
  47. Name objects are created by the factory method NameSpace getNameResolver(),
  48. which caches them subject to a class namespace change. This means that
  49. we can cache information about various types of resolution here.
  50. Currently very little if any information is cached. However with a future
  51. "optimize" setting that defeats certain dynamic behavior we might be able
  52. to cache quite a bit.
  53. */
  54. /*
  55. <strong>Implementation notes</strong>
  56. <pre>
  57. Thread safety: all of the work methods in this class must be synchronized
  58. because they share the internal intermediate evaluation state.
  59. Note about invokeMethod(): We could simply use resolveMethod and return
  60. the MethodInvoker (BshMethod or JavaMethod) however there is no easy way
  61. for the AST (BSHMehodInvocation) to use this as it doesn't have type
  62. information about the target to resolve overloaded methods.
  63. (In Java, overloaded methods are resolved at compile time... here they
  64. are, of necessity, dynamic). So it would have to do what we do here
  65. and cache by signature. We now do that for the client in Reflect.java.
  66. Note on this.caller resolution:
  67. Although references like these do work:
  68. this.caller.caller.caller... // works
  69. the equivalent using successive calls:
  70. // does *not* work
  71. for( caller=this.caller; caller != null; caller = caller.caller );
  72. is prohibited by the restriction that you can only call .caller on a
  73. literal this or caller reference. The effect is that magic caller
  74. reference only works through the current 'this' reference.
  75. The real explanation is that This referernces do not really know anything
  76. about their depth on the call stack. It might even be hard to define
  77. such a thing...
  78. For those purposes we provide :
  79. this.callstack
  80. </pre>
  81. */
  82. class Name implements java.io.Serializable
  83. {
  84. // These do not change during evaluation
  85. public NameSpace namespace;
  86. String value = null;
  87. // ---------------------------------------------------------
  88. // The following instance variables mutate during evaluation and should
  89. // be reset by the reset() method where necessary
  90. // For evaluation
  91. /** Remaining text to evaluate */
  92. private String evalName;
  93. /**
  94. The last part of the name evaluated. This is really only used for
  95. this, caller, and super resolution.
  96. */
  97. private String lastEvalName;
  98. private static String FINISHED = null; // null evalname and we're finished
  99. private Object evalBaseObject; // base object for current eval
  100. private int callstackDepth; // number of times eval hit 'this.caller'
  101. //
  102. // End mutable instance variables.
  103. // ---------------------------------------------------------
  104. // Begin Cached result structures
  105. // These are optimizations
  106. // Note: it's ok to cache class resolution here because when the class
  107. // space changes the namespace will discard cached names.
  108. /**
  109. The result is a class
  110. */
  111. Class asClass;
  112. /**
  113. The result is a static method call on the following class
  114. */
  115. Class classOfStaticMethod;
  116. // End Cached result structures
  117. private void reset() {
  118. evalName = value;
  119. evalBaseObject = null;
  120. callstackDepth = 0;
  121. }
  122. /**
  123. This constructor should *not* be used in general.
  124. Use NameSpace getNameResolver() which supports caching.
  125. @see NameSpace getNameResolver().
  126. */
  127. // I wish I could make this "friendly" to only NameSpace
  128. Name( NameSpace namespace, String s )
  129. {
  130. this.namespace = namespace;
  131. value = s;
  132. }
  133. /**
  134. Resolve possibly complex name to an object value.
  135. Throws EvalError on various failures.
  136. A null object value is indicated by a Primitive.NULL.
  137. A return type of Primitive.VOID comes from attempting to access
  138. an undefined variable.
  139. Some cases:
  140. myVariable
  141. myVariable.foo
  142. myVariable.foo.bar
  143. java.awt.GridBagConstraints.BOTH
  144. my.package.stuff.MyClass.someField.someField...
  145. Interpreter reference is necessary to allow resolution of
  146. "this.interpreter" magic field.
  147. CallStack reference is necessary to allow resolution of
  148. "this.caller" magic field.
  149. "this.callstack" magic field.
  150. */
  151. public Object toObject( CallStack callstack, Interpreter interpreter )
  152. throws UtilEvalError
  153. {
  154. return toObject( callstack, interpreter, false );
  155. }
  156. /**
  157. @see toObject()
  158. @param forceClass if true then resolution will only produce a class.
  159. This is necessary to disambiguate in cases where the grammar knows
  160. that we want a class; where in general the var path may be taken.
  161. */
  162. synchronized public Object toObject(
  163. CallStack callstack, Interpreter interpreter, boolean forceClass )
  164. throws UtilEvalError
  165. {
  166. reset();
  167. Object obj = null;
  168. while( evalName != null )
  169. obj = consumeNextObjectField(
  170. callstack, interpreter, forceClass, false/*autoalloc*/ );
  171. if ( obj == null )
  172. throw new InterpreterError("null value in toObject()");
  173. return obj;
  174. }
  175. private Object completeRound(
  176. String lastEvalName, String nextEvalName, Object returnObject )
  177. {
  178. this.lastEvalName = lastEvalName;
  179. this.evalName = nextEvalName;
  180. this.evalBaseObject = returnObject;
  181. return returnObject;
  182. }
  183. /**
  184. Get the next object by consuming one or more components of evalName.
  185. Often this consumes just one component, but if the name is a classname
  186. it will consume all of the components necessary to make the class
  187. identifier.
  188. */
  189. private Object consumeNextObjectField(
  190. CallStack callstack, Interpreter interpreter,
  191. boolean forceClass, boolean autoAllocateThis )
  192. throws UtilEvalError
  193. {
  194. /*
  195. Is it a simple variable name?
  196. Doing this first gives the correct Java precedence for vars
  197. vs. imported class names (at least in the simple case - see
  198. tests/precedence1.bsh). It should also speed things up a bit.
  199. */
  200. if ( (evalBaseObject == null && !isCompound(evalName) )
  201. && !forceClass )
  202. {
  203. Object obj = resolveThisFieldReference(
  204. callstack, namespace, interpreter, evalName, false );
  205. if ( obj != Primitive.VOID )
  206. return completeRound( evalName, FINISHED, obj );
  207. }
  208. /*
  209. Is it a bsh script variable reference?
  210. If we're just starting the eval of name (no base object)
  211. or we're evaluating relative to a This type reference check.
  212. */
  213. if ( ( evalBaseObject == null || evalBaseObject instanceof This )
  214. && !forceClass )
  215. {
  216. String varName = prefix(evalName, 1);
  217. if ( Interpreter.DEBUG )
  218. Interpreter.debug("trying to resolve variable: " + varName);
  219. Object obj;
  220. // switch namespace and special var visibility
  221. if ( evalBaseObject == null ) {
  222. obj = resolveThisFieldReference(
  223. callstack, namespace, interpreter, varName, false );
  224. } else {
  225. obj = resolveThisFieldReference(
  226. callstack, ((This)evalBaseObject).namespace,
  227. interpreter, varName, true );
  228. }
  229. // No variable found in 'this' type ref.
  230. // if autoAllocateThis then create one; a child 'this'.
  231. if ( obj == Primitive.VOID && autoAllocateThis )
  232. {
  233. NameSpace targetNameSpace =
  234. ( evalBaseObject == null ) ?
  235. namespace : ((This)evalBaseObject).namespace;
  236. obj = new NameSpace(
  237. targetNameSpace, "auto: "+varName ).getThis( interpreter );
  238. targetNameSpace.setVariable( varName, obj, false );
  239. }
  240. if ( obj != Primitive.VOID )
  241. {
  242. // Resolved the variable
  243. if ( Interpreter.DEBUG )
  244. Interpreter.debug( "resolved variable: " + varName +
  245. " in namespace: "+namespace);
  246. return completeRound( varName, suffix(evalName), obj );
  247. }
  248. }
  249. /*
  250. Is it a class name?
  251. If we're just starting eval of name try to make it, else fail.
  252. */
  253. if ( evalBaseObject == null ) {
  254. if ( Interpreter.DEBUG )
  255. Interpreter.debug( "trying class: " + evalName);
  256. /*
  257. Keep adding parts until we have a class
  258. */
  259. Class clas = null;
  260. int i = 1;
  261. String className = null;
  262. for(; i <= countParts(evalName); i++)
  263. {
  264. className = prefix(evalName, i);
  265. if ( (clas = namespace.getClass(className)) != null )
  266. break;
  267. }
  268. if ( clas != null ) {
  269. return completeRound(
  270. className,
  271. suffix( evalName, countParts(evalName)-i ),
  272. new ClassIdentifier(clas)
  273. );
  274. }
  275. // not a class (or variable per above)
  276. if ( Interpreter.DEBUG )
  277. Interpreter.debug( "not a class, trying var prefix "+evalName );
  278. }
  279. /*
  280. If we didn't find a class or variable name (or prefix) above
  281. there are two possibilities:
  282. - If we are a simple name then we can pass as a void variable
  283. reference.
  284. - If we are compound then we must fail at this point.
  285. */
  286. if ( evalBaseObject == null ) {
  287. if ( !isCompound(evalName) ) {
  288. return completeRound( evalName, FINISHED, Primitive.VOID );
  289. } else
  290. throw new UtilEvalError(
  291. "Class or variable not found: " + evalName);
  292. }
  293. /*
  294. --------------------------------------------------------
  295. After this point we're definitely evaluating relative to
  296. a base object.
  297. --------------------------------------------------------
  298. */
  299. /*
  300. Do some basic validity checks.
  301. */
  302. if ( evalBaseObject == Primitive.NULL) // previous round produced null
  303. throw new UtilTargetError( new NullPointerException(
  304. "Null Pointer while evaluating: " +value ) );
  305. if ( evalBaseObject == Primitive.VOID) // previous round produced void
  306. throw new UtilEvalError(
  307. "Undefined variable or class name while evaluating: "+value);
  308. if ( evalBaseObject instanceof Primitive)
  309. throw new UtilEvalError("Can't treat primitive like an object. "+
  310. "Error while evaluating: "+value);
  311. /*
  312. Resolve relative to a class type
  313. static field, inner class, ?
  314. */
  315. if ( evalBaseObject instanceof ClassIdentifier )
  316. {
  317. Class clas = ((ClassIdentifier)evalBaseObject).getTargetClass();
  318. String field = prefix(evalName, 1);
  319. Object obj = null;
  320. // static field?
  321. try {
  322. if ( Interpreter.DEBUG )
  323. Interpreter.debug("Name call to getStaticField, class: "
  324. +clas+", field:"+field);
  325. obj = Reflect.getStaticField(clas, field);
  326. } catch( ReflectError e ) {
  327. if ( Interpreter.DEBUG )
  328. Interpreter.debug("field reflect error: "+e);
  329. }
  330. // inner class?
  331. if ( obj == null ) {
  332. String iclass = clas.getName()+"$"+field;
  333. Class c = namespace.getClass( iclass );
  334. if ( c != null )
  335. obj = new ClassIdentifier(c);
  336. }
  337. if ( obj == null )
  338. throw new UtilEvalError(
  339. "No static field or inner class: " + field + " of " + clas);
  340. return completeRound( field, suffix(evalName), obj );
  341. }
  342. /*
  343. If we've fallen through here we are no longer resolving to
  344. a class type.
  345. */
  346. if ( forceClass )
  347. throw new UtilEvalError(
  348. value +" does not resolve to a class name." );
  349. /*
  350. Some kind of field access?
  351. */
  352. String field = prefix(evalName, 1);
  353. // length access on array?
  354. if ( field.equals("length") && evalBaseObject.getClass().isArray() )
  355. {
  356. Object obj = new Primitive(Array.getLength(evalBaseObject));
  357. return completeRound( field, suffix(evalName), obj );
  358. }
  359. // Check for field on object
  360. // Note: could eliminate throwing the exception somehow
  361. try {
  362. Object obj = Reflect.getObjectField(evalBaseObject, field);
  363. return completeRound( field, suffix(evalName), obj );
  364. } catch(ReflectError e) { /* not a field */ }
  365. // if we get here we have failed
  366. throw new UtilEvalError(
  367. "Cannot access field: " + field + ", on object: " + evalBaseObject);
  368. }
  369. /**
  370. Resolve a variable relative to a This reference.
  371. This is the general variable resolution method, accomodating special
  372. fields from the This context. Together the namespace and interpreter
  373. comprise the This context. The callstack, if available allows for the
  374. this.caller construct.
  375. Optionally interpret special "magic" field names: e.g. interpreter.
  376. <p/>
  377. @param callstack may be null, but this is only legitimate in special
  378. cases where we are sure resolution will not involve this.caller.
  379. @param namespace the namespace of the this reference (should be the
  380. same as the top of the stack?
  381. */
  382. Object resolveThisFieldReference(
  383. CallStack callstack, NameSpace thisNameSpace, Interpreter interpreter,
  384. String varName, boolean specialFieldsVisible )
  385. throws UtilEvalError
  386. {
  387. Object obj = null;
  388. if ( varName.equals("this") )
  389. {
  390. /*
  391. Somewhat of a hack. If the special fields are visible (we're
  392. operating relative to a 'this' type already) dissallow further
  393. .this references to prevent user from skipping to things like
  394. super.this.caller
  395. */
  396. if ( specialFieldsVisible )
  397. throw new UtilEvalError("Redundant to call .this on This type");
  398. // Allow getThis() to work through BlockNameSpace to the method
  399. // namespace
  400. This ths = thisNameSpace.getThis( interpreter );
  401. thisNameSpace= ths.getNameSpace();
  402. /*
  403. The following test handles the case of a scripted method in a
  404. scripted class instance namespace. This should really be in a
  405. subclass of Name or NameSpace. A reference to 'this' from
  406. inside a method in a class instance should refer to the
  407. enclosing class instance, as in Java.
  408. */
  409. if ( thisNameSpace.isMethod
  410. && thisNameSpace.getParent() != null
  411. && thisNameSpace.getParent() instanceof ClassNameSpace
  412. && ((ClassNameSpace)(thisNameSpace.getParent()))
  413. .isClassInstance()
  414. )
  415. ths = thisNameSpace.getParent().getThis( interpreter );
  416. return ths;
  417. }
  418. /*
  419. Some duplication for "super". See notes for "this" above
  420. If we're in an enclsing class instance and have a superclass
  421. instance our super is the superclass instance.
  422. */
  423. if ( varName.equals("super") )
  424. {
  425. //if ( specialFieldsVisible )
  426. //throw new UtilEvalError("Redundant to call .this on This type");
  427. // Allow getSuper() to through BlockNameSpace to the method's super
  428. This ths = thisNameSpace.getSuper().getThis(interpreter);
  429. thisNameSpace = ths.getNameSpace();
  430. // super is now the closure's super or class instance
  431. // If we're a class instance and the parent is also a class instance
  432. // then super means our parent.
  433. if (
  434. thisNameSpace instanceof ClassNameSpace
  435. && ((ClassNameSpace)thisNameSpace).isClassInstance()
  436. && thisNameSpace.getParent() != null
  437. && thisNameSpace.getParent() instanceof ClassNameSpace
  438. && ((ClassNameSpace)(thisNameSpace.getParent()))
  439. .isClassInstance()
  440. )
  441. ths = thisNameSpace.getParent().getThis( interpreter );
  442. return ths;
  443. }
  444. if ( varName.equals("global") )
  445. obj = thisNameSpace.getGlobal().getThis( interpreter );
  446. if ( obj == null && specialFieldsVisible )
  447. {
  448. if (varName.equals("namespace"))
  449. obj = thisNameSpace;
  450. else if (varName.equals("variables"))
  451. obj = thisNameSpace.getVariableNames();
  452. else if (varName.equals("methods"))
  453. obj = thisNameSpace.getMethodNames();
  454. else if ( varName.equals("interpreter") )
  455. if ( lastEvalName.equals("this") )
  456. obj = interpreter;
  457. else
  458. throw new UtilEvalError(
  459. "Can only call .interpreter on literal 'this'");
  460. }
  461. if ( obj == null && specialFieldsVisible && varName.equals("caller") )
  462. {
  463. if ( lastEvalName.equals("this") || lastEvalName.equals("caller") )
  464. {
  465. // get the previous context (see notes for this class)
  466. if ( callstack == null )
  467. throw new InterpreterError("no callstack");
  468. obj = callstack.get( ++callstackDepth ).getThis(
  469. interpreter );
  470. }
  471. else
  472. throw new UtilEvalError(
  473. "Can only call .caller on literal 'this' or literal '.caller'");
  474. // early return
  475. return obj;
  476. }
  477. if ( obj == null && specialFieldsVisible
  478. && varName.equals("callstack") )
  479. {
  480. if ( lastEvalName.equals("this") )
  481. {
  482. // get the previous context (see notes for this class)
  483. if ( callstack == null )
  484. throw new InterpreterError("no callstack");
  485. obj = callstack;
  486. }
  487. else
  488. throw new UtilEvalError(
  489. "Can only call .callstack on literal 'this'");
  490. }
  491. if ( obj == null )
  492. obj = thisNameSpace.getVariable(varName);
  493. return obj;
  494. }
  495. /**
  496. Check the cache, else use toObject() to try to resolve to a class
  497. identifier.
  498. @throws ClassNotFoundException on class not found.
  499. @throws ClassPathException (type of EvalError) on special case of
  500. ambiguous unqualified name after super import.
  501. */
  502. synchronized public Class toClass()
  503. throws ClassNotFoundException, UtilEvalError
  504. {
  505. if ( asClass != null )
  506. return asClass;
  507. reset();
  508. // "var" means untyped, return null class
  509. if ( evalName.equals("var") )
  510. return asClass = null;
  511. /* Try straightforward class name first */
  512. Class clas = namespace.getClass( evalName );
  513. if ( clas == null )
  514. {
  515. /*
  516. Try toObject() which knows how to work through inner classes
  517. and see what we end up with
  518. */
  519. Object obj = null;
  520. try {
  521. // Null interpreter and callstack references.
  522. // class only resolution should not require them.
  523. obj = toObject( null, null, true );
  524. } catch ( UtilEvalError e ) { }; // couldn't resolve it
  525. if ( obj instanceof ClassIdentifier )
  526. clas = ((ClassIdentifier)obj).getTargetClass();
  527. }
  528. if ( clas == null )
  529. throw new ClassNotFoundException(
  530. "Class: " + value+ " not found in namespace");
  531. asClass = clas;
  532. return asClass;
  533. }
  534. /*
  535. */
  536. synchronized public LHS toLHS(
  537. CallStack callstack, Interpreter interpreter )
  538. throws UtilEvalError
  539. {
  540. // Should clean this up to a single return statement
  541. reset();
  542. LHS lhs;
  543. // Simple (non-compound) variable assignment e.g. x=5;
  544. if ( !isCompound(evalName) )
  545. {
  546. // Interpreter.debug("Simple var LHS...");
  547. lhs = new LHS( namespace, evalName, false/*bubble up if allowed*/);
  548. return lhs;
  549. }
  550. // Field e.g. foo.bar=5;
  551. Object obj = null;
  552. try {
  553. while( evalName != null && isCompound( evalName ) )
  554. obj = consumeNextObjectField( callstack, interpreter,
  555. false/*forcclass*/, true/*autoallocthis*/ );
  556. }
  557. catch( UtilEvalError e ) {
  558. throw new UtilEvalError("LHS evaluation: " + e);
  559. }
  560. if ( obj instanceof ClassIdentifier )
  561. throw new UtilEvalError("Can't assign to class: " + value );
  562. if ( obj == null )
  563. throw new UtilEvalError("Error in LHS: " + value );
  564. // e.g. this.x=5; or someThisType.x=5;
  565. if ( obj instanceof This )
  566. {
  567. // dissallow assignment to magic fields
  568. if (
  569. evalName.equals("namespace")
  570. || evalName.equals("variables")
  571. || evalName.equals("methods")
  572. || evalName.equals("caller")
  573. )
  574. throw new UtilEvalError(
  575. "Can't assign to special variable: "+evalName );
  576. Interpreter.debug("found This reference evaluating LHS");
  577. /*
  578. If this was a literal "super" reference then we allow recursion
  579. in setting the variable to get the normal effect of finding the
  580. nearest definition starting at the super scope. On any other
  581. resolution qualified by a 'this' type reference we want to set
  582. the variable directly in that scope. e.g. this.x=5; or
  583. someThisType.x=5;
  584. In the old scoping rules super didn't do this.
  585. */
  586. boolean localVar = !lastEvalName.equals("super");
  587. return new LHS( ((This)obj).namespace, evalName, localVar );
  588. }
  589. if ( evalName != null )
  590. {
  591. try {
  592. if ( obj instanceof ClassIdentifier )
  593. {
  594. Class clas = ((ClassIdentifier)obj).getTargetClass();
  595. lhs = Reflect.getLHSStaticField(clas, evalName);
  596. return lhs;
  597. } else {
  598. lhs = Reflect.getLHSObjectField(obj, evalName);
  599. return lhs;
  600. }
  601. } catch(ReflectError e) {
  602. throw new UtilEvalError("Field access: "+e);
  603. }
  604. }
  605. throw new InterpreterError("Internal error in lhs...");
  606. }
  607. /**
  608. Invoke the method identified by this name.
  609. Performs caching of method resolution using SignatureKey.
  610. <p>
  611. Name contains a wholely unqualfied messy name; resolve it to
  612. ( object | static prefix ) + method name and invoke.
  613. <p>
  614. The interpreter is necessary to support 'this.interpreter' references
  615. in the called code. (e.g. debug());
  616. <p>
  617. <pre>
  618. Some cases:
  619. // dynamic
  620. local();
  621. myVariable.foo();
  622. myVariable.bar.blah.foo();
  623. // static
  624. java.lang.Integer.getInteger("foo");
  625. </pre>
  626. */
  627. public Object invokeMethod(
  628. Interpreter interpreter, Object[] args, CallStack callstack,
  629. SimpleNode callerInfo
  630. )
  631. throws UtilEvalError, EvalError, ReflectError, InvocationTargetException
  632. {
  633. String methodName = Name.suffix(value, 1);
  634. BshClassManager bcm = callstack.top().getClassManager();
  635. // Optimization - If classOfStaticMethod is set then we have already
  636. // been here and determined that this is a static method invocation.
  637. // Note: maybe factor this out with path below... clean up.
  638. if ( classOfStaticMethod != null )
  639. {
  640. return Reflect.invokeStaticMethod(
  641. bcm, classOfStaticMethod, methodName, args );
  642. }
  643. if ( !Name.isCompound(value) )
  644. return invokeLocalMethod(
  645. interpreter, args, callstack, callerInfo );
  646. // Note: if we want methods declared inside blocks to be accessible via
  647. // this.methodname() inside the block we could handle it here as a
  648. // special case. See also resolveThisFieldReference() special handling
  649. // for BlockNameSpace case. They currently work via the direct name
  650. // e.g. methodName().
  651. // Find target object or class identifier
  652. Name targetName = namespace.getNameResolver( Name.prefix(value));
  653. Object obj = targetName.toObject( callstack, interpreter );
  654. if ( obj == Primitive.VOID )
  655. throw new UtilEvalError( "Attempt to resolve method: "+methodName
  656. +"() on undefined variable or class name: "+targetName);
  657. // if we've got an object, resolve the method
  658. if ( !(obj instanceof ClassIdentifier) ) {
  659. if (obj instanceof Primitive) {
  660. if (obj == Primitive.NULL)
  661. throw new UtilTargetError( new NullPointerException(
  662. "Null Pointer in Method Invocation" ) );
  663. // some other primitive
  664. // should avoid calling methods on primitive, as we do
  665. // in Name (can't treat primitive like an object message)
  666. // but the hole is useful right now.
  667. if ( Interpreter.DEBUG )
  668. interpreter.debug(
  669. "Attempt to access method on primitive..."
  670. + " allowing bsh.Primitive to peek through for debugging");
  671. }
  672. // found an object and it's not an undefined variable
  673. return Reflect.invokeObjectMethod(
  674. obj, methodName, args, interpreter, callstack, callerInfo );
  675. }
  676. // It's a class
  677. // try static method
  678. if ( Interpreter.DEBUG )
  679. Interpreter.debug("invokeMethod: trying static - " + targetName);
  680. Class clas = ((ClassIdentifier)obj).getTargetClass();
  681. // cache the fact that this is a static method invocation on this class
  682. classOfStaticMethod = clas;
  683. if ( clas != null )
  684. return Reflect.invokeStaticMethod( bcm, clas, methodName, args );
  685. // return null; ???
  686. throw new UtilEvalError("invokeMethod: unknown target: " + targetName);
  687. }
  688. /**
  689. Invoke a locally declared method or a bsh command.
  690. If the method is not already declared in the namespace then try
  691. to load it as a resource from the /bsh/commands path.
  692. */
  693. private Object invokeLocalMethod(
  694. Interpreter interpreter, Object[] args, CallStack callstack,
  695. SimpleNode callerInfo
  696. )
  697. throws EvalError/*, ReflectError, InvocationTargetException*/
  698. {
  699. if ( Interpreter.DEBUG )
  700. Interpreter.debug( "invokeLocalMethod: " + value );
  701. if ( interpreter == null )
  702. throw new InterpreterError(
  703. "invokeLocalMethod: interpreter = null");
  704. String commandName = value;
  705. Class [] argTypes = Reflect.getTypes( args );
  706. // Check for existing method
  707. BshMethod meth = null;
  708. try {
  709. meth = namespace.getMethod( commandName, argTypes );
  710. } catch ( UtilEvalError e ) {
  711. throw e.toEvalError(
  712. "Local method invocation", callerInfo, callstack );
  713. }
  714. // If defined, invoke it
  715. if ( meth != null )
  716. return meth.invoke( args, interpreter, callstack, callerInfo );
  717. BshClassManager bcm = interpreter.getClassManager();
  718. Object commandObject;
  719. try {
  720. commandObject = namespace.getCommand(
  721. commandName, argTypes, interpreter );
  722. } catch ( UtilEvalError e ) {
  723. throw e.toEvalError("Error loading command: ",
  724. callerInfo, callstack );
  725. }
  726. // should try to print usage here if nothing found
  727. if ( commandObject == null )
  728. {
  729. // Look for a default invoke() handler method in the namespace
  730. // Note: this code duplicates that in This.java... should it?
  731. // Call on 'This' can never be a command
  732. BshMethod invokeMethod = null;
  733. try {
  734. invokeMethod = namespace.getMethod(
  735. "invoke", new Class [] { null, null } );
  736. } catch ( UtilEvalError e ) {
  737. throw e.toEvalError(
  738. "Local method invocation", callerInfo, callstack );
  739. }
  740. if ( invokeMethod != null )
  741. return invokeMethod.invoke(
  742. new Object [] { commandName, args },
  743. interpreter, callstack, callerInfo );
  744. throw new EvalError( "Command not found: "
  745. +StringUtil.methodString( commandName, argTypes ),
  746. callerInfo, callstack );
  747. }
  748. if ( commandObject instanceof BshMethod )
  749. return ((BshMethod)commandObject).invoke(
  750. args, interpreter, callstack, callerInfo );
  751. if ( commandObject instanceof Class )
  752. try {
  753. return Reflect.invokeCompiledCommand(
  754. ((Class)commandObject), args, interpreter, callstack );
  755. } catch ( UtilEvalError e ) {
  756. throw e.toEvalError("Error invoking compiled command: ",
  757. callerInfo, callstack );
  758. }
  759. throw new InterpreterError("invalid command type");
  760. }
  761. /*
  762. private String getHelp( String name )
  763. throws UtilEvalError
  764. {
  765. try {
  766. // should check for null namespace here
  767. return get( "bsh.help."+name, null/interpreter/ );
  768. } catch ( Exception e ) {
  769. return "usage: "+name;
  770. }
  771. }
  772. private String getHelp( Class commandClass )
  773. throws UtilEvalError
  774. {
  775. try {
  776. return (String)Reflect.invokeStaticMethod(
  777. null/bcm/, commandClass, "usage", null );
  778. } catch( Exception e )
  779. return "usage: "+name;
  780. }
  781. }
  782. */
  783. // Static methods that operate on compound ('.' separated) names
  784. // I guess we could move these to StringUtil someday
  785. public static boolean isCompound(String value)
  786. {
  787. return value.indexOf('.') != -1 ;
  788. //return countParts(value) > 1;
  789. }
  790. static int countParts(String value)
  791. {
  792. if(value == null)
  793. return 0;
  794. int count = 0;
  795. int index = -1;
  796. while((index = value.indexOf('.', index + 1)) != -1)
  797. count++;
  798. return count + 1;
  799. }
  800. static String prefix(String value)
  801. {
  802. if(!isCompound(value))
  803. return null;
  804. return prefix(value, countParts(value) - 1);
  805. }
  806. static String prefix(String value, int parts)
  807. {
  808. if (parts < 1 )
  809. return null;
  810. int count = 0;
  811. int index = -1;
  812. while( ((index = value.indexOf('.', index + 1)) != -1)
  813. && (++count < parts) )
  814. { ; }
  815. return (index == -1) ? value : value.substring(0, index);
  816. }
  817. static String suffix(String name)
  818. {
  819. if(!isCompound(name))
  820. return null;
  821. return suffix(name, countParts(name) - 1);
  822. }
  823. public static String suffix(String value, int parts)
  824. {
  825. if (parts < 1)
  826. return null;
  827. int count = 0;
  828. int index = value.length() + 1;
  829. while ( ((index = value.lastIndexOf('.', index - 1)) != -1)
  830. && (++count < parts) );
  831. return (index == -1) ? value : value.substring(index + 1);
  832. }
  833. // end compound name routines
  834. public String toString() { return value; }
  835. }