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