001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.script.el;
020:
021: import java.util.Collections;
022: import java.util.LinkedList;
023: import java.util.List;
024:
025: import org.apache.beehive.netui.script.Expression;
026: import org.apache.beehive.netui.script.el.tokens.ArrayIndexToken;
027: import org.apache.beehive.netui.script.el.tokens.ContextToken;
028: import org.apache.beehive.netui.script.el.tokens.ExpressionToken;
029: import org.apache.beehive.netui.script.el.tokens.IdentifierToken;
030: import org.apache.beehive.netui.script.el.tokens.MapKeyToken;
031: import org.apache.beehive.netui.script.el.util.WrappedObject;
032: import org.apache.beehive.netui.script.el.util.ParseUtils;
033: import org.apache.beehive.netui.util.internal.InternalStringBuilder;
034: import org.apache.beehive.netui.util.logging.Logger;
035:
036: /**
037: * This class represents an executable expression that is evaluated against an object and can read or
038: * write properties, arrays, lists, and maps on that object.
039: */
040: public class ExpressionTerm extends Expression implements Term {
041:
042: private static Logger LOGGER = Logger
043: .getInstance(ExpressionTerm.class);
044: private static boolean TRACE_ENABLED = LOGGER.isTraceEnabled();
045:
046: private String _expressionString = null;
047: private boolean _sealed = false;
048: private ContextToken _implicitObjectToken = null;
049: private ExpressionToken[] _tokenArray = new ExpressionToken[0];
050: private List _noModTokens = null;
051:
052: public ExpressionTerm() {
053: super ();
054: }
055:
056: public void seal() {
057: _sealed = true;
058:
059: assert _tokenArray[0] instanceof ContextToken;
060: _implicitObjectToken = (ContextToken) _tokenArray[0];
061:
062: InternalStringBuilder buf = new InternalStringBuilder();
063: for (int i = 0; i < _tokenArray.length; i++)
064: buf.append(_tokenArray[i].getTokenString());
065:
066: _expressionString = buf.toString();
067: }
068:
069: public Object read(NetUIVariableResolver vr) {
070: assert _tokenArray != null;
071: return _evaluate(_tokenArray.length, vr);
072: }
073:
074: public String getContext() {
075: assert _implicitObjectToken != null;
076: return _implicitObjectToken.getName();
077: }
078:
079: public List getTokens() {
080: if (_noModTokens == null) {
081: _noModTokens = new LinkedList();
082: for (int i = 0; i < _tokenArray.length; i++)
083: _noModTokens.add(_tokenArray[i]);
084: _noModTokens = Collections.unmodifiableList(_noModTokens);
085: }
086:
087: return _noModTokens;
088: }
089:
090: public int getTokenCount() {
091: assert _tokenArray != null;
092: return _tokenArray.length;
093: }
094:
095: public ExpressionToken getToken(int index) {
096: assert _tokenArray != null;
097: assert index >= 0;
098: assert index < _tokenArray.length;
099: return _tokenArray[index];
100: }
101:
102: public String getExpressionString() {
103: return _expressionString;
104: }
105:
106: public String toString() {
107: return _expressionString;
108: }
109:
110: public String getExpression(int start) {
111: if (start >= _tokenArray.length)
112: throw new IllegalStateException(
113: "The index \""
114: + start
115: + "\" is an invalid reference into an expression with \""
116: + _tokenArray.length + "\" _tokens.");
117:
118: boolean needDot = true;
119: InternalStringBuilder buf = new InternalStringBuilder();
120: buf.append("{");
121: for (int i = start; i < _tokenArray.length; i++) {
122: ExpressionToken tok = _tokenArray[i];
123: if (tok instanceof ArrayIndexToken) {
124: buf.append(tok.getTokenString());
125: needDot = false;
126: } else if (tok instanceof IdentifierToken) {
127: if (needDot && i != start)
128: buf.append(".");
129: buf.append(tok.toString());
130: needDot = true;
131: } else if (tok instanceof MapKeyToken) {
132: buf.append(tok.getTokenString());
133: needDot = false;
134: }
135: }
136: buf.append("}");
137: return buf.toString();
138: }
139:
140: public void addToken(ExpressionToken token) {
141: if (_sealed)
142: throw new IllegalStateException(
143: "Can't add token to already sealed expression");
144:
145: assert _tokenArray != null;
146: ExpressionToken[] newToken = new ExpressionToken[_tokenArray.length + 1];
147: System.arraycopy(_tokenArray, 0, newToken, 0,
148: _tokenArray.length);
149: newToken[_tokenArray.length] = token;
150: _tokenArray = newToken;
151: }
152:
153: public void update(Object newValue, NetUIVariableResolver vr) {
154:
155: // Execute the expression up to the last token. This will return the object that should be updated
156: Object branch = _evaluate(_tokenArray.length - 1, vr);
157:
158: // Get the token to update
159: ExpressionToken token = _tokenArray[_tokenArray.length - 1];
160:
161: if (TRACE_ENABLED)
162: LOGGER.trace("Update leaf token: " + token + " on object: "
163: + branch);
164:
165: // update the object
166: token.write(branch, newValue);
167: }
168:
169: public String changeContext(String previousImplicitObject,
170: String newImplicitObject, Object lookupKey) {
171: String this Expr = getExpressionString();
172:
173: if (TRACE_ENABLED)
174: LOGGER.trace("previous implicit object: "
175: + previousImplicitObject + " new implicit object: "
176: + newImplicitObject + " expression: " + this Expr);
177:
178: // needs to be checked for atomicity
179: ParsedExpression pe = ParseUtils.parse(newImplicitObject);
180:
181: if (!pe.isExpression()) {
182: String msg = "The expression can not be qualified into new _context because the new _context is not atomic.";
183: LOGGER.error(msg);
184: throw new RuntimeException(msg);
185: }
186:
187: // this isn't a failure; it just means that there isn't anything else to replace
188: if (!this Expr.startsWith(previousImplicitObject))
189: return "{" + this Expr + "}";
190:
191: if (lookupKey instanceof Integer
192: && ((Integer) lookupKey).intValue() > 32767) {
193: String msg = "Can not create an indexed expression with an array index greater than "
194: + "the Java array limit array length \""
195: + this Expr
196: + "\"";
197: LOGGER.warn(msg);
198: throw new RuntimeException(msg);
199: }
200:
201: newImplicitObject = pe.getExpressionString() + "[" + lookupKey
202: + "]";
203:
204: if (TRACE_ENABLED)
205: LOGGER.trace("expression: " + this Expr
206: + " implicit object: " + newImplicitObject);
207:
208: this Expr = this Expr.replaceFirst(previousImplicitObject,
209: newImplicitObject);
210:
211: InternalStringBuilder buf = new InternalStringBuilder();
212: buf.append("{").append(this Expr).append("}");
213: return buf.toString();
214: }
215:
216: public String qualify(String contextName) {
217: InternalStringBuilder buf = new InternalStringBuilder();
218: buf.append("{").append(contextName).append(".").append(
219: getExpressionString()).append("}");
220: return buf.toString();
221: }
222:
223: private Object _evaluate(int index, NetUIVariableResolver vr) {
224: assert _tokenArray != null;
225:
226: Object result = vr.resolveVariable(getContext());
227:
228: /*
229: This is a special case used to unwrap something that has been wrapped in
230: a Map facade but that delegates to some underlying implementation. For
231: example, an HttpSession might be wrapped inside of a Map facade in order
232: to expose it as a Map for reads / updates in the expression engine.
233: */
234: if (_tokenArray.length == 1 && result instanceof WrappedObject) {
235: result = ((WrappedObject) result).unwrap();
236: }
237: /*
238: Otherwise, read each additional term in the expression with the result of the previous term.
239: */
240: else {
241: for (int i = 1; i < index; i++)
242: result = _tokenArray[i].read(result);
243: }
244:
245: return result;
246: }
247: }
|