PageRenderTime 156ms CodeModel.GetById 107ms app.highlight 39ms RepoModel.GetById 1ms app.codeStats 0ms

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

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