PageRenderTime 163ms CodeModel.GetById 151ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 274 lines | 168 code | 28 blank | 78 comment | 23 complexity | c97dda061a2464fda350d3dcf9461e5a 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.util.Hashtable;
 38import java.lang.reflect.Array;
 39import java.lang.reflect.InvocationTargetException;
 40
 41class 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 index;
 51	public String 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 doSuffix(
 69		Object 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 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 doName(
145		Object obj, boolean toLHS, 
146		CallStack callstack, Interpreter interpreter) 
147		throws EvalError, ReflectError, InvocationTargetException
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.getObjectField( obj, field );
164
165			// Method invocation
166			// (LHS or non LHS evaluation can both encounter method calls)
167			Object[] oa = ((BSHArguments)jjtGetChild(0)).getArguments(
168				callstack, interpreter);
169			return Reflect.invokeObjectMethod( 
170				obj, field, oa, interpreter, callstack, this );
171
172		} catch ( UtilEvalError e ) {
173			throw e.toEvalError( this, callstack );
174		}
175	}
176
177	/**
178	*/
179	static int getIndexAux(
180		Object obj, CallStack callstack, Interpreter interpreter, 
181		SimpleNode callerInfo ) 
182		throws EvalError
183	{
184		if ( !obj.getClass().isArray() )
185			throw new EvalError("Not an array", callerInfo, callstack );
186
187		int index;
188		try {
189			Object indexVal = 
190				((SimpleNode)callerInfo.jjtGetChild(0)).eval( 
191					callstack, interpreter );
192			if ( !(indexVal instanceof Primitive) )
193				indexVal = Types.getAssignableForm( indexVal, Integer.TYPE);
194			index = ((Primitive)indexVal).intValue();
195		} catch( UtilEvalError e ) {
196			Interpreter.debug("doIndex: "+e);
197			throw e.toEvalError( 
198				"Arrays may only be indexed by integer types.", 
199				callerInfo, callstack );
200		}
201
202		return index;
203	}
204
205	/**
206		array index.
207		Must handle toLHS case.
208	*/
209	private Object doIndex( 
210		Object obj, boolean toLHS, 
211		CallStack callstack, Interpreter interpreter ) 
212		throws EvalError, ReflectError
213	{
214		int index = getIndexAux( obj, callstack, interpreter, this );
215		if ( toLHS )
216			return new LHS(obj, index);
217		else
218			try {
219				return Reflect.getIndex(obj, index);
220			} catch ( UtilEvalError e ) {
221				throw e.toEvalError( this, callstack );
222			}
223	}
224
225	/**
226		Property access.
227		Must handle toLHS case.
228	*/
229	private Object doProperty( boolean toLHS,
230		Object obj, CallStack callstack, Interpreter interpreter ) 
231		throws EvalError
232	{
233		if(obj == Primitive.VOID)
234			throw new EvalError( 
235			"Attempt to access property on undefined variable or class name", 
236				this, callstack );
237
238		if ( obj instanceof Primitive )
239			throw new EvalError("Attempt to access property on a primitive", 
240				this, callstack );
241
242		Object value = ((SimpleNode)jjtGetChild(0)).eval(
243			callstack, interpreter);
244
245		if ( !( value instanceof String ) )
246			throw new EvalError(
247				"Property expression must be a String or identifier.", 
248				this, callstack );
249
250		if ( toLHS )
251			return new LHS(obj, (String)value);
252
253		// Property style access to Hashtable or Map
254		CollectionManager cm = CollectionManager.getCollectionManager();
255		if ( cm.isMap( obj ) )
256		{
257			Object val = cm.getFromMap( obj, value/*key*/ );
258			return ( val == null ?  val = Primitive.NULL : val );
259		}
260
261		try {
262			return Reflect.getObjectProperty( obj, (String)value );
263		}
264		catch ( UtilEvalError e)  
265		{
266			throw e.toEvalError( "Property: "+value, this, callstack );
267		}
268		catch (ReflectError e) 
269		{
270			throw new EvalError("No such property: " + value, this, callstack );
271		}
272	}
273}
274