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