PageRenderTime 51ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

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

#
Java | 1673 lines | 820 code | 184 blank | 669 comment | 246 complexity | 85400bf2fb5b9778fd554da7eebdbc8d 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.util.*;
  35. import java.io.InputStream;
  36. import java.io.BufferedReader;
  37. import java.io.InputStreamReader;
  38. import java.io.IOException;
  39. import java.net.URL;
  40. /**
  41. A namespace in which methods, variables, and imports (class names) live.
  42. This is package public because it is used in the implementation of some
  43. bsh commands. However for normal use you should be using methods on
  44. bsh.Interpreter to interact with your scripts.
  45. <p>
  46. A bsh.This object is a thin layer over a NameSpace that associates it with
  47. an Interpreter instance. Together they comprise a Bsh scripted object
  48. context.
  49. <p>
  50. Note: I'd really like to use collections here, but we have to keep this
  51. compatible with JDK1.1
  52. */
  53. /*
  54. Thanks to Slava Pestov (of jEdit fame) for import caching enhancements.
  55. Note: This class has gotten too big. It should be broken down a bit.
  56. */
  57. public class NameSpace
  58. implements java.io.Serializable, BshClassManager.Listener,
  59. NameSource
  60. {
  61. public static final NameSpace JAVACODE =
  62. new NameSpace((BshClassManager)null, "Called from compiled Java code.");
  63. static {
  64. JAVACODE.isMethod = true;
  65. }
  66. // Begin instance data
  67. // Note: if we add something here we should reset it in the clear() method.
  68. /**
  69. The name of this namespace. If the namespace is a method body
  70. namespace then this is the name of the method. If it's a class or
  71. class instance then it's the name of the class.
  72. */
  73. public String nsName;
  74. private NameSpace parent;
  75. private Hashtable variables;
  76. private Hashtable methods;
  77. private Hashtable importedClasses;
  78. private Vector importedPackages;
  79. private Vector importedCommands;
  80. transient private BshClassManager classManager;
  81. // See notes in getThis()
  82. private This thisReference;
  83. /** Name resolver objects */
  84. private Hashtable names;
  85. /** The node associated with the creation of this namespace.
  86. This is used support getInvocationLine() and getInvocationText(). */
  87. SimpleNode callerInfoNode;
  88. /**
  89. Note that the namespace is a method body namespace. This is used for
  90. printing stack traces in exceptions.
  91. */
  92. boolean isMethod;
  93. /**
  94. Local class cache for classes resolved through this namespace using
  95. getClass() (taking into account imports). Only unqualified class names
  96. are cached here (those which might be imported). Qualified names are
  97. always absolute and are cached by BshClassManager.
  98. */
  99. transient private Hashtable classCache;
  100. // End instance data
  101. // Begin constructors
  102. /**
  103. @parent the parent namespace of this namespace. Child namespaces
  104. inherit all variables and methods of their parent and can (of course)
  105. override / shadow them.
  106. */
  107. public NameSpace( NameSpace parent, String name )
  108. {
  109. // Note: in this case parent must have a class manager.
  110. this( parent, null, name );
  111. }
  112. public NameSpace( BshClassManager classManager, String name )
  113. {
  114. this( null, classManager, name );
  115. }
  116. public NameSpace(
  117. NameSpace parent, BshClassManager classManager, String name )
  118. {
  119. // We might want to do this here rather than explicitly in Interpreter
  120. // for global (see also prune())
  121. //if ( classManager == null && (parent == null ) )
  122. // create our own class manager?
  123. setName(name);
  124. setParent(parent);
  125. setClassManager( classManager );
  126. // Register for notification of classloader change
  127. if ( classManager != null )
  128. classManager.addListener(this);
  129. }
  130. // End constructors
  131. public void setName( String name ) {
  132. this.nsName = name;
  133. }
  134. public String getName() {
  135. return this.nsName;
  136. }
  137. /**
  138. Set the node associated with the creation of this namespace.
  139. This is used in debugging and to support the getInvocationLine()
  140. and getInvocationText() methods.
  141. */
  142. void setNode( SimpleNode node ) {
  143. callerInfoNode = node;
  144. }
  145. /**
  146. */
  147. SimpleNode getNode()
  148. {
  149. if ( callerInfoNode != null )
  150. return callerInfoNode;
  151. if ( parent != null )
  152. return parent.getNode();
  153. else
  154. return null;
  155. }
  156. /**
  157. Resolve name to an object through this namespace.
  158. */
  159. public Object get( String name, Interpreter interpreter )
  160. throws UtilEvalError
  161. {
  162. CallStack callstack = new CallStack( this );
  163. return getNameResolver( name ).toObject( callstack, interpreter );
  164. }
  165. public void setVariable(String name, Object value) throws UtilEvalError
  166. {
  167. setVariable(name,value,false);
  168. }
  169. /**
  170. Set the variable through this namespace.
  171. This method obeys the LOCALSCOPING property to determine how variables
  172. are set.
  173. <p>
  174. Note: this method is primarily intended for use internally. If you use
  175. this method outside of the bsh package and wish to set variables with
  176. primitive values you will have to wrap them using bsh.Primitive.
  177. @see bsh.Primitive
  178. <p>
  179. Setting a new variable (which didn't exist before) or removing
  180. a variable causes a namespace change.
  181. @param strictJava specifies whether strict java rules are applied.
  182. */
  183. public void setVariable( String name, Object value, boolean strictJava )
  184. throws UtilEvalError
  185. {
  186. // if localscoping switch follow strictJava, else recurse
  187. boolean recurse = Interpreter.LOCALSCOPING ? strictJava : true;
  188. setVariable( name, value, strictJava, recurse );
  189. }
  190. /**
  191. Set a variable explicitly in the local scope.
  192. */
  193. void setLocalVariable(
  194. String name, Object value, boolean strictJava )
  195. throws UtilEvalError
  196. {
  197. setVariable( name, value, strictJava, false/*recurse*/ );
  198. }
  199. /**
  200. Set the value of a the variable 'name' through this namespace.
  201. The variable may be an existing or non-existing variable.
  202. It may live in this namespace or in a parent namespace if recurse is
  203. true.
  204. <p>
  205. Note: This method is not public and does *not* know about LOCALSCOPING.
  206. Its caller methods must set recurse intelligently in all situations
  207. (perhaps based on LOCALSCOPING).
  208. <p>
  209. Note: this method is primarily intended for use internally. If you use
  210. this method outside of the bsh package and wish to set variables with
  211. primitive values you will have to wrap them using bsh.Primitive.
  212. @see bsh.Primitive
  213. <p>
  214. Setting a new variable (which didn't exist before) or removing
  215. a variable causes a namespace change.
  216. @param strictJava specifies whether strict java rules are applied.
  217. @param recurse determines whether we will search for the variable in
  218. our parent's scope before assigning locally.
  219. */
  220. void setVariable(
  221. String name, Object value, boolean strictJava, boolean recurse )
  222. throws UtilEvalError
  223. {
  224. if ( variables == null )
  225. variables = new Hashtable();
  226. // primitives should have been wrapped
  227. if ( value == null )
  228. {
  229. // no! this breaks the ABI
  230. //throw new InterpreterError("null variable value");
  231. unsetVariable(name);
  232. return;
  233. }
  234. // Locate the variable definition if it exists.
  235. Variable existing = getVariableImpl( name, recurse );
  236. // Found an existing variable here (or above if recurse allowed)
  237. if ( existing != null )
  238. {
  239. try {
  240. existing.setValue( value );
  241. } catch ( UtilEvalError e ) {
  242. throw new UtilEvalError(
  243. "Variable assignment: " + name + ": " + e.getMessage());
  244. }
  245. } else
  246. // No previous variable definition found here (or above if recurse)
  247. {
  248. if ( strictJava )
  249. throw new UtilEvalError(
  250. "(Strict Java mode) Assignment to undeclared variable: "
  251. +name );
  252. // If recurse, set global untyped var, else set it here.
  253. //NameSpace varScope = recurse ? getGlobal() : this;
  254. // This modification makes default allocation local
  255. NameSpace varScope = this;
  256. varScope.variables.put(
  257. name, new Variable( name, value, null/*modifiers*/ ) );
  258. // nameSpaceChanged() on new variable addition
  259. nameSpaceChanged();
  260. }
  261. }
  262. /**
  263. Remove the variable from the namespace.
  264. */
  265. public void unsetVariable( String name )
  266. {
  267. variables.remove( name );
  268. nameSpaceChanged();
  269. }
  270. /**
  271. Get the names of variables defined in this namespace.
  272. (This does not show variables in parent namespaces).
  273. */
  274. public String [] getVariableNames() {
  275. if ( variables == null )
  276. return new String [0];
  277. else
  278. return enumerationToStringArray( variables.keys() );
  279. }
  280. /**
  281. Get the names of methods defined in this namespace.
  282. (This does not show methods in parent namespaces).
  283. */
  284. public String [] getMethodNames() {
  285. if ( methods == null )
  286. return new String [0];
  287. else
  288. return enumerationToStringArray( methods.keys() );
  289. }
  290. /**
  291. Get the methods defined in this namespace.
  292. (This does not show methods in parent namespaces).
  293. */
  294. /*
  295. This should probably be renamed getDeclaredMethod() and have
  296. it return all methods in super
  297. */
  298. public BshMethod [] getMethods() {
  299. if ( methods == null )
  300. return new BshMethod [0];
  301. else
  302. return flattenMethodCollection( methods.elements() );
  303. }
  304. private String [] enumerationToStringArray( Enumeration e ) {
  305. Vector v = new Vector();
  306. while ( e.hasMoreElements() )
  307. v.addElement( e.nextElement() );
  308. String [] sa = new String [ v.size() ];
  309. v.copyInto( sa );
  310. return sa;
  311. }
  312. /**
  313. Support for friendly getMethods();
  314. */
  315. private BshMethod [] flattenMethodCollection( Enumeration e ) {
  316. Vector v = new Vector();
  317. while ( e.hasMoreElements() ) {
  318. Object o = e.nextElement();
  319. if ( o instanceof BshMethod )
  320. v.addElement( o );
  321. else {
  322. Vector ov = (Vector)o;
  323. for(int i=0; i<ov.size(); i++)
  324. v.addElement( ov.elementAt( i ) );
  325. }
  326. }
  327. BshMethod [] bma = new BshMethod [ v.size() ];
  328. v.copyInto( bma );
  329. return bma;
  330. }
  331. /**
  332. Get the parent namespace.
  333. Note: this isn't quite the same as getSuper().
  334. getSuper() returns 'this' if we are at the root namespace.
  335. */
  336. public NameSpace getParent() {
  337. return parent;
  338. }
  339. /**
  340. Get the parent namespace or this namespace if we are the top.
  341. Note: this method should probably return type bsh.This to be consistent
  342. with getThis();
  343. */
  344. public NameSpace getSuper()
  345. {
  346. if (parent != null)
  347. return parent;
  348. else
  349. return this;
  350. }
  351. /**
  352. Get the top level namespace or this namespace if we are the top.
  353. Note: this method should probably return type bsh.This to be consistent
  354. with getThis();
  355. */
  356. public NameSpace getGlobal()
  357. {
  358. if ( parent != null )
  359. return parent.getGlobal();
  360. else
  361. return this;
  362. }
  363. /**
  364. A This object is a thin layer over a namespace, comprising a bsh object
  365. context. It handles things like the interface types the bsh object
  366. supports and aspects of method invocation on it.
  367. <p>
  368. The declaringInterpreter is here to support callbacks from Java through
  369. generated proxies. The scripted object "remembers" who created it for
  370. things like printing messages and other per-interpreter phenomenon
  371. when called externally from Java.
  372. */
  373. /*
  374. Note: we need a singleton here so that things like 'this == this' work
  375. (and probably a good idea for speed).
  376. Caching a single instance here seems technically incorrect,
  377. considering the declaringInterpreter could be different under some
  378. circumstances. (Case: a child interpreter running a source() / eval()
  379. command ). However the effect is just that the main interpreter that
  380. executes your script should be the one involved in call-backs from Java.
  381. I do not know if there are corner cases where a child interpreter would
  382. be the first to use a This reference in a namespace or if that would
  383. even cause any problems if it did... We could do some experiments
  384. to find out... and if necessary we could cache on a per interpreter
  385. basis if we had weak references... We might also look at skipping
  386. over child interpreters and going to the parent for the declaring
  387. interpreter, so we'd be sure to get the top interpreter.
  388. */
  389. This getThis( Interpreter declaringInterpreter )
  390. {
  391. if ( thisReference == null )
  392. thisReference = This.getThis( this, declaringInterpreter );
  393. return thisReference;
  394. }
  395. public BshClassManager getClassManager()
  396. {
  397. if ( classManager != null )
  398. return classManager;
  399. if ( parent != null && parent != JAVACODE )
  400. return parent.getClassManager();
  401. Interpreter.debug("No class manager namespace:" +this);
  402. return null;
  403. }
  404. void setClassManager( BshClassManager classManager ) {
  405. this.classManager = classManager;
  406. }
  407. /**
  408. Used for serialization
  409. */
  410. public void prune()
  411. {
  412. // Cut off from parent, we must have our own class manager.
  413. // Can't do this in the run() command (needs to resolve stuff)
  414. // Should we do it by default when we create a namespace will no
  415. // parent of class manager?
  416. if ( this.classManager == null )
  417. setClassManager( BshClassManager.createClassManager() );
  418. setParent( null );
  419. }
  420. public void setParent( NameSpace parent )
  421. {
  422. this.parent = parent;
  423. // If we are disconnected from root we need to handle the def imports
  424. if ( parent == null )
  425. loadDefaultImports();
  426. }
  427. /**
  428. Get the specified variable in this namespace or a parent namespace.
  429. <p>
  430. Note: this method is primarily intended for use internally. If you use
  431. this method outside of the bsh package you will have to use
  432. Primitive.unwrap() to get primitive values.
  433. @see Primitive#unwrap( Object )
  434. @return The variable value or Primitive.VOID if it is not defined.
  435. */
  436. public Object getVariable( String name )
  437. throws UtilEvalError
  438. {
  439. return getVariable( name, true );
  440. }
  441. /**
  442. Get the specified variable in this namespace.
  443. @param recurse If recurse is true then we recursively search through
  444. parent namespaces for the variable.
  445. <p>
  446. Note: this method is primarily intended for use internally. If you use
  447. this method outside of the bsh package you will have to use
  448. Primitive.unwrap() to get primitive values.
  449. @see Primitive#unwrap( Object )
  450. @return The variable value or Primitive.VOID if it is not defined.
  451. */
  452. public Object getVariable( String name, boolean recurse )
  453. throws UtilEvalError
  454. {
  455. Variable var = getVariableImpl( name, recurse );
  456. return unwrapVariable( var );
  457. }
  458. /**
  459. Locate a variable and return the Variable object with optional
  460. recursion through parent name spaces.
  461. <p/>
  462. If this namespace is static, return only static variables.
  463. @return the Variable value or null if it is not defined
  464. */
  465. protected Variable getVariableImpl( String name, boolean recurse )
  466. throws UtilEvalError
  467. {
  468. Variable var = null;
  469. if ( variables != null )
  470. var = (Variable)variables.get(name);
  471. if ( !isVisible( var ) )
  472. var = null;
  473. // try parent
  474. if ( recurse && (var == null) && (parent != null) )
  475. var = parent.getVariableImpl( name, recurse );
  476. return var;
  477. }
  478. /**
  479. This is a hook to allow ClassNameSpace to add functionality here.
  480. */
  481. protected boolean isVisible( Variable var )
  482. throws UtilEvalError
  483. {
  484. return true;
  485. }
  486. /**
  487. Unwrap a variable to its value.
  488. @return return the variable value. A null var is mapped to
  489. Primitive.VOID
  490. */
  491. protected Object unwrapVariable( Variable var )
  492. {
  493. return (var == null) ? Primitive.VOID : var.getValue();
  494. }
  495. /**
  496. @deprecated See #setTypedVariable( String, Class, Object, Modifiers )
  497. */
  498. public void setTypedVariable(
  499. String name, Class type, Object value, boolean isFinal )
  500. throws UtilEvalError
  501. {
  502. Modifiers modifiers = new Modifiers();
  503. if ( isFinal )
  504. modifiers.addModifier( Modifiers.FIELD, "final" );
  505. setTypedVariable( name, type, value, modifiers );
  506. }
  507. /**
  508. Declare a variable in the local scope and set its initial value.
  509. Value may be null to indicate that we would like the default value
  510. for the variable type. (e.g. 0 for integer types, null for object
  511. types). An existing typed variable may only be set to the same type.
  512. If an untyped variable of the same name exists it will be overridden
  513. with the new typed var.
  514. The set will perform a getAssignableForm() on the value if necessary.
  515. <p>
  516. Note: this method is primarily intended for use internally. If you use
  517. this method outside of the bsh package and wish to set variables with
  518. primitive values you will have to wrap them using bsh.Primitive.
  519. @see bsh.Primitive
  520. @param value If value is null, you'll get the default value for the type
  521. @param modifiers may be null
  522. */
  523. public void setTypedVariable(
  524. String name, Class type, Object value, Modifiers modifiers )
  525. throws UtilEvalError
  526. {
  527. checkVariableModifiers( name, modifiers );
  528. if ( variables == null )
  529. variables = new Hashtable();
  530. // Setting a typed variable is always a local operation.
  531. Variable existing = getVariableImpl( name, false/*recurse*/ );
  532. // Null value is just a declaration
  533. // Note: we might want to keep any existing value here instead of reset
  534. if ( value == null )
  535. value = Primitive.getDefaultValue( type );
  536. // does the variable already exist?
  537. if ( existing != null )
  538. {
  539. // Is it typed?
  540. if ( existing.getType() != null )
  541. {
  542. // If it had a different type throw error.
  543. // This allows declaring the same var again, but not with
  544. // a different (even if assignable) type.
  545. if ( existing.getType() != type )
  546. {
  547. throw new UtilEvalError( "Typed variable: "+name
  548. +" was previously declared with type: "
  549. + existing.getType() );
  550. } else
  551. {
  552. // else set it and return
  553. existing.setValue( value );
  554. return;
  555. }
  556. }
  557. // Careful here:
  558. // else fall through to override and install the new typed version
  559. }
  560. // Add the new typed var
  561. variables.put( name, new Variable( name, type, value, modifiers ) );
  562. }
  563. /**
  564. Dissallow static vars outside of a class
  565. @param name is here just to allow the error message to use it
  566. */
  567. protected void checkVariableModifiers( String name, Modifiers modifiers )
  568. throws UtilEvalError
  569. {
  570. if ( modifiers!=null && modifiers.hasModifier("static") )
  571. throw new UtilEvalError(
  572. "Can't declare static variable outside of class: "+name );
  573. }
  574. /**
  575. Note: this is primarily for internal use.
  576. @see Interpreter#source( String )
  577. @see Interpreter#eval( String )
  578. */
  579. public void setMethod( String name, BshMethod method )
  580. throws UtilEvalError
  581. {
  582. checkMethodModifiers( method );
  583. if ( methods == null )
  584. methods = new Hashtable();
  585. Object m = methods.get(name);
  586. if ( m == null )
  587. methods.put(name, method);
  588. else
  589. if ( m instanceof BshMethod ) {
  590. Vector v = new Vector();
  591. v.addElement( m );
  592. v.addElement( method );
  593. methods.put( name, v );
  594. } else // Vector
  595. ((Vector)m).addElement( method );
  596. }
  597. protected void checkMethodModifiers( BshMethod method )
  598. throws UtilEvalError
  599. {
  600. if ( method.hasModifier("static") )
  601. throw new UtilEvalError(
  602. "Can't declare static method outside of class: "
  603. +method.getName() );
  604. }
  605. /**
  606. Get the bsh method matching the specified signature declared in
  607. this name space or a parent.
  608. <p>
  609. Note: this method is primarily intended for use internally. If you use
  610. this method outside of the bsh package you will have to be familiar
  611. with BeanShell's use of the Primitive wrapper class.
  612. @see bsh.Primitive
  613. @return the BshMethod or null if not found
  614. */
  615. public BshMethod getMethod( String name, Class [] sig )
  616. throws UtilEvalError
  617. {
  618. BshMethod method = null;
  619. Object m = null;
  620. if ( methods != null )
  621. m = methods.get(name);
  622. // m contains either BshMethod or Vector of BshMethod
  623. if ( m != null )
  624. {
  625. // unwrap
  626. BshMethod [] ma;
  627. if ( m instanceof Vector )
  628. {
  629. Vector vm = (Vector)m;
  630. ma = new BshMethod[ vm.size() ];
  631. vm.copyInto( ma );
  632. } else
  633. ma = new BshMethod[] { (BshMethod)m };
  634. // Apply most specific signature matching
  635. Class [][] candidates = new Class[ ma.length ][];
  636. for( int i=0; i< ma.length; i++ )
  637. candidates[i] = ma[i].getArgumentTypes();
  638. int match = Reflect.findMostSpecificSignature( sig, candidates );
  639. if ( match != -1 )
  640. method = ma[match];
  641. }
  642. if ( !isVisible( method ) )
  643. method = null;
  644. // try parent
  645. if ( (method == null) && (parent != null) )
  646. return parent.getMethod( name, sig );
  647. return method;
  648. }
  649. /**
  650. This is a hook to allow ClassNameSpace to add functionality to
  651. getMethod()
  652. */
  653. protected boolean isVisible( BshMethod method )
  654. throws UtilEvalError
  655. {
  656. return true;
  657. }
  658. /**
  659. Import a class name.
  660. Subsequent imports override earlier ones
  661. */
  662. public void importClass(String name)
  663. {
  664. if ( importedClasses == null )
  665. importedClasses = new Hashtable();
  666. importedClasses.put( Name.suffix(name, 1), name );
  667. nameSpaceChanged();
  668. }
  669. /**
  670. subsequent imports override earlier ones
  671. */
  672. public void importPackage(String name)
  673. {
  674. if(importedPackages == null)
  675. importedPackages = new Vector();
  676. // If it exists, remove it and add it at the end (avoid memory leak)
  677. if ( importedPackages.contains( name ) )
  678. importedPackages.remove( name );
  679. importedPackages.addElement(name);
  680. nameSpaceChanged();
  681. }
  682. static class CommandPathEntry
  683. {
  684. String path;
  685. Class clas;
  686. CommandPathEntry(String path, Class clas)
  687. {
  688. this.path = path;
  689. this.clas = clas;
  690. }
  691. }
  692. /**
  693. Adds a URL to the command path.
  694. */
  695. public void addCommandPath(String path, Class clas)
  696. {
  697. if(importedCommands == null)
  698. importedCommands = new Vector();
  699. if(!path.endsWith("/"))
  700. path = path + "/";
  701. importedCommands.addElement(new CommandPathEntry(path,clas));
  702. }
  703. /**
  704. Remove a URLfrom the command path.
  705. */
  706. public void removeCommandPath(String path, Class clas)
  707. {
  708. if(importedCommands == null)
  709. return;
  710. for(int i = 0; i < importedCommands.size(); i++)
  711. {
  712. CommandPathEntry entry = (CommandPathEntry)importedCommands
  713. .elementAt(i);
  714. if(entry.path.equals(path) && entry.clas == clas)
  715. {
  716. importedCommands.removeElementAt(i);
  717. return;
  718. }
  719. }
  720. }
  721. /**
  722. Looks up a command.
  723. */
  724. public InputStream getCommand(String name)
  725. {
  726. if(importedCommands != null)
  727. {
  728. String extName = name + ".bsh";
  729. for(int i = importedCommands.size() - 1; i >= 0; i--)
  730. {
  731. CommandPathEntry entry = (CommandPathEntry)importedCommands
  732. .elementAt(i);
  733. InputStream in = entry.clas.getResourceAsStream(entry.path + extName);
  734. if(in != null)
  735. return in;
  736. }
  737. }
  738. if(parent == null)
  739. return null;
  740. else
  741. return parent.getCommand(name);
  742. }
  743. /**
  744. A command is a scripted method or compiled command class implementing a
  745. specified method signature. Commands are loaded from the classpath
  746. and may be imported using the importCommands() method.
  747. <p/>
  748. This method searches the imported commands packages for a script or
  749. command object corresponding to the name of the method. If it is a
  750. script the script is sourced into this namespace and the BshMethod for
  751. the requested signature is returned. If it is a compiled class the
  752. class is returned. (Compiled command classes implement static invoke()
  753. methods).
  754. <p/>
  755. The imported packages are searched in reverse order, so that later
  756. imports take priority.
  757. Currently only the first object (script or class) with the appropriate
  758. name is checked. If another, overloaded form, is located in another
  759. package it will not currently be found. This could be fixed.
  760. <p/>
  761. @return a BshMethod, Class, or null if no such command is found.
  762. @param name is the name of the desired command method
  763. @param argTypes is the signature of the desired command method.
  764. @throws UtilEvalError if loadScriptedCommand throws UtilEvalError
  765. i.e. on errors loading a script that was found
  766. */
  767. public Object getCommand(
  768. String name, Class [] argTypes, Interpreter interpreter )
  769. throws UtilEvalError
  770. {
  771. if (Interpreter.DEBUG) Interpreter.debug("getCommand: "+name);
  772. BshClassManager bcm = interpreter.getClassManager();
  773. InputStream in = getCommand( name );
  774. if ( in != null )
  775. return loadScriptedCommand(
  776. in, name, argTypes, name, interpreter );
  777. /* // Chop leading "/" and change "/" to "."
  778. String className;
  779. if ( path.equals("/") )
  780. className = name;
  781. else
  782. className = path.substring(1).replace('/','.') +"."+name;
  783. Class clas = bcm.classForName( className );
  784. if ( clas != null )
  785. return clas; */
  786. if ( parent != null )
  787. return parent.getCommand( name, argTypes, interpreter );
  788. else
  789. return null;
  790. }
  791. /**
  792. Load a command script from the input stream and find the BshMethod in
  793. the target namespace.
  794. @throws UtilEvalError on error in parsing the script or if the the
  795. method is not found after parsing the script.
  796. */
  797. /*
  798. If we want to support multiple commands in the command path we need to
  799. change this to not throw the exception.
  800. */
  801. private BshMethod loadScriptedCommand(
  802. InputStream in, String name, Class [] argTypes, String resourcePath,
  803. Interpreter interpreter )
  804. throws UtilEvalError
  805. {
  806. try {
  807. interpreter.eval(
  808. new InputStreamReader(in), this, resourcePath );
  809. } catch ( EvalError e ) {
  810. /*
  811. Here we catch any EvalError from the interpreter because we are
  812. using it as a tool to load the command, not as part of the
  813. execution path.
  814. */
  815. Interpreter.debug( e.toString() );
  816. throw new UtilEvalError(
  817. "Error loading script: "+ e.getMessage());
  818. }
  819. // Look for the loaded command
  820. BshMethod meth = getMethod( name, argTypes );
  821. /*
  822. if ( meth == null )
  823. throw new UtilEvalError("Loaded resource: " + resourcePath +
  824. "had an error or did not contain the correct method" );
  825. */
  826. return meth;
  827. }
  828. /**
  829. Helper that caches class.
  830. */
  831. private void cacheClass( String name, Class c ) {
  832. if ( classCache == null ) {
  833. classCache = new Hashtable();
  834. //cacheCount++; // debug
  835. }
  836. classCache.put(name, c);
  837. }
  838. /**
  839. Load a class through this namespace taking into account imports.
  840. The class search will proceed through the parent namespaces if
  841. necessary.
  842. @return null if not found.
  843. */
  844. public Class getClass( String name)
  845. throws UtilEvalError
  846. {
  847. Class c = getClassImpl(name);
  848. if ( c != null )
  849. return c;
  850. else
  851. // implement the recursion for getClassImpl()
  852. if ( parent != null )
  853. return parent.getClass( name );
  854. else
  855. return null;
  856. }
  857. /**
  858. Implementation of getClass()
  859. Load a class through this namespace taking into account imports.
  860. <p>
  861. Check the cache first. If an unqualified name look for imported
  862. class or package. Else try to load absolute name.
  863. <p>
  864. This method implements caching of unqualified names (normally imports).
  865. Qualified names are cached by the BshClassManager.
  866. Unqualified absolute class names (e.g. unpackaged Foo) are cached too
  867. so that we don't go searching through the imports for them each time.
  868. @return null if not found.
  869. */
  870. private Class getClassImpl( String name )
  871. throws UtilEvalError
  872. {
  873. Class c = null;
  874. // Check the cache
  875. if (classCache != null) {
  876. c = (Class)classCache.get(name);
  877. if ( c != null )
  878. return c;
  879. }
  880. // Unqualified (simple, non-compound) name
  881. boolean unqualifiedName = !Name.isCompound(name);
  882. // Unqualified name check imported
  883. if ( unqualifiedName )
  884. {
  885. // Temporary workaround for scripted classes
  886. c = getTypeForScriptedClass( name );
  887. // Try imported class
  888. if ( c == null )
  889. c = getImportedClassImpl( name );
  890. // if found as imported also cache it
  891. if ( c != null ) {
  892. cacheClass( name, c );
  893. return c;
  894. }
  895. }
  896. // Try absolute
  897. c = classForName( name );
  898. if ( c != null ) {
  899. // Cache unqualified names to prevent import check again
  900. if ( unqualifiedName )
  901. cacheClass( name, c );
  902. return c;
  903. }
  904. // Not found
  905. if ( Interpreter.DEBUG )
  906. Interpreter.debug("getClass(): " + name + " not found in "+this);
  907. return null;
  908. }
  909. /**
  910. Temporary workaround for scripted classes. Return bsh.This class.
  911. */
  912. private Class getTypeForScriptedClass( String name )
  913. throws UtilEvalError // probably shouldn't
  914. {
  915. Object obj = getVariable( name, true/*recurse*/ );
  916. if ( ClassNameSpace.isScriptedClass( obj ) )
  917. return This.class;
  918. return null;
  919. }
  920. /**
  921. Try to make the name into an imported class.
  922. This method takes into account only imports (class or package)
  923. found directly in this NameSpace (no parent chain).
  924. */
  925. private Class getImportedClassImpl( String name )
  926. throws UtilEvalError
  927. {
  928. // Try explicitly imported class, e.g. import foo.Bar;
  929. String fullname = null;
  930. if ( importedClasses != null )
  931. fullname = (String)importedClasses.get(name);
  932. if ( fullname != null )
  933. {
  934. /*
  935. Found the full name in imported classes.
  936. */
  937. // Try to make the full imported name
  938. Class clas=classForName(fullname);
  939. // Handle imported inner class case
  940. if ( clas == null )
  941. {
  942. // Imported full name wasn't found as an absolute class
  943. // If it is compound, try to resolve to an inner class.
  944. // (maybe this should happen in the BshClassManager?)
  945. if ( Name.isCompound( fullname ) )
  946. try {
  947. clas = getNameResolver( fullname ).toClass();
  948. } catch ( ClassNotFoundException e ) { /* not a class */ }
  949. else
  950. if ( Interpreter.DEBUG ) Interpreter.debug(
  951. "imported unpackaged name not found:" +fullname);
  952. // If found cache the full name in the BshClassManager
  953. if ( clas != null ) {
  954. // (should we cache info in not a class case too?)
  955. getClassManager().cacheClassInfo( fullname, clas );
  956. return clas;
  957. }
  958. } else
  959. return clas;
  960. // It was explicitly imported, but we don't know what it is.
  961. // should we throw an error here??
  962. return null;
  963. }
  964. /*
  965. Try imported packages, e.g. "import foo.bar.*;"
  966. in reverse order of import...
  967. (give later imports precedence...)
  968. */
  969. if ( importedPackages != null )
  970. for(int i=importedPackages.size()-1; i>=0; i--)
  971. {
  972. String s = ((String)importedPackages.elementAt(i)) + "." + name;
  973. Class c=classForName(s);
  974. if ( c != null )
  975. return c;
  976. }
  977. BshClassManager bcm = getClassManager();
  978. /*
  979. Try super import if available
  980. Note: we do this last to allow explicitly imported classes
  981. and packages to take priority. This method will also throw an
  982. error indicating ambiguity if it exists...
  983. */
  984. if ( bcm.hasSuperImport() )
  985. {
  986. String s = bcm.getClassNameByUnqName( name );
  987. if ( s != null )
  988. return classForName( s );
  989. }
  990. return null;
  991. }
  992. private Class classForName( String name )
  993. {
  994. return getClassManager().classForName( name );
  995. }
  996. /**
  997. Implements NameSource
  998. @return all variable and method names in this and all parent
  999. namespaces
  1000. */
  1001. public String [] getAllNames()
  1002. {
  1003. Vector vec = new Vector();
  1004. getAllNamesAux( vec );
  1005. String [] names = new String [ vec.size() ];
  1006. vec.copyInto( names );
  1007. return names;
  1008. }
  1009. /**
  1010. Helper for implementing NameSource
  1011. */
  1012. protected void getAllNamesAux( Vector vec )
  1013. {
  1014. Enumeration varNames = variables.keys();
  1015. while( varNames.hasMoreElements() )
  1016. vec.addElement( varNames.nextElement() );
  1017. Enumeration methodNames = methods.keys();
  1018. while( methodNames.hasMoreElements() )
  1019. vec.addElement( methodNames.nextElement() );
  1020. if ( parent != null )
  1021. parent.getAllNamesAux( vec );
  1022. }
  1023. Vector nameSourceListeners;
  1024. /**
  1025. Implements NameSource
  1026. Add a listener who is notified upon changes to names in this space.
  1027. */
  1028. public void addNameSourceListener( NameSource.Listener listener ) {
  1029. if ( nameSourceListeners == null )
  1030. nameSourceListeners = new Vector();
  1031. nameSourceListeners.addElement( listener );
  1032. }
  1033. /**
  1034. Perform "import *;" causing the entire classpath to be mapped.
  1035. This can take a while.
  1036. */
  1037. public void doSuperImport()
  1038. throws UtilEvalError
  1039. {
  1040. getClassManager().doSuperImport();
  1041. }
  1042. /**
  1043. Determine if the RHS object can be assigned to the LHS type:
  1044. <p/>
  1045. 1) As in a legal Java assignment (as determined by
  1046. Reflect.isJavaAssignable()) through widening or promotion
  1047. <p/>
  1048. 2) Via special BeanShell extensions like interface generation or
  1049. (gag) numeric-style promotion of primitive wrappers
  1050. (e.g. Short to Integer).
  1051. <p/>
  1052. If the assignment is legal in BeanShell return the assignable form of
  1053. the RHS.
  1054. <p/>
  1055. This method is used in many places throughout bsh including assignment
  1056. operations and method selection.
  1057. <p/>
  1058. In normal cases this functions as a simple check for assignability
  1059. and the value is returned unchanged. e.g. a String is assignable to
  1060. an Object, but no conversion is necessary. Similarly an int is
  1061. assignable to a long, so no conversion is done.
  1062. In this sense assignability is in terms of what the Java reflection API
  1063. will allow since the reflection api will do widening conversions in the
  1064. case of sets on fields and arrays. (CLARIFY: what about method args?)
  1065. <p>
  1066. The primary purpose of the "returning the assignable form"
  1067. abstraction is to allow non-standard Java assignment conversions. e.g.
  1068. wrapper conversion for boxing and unboxing. Some of this will be
  1069. considered standard in Java 1.5.
  1070. <p>
  1071. @param lhsType lhsType is a real Java class type or Java primitive TYPE
  1072. @param rhs is a value, bsh.Primitive wrapper, or Primitive.NULL.
  1073. If it is a Primitive wrapper it will be unwrapped and compared using
  1074. Reflect.isJavaAssignableFrom(). If it is Primtive.VOID an error will
  1075. occur.
  1076. @return an assignable form of the rhs, usually the original rhs if
  1077. assignable. If the rhs was a Primitive and it was assignable the
  1078. Primitive will be returned. If the Primitive needed to be auto-boxed
  1079. to a wrapper type, the wrapper type will be returned.
  1080. @throws UtilEvalError if the assignment cannot be made legally
  1081. @throws UtilEvalError on rhs of Primitive.VOID (void assignment)
  1082. @see BSHCastExpression#castObject( java.lang.Object, java.lang.Class )
  1083. */
  1084. public static Object getAssignableForm( Object rhs, Class lhsType )
  1085. throws UtilEvalError
  1086. {
  1087. /*
  1088. This is very confusing in general... need to simplify and clarify the
  1089. various places things are happening:
  1090. Reflect.isJavaAssignableFrom()
  1091. Primitive?
  1092. here...
  1093. We should probably move all of that stuff to a common area.
  1094. */
  1095. Class originalType;
  1096. if ( lhsType == null || rhs == null )
  1097. throw new InterpreterError(
  1098. "Null value in getAssignableForm: "+lhsType+", "+rhs );
  1099. if ( rhs == Primitive.VOID)
  1100. throw new UtilEvalError( "Undefined variable or class name");
  1101. // Null is assignable to reference types
  1102. if ( rhs == Primitive.NULL )
  1103. if ( !lhsType.isPrimitive() )
  1104. return rhs;
  1105. else
  1106. throw new UtilEvalError(
  1107. "Can't assign null to primitive type " + lhsType.getName());
  1108. Class rhsType;
  1109. if ( rhs instanceof Primitive )
  1110. {
  1111. // Set the rhsType to the Java primitive TYPE
  1112. rhsType = originalType = ((Primitive)rhs).getType();
  1113. // If lhsType is an object type (non primitive) try to get the rhs
  1114. // into an object type if applicable, else we have primitive to
  1115. // primitive, fall through to test
  1116. if ( !lhsType.isPrimitive() )
  1117. {
  1118. /*
  1119. If the lhs is a primitive wrapper type or Object, get the
  1120. rhs as its wrapper value so it will be tested for
  1121. assignability below. If lhs is not a wrapper type or
  1122. Object, we cannot assign a Primitive to it.
  1123. */
  1124. if ( lhsType == Boolean.class
  1125. || lhsType == Character.class
  1126. || Number.class.isAssignableFrom(lhsType)
  1127. || lhsType == Object.class )
  1128. {
  1129. // get the value as its wrapper
  1130. rhs = ((Primitive)rhs).getValue();
  1131. // type is now the wrapper class type
  1132. rhsType = rhs.getClass();
  1133. }
  1134. else
  1135. assignmentError(lhsType, originalType);
  1136. }
  1137. } else
  1138. {
  1139. // Set the rhs type
  1140. rhsType = originalType = rhs.getClass();
  1141. // Check for primitive/non-primitive mismatch
  1142. if ( lhsType.isPrimitive() )
  1143. {
  1144. // Attempt unwrapping wrapper class for assignment
  1145. // to a primitive
  1146. if (rhsType == Boolean.class)
  1147. {
  1148. rhs = new Primitive((Boolean)rhs);
  1149. rhsType = Boolean.TYPE;
  1150. }
  1151. else if (rhsType == Character.class)
  1152. {
  1153. rhs = new Primitive((Character)rhs);
  1154. rhsType = Character.TYPE;
  1155. }
  1156. else if (Number.class.isAssignableFrom(rhsType))
  1157. {
  1158. rhs = new Primitive((Number)rhs);
  1159. rhsType = ((Primitive)rhs).getType();
  1160. }
  1161. else
  1162. assignmentError( lhsType, originalType );
  1163. }
  1164. }
  1165. // This handles both class types and primitive .TYPE types
  1166. if ( Reflect.isJavaAssignableFrom( lhsType, rhsType ) )
  1167. return rhs;
  1168. /*
  1169. Begin: support for numeric style promotion of wrapper types.
  1170. Attempt conversions as defined in JLS 5.1.2
  1171. except perform them on the primitive wrapper objects.
  1172. Do we really need this??
  1173. */
  1174. if (lhsType == Short.class) {
  1175. if(rhsType == Byte.class)
  1176. return new Short(((Number)rhs).shortValue());
  1177. }
  1178. if (lhsType == Integer.class) {
  1179. if(rhsType == Byte.class || rhsType == Short.class)
  1180. return new Integer(((Number)rhs).intValue());
  1181. if(rhsType == Character.class)
  1182. return new Integer(((Number)rhs).intValue());
  1183. }
  1184. if (lhsType == Long.class) {
  1185. if(rhsType == Byte.class || rhsType == Short.class ||
  1186. rhsType == Integer.class)
  1187. return new Long(((Number)rhs).longValue());
  1188. if(rhsType == Character.class)
  1189. return new Long(((Number)rhs).longValue());
  1190. }
  1191. if (lhsType == Float.class) {
  1192. if(rhsType == Byte.class || rhsType == Short.class ||
  1193. rhsType == Integer.class || rhsType == Long.class)
  1194. return new Float(((Number)rhs).floatValue());
  1195. if(rhsType == Character.class)
  1196. return new Float(((Number)rhs).floatValue());
  1197. }
  1198. if (lhsType == Double.class) {
  1199. if(rhsType == Byte.class || rhsType == Short.class ||
  1200. rhsType == Integer.class || rhsType == Long.class ||
  1201. rhsType == Float.class)
  1202. return new Double(((Number)rhs).doubleValue());
  1203. if(rhsType == Character.class)
  1204. return new Double(((Number)rhs).doubleValue());
  1205. }
  1206. // End: support for numeric style promotion of wrapper types.
  1207. /*
  1208. Bsh This objects may be able to use the proxy mechanism to
  1209. become an LHS type.
  1210. */
  1211. if ( Capabilities.canGenerateInterfaces() &&
  1212. lhsType.isInterface() && ( rhs instanceof bsh.This ) )
  1213. {
  1214. return ((bsh.This)rhs).getInterface( lhsType );
  1215. }
  1216. assignmentError(lhsType, originalType);
  1217. return rhs;
  1218. }
  1219. private static void assignmentError(Class lhs, Class rhs)
  1220. throws UtilEvalError
  1221. {
  1222. String lhsType = Reflect.normalizeClassName(lhs);
  1223. String rhsType = Reflect.normalizeClassName(rhs);
  1224. throw new UtilEvalError ("Can't assign " + rhsType + " to " + lhsType);
  1225. }
  1226. public String toString() {
  1227. return "NameSpace: "
  1228. + ( nsName==null
  1229. ? super.toString()
  1230. : nsName + " (" + super.toString() +")" );
  1231. }
  1232. /*
  1233. For serialization.
  1234. Don't serialize non-serializable objects.
  1235. */
  1236. private synchronized void writeObject(java.io.ObjectOutputStream s)
  1237. throws IOException
  1238. {
  1239. // clear name resolvers... don't know if this is necessary.
  1240. names = null;
  1241. s.defaultWriteObject();
  1242. }
  1243. /**
  1244. Invoke a method in this namespace with the specified args and
  1245. interpreter reference. No caller information or call stack is
  1246. required. The method will appear as if called externally from Java.
  1247. <p>
  1248. @see bsh.This.invokeMethod(
  1249. String methodName, Object [] args, Interpreter interpreter,
  1250. CallStack callstack, SimpleNode callerInfo )
  1251. */
  1252. public Object invokeMethod(
  1253. String methodName, Object [] args, Interpreter interpreter )
  1254. throws EvalError
  1255. {
  1256. return invokeMethod( methodName, args, interpreter, null, null );
  1257. }
  1258. /**
  1259. This method simply delegates to This.invokeMethod();
  1260. <p>
  1261. @see bsh.This.invokeMethod(
  1262. String methodName, Object [] args, Interpreter interpreter,
  1263. CallStack callstack, SimpleNode callerInfo )
  1264. */
  1265. public Object invokeMethod(
  1266. String methodName, Object [] args, Interpreter interpreter,
  1267. CallStack callstack, SimpleNode callerInfo )
  1268. throws EvalError
  1269. {
  1270. return getThis( interpreter ).invokeMethod(
  1271. methodName, args, interpreter, callstack, callerInfo);
  1272. }
  1273. /**
  1274. Clear all cached classes and names
  1275. */
  1276. public void classLoaderChanged() {
  1277. nameSpaceChanged();
  1278. }
  1279. /**
  1280. Clear all cached classes and names
  1281. */
  1282. public void nameSpaceChanged() {
  1283. classCache = null;
  1284. names = null;
  1285. }
  1286. /**
  1287. Import standard packages. Currently:
  1288. <pre>
  1289. importClass("bsh.EvalError");
  1290. importClass("bsh.Interpreter");
  1291. importPackage("javax.swing.event");
  1292. importPackage("javax.swing");
  1293. importPackage("java.awt.event");
  1294. importPackage("java.awt");
  1295. importPackage("java.net");
  1296. importPackage("java.util");
  1297. importPackage("java.io");
  1298. importPackage("java.lang");
  1299. importCommands("/bsh/commands");
  1300. </pre>
  1301. */
  1302. public void loadDefaultImports()
  1303. {
  1304. /**
  1305. Note: the resolver looks through these in reverse order, per
  1306. precedence rules... so for max efficiency put the most common
  1307. ones later.
  1308. */
  1309. importClass("bsh.EvalError");
  1310. importClass("bsh.Interpreter");
  1311. importPackage("javax.swing.event");
  1312. importPackage("javax.swing");
  1313. importPackage("java.awt.event");
  1314. importPackage("java.awt");
  1315. importPackage("java.net");
  1316. importPackage("java.util");
  1317. importPackage("java.io");
  1318. importPackage("java.lang");
  1319. addCommandPath("/bsh/commands",getClass());
  1320. }
  1321. /**
  1322. This is the factory for Name objects which resolve names within
  1323. this namespace (e.g. toObject(), toClass(), toLHS()).
  1324. <p>
  1325. This was intended to support name resolver caching, allowing
  1326. Name objects to cache info about the resolution of names for
  1327. performance reasons. However this not proven useful yet.
  1328. <p>
  1329. We'll leave the caching as it will at least minimize Name object
  1330. creation.
  1331. <p>
  1332. (This method would be called getName() if it weren't already used for
  1333. the simple name of the NameSpace)
  1334. <p>
  1335. This method was public for a time, which was a mistake.
  1336. Use get() instead.
  1337. */
  1338. Name getNameResolver( String ambigname )
  1339. {
  1340. if ( names == null )
  1341. names = new Hashtable();
  1342. Name name = (Name)names.get( ambigname );
  1343. if ( name == null ) {
  1344. name = new Name( this, ambigname );
  1345. names.put( ambigname, name );
  1346. }
  1347. return name;
  1348. }
  1349. public int getInvocationLine() {
  1350. SimpleNode node = getNode();
  1351. if ( node != null )
  1352. return node.getLineNumber();
  1353. else
  1354. return -1;
  1355. }
  1356. public String getInvocationText() {
  1357. SimpleNode node = getNode();
  1358. if ( node != null )
  1359. return node.getText();
  1360. else
  1361. return "<invoked from Java code>";
  1362. }
  1363. /**
  1364. This is a helper method for working inside of bsh scripts and commands.
  1365. In that context it is impossible to see a ClassIdentifier object
  1366. for what it is. Attempting to access a method on a ClassIdentifier
  1367. will look like a static method invocation.
  1368. This method is in NameSpace for convenience (you don't have to import
  1369. bsh.ClassIdentifier to use it );
  1370. */
  1371. public static Class identifierToClass( ClassIdentifier ci )
  1372. {
  1373. return ci.getTargetClass();
  1374. }
  1375. /**
  1376. Clear all variables, methods, and imports from this namespace.
  1377. If this namespace is the root, it will be reset to the default
  1378. imports.
  1379. @see #loadDefaultImports()
  1380. */
  1381. public void clear()
  1382. {
  1383. variables = null;
  1384. methods = null;
  1385. importedClasses = null;
  1386. importedPackages = null;
  1387. if ( parent == null )
  1388. loadDefaultImports();
  1389. classCache = null;
  1390. names = null;
  1391. }
  1392. static class Variable implements java.io.Serializable
  1393. {
  1394. /** A null type means an untyped variable */
  1395. String name;
  1396. Class type = null;
  1397. Object value;
  1398. Modifiers modifiers;
  1399. Variable( String name, Object value, Modifiers modifiers )
  1400. throws UtilEvalError
  1401. {
  1402. this( name, null/*type*/, value, modifiers );
  1403. }
  1404. Variable( String name, Class type, Object value, Modifiers modifiers )
  1405. throws UtilEvalError
  1406. {
  1407. this.name=name;
  1408. this.type = type;
  1409. this.modifiers = modifiers;
  1410. setValue( value );
  1411. }
  1412. /**
  1413. Set the value of the typed variable.
  1414. */
  1415. void setValue( Object val ) throws UtilEvalError
  1416. {
  1417. if ( hasModifier("final") && value != null )
  1418. throw new UtilEvalError ("Final variable, can't re-assign.");
  1419. if ( type != null )
  1420. {
  1421. // Do basic assignability check / conversion
  1422. val = getAssignableForm( val, type );
  1423. /*
  1424. If we are a numeric primitive type we want to convert to
  1425. the actual numeric type of this variable. Being
  1426. assignable is not good enough.
  1427. */
  1428. if ( val instanceof Primitive && ((Primitive)val).isNumber() )
  1429. try {
  1430. val = BSHCastExpression.castPrimitive(
  1431. (Primitive)val, type );
  1432. } catch ( UtilEvalError e ) {
  1433. throw new InterpreterError(
  1434. "Assignment auto cast failed");
  1435. }
  1436. }
  1437. this.value= val;
  1438. }
  1439. Object getValue() { return value; }
  1440. /** A type of null means loosely typed variable */
  1441. Class getType() { return type; }
  1442. public boolean hasModifier( String name ) {
  1443. return modifiers != null && modifiers.hasModifier(name);
  1444. }
  1445. public String toString() {
  1446. return "Variable: "+name+", type:"+type+", value:"+value;
  1447. }
  1448. }
  1449. }