PageRenderTime 103ms CodeModel.GetById 75ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-3-pre5/bsh/BSHAllocationExpression.java

#
Java | 312 lines | 193 code | 31 blank | 88 comment | 18 complexity | d6b32f5736428b2fdbae60abc3b6716d MD5 | raw file
  1/*****************************************************************************
  2 *                                                                           *
  3 *  This file is part of the BeanShell Java Scripting distribution.          *
  4 *  Documentation and updates may be found at http://www.beanshell.org/      *
  5 *                                                                           *
  6 *  Sun Public License Notice:                                               *
  7 *                                                                           *
  8 *  The contents of this file are subject to the Sun Public License Version  *
  9 *  1.0 (the "License"); you may not use this file except in compliance with *
 10 *  the License. A copy of the License is available at http://www.sun.com    * 
 11 *                                                                           *
 12 *  The Original Code is BeanShell. The Initial Developer of the Original    *
 13 *  Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright     *
 14 *  (C) 2000.  All Rights Reserved.                                          *
 15 *                                                                           *
 16 *  GNU Public License Notice:                                               *
 17 *                                                                           *
 18 *  Alternatively, the contents of this file may be used under the terms of  *
 19 *  the GNU Lesser General Public License (the "LGPL"), in which case the    *
 20 *  provisions of LGPL are applicable instead of those above. If you wish to *
 21 *  allow use of your version of this file only under the  terms of the LGPL *
 22 *  and not to allow others to use your version of this file under the SPL,  *
 23 *  indicate your decision by deleting the provisions above and replace      *
 24 *  them with the notice and other provisions required by the LGPL.  If you  *
 25 *  do not delete the provisions above, a recipient may use your version of  *
 26 *  this file under either the SPL or the LGPL.                              *
 27 *                                                                           *
 28 *  Patrick Niemeyer (pat@pat.net)                                           *
 29 *  Author of Learning Java, O'Reilly & Associates                           *
 30 *  http://www.pat.net/~pat/                                                 *
 31 *                                                                           *
 32 *****************************************************************************/
 33
 34
 35package bsh;
 36
 37import java.lang.reflect.Array;
 38import java.lang.reflect.InvocationTargetException;
 39
 40/**
 41	New object, new array, or inner class style allocation with body.
 42*/
 43class BSHAllocationExpression extends SimpleNode
 44{
 45    BSHAllocationExpression(int id) { super(id); }
 46	private static int innerClassCount = 0;
 47	
 48    public Object 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 objectAllocation(
 74		BSHAmbiguousName nameNode, BSHArguments argumentsNode, 
 75		CallStack callstack, Interpreter interpreter 
 76	) 
 77		throws EvalError
 78    {
 79		NameSpace namespace = callstack.top();
 80
 81        Object[] 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 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 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 constructObject( 
118		Class type, Object[] args, CallStack callstack ) 
119		throws EvalError
120	{
121		Object 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 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 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 constructWithClassBody( 
171		Class type, Object[] args, BSHBlock block,
172		CallStack callstack, Interpreter interpreter ) 
173		throws EvalError
174	{
175		String name = callstack.top().getName() + "$" + (++innerClassCount);
176		Modifiers modifiers = new Modifiers();
177		modifiers.addModifier( Modifiers.CLASS, "public" );
178		Class 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 e ) {
189			if ( e instanceof InvocationTargetException )
190				e = (Exception)((InvocationTargetException)e)
191					.getTargetException();
192			throw new EvalError(
193				"Error constructing inner class instance: "+e, this, callstack
194			);
195		}
196	}
197
198	private Object constructWithInterfaceBody( 
199		Class type, Object[] 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 objectArrayAllocation(
219		BSHAmbiguousName nameNode, BSHArrayDimensions dimensionsNode, 
220		CallStack callstack, Interpreter interpreter 
221	) 
222		throws EvalError
223    {
224		NameSpace namespace = callstack.top();
225        Class 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 primitiveArrayAllocation(
234		BSHPrimitiveType typeNode, BSHArrayDimensions dimensionsNode, 
235		CallStack callstack, Interpreter interpreter 
236	) 
237		throws EvalError
238    {
239        Class type = typeNode.getType();
240
241		return arrayAllocation( dimensionsNode, type, callstack, interpreter );
242    }
243
244	private Object arrayAllocation( 
245		BSHArrayDimensions dimensionsNode, Class 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 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 arrayNewInstance( 
292		Class type, BSHArrayDimensions dimensionsNode, CallStack callstack )
293		throws EvalError
294	{
295		if ( dimensionsNode.numUndefinedDims > 0 )
296		{
297            Object 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 e1 ) {
306			throw new TargetError( e1, this, callstack );
307        } catch( Exception e ) {
308            throw new EvalError("Can't construct primitive array: " +
309                e.getMessage(), this, callstack);
310        }
311	}
312}