PageRenderTime 160ms CodeModel.GetById 106ms app.highlight 44ms RepoModel.GetById 1ms app.codeStats 1ms

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

#
Java | 881 lines | 432 code | 116 blank | 333 comment | 122 complexity | a34f41703c51023742685007b9f9ebe2 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 localy declared bsh method.
 51	<p>
 52
 53	*** implementing ***
 54	Name objects are not to be constructed arbitrarily, but are to be 
 55	factoried by NameSpace.getNameResolver, which caches them subject to
 56	a namespace change.  This means that we can cache information about various
 57	types of resolution here.
 58*/
 59/*
 60	<strong>Implementation notes</strong>
 61	<pre>
 62
 63	*** implementing ***
 64	Name objects are not to be constructed arbitrarily, but are to be 
 65	factoried by NameSpace.getNameResolver, which caches them subject to
 66	a namespace change.  This means that we can cache information about various
 67	types of resolution here.
 68	Note that we cannot simply cache any result, we must be smart about it.
 69	For example, the value of a variable may change between calls.  So we could	
 70	cache the knowledge that it is a variable, but must re-fetch the value
 71	each time.  We could even do cool optimizations such as knowing about
 72	'final' variables ;)
 73	
 74	Questions: how much will this actually buy us?  The simple cases are not
 75	expensive here, are they?  How often are long chains of names evaluated?
 76	*** implementing ***
 77
 78	Threads:
 79	Thread safety: all of the work methods in this class must be synchronized
 80	because they share the internal intermediate evaluation state.
 81
 82	Random note:
 83	In some ways Name wants to be a private inner class of NameSpace... 
 84	However it is used elsewhere as an absraction for objects which haven't
 85	been pinned down yet.  So it is exposed.
 86
 87	Note on this.caller resolution:
 88	Although references like these do work:
 89
 90		this.caller.caller.caller...   // works
 91
 92	the equivalent using successive calls:
 93
 94		// does *not* work
 95		for( caller=this.caller; caller != null; caller = caller.caller );
 96
 97	is prohibited by the restriction that you can only call .caller on a 
 98	literal	this or caller reference.
 99	The effect is that magic caller reference only works through the current 
100	'this' reference.
101	The real explanation is that This referernces do not really know anything
102	about their depth on the call stack.  It might even be hard to define
103	such a thing...
104
105	For those purposes we provide :
106
107		this.callstack
108
109	</pre>
110*/
111class Name implements java.io.Serializable
112{
113	// These do not change during evaluation
114	public NameSpace namespace;
115	String value = null;
116	
117	// ---------------------------------------------------------
118	// The following instance variables mutate during evaluation and should
119	// be reset by the reset() method where necessary
120
121	// For evaluation
122	private String evalName;		// text left to eval
123	private Object evalBaseObject;	// base object for current eval
124
125	private int callstackDepth;		// number of times eval hit 'this.caller'
126	/** 
127		The last round consume the literal 'this' reference (not super, 
128		global, or another This type var).  We use this flag to support magic
129		variables that can only be referenced through 'this.xxx', e.g.
130		this.interpreter and this.caller;
131	*/
132	private boolean literalThisReference;
133	/** 
134		The last round consume the literal 'caller' reference (not super, 
135		global, or another This type var).  This is used to limit references
136		to .caller to only after a literal 'this' or compound '.caller'.
137	*/
138	private boolean literalCallerReference;
139
140	//  
141	//  End mutable instance variables.
142	// ---------------------------------------------------------
143
144
145	private void reset() {
146		evalName = value;
147		evalBaseObject = null;
148		callstackDepth = 0;
149		literalThisReference=false;
150		literalCallerReference=false;
151	}
152
153	/**
154		This constructor should *not* be used in general. 
155		Use NameSpace getNameResolver() which supports caching.
156		I wish I could make this "friendly" to just that class.
157		@see NameSpace getNameResolver().
158	*/
159	public Name(NameSpace namespace, String s)
160	{
161		this.namespace = namespace;
162		value = s;
163	}
164
165	/**
166		Resolve possibly complex name to an object value.
167
168		Throws EvalError on various failures.
169		A null object value is indicated by a Primitive.NULL.
170		A return type of Primitive.VOID comes from attempting to access
171		an undefined variable.
172
173		Some cases:
174			myVariable
175			myVariable.foo
176			myVariable.foo.bar
177			java.awt.GridBagConstraints.BOTH
178			my.package.stuff.MyClass.someField.someField...
179
180		Interpreter reference is necessary to allow resolution of 
181		"this.interpreter" magic field.
182		CallStack reference is necessary to allow resolution of 
183		"this.caller" magic field.
184		"this.callstack" magic field.
185	*/
186	public Object toObject( CallStack callstack, Interpreter interpreter ) 
187		throws EvalError
188	{
189		return toObject( callstack, interpreter, false );
190	}
191
192	/**
193		@see toObject()
194		@param forceClass if true then resolution will only produce a class.
195		This is necessary to disambiguate in cases where the grammar knows
196		that we want a class; where in general the var path may be taken.
197	*/
198	synchronized public Object toObject( 
199		CallStack callstack, Interpreter interpreter, boolean forceClass ) 
200		throws EvalError
201	{
202		reset();
203
204		Object obj = null;
205		while( evalName != null )
206			obj = consumeNextObjectField( callstack, interpreter, forceClass );
207
208		if ( obj == null )
209			throw new InterpreterError("null value in toObject()");
210
211		return obj;
212	}
213
214	/**
215		Get next prefixed object field component
216	*/
217	private Object consumeNextObjectField( 	
218		CallStack callstack, Interpreter interpreter, boolean forceClass ) 
219		throws EvalError
220	{
221		/*
222			Is it a simple variable name?
223			Doing this first gives the correct Java precedence for vars 
224			vs. imported class names (at least in the simple case - see
225			tests/precedence1.bsh).  It should also speed things up a bit.
226		*/
227		if ( (evalBaseObject == null && !isCompound(evalName) )
228			&& !forceClass ) 
229		{
230			Object obj = resolveThisFieldReference( 
231				callstack, namespace, interpreter, evalName, false );
232
233			if ( obj != Primitive.VOID ) {
234				evalName = null; // finished
235				return evalBaseObject = obj;  // convention
236			}
237		}
238
239		/*
240			Is it a bsh script variable reference?
241			If we're just starting the eval of name (no base object)
242			or we're evaluating relative to a This reference check.
243		*/
244		if ( ( evalBaseObject == null || evalBaseObject instanceof This  )
245			&& !forceClass ) 
246		{
247			String varName = prefix(evalName, 1);
248			Interpreter.debug("trying to resolve variable: " + varName);
249			Object obj;
250			if ( evalBaseObject == null ) {
251				obj = resolveThisFieldReference( 
252					callstack, namespace, interpreter, varName, false );
253			} else {
254				// null callstack, cannot be caller reference
255				obj = resolveThisFieldReference( 
256					callstack, ((This)evalBaseObject).namespace, 
257					interpreter, varName, true );
258			}
259
260			if ( obj != Primitive.VOID ) 
261			{
262				// Resolved the variable
263				Interpreter.debug( "resolved variable: " + varName + 
264					" in namespace: "+namespace);
265				evalName = suffix(evalName);
266				return evalBaseObject = obj;
267			}
268		}
269
270		/*
271			Is it a class name?
272			If we're just starting eval of name try to make it, else fail.
273		*/
274		if ( evalBaseObject == null ) {
275			Interpreter.debug( "trying class: " + evalName);
276			
277			/*
278				Keep adding parts until we have a class 
279			*/
280			Class clas = null;
281			int i = 1;
282			for(; i <= countParts(evalName); i++)
283				if ( (clas = namespace.getClass(prefix(evalName, i))) != null )
284					break;
285		
286			if( clas != null )  {
287				evalName = suffix(evalName, countParts(evalName) - i);
288				return ( evalBaseObject = new ClassIdentifier(clas) );
289			}
290			// not a class (or variable per above)
291			Interpreter.debug( "not a class, trying var prefix "+evalName );
292		}
293
294
295		/*
296			If we didn't find a class or variable name (or prefix) above
297			there are two possibilities:
298
299			- If we are a simple name then we can pass as a void variable 
300			reference.
301			- If we are compound then we must fail at this point.
302		*/
303		if ( evalBaseObject == null ) {
304			if( !isCompound(evalName) ) {
305				evalName = null; // finished
306				return evalBaseObject = Primitive.VOID;  // convention
307			} else
308				throw new EvalError(
309					"Class or variable not found:" + evalName);
310		}
311
312		/*
313			--------------------------------------------------------
314			After this point we're definitely evaluating relative to
315			a base object.
316			--------------------------------------------------------
317		*/
318
319		/*
320			Do some basic validity checks.
321		*/
322
323		if(evalBaseObject == Primitive.NULL) // previous round produced null
324			throw new TargetError( "Null Pointer while evaluating: "
325				+value, new NullPointerException() );
326
327		if(evalBaseObject == Primitive.VOID) // previous round produced void
328			throw new EvalError(
329				"Undefined variable or class name while evaluating: "+value);
330
331		if(evalBaseObject instanceof Primitive)
332			throw new EvalError("Can't treat primitive like an object. "+
333			"Error while evaluating: "+value);
334
335		/* 
336			Resolve relative to a class type
337			static field, inner class, ?
338		*/
339		if ( evalBaseObject instanceof ClassIdentifier ) 
340		{
341			Class clas = ((ClassIdentifier)evalBaseObject).getTargetClass();
342			String field = prefix(evalName, 1);
343
344			Object obj = null;
345			// static field?
346			try {
347				Interpreter.debug("Name call to getStaticField, class: " 
348					+clas+", field:"+field);
349				obj = Reflect.getStaticField(clas, field);
350			} catch( ReflectError e ) { 
351				Interpreter.debug("field reflect error: "+e);
352			}
353
354			// inner class?
355			if ( obj == null ) {
356				String iclass = clas.getName()+"$"+field;
357				Class c = namespace.getClass( iclass );
358				if ( c != null )
359					obj = new ClassIdentifier(c);
360			}
361
362			if ( obj == null )
363				throw new EvalError(
364					"No static field or inner class: " + field + " of " + clas);
365
366			evalName = suffix(evalName);
367			return (evalBaseObject = obj);
368		}
369
370		/*
371			If we've fallen through here we are no longer resolving to
372			a class type.
373		*/
374		if ( forceClass )
375			throw new EvalError( value +" does not resolve to a class name." );
376
377		/* 
378			Some kind of field access?
379		*/
380
381		String field = prefix(evalName, 1);
382
383		/* length access on array? */
384		if(field.equals("length") && evalBaseObject.getClass().isArray())
385		{
386			Object obj = new Primitive(Array.getLength(evalBaseObject));
387			evalName = suffix(evalName);
388			return (evalBaseObject = obj);
389		}
390
391		/* check for field on object */
392		// Note: could eliminate throwing the exception somehow
393		try
394		{
395			Object obj = Reflect.getObjectField(evalBaseObject, field);
396			evalName = suffix(evalName);
397			return (evalBaseObject = obj);
398		}
399		catch(ReflectError e) { /* not a field */ }
400	
401		// if we get here we have failed
402		throw new EvalError(
403			"Cannot access field: " + field + ", on object: " + evalBaseObject);
404	}
405
406	/**
407		Resolve a variable relative to a This reference.
408
409		This is the general variable resolution method, accomodating special
410		fields from the This context.  Together the namespace and interpreter
411		comprise the This context.  The callstack, if available allows for the
412		this.caller construct.  
413		Optionally interpret special "magic" field names: e.g. interpreter.
414
415		@param callstack may be null, but this is only legitimate in special
416		cases where we are sure resolution will not involve this.caller.
417
418		@param namespace the namespace of the this reference (should be the
419		same as the top of the stack?
420	*/
421	Object resolveThisFieldReference( 
422		CallStack callstack, NameSpace thisNamespace, Interpreter interpreter, 
423		String varName, boolean specialFieldsVisible ) 
424		throws EvalError
425	{
426		Object obj = null;
427		// preserve the state of the last round flags until the end
428		boolean 
429			wasThis = false,		
430			wasCaller = false;
431
432		if ( varName.equals("this") ) {
433			// Hack! If the special fields are visible turn of further .this
434			// prevent user from skipping to things like super.this.caller
435			if ( specialFieldsVisible )
436				throw new EvalError("Redundant to call .this on This type");
437			obj = thisNamespace.getThis( interpreter );
438			wasThis = true;
439		} 
440
441		if ( obj == null ) {
442			if ( varName.equals("super") )
443				obj = thisNamespace.getSuper().getThis( interpreter );
444			else if ( varName.equals("global") )
445				obj = thisNamespace.getGlobal().getThis( interpreter );
446		}
447
448		if ( obj == null && specialFieldsVisible ) {
449			if (varName.equals("namespace"))
450				obj = thisNamespace;
451			else if (varName.equals("variables"))
452				obj = thisNamespace.getVariableNames();
453			else if (varName.equals("methods"))
454				obj = thisNamespace.getMethodNames();
455			else if ( varName.equals("interpreter") )
456				if ( literalThisReference )
457					obj = interpreter;
458				else
459					throw new EvalError(
460						"Can only call .interpreter on literal 'this'");
461		}
462
463		if ( obj == null && specialFieldsVisible && varName.equals("caller") )
464		{
465			if ( literalThisReference || literalCallerReference ) 
466			{
467				// get the previous context (see notes for this class)
468				if ( callstack == null )
469					throw new InterpreterError("no callstack");
470				obj = callstack.get( ++callstackDepth ).getThis( 
471					interpreter ); 
472			}
473			else
474				throw new EvalError(
475				"Can only call .caller on literal 'this' or literal '.caller'");
476
477			wasCaller = true;
478		}
479
480		if ( obj == null && specialFieldsVisible 
481			&& varName.equals("callstack") )
482		{
483			if ( literalThisReference ) 
484			{
485				// get the previous context (see notes for this class)
486				if ( callstack == null )
487					throw new InterpreterError("no callstack");
488				obj = callstack;
489			}
490			else
491				throw new EvalError(
492				"Can only call .callstack on literal 'this'");
493		}
494
495
496		if ( obj == null )
497			obj = thisNamespace.getVariable(varName);
498
499		literalThisReference = wasThis;
500		literalCallerReference = wasCaller;
501		return obj;
502	}
503
504	/**
505		Check the cache, else use toObject() to try to resolve to a class
506		identifier.  
507
508		Throws EvalError on class not found...
509	*/
510	synchronized public Class toClass() throws EvalError 
511	{
512		reset();
513
514		/* Try straightforward class name first */
515		Class clas = namespace.getClass(evalName);
516
517		if ( clas == null ) {
518			/* 
519				Try toObject() which knows how to work through inner classes
520				and see what we end up with 
521			*/
522			Object obj = null;
523			try {
524				// Null interpreter and callstack references.
525				// class only resolution should not require them.
526				obj = toObject( null, null, true );  
527			} catch ( EvalError  e ) { }; // couldn't resolve it
528		
529			if ( obj instanceof ClassIdentifier )
530				clas = ((ClassIdentifier)obj).getTargetClass();
531		}
532
533		if( clas == null )
534			throw new EvalError(
535				"Class: " + value+ " not found in namespace");
536
537		return clas;
538	}
539
540	/*
541	*/
542	synchronized public LHS toLHS( 
543		CallStack callstack, Interpreter interpreter )
544		throws EvalError
545	{
546		reset();
547
548		//Interpreter.debug("Name toLHS: "+evalName+ " isCompound = "
549			//+isCompound(evalName));
550
551		// variable
552		if(!isCompound(evalName)) {
553			//Interpreter.debug("returning simple var LHS...");
554			return new LHS(namespace,evalName);
555		}
556
557		// field
558		Object obj = null;
559		try
560		{
561			while(isCompound(evalName))
562				obj = consumeNextObjectField( callstack, interpreter, false );
563		}
564		catch( EvalError e )
565		{
566			throw new EvalError("LHS evaluation: " + e);
567		}
568
569		if ( obj == null )
570			throw new InterpreterError("internal error 2893749283");
571
572		if(obj instanceof This)
573		{
574			Interpreter.debug("found This reference evaluating LHS");
575			return new LHS(((This)obj).namespace, evalName);
576		}
577
578		if(evalName != null)
579		{
580			try
581			{
582//System.err.println("Name getLHSObjectField call obj = "
583//	+obj+", name="+evalName);
584
585				if ( obj instanceof ClassIdentifier ) 
586				{
587					Class clas = ((ClassIdentifier)obj).getTargetClass();
588					return Reflect.getLHSStaticField(clas, evalName);
589				} else
590					return Reflect.getLHSObjectField(obj, evalName);
591			} catch(ReflectError e)
592			{
593				throw new EvalError("Field access: "+e);
594			}
595		}
596
597		throw new InterpreterError("Internal error in lhs...");
598
599	/*
600	This appears to have been something very old and incorrect...
601	I don't think we need it anymore.
602
603		// We bit off our field in the very first bite
604		// have to back off and make a class out of the prefix
605		Interpreter.debug("very first field was it...");
606
607		Class clas = namespace.getClass(prefix(value));
608		if(clas == null)
609			throw new InterpreterError("internal error 238974983");
610
611		String field = suffix(value, 1);
612
613		try
614		{
615			return Reflect.getLHSStaticField(clas, field);
616		}
617		catch(ReflectError e)
618		{
619			Interpreter.debug("reflect error:" + e);
620			return null;
621		}
622	*/
623
624	}
625	
626	private BshMethod toLocalMethod( Object [] args )
627	{
628		Class [] sig = Reflect.getTypes( args );
629		return namespace.getMethod( value, sig );
630	}
631
632
633    /**
634		Invoke the method identified by name.
635
636        Name contains a wholely unqualfied messy name; resolve it to 
637		( object | static prefix ) + method name and invoke.
638
639        The interpreter is necessary to support 'this.interpreter' references
640		in the called code. (e.g. debug());
641
642        Some cases:
643
644            // dynamic
645            local();
646            myVariable.foo();
647            myVariable.bar.blah.foo();
648            // static
649            java.lang.Integer.getInteger("foo");
650
651    */
652    public Object invokeMethod(
653		Interpreter interpreter, Object[] args, CallStack callstack,
654		SimpleNode callerInfo
655	)
656        throws EvalError, ReflectError, InvocationTargetException
657    {
658        if ( !Name.isCompound(value) )
659            return invokeLocalMethod(interpreter, args, callstack, callerInfo);
660
661        // find target object
662        Name targetName = namespace.getNameResolver( Name.prefix(value));
663        String methodName = Name.suffix(value, 1);
664
665        Object obj = targetName.toObject( callstack, interpreter );
666
667		if ( obj == Primitive.VOID ) 
668			throw new EvalError( "Attempt to invoke method: "+methodName
669					+"() on undefined variable or class name: "+targetName);
670
671        // if we've got an object, invoke the method
672        if ( !(obj instanceof Name.ClassIdentifier) ) {
673
674            if (obj instanceof Primitive) {
675
676                if (obj == Primitive.NULL)
677                    throw new TargetError( "Null Pointer in Method Invocation",
678					new NullPointerException() );
679
680                // some other primitive
681                // should avoid calling methods on primitive, as we do
682                // in Name (can't treat primitive like an object message)
683                // but the hole is useful right now.
684                interpreter.error("Attempt to access method on primitive..." +
685                    " allowing bsh.Primitive to peek through for debugging");
686            }
687
688            // found an object and it's not an undefined variable
689            return Reflect.invokeObjectMethod(
690				interpreter, obj, methodName, args, callerInfo);
691        }
692
693        // try static method
694        Interpreter.debug("invokeMethod: trying static - " + targetName);
695
696        Class clas = ((Name.ClassIdentifier)obj).getTargetClass();
697        if (clas != null)
698            return Reflect.invokeStaticMethod(clas, methodName, args);
699
700        // return null; ???
701		throw new EvalError("unknown target: " + targetName);
702    }
703
704	/**
705		Invoke a locally declared method or a bsh command.
706		If the method is not already declared in the namespace then try
707		to load it as a resource from the /bsh/commands path.
708	
709		Note: instead of invoking the method directly here we should probably
710		call invokeObjectMethod passing a This reference.  That would have
711		the side effect of allowing a locally defined invoke() method to
712		handle undeclared method invocations just like in objects.  Not sure
713		if this is desirable...  It seems that if you invoke a method directly
714		in scope it should be there.
715
716		Keeping this code separate allows us to differentiate between methods
717		invoked directly in scope and those invoked through object references.
718	*/
719    public Object invokeLocalMethod( 
720		Interpreter interpreter, Object[] args, CallStack callstack,
721		SimpleNode callerInfo
722	)
723        throws EvalError, ReflectError, InvocationTargetException
724    {
725        Interpreter.debug("invoke local method: " + value);
726
727        // Check for locally declared method
728        BshMethod meth = toLocalMethod( args );
729        if ( meth != null )
730            return meth.invokeDeclaredMethod( args, interpreter, callstack, callerInfo );
731        else
732            Interpreter.debug("no locally declared method: " + value);
733
734        /*
735			Look for scripted command as resource
736		*/
737		// Why not /bsh/commands here?  Why relative to Interpreter?
738        String commandName = "commands/" + value + ".bsh";
739        InputStream in = Interpreter.class.getResourceAsStream(commandName);
740        if (in != null)
741        {
742            Interpreter.debug("loading resource: " + commandName);
743
744			if ( interpreter == null )
745				throw new InterpreterError("2234432 interpreter = null");
746
747            interpreter.eval( 
748				new InputStreamReader(in), namespace, commandName);
749
750            // try again
751            meth = toLocalMethod( args );
752            if(meth != null)
753                return meth.invokeDeclaredMethod( 
754					args, interpreter, callstack, callerInfo );
755            else
756                throw new EvalError("Loaded resource: " + commandName +
757                    "had an error or did not contain the correct method");
758        }
759
760        // check for compiled bsh command class
761        commandName = "bsh.commands." + value;
762        // create class outside of any namespace
763        Class c = BshClassManager.classForName( commandName );
764        if(c == null)
765            throw new EvalError("Command not found: " + value);
766
767        // add interpereter and namespace to args list
768        Object[] invokeArgs = new Object[args.length + 2];
769        invokeArgs[0] = interpreter;
770        invokeArgs[1] = namespace;
771        System.arraycopy(args, 0, invokeArgs, 2, args.length);
772        try
773        {
774            return Reflect.invokeStaticMethod(c, "invoke", invokeArgs);
775        }
776        catch(ReflectError e)
777        {
778            Interpreter.debug("invoke command args error:" + e);
779            // bad args
780        }
781        // try to print help
782        try
783        {
784            String s = (String)Reflect.invokeStaticMethod(c, "usage", null);
785            interpreter.println(s);
786            return Primitive.VOID;
787        }
788        catch(ReflectError e)
789        {
790            Interpreter.debug("usage threw: " + e);
791            throw new EvalError("Wrong number or type of args for command");
792        }
793    }
794
795	// Static methods that operate on compound ('.' separated) names
796
797	static boolean isCompound(String value)
798	{
799		return countParts(value) > 1;
800	}
801
802	static int countParts(String value)
803	{
804		if(value == null)
805			return 0;
806
807		int count = 0;
808		int index = -1;
809		while((index = value.indexOf('.', index + 1)) != -1)
810			count++;
811		return count + 1;
812	}
813
814	static String prefix(String value)
815	{
816		if(!isCompound(value))
817			return null;
818
819		return prefix(value, countParts(value) - 1);
820	}
821
822	static String prefix(String value, int parts)
823	{
824		if(parts < 1)
825			return null;
826
827		int count = 0;
828		int index = -1;
829
830		while(((index = value.indexOf('.', index + 1)) != -1) && (++count < parts))
831		{ ; }
832
833		return (index == -1) ? value : value.substring(0, index);
834	}
835
836	static String suffix(String name)
837	{
838		if(!isCompound(name))
839			return null;
840
841		return suffix(name, countParts(name) - 1);
842	}
843
844	public static String suffix(String value, int parts)
845	{
846		if(parts < 1)
847			return null;
848
849		int count = 0;
850		int index = value.length() + 1;
851
852		while(((index = value.lastIndexOf('.', index - 1)) != -1) && (++count < parts))
853		{ ; }
854
855		return (index == -1) ? value : value.substring(index + 1);
856	}
857
858	// end compound name routines
859
860
861	public String toString() { return value; }
862
863	static class ClassIdentifier {
864		Class clas;
865
866		public ClassIdentifier( Class clas ) {
867			this.clas = clas;
868		}
869
870		public Class getTargetClass() {
871			return clas;
872		}
873
874		public String toString() {
875			return "Class Identifier: "+clas.getName();
876		}
877	}
878
879
880}
881