PageRenderTime 153ms CodeModel.GetById 93ms app.highlight 51ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-3-pre5/bsh/NameSpace.java

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