PageRenderTime 286ms CodeModel.GetById 251ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 0ms

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

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