001: /*****************************************************************************
002: * *
003: * This file is part of the BeanShell Java Scripting distribution. *
004: * Documentation and updates may be found at http://www.beanshell.org/ *
005: * *
006: * Sun Public License Notice: *
007: * *
008: * The contents of this file are subject to the Sun Public License Version *
009: * 1.0 (the "License"); you may not use this file except in compliance with *
010: * the License. A copy of the License is available at http://www.sun.com *
011: * *
012: * The Original Code is BeanShell. The Initial Developer of the Original *
013: * Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright *
014: * (C) 2000. All Rights Reserved. *
015: * *
016: * GNU Public License Notice: *
017: * *
018: * Alternatively, the contents of this file may be used under the terms of *
019: * the GNU Lesser General Public License (the "LGPL"), in which case the *
020: * provisions of LGPL are applicable instead of those above. If you wish to *
021: * allow use of your version of this file only under the terms of the LGPL *
022: * and not to allow others to use your version of this file under the SPL, *
023: * indicate your decision by deleting the provisions above and replace *
024: * them with the notice and other provisions required by the LGPL. If you *
025: * do not delete the provisions above, a recipient may use your version of *
026: * this file under either the SPL or the LGPL. *
027: * *
028: * Patrick Niemeyer (pat@pat.net) *
029: * Author of Learning Java, O'Reilly & Associates *
030: * http://www.pat.net/~pat/ *
031: * *
032: *****************************************************************************/package org.gjt.sp.jedit.bsh;
033:
034: import java.lang.reflect.Array;
035: import java.lang.reflect.InvocationTargetException;
036:
037: class BSHPrimarySuffix extends SimpleNode {
038: public static final int CLASS = 0, INDEX = 1, NAME = 2,
039: PROPERTY = 3;
040:
041: public int operation;
042: Object index;
043: public String field;
044:
045: BSHPrimarySuffix(int id) {
046: super (id);
047: }
048:
049: /*
050: Perform a suffix operation on the given object and return the
051: new value.
052: <p>
053:
054: obj will be a Node when suffix evaluation begins, allowing us to
055: interpret it contextually. (e.g. for .class) Thereafter it will be
056: an value object or LHS (as determined by toLHS).
057: <p>
058:
059: We must handle the toLHS case at each point here.
060: <p>
061: */
062: public Object doSuffix(Object obj, boolean toLHS,
063: CallStack callstack, Interpreter interpreter)
064: throws EvalError {
065: // Handle ".class" suffix operation
066: // Prefix must be a BSHType
067: if (operation == CLASS)
068: if (obj instanceof BSHType) {
069: if (toLHS)
070: throw new EvalError("Can't assign .class", this ,
071: callstack);
072: NameSpace namespace = callstack.top();
073: return ((BSHType) obj).getType(callstack, interpreter);
074: } else
075: throw new EvalError(
076: "Attempt to use .class suffix on non class.",
077: this , callstack);
078:
079: /*
080: Evaluate our prefix if it needs evaluating first.
081: If this is the first evaluation our prefix mayb be a Node
082: (directly from the PrimaryPrefix) - eval() it to an object.
083: If it's an LHS, resolve to a value.
084:
085: Note: The ambiguous name construct is now necessary where the node
086: may be an ambiguous name. If this becomes common we might want to
087: make a static method nodeToObject() or something. The point is
088: that we can't just eval() - we need to direct the evaluation to
089: the context sensitive type of result; namely object, class, etc.
090: */
091: if (obj instanceof SimpleNode)
092: if (obj instanceof BSHAmbiguousName)
093: obj = ((BSHAmbiguousName) obj).toObject(callstack,
094: interpreter);
095: else
096: obj = ((SimpleNode) obj).eval(callstack, interpreter);
097: else if (obj instanceof LHS)
098: try {
099: obj = ((LHS) obj).getValue();
100: } catch (UtilEvalError e) {
101: throw e.toEvalError(this , callstack);
102: }
103:
104: try {
105: switch (operation) {
106: case INDEX:
107: return doIndex(obj, toLHS, callstack, interpreter);
108:
109: case NAME:
110: return doName(obj, toLHS, callstack, interpreter);
111:
112: case PROPERTY:
113: return doProperty(toLHS, obj, callstack, interpreter);
114:
115: default:
116: throw new InterpreterError("Unknown suffix type");
117: }
118: } catch (ReflectError e) {
119: throw new EvalError("reflection error: " + e, this ,
120: callstack);
121: } catch (InvocationTargetException e) {
122: throw new TargetError("target exception", e
123: .getTargetException(), this , callstack, true);
124: }
125: }
126:
127: /*
128: Field access, .length on array, or a method invocation
129: Must handle toLHS case for each.
130: */
131: private Object doName(Object obj, boolean toLHS,
132: CallStack callstack, Interpreter interpreter)
133: throws EvalError, ReflectError, InvocationTargetException {
134: try {
135: // .length on array
136: if (field.equals("length") && obj.getClass().isArray())
137: if (toLHS)
138: throw new EvalError("Can't assign array length",
139: this , callstack);
140: else
141: return new Primitive(Array.getLength(obj));
142:
143: // field access
144: if (jjtGetNumChildren() == 0)
145: if (toLHS)
146: return Reflect.getLHSObjectField(obj, field);
147: else
148: return Reflect.getObjectFieldValue(obj, field);
149:
150: // Method invocation
151: // (LHS or non LHS evaluation can both encounter method calls)
152: Object[] oa = ((BSHArguments) jjtGetChild(0)).getArguments(
153: callstack, interpreter);
154:
155: // TODO:
156: // Note: this try/catch block is copied from BSHMethodInvocation
157: // we need to factor out this common functionality and make sure
158: // we handle all cases ... (e.g. property style access, etc.)
159: // maybe move this to Reflect ?
160: try {
161: return Reflect.invokeObjectMethod(obj, field, oa,
162: interpreter, callstack, this );
163: } catch (ReflectError e) {
164: throw new EvalError("Error in method invocation: "
165: + e.getMessage(), this , callstack);
166: } catch (InvocationTargetException e) {
167: String msg = "Method Invocation " + field;
168: Throwable te = e.getTargetException();
169:
170: /*
171: Try to squeltch the native code stack trace if the exception
172: was caused by a reflective call back into the bsh interpreter
173: (e.g. eval() or source()
174: */
175: boolean isNative = true;
176: if (te instanceof EvalError)
177: if (te instanceof TargetError)
178: isNative = ((TargetError) te).inNativeCode();
179: else
180: isNative = false;
181:
182: throw new TargetError(msg, te, this , callstack,
183: isNative);
184: }
185:
186: } catch (UtilEvalError e) {
187: throw e.toEvalError(this , callstack);
188: }
189: }
190:
191: /**
192: */
193: static int getIndexAux(Object obj, CallStack callstack,
194: Interpreter interpreter, SimpleNode callerInfo)
195: throws EvalError {
196: if (!obj.getClass().isArray())
197: throw new EvalError("Not an array", callerInfo, callstack);
198:
199: int index;
200: try {
201: Object indexVal = ((SimpleNode) callerInfo.jjtGetChild(0))
202: .eval(callstack, interpreter);
203: if (!(indexVal instanceof Primitive))
204: indexVal = Types.castObject(indexVal, Integer.TYPE,
205: Types.ASSIGNMENT);
206: index = ((Primitive) indexVal).intValue();
207: } catch (UtilEvalError e) {
208: Interpreter.debug("doIndex: " + e);
209: throw e.toEvalError(
210: "Arrays may only be indexed by integer types.",
211: callerInfo, callstack);
212: }
213:
214: return index;
215: }
216:
217: /**
218: array index.
219: Must handle toLHS case.
220: */
221: private Object doIndex(Object obj, boolean toLHS,
222: CallStack callstack, Interpreter interpreter)
223: throws EvalError, ReflectError {
224: int index = getIndexAux(obj, callstack, interpreter, this );
225: if (toLHS)
226: return new LHS(obj, index);
227: else
228: try {
229: return Reflect.getIndex(obj, index);
230: } catch (UtilEvalError e) {
231: throw e.toEvalError(this , callstack);
232: }
233: }
234:
235: /**
236: Property access.
237: Must handle toLHS case.
238: */
239: private Object doProperty(boolean toLHS, Object obj,
240: CallStack callstack, Interpreter interpreter)
241: throws EvalError {
242: if (obj == Primitive.VOID)
243: throw new EvalError(
244: "Attempt to access property on undefined variable or class name",
245: this , callstack);
246:
247: if (obj instanceof Primitive)
248: throw new EvalError(
249: "Attempt to access property on a primitive", this ,
250: callstack);
251:
252: Object value = ((SimpleNode) jjtGetChild(0)).eval(callstack,
253: interpreter);
254:
255: if (!(value instanceof String))
256: throw new EvalError(
257: "Property expression must be a String or identifier.",
258: this , callstack);
259:
260: if (toLHS)
261: return new LHS(obj, (String) value);
262:
263: // Property style access to Hashtable or Map
264: CollectionManager cm = CollectionManager.getCollectionManager();
265: if (cm.isMap(obj)) {
266: Object val = cm.getFromMap(obj, value/*key*/);
267: return (val == null ? val = Primitive.NULL : val);
268: }
269:
270: try {
271: return Reflect.getObjectProperty(obj, (String) value);
272: } catch (UtilEvalError e) {
273: throw e.toEvalError("Property: " + value, this , callstack);
274: } catch (ReflectError e) {
275: throw new EvalError("No such property: " + value, this,
276: callstack);
277: }
278: }
279: }
|