PageRenderTime 339ms CodeModel.GetById 217ms app.highlight 100ms RepoModel.GetById 1ms app.codeStats 1ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/bsh/Name.java

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