001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.el;
031:
032: import com.caucho.util.BeanUtil;
033: import com.caucho.vfs.WriteStream;
034:
035: import javax.el.ELContext;
036: import javax.el.ELException;
037: import java.io.IOException;
038: import java.lang.reflect.Array;
039: import java.lang.reflect.Method;
040: import java.util.List;
041: import java.util.Map;
042:
043: /**
044: * Represents an array reference:
045: *
046: * <pre>
047: * a[b]
048: * </pre>
049: */
050: public class ArrayExpr extends Expr {
051: private Expr _left;
052: private Expr _right;
053:
054: // cached getter method
055: private transient Class _lastClass;
056: private transient String _lastField;
057: private transient Method _lastMethod;
058:
059: /**
060: * Creates a new array expression.
061: *
062: * @param left the object expression
063: * @param right the index expression.
064: */
065: public ArrayExpr(Expr left, Expr right) {
066: _left = left;
067: _right = right;
068: }
069:
070: /**
071: * Returns the base expression.
072: */
073: public Expr getExpr() {
074: return _left;
075: }
076:
077: /**
078: * Returns the index expression.
079: */
080: public Expr getIndex() {
081: return _right;
082: }
083:
084: /**
085: * Creates a method for constant arrays.
086: */
087: @Override
088: public Expr createMethod(Expr[] args) {
089: if (!(_right instanceof StringLiteral))
090: return null;
091:
092: StringLiteral literal = (StringLiteral) _right;
093:
094: return new MethodExpr(_left, literal.getValue(), args);
095: }
096:
097: /**
098: * Evaluate the expression as an object.
099: *
100: * @param env the variable environment
101: *
102: * @return the evaluated object
103: */
104: @Override
105: public Object getValue(ELContext env) throws ELException {
106: Object aObj = _left.getValue(env);
107:
108: if (aObj == null)
109: return null;
110:
111: Object fieldObj = _right.getValue(env);
112: if (fieldObj == null)
113: return null;
114:
115: if (aObj instanceof Map) {
116: return ((Map) aObj).get(fieldObj);
117: }
118:
119: if (aObj instanceof List) {
120: int ref = (int) toLong(fieldObj, null);
121:
122: try {
123: List list = (List) aObj;
124:
125: if (ref < 0 || list.size() < ref)
126: return null;
127: else
128: return list.get(ref);
129: } catch (IndexOutOfBoundsException e) {
130: } catch (Exception e) {
131: return invocationError(e);
132: }
133: }
134:
135: Class aClass = aObj.getClass();
136:
137: if (aClass.isArray()) {
138: int ref = (int) toLong(fieldObj, null);
139:
140: try {
141: return Array.get(aObj, ref);
142: } catch (IndexOutOfBoundsException e) {
143: } catch (Exception e) {
144: return error(e, env);
145: }
146: }
147:
148: String fieldName = toString(fieldObj, env);
149:
150: Method getMethod = null;
151: try {
152: synchronized (this ) {
153: if (_lastClass == aClass
154: && _lastField.equals(fieldName))
155: getMethod = _lastMethod;
156: else {
157: // XXX: the Introspection is a memory hog
158: // BeanInfo info = Introspector.getBeanInfo(aClass);
159: getMethod = BeanUtil
160: .getGetMethod(aClass, fieldName);
161: _lastClass = aClass;
162: _lastField = fieldName;
163: _lastMethod = getMethod;
164: }
165: }
166:
167: if (getMethod != null)
168: return getMethod.invoke(aObj, (Object[]) null);
169: } catch (Exception e) {
170: return invocationError(e);
171: }
172:
173: try {
174: getMethod = aClass.getMethod("get",
175: new Class[] { String.class });
176:
177: if (getMethod != null)
178: return getMethod.invoke(aObj,
179: new Object[] { fieldName });
180: } catch (NoSuchMethodException e) {
181: return null;
182: } catch (Exception e) {
183: return invocationError(e);
184: }
185:
186: try {
187: getMethod = aClass.getMethod("get",
188: new Class[] { Object.class });
189:
190: if (getMethod != null)
191: return getMethod
192: .invoke(aObj, new Object[] { fieldObj });
193: } catch (Exception e) {
194: return invocationError(e);
195: }
196:
197: ELException e = new ELException(L.l(
198: "no get method {0} for class {1}", fieldName, aClass
199: .getName()));
200:
201: error(e, env);
202:
203: return null;
204: }
205:
206: /**
207: * Prints the code to create an LongLiteral.
208: *
209: * @param os stream to the generated *.java code
210: */
211: @Override
212: public void printCreate(WriteStream os) throws IOException {
213: os.print("new com.caucho.el.ArrayExpr(");
214: _left.printCreate(os);
215: os.print(", ");
216: _right.printCreate(os);
217: os.print(")");
218: }
219:
220: /**
221: * Returns true for equal strings.
222: */
223: public boolean equals(Object o) {
224: if (!(o instanceof ArrayExpr))
225: return false;
226:
227: ArrayExpr expr = (ArrayExpr) o;
228:
229: return (_left.equals(expr._left) && _right.equals(expr._right));
230: }
231:
232: /**
233: * Returns a readable representation of the expr.
234: */
235: public String toString() {
236: return _left + "[" + _right + "]";
237: }
238: }
|