PageRenderTime 2ms CodeModel.GetById 3ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-0-pre3/bsh/Name.java

#
Java | 879 lines | 428 code | 116 blank | 335 comment | 122 complexity | 0886a33fec5f8a2682bb72b1115282ba 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//System.err.println("Name call to getStaticField, class: "
348	//+clas+", field:"+field);
349				obj = Reflect.getStaticField(clas, field);
350			} catch(ReflectError e) { }
351
352			// inner class?
353			if ( obj == null ) {
354				String iclass = clas.getName()+"$"+field;
355				Class c = namespace.getClass( iclass );
356				if ( c != null )
357					obj = new ClassIdentifier(c);
358			}
359
360			if ( obj == null )
361				throw new EvalError(
362					"No static field or inner class: " + field + " of " + clas);
363
364			evalName = suffix(evalName);
365			return (evalBaseObject = obj);
366		}
367
368		/*
369			If we've fallen through here we are no longer resolving to
370			a class type.
371		*/
372		if ( forceClass )
373			throw new EvalError( value +" does not resolve to a class name." );
374
375		/* 
376			Some kind of field access?
377		*/
378
379		String field = prefix(evalName, 1);
380
381		/* length access on array? */
382		if(field.equals("length") && evalBaseObject.getClass().isArray())
383		{
384			Object obj = new Primitive(Array.getLength(evalBaseObject));
385			evalName = suffix(evalName);
386			return (evalBaseObject = obj);
387		}
388
389		/* check for field on object */
390		// Note: could eliminate throwing the exception somehow
391		try
392		{
393			Object obj = Reflect.getObjectField(evalBaseObject, field);
394			evalName = suffix(evalName);
395			return (evalBaseObject = obj);
396		}
397		catch(ReflectError e) { /* not a field */ }
398	
399		// if we get here we have failed
400		throw new EvalError(
401			"Cannot access field: " + field + ", on object: " + evalBaseObject);
402	}
403
404	/**
405		Resolve a variable relative to a This reference.
406
407		This is the general variable resolution method, accomodating special
408		fields from the This context.  Together the namespace and interpreter
409		comprise the This context.  The callstack, if available allows for the
410		this.caller construct.  
411		Optionally interpret special "magic" field names: e.g. interpreter.
412
413		@param callstack may be null, but this is only legitimate in special
414		cases where we are sure resolution will not involve this.caller.
415
416		@param namespace the namespace of the this reference (should be the
417		same as the top of the stack?
418	*/
419	Object resolveThisFieldReference( 
420		CallStack callstack, NameSpace thisNamespace, Interpreter interpreter, 
421		String varName, boolean specialFieldsVisible ) 
422		throws EvalError
423	{
424		Object obj = null;
425		// preserve the state of the last round flags until the end
426		boolean 
427			wasThis = false,		
428			wasCaller = false;
429
430		if ( varName.equals("this") ) {
431			// Hack! If the special fields are visible turn of further .this
432			// prevent user from skipping to things like super.this.caller
433			if ( specialFieldsVisible )
434				throw new EvalError("Redundant to call .this on This type");
435			obj = thisNamespace.getThis( interpreter );
436			wasThis = true;
437		} 
438
439		if ( obj == null ) {
440			if ( varName.equals("super") )
441				obj = thisNamespace.getSuper().getThis( interpreter );
442			else if ( varName.equals("global") )
443				obj = thisNamespace.getGlobal().getThis( interpreter );
444		}
445
446		if ( obj == null && specialFieldsVisible ) {
447			if (varName.equals("namespace"))
448				obj = thisNamespace;
449			else if (varName.equals("variables"))
450				obj = thisNamespace.getVariableNames();
451			else if (varName.equals("methods"))
452				obj = thisNamespace.getMethodNames();
453			else if ( varName.equals("interpreter") )
454				if ( literalThisReference )
455					obj = interpreter;
456				else
457					throw new EvalError(
458						"Can only call .interpreter on literal 'this'");
459		}
460
461		if ( obj == null && specialFieldsVisible && varName.equals("caller") )
462		{
463			if ( literalThisReference || literalCallerReference ) 
464			{
465				// get the previous context (see notes for this class)
466				if ( callstack == null )
467					throw new InterpreterError("no callstack");
468				obj = callstack.get( ++callstackDepth ).getThis( 
469					interpreter ); 
470			}
471			else
472				throw new EvalError(
473				"Can only call .caller on literal 'this' or literal '.caller'");
474
475			wasCaller = true;
476		}
477
478		if ( obj == null && specialFieldsVisible 
479			&& varName.equals("callstack") )
480		{
481			if ( literalThisReference ) 
482			{
483				// get the previous context (see notes for this class)
484				if ( callstack == null )
485					throw new InterpreterError("no callstack");
486				obj = callstack;
487			}
488			else
489				throw new EvalError(
490				"Can only call .callstack on literal 'this'");
491		}
492
493
494		if ( obj == null )
495			obj = thisNamespace.getVariable(varName);
496
497		literalThisReference = wasThis;
498		literalCallerReference = wasCaller;
499		return obj;
500	}
501
502	/**
503		Check the cache, else use toObject() to try to resolve to a class
504		identifier.  
505
506		Throws EvalError on class not found...
507	*/
508	synchronized public Class toClass() throws EvalError 
509	{
510		reset();
511
512		/* Try straightforward class name first */
513		Class clas = namespace.getClass(evalName);
514
515		if ( clas == null ) {
516			/* 
517				Try toObject() which knows how to work through inner classes
518				and see what we end up with 
519			*/
520			Object obj = null;
521			try {
522				// Null interpreter and callstack references.
523				// class only resolution should not require them.
524				obj = toObject( null, null, true );  
525			} catch ( EvalError  e ) { }; // couldn't resolve it
526		
527			if ( obj instanceof ClassIdentifier )
528				clas = ((ClassIdentifier)obj).getTargetClass();
529		}
530
531		if( clas == null )
532			throw new EvalError(
533				"Class: " + value+ " not found in namespace");
534
535		return clas;
536	}
537
538	/*
539	*/
540	synchronized public LHS toLHS( 
541		CallStack callstack, Interpreter interpreter )
542		throws EvalError
543	{
544		reset();
545
546		//Interpreter.debug("Name toLHS: "+evalName+ " isCompound = "
547			//+isCompound(evalName));
548
549		// variable
550		if(!isCompound(evalName)) {
551			//Interpreter.debug("returning simple var LHS...");
552			return new LHS(namespace,evalName);
553		}
554
555		// field
556		Object obj = null;
557		try
558		{
559			while(isCompound(evalName))
560				obj = consumeNextObjectField( callstack, interpreter, false );
561		}
562		catch( EvalError e )
563		{
564			throw new EvalError("LHS evaluation: " + e);
565		}
566
567		if ( obj == null )
568			throw new InterpreterError("internal error 2893749283");
569
570		if(obj instanceof This)
571		{
572			Interpreter.debug("found This reference evaluating LHS");
573			return new LHS(((This)obj).namespace, evalName);
574		}
575
576		if(evalName != null)
577		{
578			try
579			{
580//System.err.println("Name getLHSObjectField call obj = "
581//	+obj+", name="+evalName);
582
583				if ( obj instanceof ClassIdentifier ) 
584				{
585					Class clas = ((ClassIdentifier)obj).getTargetClass();
586					return Reflect.getLHSStaticField(clas, evalName);
587				} else
588					return Reflect.getLHSObjectField(obj, evalName);
589			} catch(ReflectError e)
590			{
591				throw new EvalError("Field access: "+e);
592			}
593		}
594
595		throw new InterpreterError("Internal error in lhs...");
596
597	/*
598	This appears to have been something very old and incorrect...
599	I don't think we need it anymore.
600
601		// We bit off our field in the very first bite
602		// have to back off and make a class out of the prefix
603		Interpreter.debug("very first field was it...");
604
605		Class clas = namespace.getClass(prefix(value));
606		if(clas == null)
607			throw new InterpreterError("internal error 238974983");
608
609		String field = suffix(value, 1);
610
611		try
612		{
613			return Reflect.getLHSStaticField(clas, field);
614		}
615		catch(ReflectError e)
616		{
617			Interpreter.debug("reflect error:" + e);
618			return null;
619		}
620	*/
621
622	}
623	
624	private BshMethod toLocalMethod( Object [] args )
625	{
626		Class [] sig = Reflect.getTypes( args );
627		return namespace.getMethod( value, sig );
628	}
629
630
631    /**
632		Invoke the method identified by name.
633
634        Name contains a wholely unqualfied messy name; resolve it to 
635		( object | static prefix ) + method name and invoke.
636
637        The interpreter is necessary to support 'this.interpreter' references
638		in the called code. (e.g. debug());
639
640        Some cases:
641
642            // dynamic
643            local();
644            myVariable.foo();
645            myVariable.bar.blah.foo();
646            // static
647            java.lang.Integer.getInteger("foo");
648
649    */
650    public Object invokeMethod(
651		Interpreter interpreter, Object[] args, CallStack callstack,
652		SimpleNode callerInfo
653	)
654        throws EvalError, ReflectError, InvocationTargetException
655    {
656        if ( !Name.isCompound(value) )
657            return invokeLocalMethod(interpreter, args, callstack, callerInfo);
658
659        // find target object
660        Name targetName = namespace.getNameResolver( Name.prefix(value));
661        String methodName = Name.suffix(value, 1);
662
663        Object obj = targetName.toObject( callstack, interpreter );
664
665		if ( obj == Primitive.VOID ) 
666			throw new EvalError( "Attempt to invoke method: "+methodName
667					+"() on undefined variable or class name: "+targetName);
668
669        // if we've got an object, invoke the method
670        if ( !(obj instanceof Name.ClassIdentifier) ) {
671
672            if (obj instanceof Primitive) {
673
674                if (obj == Primitive.NULL)
675                    throw new TargetError( "Null Pointer in Method Invocation",
676					new NullPointerException() );
677
678                // some other primitive
679                // should avoid calling methods on primitive, as we do
680                // in Name (can't treat primitive like an object message)
681                // but the hole is useful right now.
682                interpreter.error("Attempt to access method on primitive..." +
683                    " allowing bsh.Primitive to peek through for debugging");
684            }
685
686            // found an object and it's not an undefined variable
687            return Reflect.invokeObjectMethod(
688				interpreter, obj, methodName, args, callerInfo);
689        }
690
691        // try static method
692        Interpreter.debug("invokeMethod: trying static - " + targetName);
693
694        Class clas = ((Name.ClassIdentifier)obj).getTargetClass();
695        if (clas != null)
696            return Reflect.invokeStaticMethod(clas, methodName, args);
697
698        // return null; ???
699		throw new EvalError("unknown target: " + targetName);
700    }
701
702	/**
703		Invoke a locally declared method or a bsh command.
704		If the method is not already declared in the namespace then try
705		to load it as a resource from the /bsh/commands path.
706	
707		Note: instead of invoking the method directly here we should probably
708		call invokeObjectMethod passing a This reference.  That would have
709		the side effect of allowing a locally defined invoke() method to
710		handle undeclared method invocations just like in objects.  Not sure
711		if this is desirable...  It seems that if you invoke a method directly
712		in scope it should be there.
713
714		Keeping this code separate allows us to differentiate between methods
715		invoked directly in scope and those invoked through object references.
716	*/
717    public Object invokeLocalMethod( 
718		Interpreter interpreter, Object[] args, CallStack callstack,
719		SimpleNode callerInfo
720	)
721        throws EvalError, ReflectError, InvocationTargetException
722    {
723        Interpreter.debug("invoke local method: " + value);
724
725        // Check for locally declared method
726        BshMethod meth = toLocalMethod( args );
727        if ( meth != null )
728            return meth.invokeDeclaredMethod( args, interpreter, callstack, callerInfo );
729        else
730            Interpreter.debug("no locally declared method: " + value);
731
732        /*
733			Look for scripted command as resource
734		*/
735		// Why not /bsh/commands here?  Why relative to Interpreter?
736        String commandName = "commands/" + value + ".bsh";
737        InputStream in = Interpreter.class.getResourceAsStream(commandName);
738        if (in != null)
739        {
740            Interpreter.debug("loading resource: " + commandName);
741
742			if ( interpreter == null )
743				throw new InterpreterError("2234432 interpreter = null");
744
745            interpreter.eval( 
746				new InputStreamReader(in), namespace, commandName);
747
748            // try again
749            meth = toLocalMethod( args );
750            if(meth != null)
751                return meth.invokeDeclaredMethod( 
752					args, interpreter, callstack, callerInfo );
753            else
754                throw new EvalError("Loaded resource: " + commandName +
755                    "had an error or did not contain the correct method");
756        }
757
758        // check for compiled bsh command class
759        commandName = "bsh.commands." + value;
760        // create class outside of any namespace
761        Class c = BshClassManager.classForName( commandName );
762        if(c == null)
763            throw new EvalError("Command not found: " + value);
764
765        // add interpereter and namespace to args list
766        Object[] invokeArgs = new Object[args.length + 2];
767        invokeArgs[0] = interpreter;
768        invokeArgs[1] = namespace;
769        System.arraycopy(args, 0, invokeArgs, 2, args.length);
770        try
771        {
772            return Reflect.invokeStaticMethod(c, "invoke", invokeArgs);
773        }
774        catch(ReflectError e)
775        {
776            Interpreter.debug("invoke command args error:" + e);
777            // bad args
778        }
779        // try to print help
780        try
781        {
782            String s = (String)Reflect.invokeStaticMethod(c, "usage", null);
783            interpreter.println(s);
784            return Primitive.VOID;
785        }
786        catch(ReflectError e)
787        {
788            Interpreter.debug("usage threw: " + e);
789            throw new EvalError("Wrong number or type of args for command");
790        }
791    }
792
793	// Static methods that operate on compound ('.' separated) names
794
795	static boolean isCompound(String value)
796	{
797		return countParts(value) > 1;
798	}
799
800	static int countParts(String value)
801	{
802		if(value == null)
803			return 0;
804
805		int count = 0;
806		int index = -1;
807		while((index = value.indexOf('.', index + 1)) != -1)
808			count++;
809		return count + 1;
810	}
811
812	static String prefix(String value)
813	{
814		if(!isCompound(value))
815			return null;
816
817		return prefix(value, countParts(value) - 1);
818	}
819
820	static String prefix(String value, int parts)
821	{
822		if(parts < 1)
823			return null;
824
825		int count = 0;
826		int index = -1;
827
828		while(((index = value.indexOf('.', index + 1)) != -1) && (++count < parts))
829		{ ; }
830
831		return (index == -1) ? value : value.substring(0, index);
832	}
833
834	static String suffix(String name)
835	{
836		if(!isCompound(name))
837			return null;
838
839		return suffix(name, countParts(name) - 1);
840	}
841
842	public static String suffix(String value, int parts)
843	{
844		if(parts < 1)
845			return null;
846
847		int count = 0;
848		int index = value.length() + 1;
849
850		while(((index = value.lastIndexOf('.', index - 1)) != -1) && (++count < parts))
851		{ ; }
852
853		return (index == -1) ? value : value.substring(index + 1);
854	}
855
856	// end compound name routines
857
858
859	public String toString() { return value; }
860
861	static class ClassIdentifier {
862		Class clas;
863
864		public ClassIdentifier( Class clas ) {
865			this.clas = clas;
866		}
867
868		public Class getTargetClass() {
869			return clas;
870		}
871
872		public String toString() {
873			return "Class Identifier: "+clas.getName();
874		}
875	}
876
877
878}
879