PageRenderTime 176ms CodeModel.GetById 159ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 233 lines | 134 code | 25 blank | 74 comment | 18 complexity | a46b3caa913aa30ec50d5be98e01d4ea 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
 35/*
 36	Warning: this is a hack... should be unified with BSHLHSPrimarySuffix
 37*/
 38package bsh;
 39
 40import java.util.Hashtable;
 41import java.lang.reflect.Array;
 42import java.lang.reflect.InvocationTargetException;
 43
 44class BSHPrimarySuffix extends SimpleNode
 45{
 46	public static final int
 47		CLASS = 0,
 48		INDEX = 1,
 49		NAME = 2,
 50		PROPERTY = 3;
 51
 52	public int operation;
 53	Object index;
 54	public String field;
 55
 56	BSHPrimarySuffix(int id) { super(id); }
 57
 58	/*
 59		Perform a suffix operation on the given object and return the 
 60		new value.
 61
 62		obj will be a Node when suffix evaluation begins, allowing us to
 63		interpret it contextually. (e.g. for .class) Thereafter it will be 
 64		a normal object.
 65	*/
 66	public Object doSuffix(
 67		Object obj, CallStack callstack, Interpreter interpreter) 
 68		throws EvalError
 69	{
 70		// Handle ".class" suffix operation
 71		/*
 72		if ( operation == CLASS )
 73			if ( obj instanceof BSHAmbiguousName ) {
 74				NameSpace namespace = callstack.top();
 75				return ((BSHAmbiguousName)obj).toClass( namespace );
 76			} else
 77				throw new EvalError(
 78					"Attemp to .class on non class...", this);
 79		*/
 80		if ( operation == CLASS )
 81			if ( obj instanceof BSHType ) {
 82				NameSpace namespace = callstack.top();
 83				return ((BSHType)obj).getType( namespace );
 84			} else
 85				throw new EvalError(
 86					"Attemp to invoke .class on non class.", this);
 87
 88		// Handle other suffix operations
 89
 90		/*
 91			eval( ) the node to an object
 92
 93			Note: This construct is now necessary where the node may be
 94			an ambiguous name.  If this becomes common we might want to 
 95			make a static method nodeToObject() or something.
 96		*/
 97		if ( obj instanceof SimpleNode )
 98			if ( obj instanceof BSHAmbiguousName )
 99				obj = ((BSHAmbiguousName)obj).toObject(callstack, interpreter);
100			else
101				obj = ((SimpleNode)obj).eval(callstack, interpreter);	
102
103		try
104		{
105			switch(operation)
106			{
107				case INDEX:
108					return doIndex(obj, callstack, interpreter );
109
110				case NAME:
111					return doName(obj, callstack, interpreter );
112
113				case PROPERTY:
114					return doProperty(obj, callstack, interpreter );
115
116				default:
117					throw new InterpreterError("LHS suffix");
118			} 
119		}
120		catch(ReflectError e)
121		{
122			throw new EvalError("reflection error: " + e, this);
123		}
124		catch(InvocationTargetException e)
125		{
126			throw new TargetError(
127				"target exception", e.getTargetException(), this, true);
128		}
129	}
130
131	/*
132		Field access or a method invocation
133		Field access might be .length on an array
134	*/
135	private Object doName(
136		Object obj, CallStack callstack, Interpreter interpreter) 
137		throws EvalError, ReflectError, InvocationTargetException
138	{
139		if(field.equals("length") && obj.getClass().isArray())
140			return new Primitive(Array.getLength(obj));
141		
142		if (jjtGetNumChildren() == 0)
143			// field access
144			return Reflect.getObjectField(obj, field);
145		else
146		{
147			// method invocation
148			Object[] oa = ((BSHArguments)jjtGetChild(0)).getArguments(
149				callstack, interpreter);
150			try {
151				return Reflect.invokeObjectMethod(interpreter, obj, field, oa, this);
152			} catch ( EvalError ee ) {
153				// catch and re-throw to get line number right
154				throw new EvalError( ee.getMessage(), this );
155			}
156		}
157	}
158
159	/**
160		Just factoring out some common code for the two suffix classes...
161		later need to complete factoring of these classes
162		(BSHPrimarySuffix, BSHLHSPrimarySuffix)
163	*/
164	static int getIndexAux(
165		Object obj, CallStack callstack, Interpreter interpreter, 
166		SimpleNode callerNode ) 
167		throws EvalError
168	{
169		if ( !obj.getClass().isArray() )
170			throw new EvalError("Not an array", callerNode );
171
172		int index;
173		try {
174			Object indexVal = 
175				((SimpleNode)callerNode.jjtGetChild(0)).eval( 
176					callstack, interpreter );
177			if ( !(indexVal instanceof Primitive) )
178				indexVal = NameSpace.getAssignableForm( indexVal, Integer.TYPE);
179			index = ((Primitive)indexVal).intValue();
180		} catch( EvalError e ) {
181			Interpreter.debug("doIndex: "+e);
182			e.reThrow(
183				"You can only index arrays by integer types", callerNode );
184			throw new Error("can't get here");
185		}
186
187		return index;
188	}
189
190	private Object doIndex(
191		Object obj, CallStack callstack, Interpreter interpreter) 
192		throws EvalError, ReflectError
193	{
194		int index = getIndexAux( obj, callstack, interpreter, this );
195		return Reflect.getIndex(obj, index);
196	}
197
198	private Object doProperty( 
199		Object obj, CallStack callstack, Interpreter interpreter ) 
200		throws EvalError
201	{
202		if(obj == Primitive.VOID)
203			throw new EvalError("Attempt to access property on undefined variable or class name", this);
204
205		if(obj instanceof Primitive)
206			throw new EvalError("Attempt to access property on a primitive", this);
207
208		Object value = ((SimpleNode)jjtGetChild(0)).eval(
209			callstack, interpreter);
210		if(!(value instanceof String))
211			throw new EvalError("Property expression must be a String or identifier.", this);
212
213		// property style access to hashtable
214		if(obj instanceof Hashtable)
215		{
216			Object val = ((Hashtable)obj).get((String)value);
217			if(val == null)
218				val = Primitive.NULL;
219			return val;
220		}
221
222		try
223		{
224			return Reflect.getObjectProperty(obj, (String)value);
225		}
226		catch(ReflectError e)
227		{
228			Interpreter.debug(e.toString());
229			throw new EvalError("No such property: " + value, this);
230		}
231	}
232}
233