PageRenderTime 133ms CodeModel.GetById 30ms app.highlight 70ms RepoModel.GetById 21ms app.codeStats 0ms

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

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