PageRenderTime 49ms CodeModel.GetById 4ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-0-pre5/bsh/Name.java

#
Java | 879 lines | 428 code | 116 blank | 335 comment | 122 complexity | 0886a33fec5f8a2682bb72b1115282ba 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 localy declared bsh method.
  46. <p>
  47. *** implementing ***
  48. Name objects are not to be constructed arbitrarily, but are to be
  49. factoried by NameSpace.getNameResolver, which caches them subject to
  50. a namespace change. This means that we can cache information about various
  51. types of resolution here.
  52. */
  53. /*
  54. <strong>Implementation notes</strong>
  55. <pre>
  56. *** implementing ***
  57. Name objects are not to be constructed arbitrarily, but are to be
  58. factoried by NameSpace.getNameResolver, which caches them subject to
  59. a namespace change. This means that we can cache information about various
  60. types of resolution here.
  61. Note that we cannot simply cache any result, we must be smart about it.
  62. For example, the value of a variable may change between calls. So we could
  63. cache the knowledge that it is a variable, but must re-fetch the value
  64. each time. We could even do cool optimizations such as knowing about
  65. 'final' variables ;)
  66. Questions: how much will this actually buy us? The simple cases are not
  67. expensive here, are they? How often are long chains of names evaluated?
  68. *** implementing ***
  69. Threads:
  70. Thread safety: all of the work methods in this class must be synchronized
  71. because they share the internal intermediate evaluation state.
  72. Random note:
  73. In some ways Name wants to be a private inner class of NameSpace...
  74. However it is used elsewhere as an absraction for objects which haven't
  75. been pinned down yet. So it is exposed.
  76. Note on this.caller resolution:
  77. Although references like these do work:
  78. this.caller.caller.caller... // works
  79. the equivalent using successive calls:
  80. // does *not* work
  81. for( caller=this.caller; caller != null; caller = caller.caller );
  82. is prohibited by the restriction that you can only call .caller on a
  83. literal this or caller reference.
  84. The effect is that magic caller reference only works through the current
  85. 'this' reference.
  86. The real explanation is that This referernces do not really know anything
  87. about their depth on the call stack. It might even be hard to define
  88. such a thing...
  89. For those purposes we provide :
  90. this.callstack
  91. </pre>
  92. */
  93. class Name implements java.io.Serializable
  94. {
  95. // These do not change during evaluation
  96. public NameSpace namespace;
  97. String value = null;
  98. // ---------------------------------------------------------
  99. // The following instance variables mutate during evaluation and should
  100. // be reset by the reset() method where necessary
  101. // For evaluation
  102. private String evalName; // text left to eval
  103. private Object evalBaseObject; // base object for current eval
  104. private int callstackDepth; // number of times eval hit 'this.caller'
  105. /**
  106. The last round consume the literal 'this' reference (not super,
  107. global, or another This type var). We use this flag to support magic
  108. variables that can only be referenced through 'this.xxx', e.g.
  109. this.interpreter and this.caller;
  110. */
  111. private boolean literalThisReference;
  112. /**
  113. The last round consume the literal 'caller' reference (not super,
  114. global, or another This type var). This is used to limit references
  115. to .caller to only after a literal 'this' or compound '.caller'.
  116. */
  117. private boolean literalCallerReference;
  118. //
  119. // End mutable instance variables.
  120. // ---------------------------------------------------------
  121. private void reset() {
  122. evalName = value;
  123. evalBaseObject = null;
  124. callstackDepth = 0;
  125. literalThisReference=false;
  126. literalCallerReference=false;
  127. }
  128. /**
  129. This constructor should *not* be used in general.
  130. Use NameSpace getNameResolver() which supports caching.
  131. I wish I could make this "friendly" to just that class.
  132. @see NameSpace getNameResolver().
  133. */
  134. public Name(NameSpace namespace, String s)
  135. {
  136. this.namespace = namespace;
  137. value = s;
  138. }
  139. /**
  140. Resolve possibly complex name to an object value.
  141. Throws EvalError on various failures.
  142. A null object value is indicated by a Primitive.NULL.
  143. A return type of Primitive.VOID comes from attempting to access
  144. an undefined variable.
  145. Some cases:
  146. myVariable
  147. myVariable.foo
  148. myVariable.foo.bar
  149. java.awt.GridBagConstraints.BOTH
  150. my.package.stuff.MyClass.someField.someField...
  151. Interpreter reference is necessary to allow resolution of
  152. "this.interpreter" magic field.
  153. CallStack reference is necessary to allow resolution of
  154. "this.caller" magic field.
  155. "this.callstack" magic field.
  156. */
  157. public Object toObject( CallStack callstack, Interpreter interpreter )
  158. throws EvalError
  159. {
  160. return toObject( callstack, interpreter, false );
  161. }
  162. /**
  163. @see toObject()
  164. @param forceClass if true then resolution will only produce a class.
  165. This is necessary to disambiguate in cases where the grammar knows
  166. that we want a class; where in general the var path may be taken.
  167. */
  168. synchronized public Object toObject(
  169. CallStack callstack, Interpreter interpreter, boolean forceClass )
  170. throws EvalError
  171. {
  172. reset();
  173. Object obj = null;
  174. while( evalName != null )
  175. obj = consumeNextObjectField( callstack, interpreter, forceClass );
  176. if ( obj == null )
  177. throw new InterpreterError("null value in toObject()");
  178. return obj;
  179. }
  180. /**
  181. Get next prefixed object field component
  182. */
  183. private Object consumeNextObjectField(
  184. CallStack callstack, Interpreter interpreter, boolean forceClass )
  185. throws EvalError
  186. {
  187. /*
  188. Is it a simple variable name?
  189. Doing this first gives the correct Java precedence for vars
  190. vs. imported class names (at least in the simple case - see
  191. tests/precedence1.bsh). It should also speed things up a bit.
  192. */
  193. if ( (evalBaseObject == null && !isCompound(evalName) )
  194. && !forceClass )
  195. {
  196. Object obj = resolveThisFieldReference(
  197. callstack, namespace, interpreter, evalName, false );
  198. if ( obj != Primitive.VOID ) {
  199. evalName = null; // finished
  200. return evalBaseObject = obj; // convention
  201. }
  202. }
  203. /*
  204. Is it a bsh script variable reference?
  205. If we're just starting the eval of name (no base object)
  206. or we're evaluating relative to a This reference check.
  207. */
  208. if ( ( evalBaseObject == null || evalBaseObject instanceof This )
  209. && !forceClass )
  210. {
  211. String varName = prefix(evalName, 1);
  212. Interpreter.debug("trying to resolve variable: " + varName);
  213. Object obj;
  214. if ( evalBaseObject == null ) {
  215. obj = resolveThisFieldReference(
  216. callstack, namespace, interpreter, varName, false );
  217. } else {
  218. // null callstack, cannot be caller reference
  219. obj = resolveThisFieldReference(
  220. callstack, ((This)evalBaseObject).namespace,
  221. interpreter, varName, true );
  222. }
  223. if ( obj != Primitive.VOID )
  224. {
  225. // Resolved the variable
  226. Interpreter.debug( "resolved variable: " + varName +
  227. " in namespace: "+namespace);
  228. evalName = suffix(evalName);
  229. return evalBaseObject = obj;
  230. }
  231. }
  232. /*
  233. Is it a class name?
  234. If we're just starting eval of name try to make it, else fail.
  235. */
  236. if ( evalBaseObject == null ) {
  237. Interpreter.debug( "trying class: " + evalName);
  238. /*
  239. Keep adding parts until we have a class
  240. */
  241. Class clas = null;
  242. int i = 1;
  243. for(; i <= countParts(evalName); i++)
  244. if ( (clas = namespace.getClass(prefix(evalName, i))) != null )
  245. break;
  246. if( clas != null ) {
  247. evalName = suffix(evalName, countParts(evalName) - i);
  248. return ( evalBaseObject = new ClassIdentifier(clas) );
  249. }
  250. // not a class (or variable per above)
  251. Interpreter.debug( "not a class, trying var prefix "+evalName );
  252. }
  253. /*
  254. If we didn't find a class or variable name (or prefix) above
  255. there are two possibilities:
  256. - If we are a simple name then we can pass as a void variable
  257. reference.
  258. - If we are compound then we must fail at this point.
  259. */
  260. if ( evalBaseObject == null ) {
  261. if( !isCompound(evalName) ) {
  262. evalName = null; // finished
  263. return evalBaseObject = Primitive.VOID; // convention
  264. } else
  265. throw new EvalError(
  266. "Class or variable not found:" + evalName);
  267. }
  268. /*
  269. --------------------------------------------------------
  270. After this point we're definitely evaluating relative to
  271. a base object.
  272. --------------------------------------------------------
  273. */
  274. /*
  275. Do some basic validity checks.
  276. */
  277. if(evalBaseObject == Primitive.NULL) // previous round produced null
  278. throw new TargetError( "Null Pointer while evaluating: "
  279. +value, new NullPointerException() );
  280. if(evalBaseObject == Primitive.VOID) // previous round produced void
  281. throw new EvalError(
  282. "Undefined variable or class name while evaluating: "+value);
  283. if(evalBaseObject instanceof Primitive)
  284. throw new EvalError("Can't treat primitive like an object. "+
  285. "Error while evaluating: "+value);
  286. /*
  287. Resolve relative to a class type
  288. static field, inner class, ?
  289. */
  290. if ( evalBaseObject instanceof ClassIdentifier )
  291. {
  292. Class clas = ((ClassIdentifier)evalBaseObject).getTargetClass();
  293. String field = prefix(evalName, 1);
  294. Object obj = null;
  295. // static field?
  296. try {
  297. //System.err.println("Name call to getStaticField, class: "
  298. //+clas+", field:"+field);
  299. obj = Reflect.getStaticField(clas, field);
  300. } catch(ReflectError e) { }
  301. // inner class?
  302. if ( obj == null ) {
  303. String iclass = clas.getName()+"$"+field;
  304. Class c = namespace.getClass( iclass );
  305. if ( c != null )
  306. obj = new ClassIdentifier(c);
  307. }
  308. if ( obj == null )
  309. throw new EvalError(
  310. "No static field or inner class: " + field + " of " + clas);
  311. evalName = suffix(evalName);
  312. return (evalBaseObject = obj);
  313. }
  314. /*
  315. If we've fallen through here we are no longer resolving to
  316. a class type.
  317. */
  318. if ( forceClass )
  319. throw new EvalError( value +" does not resolve to a class name." );
  320. /*
  321. Some kind of field access?
  322. */
  323. String field = prefix(evalName, 1);
  324. /* length access on array? */
  325. if(field.equals("length") && evalBaseObject.getClass().isArray())
  326. {
  327. Object obj = new Primitive(Array.getLength(evalBaseObject));
  328. evalName = suffix(evalName);
  329. return (evalBaseObject = obj);
  330. }
  331. /* check for field on object */
  332. // Note: could eliminate throwing the exception somehow
  333. try
  334. {
  335. Object obj = Reflect.getObjectField(evalBaseObject, field);
  336. evalName = suffix(evalName);
  337. return (evalBaseObject = obj);
  338. }
  339. catch(ReflectError e) { /* not a field */ }
  340. // if we get here we have failed
  341. throw new EvalError(
  342. "Cannot access field: " + field + ", on object: " + evalBaseObject);
  343. }
  344. /**
  345. Resolve a variable relative to a This reference.
  346. This is the general variable resolution method, accomodating special
  347. fields from the This context. Together the namespace and interpreter
  348. comprise the This context. The callstack, if available allows for the
  349. this.caller construct.
  350. Optionally interpret special "magic" field names: e.g. interpreter.
  351. @param callstack may be null, but this is only legitimate in special
  352. cases where we are sure resolution will not involve this.caller.
  353. @param namespace the namespace of the this reference (should be the
  354. same as the top of the stack?
  355. */
  356. Object resolveThisFieldReference(
  357. CallStack callstack, NameSpace thisNamespace, Interpreter interpreter,
  358. String varName, boolean specialFieldsVisible )
  359. throws EvalError
  360. {
  361. Object obj = null;
  362. // preserve the state of the last round flags until the end
  363. boolean
  364. wasThis = false,
  365. wasCaller = false;
  366. if ( varName.equals("this") ) {
  367. // Hack! If the special fields are visible turn of further .this
  368. // prevent user from skipping to things like super.this.caller
  369. if ( specialFieldsVisible )
  370. throw new EvalError("Redundant to call .this on This type");
  371. obj = thisNamespace.getThis( interpreter );
  372. wasThis = true;
  373. }
  374. if ( obj == null ) {
  375. if ( varName.equals("super") )
  376. obj = thisNamespace.getSuper().getThis( interpreter );
  377. else if ( varName.equals("global") )
  378. obj = thisNamespace.getGlobal().getThis( interpreter );
  379. }
  380. if ( obj == null && specialFieldsVisible ) {
  381. if (varName.equals("namespace"))
  382. obj = thisNamespace;
  383. else if (varName.equals("variables"))
  384. obj = thisNamespace.getVariableNames();
  385. else if (varName.equals("methods"))
  386. obj = thisNamespace.getMethodNames();
  387. else if ( varName.equals("interpreter") )
  388. if ( literalThisReference )
  389. obj = interpreter;
  390. else
  391. throw new EvalError(
  392. "Can only call .interpreter on literal 'this'");
  393. }
  394. if ( obj == null && specialFieldsVisible && varName.equals("caller") )
  395. {
  396. if ( literalThisReference || literalCallerReference )
  397. {
  398. // get the previous context (see notes for this class)
  399. if ( callstack == null )
  400. throw new InterpreterError("no callstack");
  401. obj = callstack.get( ++callstackDepth ).getThis(
  402. interpreter );
  403. }
  404. else
  405. throw new EvalError(
  406. "Can only call .caller on literal 'this' or literal '.caller'");
  407. wasCaller = true;
  408. }
  409. if ( obj == null && specialFieldsVisible
  410. && varName.equals("callstack") )
  411. {
  412. if ( literalThisReference )
  413. {
  414. // get the previous context (see notes for this class)
  415. if ( callstack == null )
  416. throw new InterpreterError("no callstack");
  417. obj = callstack;
  418. }
  419. else
  420. throw new EvalError(
  421. "Can only call .callstack on literal 'this'");
  422. }
  423. if ( obj == null )
  424. obj = thisNamespace.getVariable(varName);
  425. literalThisReference = wasThis;
  426. literalCallerReference = wasCaller;
  427. return obj;
  428. }
  429. /**
  430. Check the cache, else use toObject() to try to resolve to a class
  431. identifier.
  432. Throws EvalError on class not found...
  433. */
  434. synchronized public Class toClass() throws EvalError
  435. {
  436. reset();
  437. /* Try straightforward class name first */
  438. Class clas = namespace.getClass(evalName);
  439. if ( clas == null ) {
  440. /*
  441. Try toObject() which knows how to work through inner classes
  442. and see what we end up with
  443. */
  444. Object obj = null;
  445. try {
  446. // Null interpreter and callstack references.
  447. // class only resolution should not require them.
  448. obj = toObject( null, null, true );
  449. } catch ( EvalError e ) { }; // couldn't resolve it
  450. if ( obj instanceof ClassIdentifier )
  451. clas = ((ClassIdentifier)obj).getTargetClass();
  452. }
  453. if( clas == null )
  454. throw new EvalError(
  455. "Class: " + value+ " not found in namespace");
  456. return clas;
  457. }
  458. /*
  459. */
  460. synchronized public LHS toLHS(
  461. CallStack callstack, Interpreter interpreter )
  462. throws EvalError
  463. {
  464. reset();
  465. //Interpreter.debug("Name toLHS: "+evalName+ " isCompound = "
  466. //+isCompound(evalName));
  467. // variable
  468. if(!isCompound(evalName)) {
  469. //Interpreter.debug("returning simple var LHS...");
  470. return new LHS(namespace,evalName);
  471. }
  472. // field
  473. Object obj = null;
  474. try
  475. {
  476. while(isCompound(evalName))
  477. obj = consumeNextObjectField( callstack, interpreter, false );
  478. }
  479. catch( EvalError e )
  480. {
  481. throw new EvalError("LHS evaluation: " + e);
  482. }
  483. if ( obj == null )
  484. throw new InterpreterError("internal error 2893749283");
  485. if(obj instanceof This)
  486. {
  487. Interpreter.debug("found This reference evaluating LHS");
  488. return new LHS(((This)obj).namespace, evalName);
  489. }
  490. if(evalName != null)
  491. {
  492. try
  493. {
  494. //System.err.println("Name getLHSObjectField call obj = "
  495. // +obj+", name="+evalName);
  496. if ( obj instanceof ClassIdentifier )
  497. {
  498. Class clas = ((ClassIdentifier)obj).getTargetClass();
  499. return Reflect.getLHSStaticField(clas, evalName);
  500. } else
  501. return Reflect.getLHSObjectField(obj, evalName);
  502. } catch(ReflectError e)
  503. {
  504. throw new EvalError("Field access: "+e);
  505. }
  506. }
  507. throw new InterpreterError("Internal error in lhs...");
  508. /*
  509. This appears to have been something very old and incorrect...
  510. I don't think we need it anymore.
  511. // We bit off our field in the very first bite
  512. // have to back off and make a class out of the prefix
  513. Interpreter.debug("very first field was it...");
  514. Class clas = namespace.getClass(prefix(value));
  515. if(clas == null)
  516. throw new InterpreterError("internal error 238974983");
  517. String field = suffix(value, 1);
  518. try
  519. {
  520. return Reflect.getLHSStaticField(clas, field);
  521. }
  522. catch(ReflectError e)
  523. {
  524. Interpreter.debug("reflect error:" + e);
  525. return null;
  526. }
  527. */
  528. }
  529. private BshMethod toLocalMethod( Object [] args )
  530. {
  531. Class [] sig = Reflect.getTypes( args );
  532. return namespace.getMethod( value, sig );
  533. }
  534. /**
  535. Invoke the method identified by name.
  536. Name contains a wholely unqualfied messy name; resolve it to
  537. ( object | static prefix ) + method name and invoke.
  538. The interpreter is necessary to support 'this.interpreter' references
  539. in the called code. (e.g. debug());
  540. Some cases:
  541. // dynamic
  542. local();
  543. myVariable.foo();
  544. myVariable.bar.blah.foo();
  545. // static
  546. java.lang.Integer.getInteger("foo");
  547. */
  548. public Object invokeMethod(
  549. Interpreter interpreter, Object[] args, CallStack callstack,
  550. SimpleNode callerInfo
  551. )
  552. throws EvalError, ReflectError, InvocationTargetException
  553. {
  554. if ( !Name.isCompound(value) )
  555. return invokeLocalMethod(interpreter, args, callstack, callerInfo);
  556. // find target object
  557. Name targetName = namespace.getNameResolver( Name.prefix(value));
  558. String methodName = Name.suffix(value, 1);
  559. Object obj = targetName.toObject( callstack, interpreter );
  560. if ( obj == Primitive.VOID )
  561. throw new EvalError( "Attempt to invoke method: "+methodName
  562. +"() on undefined variable or class name: "+targetName);
  563. // if we've got an object, invoke the method
  564. if ( !(obj instanceof Name.ClassIdentifier) ) {
  565. if (obj instanceof Primitive) {
  566. if (obj == Primitive.NULL)
  567. throw new TargetError( "Null Pointer in Method Invocation",
  568. new NullPointerException() );
  569. // some other primitive
  570. // should avoid calling methods on primitive, as we do
  571. // in Name (can't treat primitive like an object message)
  572. // but the hole is useful right now.
  573. interpreter.error("Attempt to access method on primitive..." +
  574. " allowing bsh.Primitive to peek through for debugging");
  575. }
  576. // found an object and it's not an undefined variable
  577. return Reflect.invokeObjectMethod(
  578. interpreter, obj, methodName, args, callerInfo);
  579. }
  580. // try static method
  581. Interpreter.debug("invokeMethod: trying static - " + targetName);
  582. Class clas = ((Name.ClassIdentifier)obj).getTargetClass();
  583. if (clas != null)
  584. return Reflect.invokeStaticMethod(clas, methodName, args);
  585. // return null; ???
  586. throw new EvalError("unknown target: " + targetName);
  587. }
  588. /**
  589. Invoke a locally declared method or a bsh command.
  590. If the method is not already declared in the namespace then try
  591. to load it as a resource from the /bsh/commands path.
  592. Note: instead of invoking the method directly here we should probably
  593. call invokeObjectMethod passing a This reference. That would have
  594. the side effect of allowing a locally defined invoke() method to
  595. handle undeclared method invocations just like in objects. Not sure
  596. if this is desirable... It seems that if you invoke a method directly
  597. in scope it should be there.
  598. Keeping this code separate allows us to differentiate between methods
  599. invoked directly in scope and those invoked through object references.
  600. */
  601. public Object invokeLocalMethod(
  602. Interpreter interpreter, Object[] args, CallStack callstack,
  603. SimpleNode callerInfo
  604. )
  605. throws EvalError, ReflectError, InvocationTargetException
  606. {
  607. Interpreter.debug("invoke local method: " + value);
  608. // Check for locally declared method
  609. BshMethod meth = toLocalMethod( args );
  610. if ( meth != null )
  611. return meth.invokeDeclaredMethod( args, interpreter, callstack, callerInfo );
  612. else
  613. Interpreter.debug("no locally declared method: " + value);
  614. /*
  615. Look for scripted command as resource
  616. */
  617. // Why not /bsh/commands here? Why relative to Interpreter?
  618. String commandName = "commands/" + value + ".bsh";
  619. InputStream in = Interpreter.class.getResourceAsStream(commandName);
  620. if (in != null)
  621. {
  622. Interpreter.debug("loading resource: " + commandName);
  623. if ( interpreter == null )
  624. throw new InterpreterError("2234432 interpreter = null");
  625. interpreter.eval(
  626. new InputStreamReader(in), namespace, commandName);
  627. // try again
  628. meth = toLocalMethod( args );
  629. if(meth != null)
  630. return meth.invokeDeclaredMethod(
  631. args, interpreter, callstack, callerInfo );
  632. else
  633. throw new EvalError("Loaded resource: " + commandName +
  634. "had an error or did not contain the correct method");
  635. }
  636. // check for compiled bsh command class
  637. commandName = "bsh.commands." + value;
  638. // create class outside of any namespace
  639. Class c = BshClassManager.classForName( commandName );
  640. if(c == null)
  641. throw new EvalError("Command not found: " + value);
  642. // add interpereter and namespace to args list
  643. Object[] invokeArgs = new Object[args.length + 2];
  644. invokeArgs[0] = interpreter;
  645. invokeArgs[1] = namespace;
  646. System.arraycopy(args, 0, invokeArgs, 2, args.length);
  647. try
  648. {
  649. return Reflect.invokeStaticMethod(c, "invoke", invokeArgs);
  650. }
  651. catch(ReflectError e)
  652. {
  653. Interpreter.debug("invoke command args error:" + e);
  654. // bad args
  655. }
  656. // try to print help
  657. try
  658. {
  659. String s = (String)Reflect.invokeStaticMethod(c, "usage", null);
  660. interpreter.println(s);
  661. return Primitive.VOID;
  662. }
  663. catch(ReflectError e)
  664. {
  665. Interpreter.debug("usage threw: " + e);
  666. throw new EvalError("Wrong number or type of args for command");
  667. }
  668. }
  669. // Static methods that operate on compound ('.' separated) names
  670. static boolean isCompound(String value)
  671. {
  672. return countParts(value) > 1;
  673. }
  674. static int countParts(String value)
  675. {
  676. if(value == null)
  677. return 0;
  678. int count = 0;
  679. int index = -1;
  680. while((index = value.indexOf('.', index + 1)) != -1)
  681. count++;
  682. return count + 1;
  683. }
  684. static String prefix(String value)
  685. {
  686. if(!isCompound(value))
  687. return null;
  688. return prefix(value, countParts(value) - 1);
  689. }
  690. static String prefix(String value, int parts)
  691. {
  692. if(parts < 1)
  693. return null;
  694. int count = 0;
  695. int index = -1;
  696. while(((index = value.indexOf('.', index + 1)) != -1) && (++count < parts))
  697. { ; }
  698. return (index == -1) ? value : value.substring(0, index);
  699. }
  700. static String suffix(String name)
  701. {
  702. if(!isCompound(name))
  703. return null;
  704. return suffix(name, countParts(name) - 1);
  705. }
  706. public static String suffix(String value, int parts)
  707. {
  708. if(parts < 1)
  709. return null;
  710. int count = 0;
  711. int index = value.length() + 1;
  712. while(((index = value.lastIndexOf('.', index - 1)) != -1) && (++count < parts))
  713. { ; }
  714. return (index == -1) ? value : value.substring(index + 1);
  715. }
  716. // end compound name routines
  717. public String toString() { return value; }
  718. static class ClassIdentifier {
  719. Class clas;
  720. public ClassIdentifier( Class clas ) {
  721. this.clas = clas;
  722. }
  723. public Class getTargetClass() {
  724. return clas;
  725. }
  726. public String toString() {
  727. return "Class Identifier: "+clas.getName();
  728. }
  729. }
  730. }