PageRenderTime 65ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre4/bsh/BSHPrimarySuffix.java

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