KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > bsh > Name


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
35 package bsh;
36
37 import java.lang.reflect.Array JavaDoc;
38 import java.util.Hashtable JavaDoc;
39 import java.io.*;
40 import java.lang.reflect.InvocationTargetException JavaDoc;
41 import java.lang.reflect.Method JavaDoc;
42
43 /**
44     What's in a name? I'll tell you...
45     Name() is a somewhat ambiguous thing in the grammar and so is this.
46     <p>
47     
48     This class is a name resolver. It holds a possibly ambiguous dot
49     separated name and reference to a namespace in which it allegedly lives.
50     It provides methods that attempt to resolve the name to various types of
51     entities: e.g. an Object, a Class, a declared scripted BeanShell method.
52     <p>
53
54     Name objects are created by the factory method NameSpace getNameResolver(),
55     which caches them subject to a class namespace change. This means that
56     we can cache information about various types of resolution here.
57     Currently very little if any information is cached. However with a future
58     "optimize" setting that defeats certain dynamic behavior we might be able
59     to cache quite a bit.
60 */

61 /*
62     <strong>Implementation notes</strong>
63     <pre>
64     Thread safety: all of the work methods in this class must be synchronized
65     because they share the internal intermediate evaluation state.
66
67     Note about invokeMethod(): We could simply use resolveMethod and return
68     the MethodInvoker (BshMethod or JavaMethod) however there is no easy way
69     for the AST (BSHMehodInvocation) to use this as it doesn't have type
70     information about the target to resolve overloaded methods.
71     (In Java, overloaded methods are resolved at compile time... here they
72     are, of necessity, dynamic). So it would have to do what we do here
73     and cache by signature. We now do that for the client in Reflect.java.
74
75     Note on this.caller resolution:
76     Although references like these do work:
77
78         this.caller.caller.caller... // works
79
80     the equivalent using successive calls:
81
82         // does *not* work
83         for( caller=this.caller; caller != null; caller = caller.caller );
84
85     is prohibited by the restriction that you can only call .caller on a
86     literal this or caller reference. The effect is that magic caller
87     reference only works through the current 'this' reference.
88     The real explanation is that This referernces do not really know anything
89     about their depth on the call stack. It might even be hard to define
90     such a thing...
91
92     For those purposes we provide :
93
94         this.callstack
95
96     </pre>
97 */

