PageRenderTime 65ms CodeModel.GetById 30ms app.highlight 19ms RepoModel.GetById 11ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/bsh/BSHPrimarySuffix.java

#
Java | 304 lines | 185 code | 31 blank | 88 comment | 25 complexity | 42260f18237ff7fe6889304cb481f264 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 org.gjt.sp.jedit.bsh;
 36
 37import java.lang.reflect.Array;
 38import java.lang.reflect.InvocationTargetException;
 39
 40class BSHPrimarySuffix extends SimpleNode
 41{
 42	public static final int
 43		CLASS = 0,
 44		INDEX = 1,
 45		NAME = 2,
 46		PROPERTY = 3;
 47
 48	public int operation;
 49	Object index;
 50	public String field;
 51
 52	BSHPrimarySuffix(int id) { super(id); }
 53
 54	/*
 55		Perform a suffix operation on the given object and return the 
 56		new value.
 57		<p>
 58
 59		obj will be a Node when suffix evaluation begins, allowing us to
 60		interpret it contextually. (e.g. for .class) Thereafter it will be 
 61		an value object or LHS (as determined by toLHS).
 62		<p>
 63		
 64		We must handle the toLHS case at each point here.
 65		<p>
 66	*/
 67	public Object doSuffix(
 68		Object obj, boolean toLHS, 
 69		CallStack callstack, Interpreter interpreter) 
 70		throws EvalError
 71	{
 72		// Handle ".class" suffix operation
 73		// Prefix must be a BSHType
 74		if ( operation == CLASS )
 75			if ( obj instanceof BSHType ) {
 76				if ( toLHS )
 77					throw new EvalError("Can't assign .class", 
 78						this, callstack );
 79				NameSpace namespace = callstack.top();
 80				return ((BSHType)obj).getType( callstack, interpreter );
 81			} else
 82				throw new EvalError(
 83					"Attempt to use .class suffix on non class.", 
 84					this, callstack );
 85
 86		/*
 87			Evaluate our prefix if it needs evaluating first.
 88			If this is the first evaluation our prefix mayb be a Node 
 89			(directly from the PrimaryPrefix) - eval() it to an object.  
 90			If it's an LHS, resolve to a value.
 91
 92			Note: The ambiguous name construct is now necessary where the node 
 93			may be an ambiguous name.  If this becomes common we might want to 
 94			make a static method nodeToObject() or something.  The point is 
 95			that we can't just eval() - we need to direct the evaluation to 
 96			the context sensitive type of result; namely object, class, etc.
 97		*/
 98		if ( obj instanceof SimpleNode )
 99			if ( obj instanceof BSHAmbiguousName )
100				obj = ((BSHAmbiguousName)obj).toObject(callstack, interpreter);
101			else
102				obj = ((SimpleNode)obj).eval(callstack, interpreter);	
103		else
104			if ( obj instanceof LHS )
105				try {
106					obj = ((LHS)obj).getValue();
107				} catch ( UtilEvalError e ) {
108					throw e.toEvalError( this, callstack );
109				}
110
111		try
112		{
113			switch(operation)
114			{
115				case INDEX:
116					return doIndex( obj, toLHS, callstack, interpreter );
117
118				case NAME:
119					return doName( obj, toLHS, callstack, interpreter );
120
121				case PROPERTY:
122					return doProperty( toLHS, obj, callstack, interpreter );
123
124				default:
125					throw new InterpreterError( "Unknown suffix type" );
126			} 
127		}
128		catch(ReflectError e)
129		{
130			throw new EvalError("reflection error: " + e, this, callstack );
131		}
132		catch(InvocationTargetException e)
133		{
134			throw new TargetError( "target exception", e.getTargetException(), 
135				this, callstack, true);
136		}
137	}
138
139	/*
140		Field access, .length on array, or a method invocation
141		Must handle toLHS case for each.
142	*/
143	private Object doName(
144		Object obj, boolean toLHS, 
145		CallStack callstack, Interpreter interpreter) 
146		throws EvalError, ReflectError, InvocationTargetException
147	{
148		try {
149			// .length on array
150			if ( field.equals("length") && obj.getClass().isArray() )
151				if ( toLHS )
152					throw new EvalError(
153						"Can't assign array length", this, callstack );
154				else
155					return new Primitive(Array.getLength(obj));
156			
157			// field access
158			if ( jjtGetNumChildren() == 0 ) 
159				if ( toLHS )
160					return Reflect.getLHSObjectField(obj, field);
161				else
162					return Reflect.getObjectFieldValue( obj, field );
163
164			// Method invocation
165			// (LHS or non LHS evaluation can both encounter method calls)
166			Object[] oa = ((BSHArguments)jjtGetChild(0)).getArguments(
167				callstack, interpreter);
168
169		// TODO:
170		// Note: this try/catch block is copied from BSHMethodInvocation
171		// we need to factor out this common functionality and make sure
172		// we handle all cases ... (e.g. property style access, etc.)
173		// maybe move this to Reflect ?
174			try {
175				return Reflect.invokeObjectMethod( 
176					obj, field, oa, interpreter, callstack, this );
177			} catch ( ReflectError e ) {
178				throw new EvalError(
179					"Error in method invocation: " + e.getMessage(), 
180					this, callstack );
181			} catch ( InvocationTargetException e ) 
182			{
183				String msg = "Method Invocation "+field;
184				Throwable te = e.getTargetException();
185
186				/*
187					Try to squeltch the native code stack trace if the exception
188					was caused by a reflective call back into the bsh interpreter
189					(e.g. eval() or source()
190				*/
191				boolean isNative = true;
192				if ( te instanceof EvalError ) 
193					if ( te instanceof TargetError )
194						isNative = ((TargetError)te).inNativeCode();
195					else
196						isNative = false;
197				
198				throw new TargetError( msg, te, this, callstack, isNative );
199			} 
200
201		} catch ( UtilEvalError e ) {
202			throw e.toEvalError( this, callstack );
203		}
204	}
205
206	/**
207	*/
208	static int getIndexAux(
209		Object obj, CallStack callstack, Interpreter interpreter, 
210		SimpleNode callerInfo ) 
211		throws EvalError
212	{
213		if ( !obj.getClass().isArray() )
214			throw new EvalError("Not an array", callerInfo, callstack );
215
216		int index;
217		try {
218			Object indexVal = 
219				((SimpleNode)callerInfo.jjtGetChild(0)).eval( 
220					callstack, interpreter );
221			if ( !(indexVal instanceof Primitive) )
222				indexVal = Types.castObject(
223					indexVal, Integer.TYPE, Types.ASSIGNMENT );
224			index = ((Primitive)indexVal).intValue();
225		} catch( UtilEvalError e ) {
226			Interpreter.debug("doIndex: "+e);
227			throw e.toEvalError( 
228				"Arrays may only be indexed by integer types.", 
229				callerInfo, callstack );
230		}
231
232		return index;
233	}
234
235	/**
236		array index.
237		Must handle toLHS case.
238	*/
239	private Object doIndex( 
240		Object obj, boolean toLHS, 
241		CallStack callstack, Interpreter interpreter ) 
242		throws EvalError, ReflectError
243	{
244		int index = getIndexAux( obj, callstack, interpreter, this );
245		if ( toLHS )
246			return new LHS(obj, index);
247		else
248			try {
249				return Reflect.getIndex(obj, index);
250			} catch ( UtilEvalError e ) {
251				throw e.toEvalError( this, callstack );
252			}
253	}
254
255	/**
256		Property access.
257		Must handle toLHS case.
258	*/
259	private Object doProperty( boolean toLHS,
260		Object obj, CallStack callstack, Interpreter interpreter ) 
261		throws EvalError
262	{
263		if(obj == Primitive.VOID)
264			throw new EvalError( 
265			"Attempt to access property on undefined variable or class name", 
266				this, callstack );
267
268		if ( obj instanceof Primitive )
269			throw new EvalError("Attempt to access property on a primitive", 
270				this, callstack );
271
272		Object value = ((SimpleNode)jjtGetChild(0)).eval(
273			callstack, interpreter);
274
275		if ( !( value instanceof String ) )
276			throw new EvalError(
277				"Property expression must be a String or identifier.", 
278				this, callstack );
279
280		if ( toLHS )
281			return new LHS(obj, (String)value);
282
283		// Property style access to Hashtable or Map
284		CollectionManager cm = CollectionManager.getCollectionManager();
285		if ( cm.isMap( obj ) )
286		{
287			Object val = cm.getFromMap( obj, value/*key*/ );
288			return ( val == null ?  val = Primitive.NULL : val );
289		}
290
291		try {
292			return Reflect.getObjectProperty( obj, (String)value );
293		}
294		catch ( UtilEvalError e)  
295		{
296			throw e.toEvalError( "Property: "+value, this, callstack );
297		}
298		catch (ReflectError e) 
299		{
300			throw new EvalError("No such property: " + value, this, callstack );
301		}
302	}
303}
304