KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > bsh > BSHAllocationExpression


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.lang.reflect.InvocationTargetException JavaDoc;
39
40 /**
41     New object, new array, or inner class style allocation with body.
42 */

43 class BSHAllocationExpression extends SimpleNode
44 {
45     BSHAllocationExpression(int id) { super(id); }
46     private static int innerClassCount = 0;
47     
48     public Object JavaDoc eval( CallStack callstack, Interpreter interpreter)
49         throws EvalError
50     {
51         // type is either a class name or a primitive type
52
SimpleNode type = (SimpleNode)jjtGetChild(0);
53
54         // args is either constructor arguments or array dimensions
55
SimpleNode args = (SimpleNode)jjtGetChild(1);
56
57         if ( type instanceof BSHAmbiguousName )
58         {
59             BSHAmbiguousName name = (BSHAmbiguousName)type;
60
61             if (args instanceof BSHArguments)
62                 return objectAllocation(name, (BSHArguments)args,
63                     callstack, interpreter );
64             else
65                 return objectArrayAllocation(name, (BSHArrayDimensions)args,
66                     callstack, interpreter );
67         }
68         else
69             return primitiveArrayAllocation((BSHPrimitiveType)type,
70                 (BSHArrayDimensions)args, callstack, interpreter );
71     }
72
73     private Object JavaDoc objectAllocation(
74         BSHAmbiguousName nameNode, BSHArguments argumentsNode,
75         CallStack callstack, Interpreter interpreter
76     )
77         throws EvalError
78     {
79         NameSpace namespace = callstack.top();
80
81         Object JavaDoc[] args = argumentsNode.getArguments( callstack, interpreter );
82         if ( args == null)
83             throw new EvalError( "Null args in new.", this, callstack );
84
85         // Look for scripted class object
86
Object JavaDoc obj = nameNode.toObject(
87             callstack, interpreter, false/* force class*/ );
88
89         // Try regular class
90

91         obj = nameNode.toObject(
92             callstack, interpreter, true/*force class*/ );
93
94         Class JavaDoc type = null;
95         if ( obj instanceof ClassIdentifier )
96             type = ((ClassIdentifier)obj).getTargetClass();
97         else
98             throw new EvalError(
99                 "Unknown class: "+nameNode.text, this, callstack );
100
101         // Is an inner class style object allocation
102
boolean hasBody = jjtGetNumChildren() > 2;
103
104         if ( hasBody )
105         {
106             BSHBlock body = (BSHBlock)jjtGetChild(2);
107             if ( type.isInterface() )
108                 return constructWithInterfaceBody(
109                     type, args, body, callstack, interpreter );
110             else
111                 return constructWithClassBody(
112                     type, args, body, callstack, interpreter );
113         } else
114             return constructObject( type, args, callstack );
115     }
116
117     private Object JavaDoc constructObject(
118         Class JavaDoc type, Object JavaDoc[] args, CallStack callstack )
119         throws EvalError
120     {
121         Object JavaDoc obj;
122         try {
123             obj = Reflect.constructObject( type, args );
124         } catch ( ReflectError e) {
125             throw new EvalError(
126                 "Constructor error: " + e.getMessage(), this, callstack );
127         } catch(InvocationTargetException JavaDoc e) {
128             // No need to wrap this debug
129
Interpreter.debug("The constructor threw an exception:\n\t" +
130                 e.getTargetException());
131             throw new TargetError(
132                 "Object constructor", e.getTargetException(),
133                 this, callstack, true);
134         }
135
136         String JavaDoc className = type.getName();
137         // Is it an inner class?
138
if ( className.indexOf("$") == -1 )
139             return obj;
140
141         // Temporary hack to support inner classes
142
// If the obj is a non-static inner class then import the context...
143
// This is not a sufficient emulation of inner classes.
144
// Replace this later...
145

146         // work through to class 'this'
147
This ths = callstack.top().getThis( null );
148         NameSpace instanceNameSpace =
149             Name.getClassNameSpace( ths.getNameSpace() );
150         
151         // Change the parent (which was the class static) to the class instance
152
// We really need to check if we're a static inner class here first...
153
// but for some reason Java won't show the static modifier on our
154
// fake inner classes... could generate a flag field.
155
if ( instanceNameSpace != null
156             && className.startsWith( instanceNameSpace.getName() +"$")
157         )
158         {
159             try {
160                 ClassGenerator.getClassGenerator().setInstanceNameSpaceParent(
161                     obj, className, instanceNameSpace );
162             } catch ( UtilEvalError e ) {
163                 throw e.toEvalError( this, callstack );
164             }
165         }
166
167         return obj;
168     }
169
170     private Object JavaDoc constructWithClassBody(
171         Class JavaDoc type, Object JavaDoc[] args, BSHBlock block,
172         CallStack callstack, Interpreter interpreter )
173         throws EvalError
174     {
175         String JavaDoc name = callstack.top().getName() + "$" + (++innerClassCount);
176         Modifiers modifiers = new Modifiers();
177         modifiers.addModifier( Modifiers.CLASS, "public" );
178         Class JavaDoc clas;
179         try {
180             clas = ClassGenerator.getClassGenerator() .generateClass(
181                 name, modifiers, null/*interfaces*/, type/*superClass*/,
182                 block, false/*isInterface*/, callstack, interpreter );
183         } catch ( UtilEvalError e ) {
184             throw e.toEvalError( this, callstack );
185         }
186         try {
187             return Reflect.constructObject( clas, args );
188         } catch ( Exception JavaDoc e ) {
189             if ( e instanceof InvocationTargetException JavaDoc )
190                 e = (Exception JavaDoc)((InvocationTargetException JavaDoc)e)
191                     .getTargetException();
192             throw new EvalError(
193                 "Error constructing inner class instance: "+e, this, callstack
194             );
195         }
196     }
197
198     private Object JavaDoc constructWithInterfaceBody(
199         Class JavaDoc type, Object JavaDoc[] args, BSHBlock body,
200         CallStack callstack, Interpreter interpreter )
201         throws EvalError
202     {
203         NameSpace namespace = callstack.top();
204         NameSpace local = new NameSpace(namespace, "AnonymousBlock");
205         callstack.push(local);
206         body.eval( callstack, interpreter, true/*overrideNamespace*/ );
207         callstack.pop();
208         // statical import fields from the interface so that code inside
209
// can refer to the fields directly (e.g. HEIGHT)
210
local.importStatic( type );
211         try {
212             return local.getThis(interpreter).getInterface( type );
213         } catch ( UtilEvalError e ) {
214             throw e.toEvalError( this, callstack );
215         }
216     }
217
218     private Object JavaDoc objectArrayAllocation(
219         BSHAmbiguousName nameNode, BSHArrayDimensions dimensionsNode,
220         CallStack callstack, Interpreter interpreter
221     )
222         throws EvalError
223     {
224         NameSpace namespace = callstack.top();
225         Class JavaDoc type = nameNode.toClass( callstack, interpreter );
226         if ( type == null )
227             throw new EvalError( "Class " + nameNode.getName(namespace)
228                 + " not found.", this, callstack );
229
230         return arrayAllocation( dimensionsNode, type, callstack, interpreter );
231     }
232
233     private Object JavaDoc primitiveArrayAllocation(
234         BSHPrimitiveType typeNode, BSHArrayDimensions dimensionsNode,
235         CallStack callstack, Interpreter interpreter
236     )
237         throws EvalError
238     {
239         Class JavaDoc type = typeNode.getType();
240
241         return arrayAllocation( dimensionsNode, type, callstack, interpreter );
242     }
243
244     private Object JavaDoc arrayAllocation(
245         BSHArrayDimensions dimensionsNode, Class JavaDoc type,
246         CallStack callstack, Interpreter interpreter )
247         throws EvalError
248     {
249         /*
250             dimensionsNode can return either a fully intialized array or VOID.
251             when VOID the prescribed array dimensions (defined and undefined)
252             are contained in the node.
253         */

254         Object JavaDoc result = dimensionsNode.eval( type, callstack, interpreter );
255         if ( result != Primitive.VOID )
256             return result;
257         else
258             return arrayNewInstance( type, dimensionsNode, callstack );
259     }
260
261     /**
262         Create an array of the dimensions specified in dimensionsNode.
263         dimensionsNode may contain a number of "undefined" as well as "defined"
264         dimensions.
265         <p>
266
267         Background: in Java arrays are implemented in arrays-of-arrays style
268         where, for example, a two dimensional array is a an array of arrays of
269         some base type. Each dimension-type has a Java class type associated
270         with it... so if foo = new int[5][5] then the type of foo is
271         int [][] and the type of foo[0] is int[], etc. Arrays may also be
272         specified with undefined trailing dimensions - meaning that the lower
273         order arrays are not allocated as objects. e.g.
274         if foo = new int [5][]; then foo[0] == null //true; and can later be
275         assigned with the appropriate type, e.g. foo[0] = new int[5];
276         (See Learning Java, O'Reilly & Associates more background).
277         <p>
278
279         To create an array with undefined trailing dimensions using the
280         reflection API we must use an array type to represent the lower order
281         (undefined) dimensions as the "base" type for the array creation...
282         Java will then create the correct type by adding the dimensions of the
283         base type to specified allocated dimensions yielding an array of
284         dimensionality base + specified with the base dimensons unallocated.
285         To create the "base" array type we simply create a prototype, zero
286         length in each dimension, array and use it to get its class
287         (Actually, I think there is a way we could do it with Class.forName()
288         but I don't trust this). The code is simpler than the explanation...
289         see below.
290     */

291     private Object JavaDoc arrayNewInstance(
292         Class JavaDoc type, BSHArrayDimensions dimensionsNode, CallStack callstack )
293         throws EvalError
294     {
295         if ( dimensionsNode.numUndefinedDims > 0 )
296         {
297             Object JavaDoc proto = Array.newInstance(
298                 type, new int [dimensionsNode.numUndefinedDims] ); // zeros
299
type = proto.getClass();
300         }
301
302         try {
303             return Array.newInstance(
304                 type, dimensionsNode.definedDimensions);
305         } catch( NegativeArraySizeException JavaDoc e1 ) {
306             throw new TargetError( e1, this, callstack );
307         } catch( Exception JavaDoc e ) {
308             throw new EvalError("Can't construct primitive array: " +
309                 e.getMessage(), this, callstack);
310         }
311     }
312 }
313
Popular Tags