98 class Name implements java.io.Serializable JavaDoc
99 {
100     // These do not change during evaluation
101
public NameSpace namespace;
102     String JavaDoc value = null;
103     
104     // ---------------------------------------------------------
105
// The following instance variables mutate during evaluation and should
106
// be reset by the reset() method where necessary
107

108     // For evaluation
109
/** Remaining text to evaluate */
110     private String JavaDoc evalName;
111     /**
112         The last part of the name evaluated. This is really only used for
113         this, caller, and super resolution.
114     */

115     private String JavaDoc lastEvalName;
116     private static String JavaDoc FINISHED = null; // null evalname and we're finished
117
private Object JavaDoc evalBaseObject; // base object for current eval
118

119     private int callstackDepth; // number of times eval hit 'this.caller'
120

121     //
122
// End mutable instance variables.
123
// ---------------------------------------------------------
124

125     // Begin Cached result structures
126
// These are optimizations
127

128     // Note: it's ok to cache class resolution here because when the class
129
// space changes the namespace will discard cached names.
130

131     /**
132         The result is a class
133     */

134     Class JavaDoc asClass;
135
136     /**
137         The result is a static method call on the following class
138     */

139     Class JavaDoc classOfStaticMethod;
140
141     // End Cached result structures
142

143     private void reset() {
144         evalName = value;
145         evalBaseObject = null;
146         callstackDepth = 0;
147     }
148
149     /**
150         This constructor should *not* be used in general.
151         Use NameSpace getNameResolver() which supports caching.
152         @see NameSpace getNameResolver().
153     */

154     // I wish I could make this "friendly" to only NameSpace
155
Name( NameSpace namespace, String JavaDoc s )
156     {
157         this.namespace = namespace;
158         value = s;
159     }
160
161     /**
162         Resolve possibly complex name to an object value.
163
164         Throws EvalError on various failures.
165         A null object value is indicated by a Primitive.NULL.
166         A return type of Primitive.VOID comes from attempting to access
167         an undefined variable.
168
169         Some cases:
170             myVariable
171             myVariable.foo
172             myVariable.foo.bar
173             java.awt.GridBagConstraints.BOTH
174             my.package.stuff.MyClass.someField.someField...
175
176         Interpreter reference is necessary to allow resolution of
177         "this.interpreter" magic field.
178         CallStack reference is necessary to allow resolution of
179         "this.caller" magic field.
180         "this.callstack" magic field.
181     */

182     public Object JavaDoc toObject( CallStack callstack, Interpreter interpreter )
183         throws UtilEvalError
184     {
185         return toObject( callstack, interpreter, false );
186     }
187
188     /**
189         @see toObject()
190         @param forceClass if true then resolution will only produce a class.
191         This is necessary to disambiguate in cases where the grammar knows
192         that we want a class; where in general the var path may be taken.
193     */

194     synchronized public Object JavaDoc toObject(
195         CallStack callstack, Interpreter interpreter, boolean forceClass )
196         throws UtilEvalError
197     {
198         reset();
199
200         Object JavaDoc obj = null;
201         while( evalName != null )
202             obj = consumeNextObjectField(
203                 callstack, interpreter, forceClass, false/*autoalloc*/ );
204
205         if ( obj == null )
206             throw new InterpreterError("null value in toObject()");
207
208         return obj;
209     }
210
211     private Object JavaDoc completeRound(
212         String JavaDoc lastEvalName, String JavaDoc nextEvalName, Object JavaDoc returnObject )
213     {
214         if ( returnObject == null )
215             throw new InterpreterError("lastEvalName = "+lastEvalName);
216         this.lastEvalName = lastEvalName;
217         this.evalName = nextEvalName;
218         this.evalBaseObject = returnObject;
219         return returnObject;
220     }
221
222     /**
223         Get the next object by consuming one or more components of evalName.
224         Often this consumes just one component, but if the name is a classname
225         it will consume all of the components necessary to make the class
226         identifier.
227     */

228     private Object JavaDoc consumeNextObjectField(
229         CallStack callstack, Interpreter interpreter,
230         boolean forceClass, boolean autoAllocateThis )
231         throws UtilEvalError
232     {
233         /*
234             Is it a simple variable name?
235             Doing this first gives the correct Java precedence for vars
236             vs. imported class names (at least in the simple case - see
237             tests/precedence1.bsh). It should also speed things up a bit.
238         */

239         if ( (evalBaseObject == null && !isCompound(evalName) )
240             && !forceClass )
241         {
242             Object JavaDoc obj = resolveThisFieldReference(
243                 callstack, namespace, interpreter, evalName, false );
244
245             if ( obj != Primitive.VOID )
246                 return completeRound( evalName, FINISHED, obj );
247         }
248
249         /*
250             Is it a bsh script variable reference?
251             If we're just starting the eval of name (no base object)
252             or we're evaluating relative to a This type reference check.
253         */

254         String JavaDoc varName = prefix(evalName, 1);
255         if ( ( evalBaseObject == null || evalBaseObject instanceof This )
256             && !forceClass )
257         {
258             if ( Interpreter.DEBUG )
259                 Interpreter.debug("trying to resolve variable: " + varName);
260
261             Object JavaDoc obj;
262             // switch namespace and special var visibility
263
if ( evalBaseObject == null ) {
264                 obj = resolveThisFieldReference(
265                     callstack, namespace, interpreter, varName, false );
266             } else {
267                 obj = resolveThisFieldReference(
268                     callstack, ((This)evalBaseObject).namespace,
269                     interpreter, varName, true );
270             }
271
272             if ( obj != Primitive.VOID )
273             {
274                 // Resolved the variable
275
if ( Interpreter.DEBUG )
276                     Interpreter.debug( "resolved variable: " + varName +
277                     " in namespace: "+namespace);
278
279                 return completeRound( varName, suffix(evalName), obj );
280             }
281         }
282
283         /*
284             Is it a class name?
285             If we're just starting eval of name try to make it, else fail.
286         */

287         if ( evalBaseObject == null )
288         {
289             if ( Interpreter.DEBUG )
290                 Interpreter.debug( "trying class: " + evalName);
291             
292             /*
293                 Keep adding parts until we have a class
294             */

295             Class JavaDoc clas = null;
296             int i = 1;
297             String JavaDoc className = null;
298             for(; i <= countParts(evalName); i++)
299             {
300                 className = prefix(evalName, i);
301                 if ( (clas = namespace.getClass(className)) != null )
302                     break;
303             }
304         
305             if ( clas != null ) {
306                 return completeRound(
307                     className,
308                     suffix( evalName, countParts(evalName)-i ),
309                     new ClassIdentifier(clas)
310                 );
311             }
312             // not a class (or variable per above)
313
if ( Interpreter.DEBUG )
314                 Interpreter.debug( "not a class, trying var prefix "+evalName );
315         }
316
317         // No variable or class found in 'this' type ref.
318
// if autoAllocateThis then create one; a child 'this'.
319
if ( ( evalBaseObject == null || evalBaseObject instanceof This )
320             && !forceClass && autoAllocateThis )
321         {
322             NameSpace targetNameSpace =
323                 ( evalBaseObject == null ) ?
324                     namespace : ((This)evalBaseObject).namespace;
325             Object JavaDoc obj = new NameSpace(
326                 targetNameSpace, "auto: "+varName ).getThis( interpreter );
327             targetNameSpace.setVariable( varName, obj, false );
328             return completeRound( varName, suffix(evalName), obj );
329         }
330
331         /*
332             If we didn't find a class or variable name (or prefix) above
333             there are two possibilities:
334
335             - If we are a simple name then we can pass as a void variable
336             reference.
337             - If we are compound then we must fail at this point.
338         */

339         if ( evalBaseObject == null ) {
340             if ( !isCompound(evalName) ) {
341                 return completeRound( evalName, FINISHED, Primitive.VOID );
342             } else
343                 throw new UtilEvalError(
344                     "Class or variable not found: " + evalName);
345         }
346
347         /*
348             --------------------------------------------------------
349             After this point we're definitely evaluating relative to
350             a base object.
351             --------------------------------------------------------
352         */

353
354         /*
355             Do some basic validity checks.
356         */

357
358         if ( evalBaseObject == Primitive.NULL) // previous round produced null
359
throw new UtilTargetError( new NullPointerException JavaDoc(
360                 "Null Pointer while evaluating: " +value ) );
361
362         if ( evalBaseObject == Primitive.VOID) // previous round produced void
363
throw new UtilEvalError(
364                 "Undefined variable or class name while evaluating: "+value);
365
366         if ( evalBaseObject instanceof Primitive)
367             throw new UtilEvalError("Can't treat primitive like an object. "+
368             "Error while evaluating: "+value);
369
370         /*
371             Resolve relative to a class type
372             static field, inner class, ?
373         */

374         if ( evalBaseObject instanceof ClassIdentifier )
375         {
376             Class JavaDoc clas = ((ClassIdentifier)evalBaseObject).getTargetClass();
377             String JavaDoc field = prefix(evalName, 1);
378
379             // Class qualified 'this' reference from inner class.
380
// e.g. 'MyOuterClass.this'
381
if ( field.equals("this") )
382             {
383                 // find the enclosing class instance space of the class name
384
NameSpace ns = namespace;
385                 while ( ns != null )
386                 {
387                     // getClassInstance() throws exception if not there
388
if ( ns.classInstance != null
389                         && ns.classInstance.getClass() == clas
390                     )
391                         return completeRound(
392                             field, suffix(evalName), ns.classInstance );
393                     ns=ns.getParent();
394                 }
395                 throw new UtilEvalError(
396                     "Can't find enclosing 'this' instance of class: "+clas);
397             }
398
399             Object JavaDoc obj = null;
400             // static field?
401
try {
402                 if ( Interpreter.DEBUG )
403                     Interpreter.debug("Name call to getStaticFieldValue, class: "
404                         +clas+", field:"+field);
405                 obj = Reflect.getStaticFieldValue(clas, field);
406             } catch( ReflectError e ) {
407                 if ( Interpreter.DEBUG )
408                     Interpreter.debug("field reflect error: "+e);
409             }
410
411             // inner class?
412
if ( obj == null ) {
413                 String JavaDoc iclass = clas.getName()+"$"+field;
414                 Class JavaDoc c = namespace.getClass( iclass );
415                 if ( c != null )
416                     obj = new ClassIdentifier(c);
417             }
418
419             if ( obj == null )
420                 throw new UtilEvalError(
421                     "No static field or inner class: "
422                     + field + " of " + clas );
423
424             return completeRound( field, suffix(evalName), obj );
425         }
426
427         /*
428             If we've fallen through here we are no longer resolving to
429             a class type.
430         */

431         if ( forceClass )
432             throw new UtilEvalError(
433                 value +" does not resolve to a class name." );
434
435         /*
436             Some kind of field access?
437         */

438
439         String JavaDoc field = prefix(evalName, 1);
440
441         // length access on array?
442
if ( field.equals("length") && evalBaseObject.getClass().isArray() )
443         {
444             Object JavaDoc obj = new Primitive(Array.getLength(evalBaseObject));
445             return completeRound( field, suffix(evalName), obj );
446         }
447
448         // Check for field on object
449
// Note: could eliminate throwing the exception somehow
450
try {
451             Object JavaDoc obj = Reflect.getObjectFieldValue(evalBaseObject, field);
452             return completeRound( field, suffix(evalName), obj );
453         } catch(ReflectError e) { /* not a field */ }
454     
455         // if we get here we have failed
456
throw new UtilEvalError(
457             "Cannot access field: " + field + ", on object: " + evalBaseObject);
458     }
459
460     /**
461         Resolve a variable relative to a This reference.
462
463         This is the general variable resolution method, accomodating special
464         fields from the This context. Together the namespace and interpreter
465         comprise the This context. The callstack, if available allows for the
466         this.caller construct.
467         Optionally interpret special "magic" field names: e.g. interpreter.
468         <p/>
469
470         @param callstack may be null, but this is only legitimate in special
471         cases where we are sure resolution will not involve this.caller.
472
473         @param namespace the namespace of the this reference (should be the
474         same as the top of the stack?
475     */

476     Object JavaDoc resolveThisFieldReference(
477         CallStack callstack, NameSpace thisNameSpace, Interpreter interpreter,
478         String JavaDoc varName, boolean specialFieldsVisible )
479         throws UtilEvalError
480     {
481         if ( varName.equals("this") )
482         {
483             /*
484                 Somewhat of a hack. If the special fields are visible (we're
485                 operating relative to a 'this' type already) dissallow further
486                 .this references to prevent user from skipping to things like
487                 super.this.caller
488             */

489             if ( specialFieldsVisible )
490                 throw new UtilEvalError("Redundant to call .this on This type");
491
492             // Allow getThis() to work through BlockNameSpace to the method
493
// namespace
494
// XXX re-eval this... do we need it?
495
This ths = thisNameSpace.getThis( interpreter );
496             thisNameSpace= ths.getNameSpace();
497             Object JavaDoc result = ths;
498
499             NameSpace classNameSpace = getClassNameSpace( thisNameSpace );
500             if ( classNameSpace != null )
501             {
502                 if ( isCompound( evalName ) )
503                     result = classNameSpace.getThis( interpreter );
504                 else
505                     result = classNameSpace.getClassInstance();
506             }
507
508             return result;
509         }
510
511         /*
512             Some duplication for "super". See notes for "this" above
513             If we're in an enclsing class instance and have a superclass
514             instance our super is the superclass instance.
515         */

516         if ( varName.equals("super") )
517         {
518             //if ( specialFieldsVisible )
519
//throw new UtilEvalError("Redundant to call .this on This type");
520

521             // Allow getSuper() to through BlockNameSpace to the method's super
522
This ths = thisNameSpace.getSuper( interpreter );
523             thisNameSpace = ths.getNameSpace();
524             // super is now the closure's super or class instance
525

526     // XXXX re-evaluate this
527
// can getSuper work by itself now?
528
// If we're a class instance and the parent is also a class instance
529
// then super means our parent.
530
if (
531                 thisNameSpace.getParent() != null
532                 && thisNameSpace.getParent().isClass
533             )
534                 ths = thisNameSpace.getParent().getThis( interpreter );
535
536             return ths;
537         }
538
539         Object JavaDoc obj = null;
540
541         if ( varName.equals("global") )
542             obj = thisNameSpace.getGlobal( interpreter );
543
544         if ( obj == null && specialFieldsVisible )
545         {
546             if (varName.equals("namespace"))
547                 obj = thisNameSpace;
548             else if (varName.equals("variables"))
549                 obj = thisNameSpace.getVariableNames();
550             else if (varName.equals("methods"))
551                 obj = thisNameSpace.getMethodNames();
552             else if ( varName.equals("interpreter") )
553                 if ( lastEvalName.equals("this") )
554                     obj = interpreter;
555                 else
556                     throw new UtilEvalError(
557                         "Can only call .interpreter on literal 'this'");
558         }
559
560         if ( obj == null && specialFieldsVisible && varName.equals("caller") )
561         {
562             if ( lastEvalName.equals("this") || lastEvalName.equals("caller") )
563             {
564                 // get the previous context (see notes for this class)
565
if ( callstack == null )
566                     throw new InterpreterError("no callstack");
567                 obj = callstack.get( ++callstackDepth ).getThis(
568                     interpreter );
569             }
570             else
571                 throw new UtilEvalError(
572                 "Can only call .caller on literal 'this' or literal '.caller'");
573
574             // early return
575
return obj;
576         }
577
578         if ( obj == null && specialFieldsVisible
579             && varName.equals("callstack") )
580         {
581             if ( lastEvalName.equals("this") )
582             {
583                 // get the previous context (see notes for this class)
584
if ( callstack == null )
585                     throw new InterpreterError("no callstack");
586                 obj = callstack;
587             }
588             else
589                 throw new UtilEvalError(
590                 "Can only call .callstack on literal 'this'");
591         }
592
593
594         if ( obj == null )
595             obj = thisNameSpace.getVariable(varName);
596
597         if ( obj == null )
598             throw new InterpreterError("null this field ref:"+varName);
599
600         return obj;
601     }
602
603     /**
604         @return the enclosing class body namespace or null if not in a class.
605     */

606     static NameSpace getClassNameSpace( NameSpace thisNameSpace )
607     {
608         // is a class instance
609
//if ( thisNameSpace.classInstance != null )
610
if ( thisNameSpace.isClass )
611             return thisNameSpace;
612
613         if ( thisNameSpace.isMethod
614             && thisNameSpace.getParent() != null
615             //&& thisNameSpace.getParent().classInstance != null
616
&& thisNameSpace.getParent().isClass
617         )
618             return thisNameSpace.getParent();
619
620         return null;
621     }
622
623     /**
624         Check the cache, else use toObject() to try to resolve to a class
625         identifier.
626
627         @throws ClassNotFoundException on class not found.
628         @throws ClassPathException (type of EvalError) on special case of
629         ambiguous unqualified name after super import.
630     */

631     synchronized public Class JavaDoc toClass()
632         throws ClassNotFoundException JavaDoc, UtilEvalError
633     {
634         if ( asClass != null )
635             return asClass;
636
637         reset();
638
639         // "var" means untyped, return null class
640
if ( evalName.equals("var") )
641             return asClass = null;
642
643         /* Try straightforward class name first */
644         Class JavaDoc clas = namespace.getClass( evalName );
645
646         if ( clas == null )
647         {
648             /*
649                 Try toObject() which knows how to work through inner classes
650                 and see what we end up with
651             */

652             Object JavaDoc obj = null;
653             try {
654                 // Null interpreter and callstack references.
655
// class only resolution should not require them.
656
obj = toObject( null, null, true );
657             } catch ( UtilEvalError e ) { }; // couldn't resolve it
658

659             if ( obj instanceof ClassIdentifier )
660                 clas = ((ClassIdentifier)obj).getTargetClass();
661         }
662
663         if ( clas == null )
664             throw new ClassNotFoundException JavaDoc(
665                 "Class: " + value+ " not found in namespace");
666
667         asClass = clas;
668         return asClass;
669     }
670
671     /*
672     */

673     synchronized public LHS toLHS(
674         CallStack callstack, Interpreter interpreter )
675         throws UtilEvalError
676     {
677         // Should clean this up to a single return statement
678
reset();
679         LHS lhs;
680
681         // Simple (non-compound) variable assignment e.g. x=5;
682
if ( !isCompound(evalName) )
683         {
684             if ( evalName.equals("this") )
685                 throw new UtilEvalError("Can't assign to 'this'." );
686
687             // Interpreter.debug("Simple var LHS...");
688
lhs = new LHS( namespace, evalName, false/*bubble up if allowed*/);
689             return lhs;
690         }
691
692         // Field e.g. foo.bar=5;
693
Object JavaDoc obj = null;
694         try {
695             while( evalName != null && isCompound( evalName ) )
696             {
697                 obj = consumeNextObjectField( callstack, interpreter,
698                     false/*forcclass*/, true/*autoallocthis*/ );
699             }
700         }
701         catch( UtilEvalError e ) {
702             throw new UtilEvalError( "LHS evaluation: " + e.getMessage() );
703         }
704
705         // Finished eval and its a class.
706
if ( evalName == null && obj instanceof ClassIdentifier )
707             throw new UtilEvalError("Can't assign to class: " + value );
708
709         if ( obj == null )
710             throw new UtilEvalError("Error in LHS: " + value );
711
712         // e.g. this.x=5; or someThisType.x=5;
713
if ( obj instanceof This )
714         {
715             // dissallow assignment to magic fields
716
if (
717                 evalName.equals("namespace")
718                 || evalName.equals("variables")
719                 || evalName.equals("methods")
720                 || evalName.equals("caller")
721             )
722                 throw new UtilEvalError(
723                     "Can't assign to special variable: "+evalName );
724
725             Interpreter.debug("found This reference evaluating LHS");
726             /*
727                 If this was a literal "super" reference then we allow recursion
728                 in setting the variable to get the normal effect of finding the
729                 nearest definition starting at the super scope. On any other
730                 resolution qualified by a 'this' type reference we want to set
731                 the variable directly in that scope. e.g. this.x=5; or
732                 someThisType.x=5;
733                 
734                 In the old scoping rules super didn't do this.
735             */

736             boolean localVar = !lastEvalName.equals("super");
737             return new LHS( ((This)obj).namespace, evalName, localVar );
738         }
739
740         if ( evalName != null )
741         {
742             try {
743                 if ( obj instanceof ClassIdentifier )
744                 {
745                     Class JavaDoc clas = ((ClassIdentifier)obj).getTargetClass();
746                     lhs = Reflect.getLHSStaticField(clas, evalName);
747                     return lhs;
748                 } else {
749                     lhs = Reflect.getLHSObjectField(obj, evalName);
750                     return lhs;
751                 }
752             } catch(ReflectError e) {
753                 throw new UtilEvalError("Field access: "+e);
754             }
755         }
756
757         throw new InterpreterError("Internal error in lhs...");
758     }
759     
760     /**
761         Invoke the method identified by this name.
762         Performs caching of method resolution using SignatureKey.
763         <p>
764
765         Name contains a wholely unqualfied messy name; resolve it to
766         ( object | static prefix ) + method name and invoke.
767         <p>
768
769         The interpreter is necessary to support 'this.interpreter' references
770         in the called code. (e.g. debug());
771         <p>
772
773         <pre>
774         Some cases:
775
776             // dynamic
777             local();
778             myVariable.foo();
779             myVariable.bar.blah.foo();
780             // static
781             java.lang.Integer.getInteger("foo");
782         </pre>
783     */

784     public Object JavaDoc invokeMethod(
785         Interpreter interpreter, Object JavaDoc[] args, CallStack callstack,
786         SimpleNode callerInfo
787     )
788         throws UtilEvalError, EvalError, ReflectError, InvocationTargetException JavaDoc
789     {
790         String JavaDoc methodName = Name.suffix(value, 1);
791         BshClassManager bcm = interpreter.getClassManager();
792         NameSpace namespace = callstack.top();
793
794         // Optimization - If classOfStaticMethod is set then we have already
795
// been here and determined that this is a static method invocation.
796
// Note: maybe factor this out with path below... clean up.
797
if ( classOfStaticMethod != null )
798         {
799             return Reflect.invokeStaticMethod(
800                 bcm, classOfStaticMethod, methodName, args );
801         }
802
803         if ( !Name.isCompound(value) )
804             return invokeLocalMethod(
805                 interpreter, args, callstack, callerInfo );
806
807         // Note: if we want methods declared inside blocks to be accessible via
808
// this.methodname() inside the block we could handle it here as a
809
// special case. See also resolveThisFieldReference() special handling
810
// for BlockNameSpace case. They currently work via the direct name
811
// e.g. methodName().
812

813         String JavaDoc prefix = Name.prefix(value);
814
815         // Superclass method invocation? (e.g. super.foo())
816
if ( prefix.equals("super") && Name.countParts(value) == 2 )
817         {
818             // Allow getThis() to work through block namespaces first
819
This ths = namespace.getThis( interpreter );
820             NameSpace thisNameSpace = ths.getNameSpace();
821             NameSpace classNameSpace = getClassNameSpace( thisNameSpace );
822             if ( classNameSpace != null )
823             {
824                 Object JavaDoc instance = classNameSpace.getClassInstance();
825                 return ClassGenerator.getClassGenerator()
826                     .invokeSuperclassMethod( bcm, instance, methodName, args );
827             }
828         }
829
830         // Find target object or class identifier
831
Name targetName = namespace.getNameResolver( prefix );
832         Object JavaDoc obj = targetName.toObject( callstack, interpreter );
833
834         if ( obj == Primitive.VOID )
835             throw new UtilEvalError( "Attempt to resolve method: "+methodName
836                     +"() on undefined variable or class name: "+targetName);
837
838         // if we've got an object, resolve the method
839
if ( !(obj instanceof ClassIdentifier) ) {
840
841             if (obj instanceof Primitive) {
842
843                 if (obj == Primitive.NULL)
844                     throw new UtilTargetError( new NullPointerException JavaDoc(
845                         "Null Pointer in Method Invocation" ) );
846
847                 // some other primitive
848
// should avoid calling methods on primitive, as we do
849
// in Name (can't treat primitive like an object message)
850
// but the hole is useful right now.
851
if ( Interpreter.DEBUG )
852                     interpreter.debug(
853                     "Attempt to access method on primitive..."
854                     + " allowing bsh.Primitive to peek through for debugging");
855             }
856
857             // found an object and it's not an undefined variable
858
return Reflect.invokeObjectMethod(
859                 obj, methodName, args, interpreter, callstack, callerInfo );
860         }
861
862         // It's a class
863

864         // try static method
865
if ( Interpreter.DEBUG )
866             Interpreter.debug("invokeMethod: trying static - " + targetName);
867
868         Class JavaDoc clas = ((ClassIdentifier)obj).getTargetClass();
869
870         // cache the fact that this is a static method invocation on this class
871
classOfStaticMethod = clas;
872         
873         if ( clas != null )
874             return Reflect.invokeStaticMethod( bcm, clas, methodName, args );
875
876         // return null; ???
877
throw new UtilEvalError("invokeMethod: unknown target: " + targetName);
878     }
879
880     /**
881         Invoke a locally declared method or a bsh command.
882         If the method is not already declared in the namespace then try
883         to load it as a resource from the imported command path (e.g.
884         /bsh/commands)
885     */

886     /*
887         Note: the bsh command code should probably not be here... we need to
888         scope it by the namespace that imported the command... so it probably
889         needs to be integrated into NameSpace.
890     */

891     private Object JavaDoc invokeLocalMethod(
892         Interpreter interpreter, Object JavaDoc[] args, CallStack callstack,
893         SimpleNode callerInfo
894     )
895         throws EvalError/*, ReflectError, InvocationTargetException*/
896     {
897         if ( Interpreter.DEBUG )
898             Interpreter.debug( "invokeLocalMethod: " + value );
899         if ( interpreter == null )
900             throw new InterpreterError(
901                 "invokeLocalMethod: interpreter = null");
902
903         String JavaDoc commandName = value;
904         Class JavaDoc [] argTypes = Types.getTypes( args );
905
906         // Check for existing method
907
BshMethod meth = null;
908         try {
909             meth = namespace.getMethod( commandName, argTypes );
910         } catch ( UtilEvalError e ) {
911             throw e.toEvalError(
912                 "Local method invocation", callerInfo, callstack );
913         }
914
915         // If defined, invoke it
916
if ( meth != null )
917             return meth.invoke( args, interpreter, callstack, callerInfo );
918
919         BshClassManager bcm = interpreter.getClassManager();
920
921         // Look for a BeanShell command
922

923         Object JavaDoc commandObject;
924         try {
925             commandObject = namespace.getCommand(
926                 commandName, argTypes, interpreter );
927         } catch ( UtilEvalError e ) {
928             throw e.toEvalError("Error loading command: ",
929                 callerInfo, callstack );
930         }
931
932         // should try to print usage here if nothing found
933
if ( commandObject == null )
934         {
935             // Look for a default invoke() handler method in the namespace
936
// Note: this code duplicates that in This.java... should it?
937
// Call on 'This' can never be a command
938
BshMethod invokeMethod = null;
939             try {
940                 invokeMethod = namespace.getMethod(
941                     "invoke", new Class JavaDoc [] { null, null } );
942             } catch ( UtilEvalError e ) {
943                 throw e.toEvalError(
944                     "Local method invocation", callerInfo, callstack );
945             }
946
947             if ( invokeMethod != null )
948                 return invokeMethod.invoke(
949                     new Object JavaDoc [] { commandName, args },
950                     interpreter, callstack, callerInfo );
951
952             throw new EvalError( "Command not found: "
953                 +StringUtil.methodString( commandName, argTypes ),
954                 callerInfo, callstack );
955         }
956
957         if ( commandObject instanceof BshMethod )
958             return ((BshMethod)commandObject).invoke(
959                 args, interpreter, callstack, callerInfo );
960
961         if ( commandObject instanceof Class JavaDoc )
962             try {
963                 return Reflect.invokeCompiledCommand(
964                     ((Class JavaDoc)commandObject), args, interpreter, callstack );
965             } catch ( UtilEvalError e ) {
966                 throw e.toEvalError("Error invoking compiled command: ",
967                 callerInfo, callstack );
968             }
969
970         throw new InterpreterError("invalid command type");
971     }
972
973 /*
974     private String getHelp( String name )
975         throws UtilEvalError
976     {
977         try {
978             // should check for null namespace here
979             return get( "bsh.help."+name, null/interpreter/ );
980         } catch ( Exception e ) {
981             return "usage: "+name;
982         }
983     }
984
985     private String getHelp( Class commandClass )
986         throws UtilEvalError
987     {
988         try {
989             return (String)Reflect.invokeStaticMethod(
990                 null/bcm/, commandClass, "usage", null );
991         } catch( Exception e )
992             return "usage: "+name;
993         }
994     }
995 */

996
997     // Static methods that operate on compound ('.' separated) names
998
// I guess we could move these to StringUtil someday
999

1000    public static boolean isCompound(String JavaDoc value)
1001    {
1002        return value.indexOf('.') != -1 ;
1003        //return countParts(value) > 1;
1004
}
1005
1006    static int countParts(String JavaDoc value)
1007    {
1008        if(value == null)
1009            return 0;
1010
1011        int count = 0;
1012        int index = -1;
1013        while((index = value.indexOf('.', index + 1)) != -1)
1014            count++;
1015        return count + 1;
1016    }
1017
1018    static String JavaDoc prefix(String JavaDoc value)
1019    {
1020        if(!isCompound(value))
1021            return null;
1022
1023        return prefix(value, countParts(value) - 1);
1024    }
1025
1026    static String JavaDoc prefix(String JavaDoc value, int parts)
1027    {
1028        if (parts < 1 )
1029            return null;
1030
1031        int count = 0;
1032        int index = -1;
1033
1034        while( ((index = value.indexOf('.', index + 1)) != -1)
1035            && (++count < parts) )
1036        { ; }
1037
1038        return (index == -1) ? value : value.substring(0, index);
1039    }
1040
1041    static String JavaDoc suffix(String JavaDoc name)
1042    {
1043        if(!isCompound(name))
1044            return null;
1045
1046        return suffix(name, countParts(name) - 1);
1047    }
1048
1049    public static String JavaDoc suffix(String JavaDoc value, int parts)
1050    {
1051        if (parts < 1)
1052            return null;
1053
1054        int count = 0;
1055        int index = value.length() + 1;
1056
1057        while ( ((index = value.lastIndexOf('.', index - 1)) != -1)
1058            && (++count < parts) );
1059
1060        return (index == -1) ? value : value.substring(index + 1);
1061    }
1062
1063    // end compound name routines
1064

1065
1066    public String JavaDoc toString() { return value; }
1067
1068}
1069
1070
Popular Tags