KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > bsh > BSHPrimarySuffix


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.util.Hashtable JavaDoc;
38 import java.lang.reflect.Array JavaDoc;
39 import java.lang.reflect.InvocationTargetException JavaDoc;
40
41 class BSHPrimarySuffix extends SimpleNode
42 {
43     public static final int
44         CLASS = 0,
45         INDEX = 1,
46         NAME = 2,
47         PROPERTY = 3;
48
49     public int operation;
50     Object JavaDoc index;
51     public String JavaDoc field;
52
53     BSHPrimarySuffix(int id) { super(id); }
54
55     /*
56         Perform a suffix operation on the given object and return the
57         new value.
58         <p>
59
60         obj will be a Node when suffix evaluation begins, allowing us to
61         interpret it contextually. (e.g. for .class) Thereafter it will be
62         an value object or LHS (as determined by toLHS).
63         <p>
64         
65         We must handle the toLHS case at each point here.
66         <p>
67     */

68     public Object JavaDoc doSuffix(
69         Object JavaDoc obj, boolean toLHS,
70         CallStack callstack, Interpreter interpreter)
71         throws EvalError
72     {
73         // Handle ".class" suffix operation
74
// Prefix must be a BSHType
75
if ( operation == CLASS )
76             if ( obj instanceof BSHType ) {
77                 if ( toLHS )
78                     throw new EvalError("Can't assign .class",
79                         this, callstack );
80                 NameSpace namespace = callstack.top();
81                 return ((BSHType)obj).getType( callstack, interpreter );
82             } else
83                 throw new EvalError(
84                     "Attempt to use .class suffix on non class.",
85                     this, callstack );
86
87         /*
88             Evaluate our prefix if it needs evaluating first.
89             If this is the first evaluation our prefix mayb be a Node
90             (directly from the PrimaryPrefix) - eval() it to an object.
91             If it's an LHS, resolve to a value.
92
93             Note: The ambiguous name construct is now necessary where the node
94             may be an ambiguous name. If this becomes common we might want to
95             make a static method nodeToObject() or something. The point is
96             that we can't just eval() - we need to direct the evaluation to
97             the context sensitive type of result; namely object, class, etc.
98         */

99         if ( obj instanceof SimpleNode )
100             if ( obj instanceof BSHAmbiguousName )
101                 obj = ((BSHAmbiguousName)obj).toObject(callstack, interpreter);
102             else
103                 obj = ((SimpleNode)obj).eval(callstack, interpreter);
104         else
105             if ( obj instanceof LHS )
106                 try {
107                     obj = ((LHS)obj).getValue();
108                 } catch ( UtilEvalError e ) {
109                     throw e.toEvalError( this, callstack );
110                 }
111
112         try
113         {
114             switch(operation)
115             {
116                 case INDEX:
117                     return doIndex( obj, toLHS, callstack, interpreter );
118
119                 case NAME:
120                     return doName( obj, toLHS, callstack, interpreter );
121
122                 case PROPERTY:
123                     return doProperty( toLHS, obj, callstack, interpreter );
124
125                 default:
126                     throw new InterpreterError( "Unknown suffix type" );
127             }
128         }
129         catch(ReflectError e)
130         {
131             throw new EvalError("reflection error: " + e, this, callstack );
132         }
133         catch(InvocationTargetException JavaDoc e)
134         {
135             throw new TargetError( "target exception", e.getTargetException(),
136                 this, callstack, true);
137         }
138     }
139
140     /*
141         Field access, .length on array, or a method invocation
142         Must handle toLHS case for each.
143     */

144     private Object JavaDoc doName(
145         Object JavaDoc obj, boolean toLHS,
146         CallStack callstack, Interpreter interpreter)
147         throws EvalError, ReflectError, InvocationTargetException JavaDoc
148     {
149         try {
150             // .length on array
151
if ( field.equals("length") && obj.getClass().isArray() )
152                 if ( toLHS )
153                     throw new EvalError(
154                         "Can't assign array length", this, callstack );
155                 else
156                     return new Primitive(Array.getLength(obj));
157             
158             // field access
159
if ( jjtGetNumChildren() == 0 )
160                 if ( toLHS )
161                     return Reflect.getLHSObjectField(obj, field);
162                 else
163                     return Reflect.getObjectFieldValue( obj, field );
164
165             // Method invocation
166
// (LHS or non LHS evaluation can both encounter method calls)
167
Object JavaDoc[] oa = ((BSHArguments)jjtGetChild(0)).getArguments(
168                 callstack, interpreter);
169
170         // TODO:
171
// Note: this try/catch block is copied from BSHMethodInvocation
172
// we need to factor out this common functionality and make sure
173
// we handle all cases ... (e.g. property style access, etc.)
174
// maybe move this to Reflect ?
175
try {
176                 return Reflect.invokeObjectMethod(
177                     obj, field, oa, interpreter, callstack, this );
178             } catch ( ReflectError e ) {
179                 throw new EvalError(
180                     "Error in method invocation: " + e.getMessage(),
181                     this, callstack );
182             } catch ( InvocationTargetException JavaDoc e )
183             {
184                 String JavaDoc msg = "Method Invocation "+field;
185                 Throwable JavaDoc te = e.getTargetException();
186
187                 /*
188                     Try to squeltch the native code stack trace if the exception
189                     was caused by a reflective call back into the bsh interpreter
190                     (e.g. eval() or source()
191                 */

192                 boolean isNative = true;
193                 if ( te instanceof EvalError )
194                     if ( te instanceof TargetError )
195                         isNative = ((TargetError)te).inNativeCode();
196                     else
197                         isNative = false;
198                 
199                 throw new TargetError( msg, te, this, callstack, isNative );
200             }
201
202         } catch ( UtilEvalError e ) {
203             throw e.toEvalError( this, callstack );
204         }
205     }
206
207     /**
208     */

209     static int getIndexAux(
210         Object JavaDoc obj, CallStack callstack, Interpreter interpreter,
211         SimpleNode callerInfo )
212         throws EvalError
213     {
214         if ( !obj.getClass().isArray() )
215             throw new EvalError("Not an array", callerInfo, callstack );
216
217         int index;
218         try {
219             Object JavaDoc indexVal =
220                 ((SimpleNode)callerInfo.jjtGetChild(0)).eval(
221                     callstack, interpreter );
222             if ( !(indexVal instanceof Primitive) )
223                 indexVal = Types.castObject(
224                     indexVal, Integer.TYPE, Types.ASSIGNMENT );
225             index = ((Primitive)indexVal).intValue();
226         } catch( UtilEvalError e ) {
227             Interpreter.debug("doIndex: "+e);
228             throw e.toEvalError(
229                 "Arrays may only be indexed by integer types.",
230                 callerInfo, callstack );
231         }
232
233         return index;
234     }
235
236     /**
237         array index.
238         Must handle toLHS case.
239     */

240     private Object JavaDoc doIndex(
241         Object JavaDoc obj, boolean toLHS,
242         CallStack callstack, Interpreter interpreter )
243         throws EvalError, ReflectError
244     {
245         int index = getIndexAux( obj, callstack, interpreter, this );
246         if ( toLHS )
247             return new LHS(obj, index);
248         else
249             try {
250                 return Reflect.getIndex(obj, index);
251             } catch ( UtilEvalError e ) {
252                 throw e.toEvalError( this, callstack );
253             }
254     }
255
256     /**
257         Property access.
258         Must handle toLHS case.
259     */

260     private Object JavaDoc doProperty( boolean toLHS,
261         Object JavaDoc obj, CallStack callstack, Interpreter interpreter )
262         throws EvalError
263     {
264         if(obj == Primitive.VOID)
265             throw new EvalError(
266             "Attempt to access property on undefined variable or class name",
267                 this, callstack );
268
269         if ( obj instanceof Primitive )
270             throw new EvalError("Attempt to access property on a primitive",
271                 this, callstack );
272
273         Object JavaDoc value = ((SimpleNode)jjtGetChild(0)).eval(
274             callstack, interpreter);
275
276         if ( !( value instanceof String JavaDoc ) )
277             throw new EvalError(
278                 "Property expression must be a String or identifier.",
279                 this, callstack );
280
281         if ( toLHS )
282             return new LHS(obj, (String JavaDoc)value);
283
284         // Property style access to Hashtable or Map
285
CollectionManager cm = CollectionManager.getCollectionManager();
286         if ( cm.isMap( obj ) )
287         {
288             Object JavaDoc val = cm.getFromMap( obj, value/*key*/ );
289             return ( val == null ? val = Primitive.NULL : val );
290         }
291
292         try {
293             return Reflect.getObjectProperty( obj, (String JavaDoc)value );
294         }
295         catch ( UtilEvalError e)
296         {
297             throw e.toEvalError( "Property: "+value, this, callstack );
298         }
299         catch (ReflectError e)
300         {
301             throw new EvalError("No such property: " + value, this, callstack );
302         }
303     }
304 }
305
306
Popular Tags