001: /*
002: * Copyright 2006, 2007 Odysseus Software GmbH
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package de.odysseus.el;
017:
018: import java.io.IOException;
019: import java.io.ObjectInputStream;
020: import java.io.PrintWriter;
021:
022: import javax.el.ELContext;
023: import javax.el.ELException;
024: import javax.el.FunctionMapper;
025: import javax.el.VariableMapper;
026:
027: import de.odysseus.el.misc.LocalMessages;
028: import de.odysseus.el.tree.Bindings;
029: import de.odysseus.el.tree.ExpressionNode;
030: import de.odysseus.el.tree.NodePrinter;
031: import de.odysseus.el.tree.Tree;
032: import de.odysseus.el.tree.TreeBuilder;
033: import de.odysseus.el.tree.TreeStore;
034:
035: /**
036: * A value expression is ready to be evaluated (by calling either
037: * {@link #getType(ELContext)}, {@link #getValue(ELContext)}, {@link #isReadOnly(ELContext)}
038: * or {@link #setValue(ELContext, Object)}.
039: *
040: * Instances of this class are usually created using an {@link ExpressionFactoryImpl}.
041: *
042: * @author Christoph Beck
043: */
044: public final class TreeValueExpression extends javax.el.ValueExpression {
045: private static final long serialVersionUID = 1L;
046:
047: private final TreeBuilder builder;
048: private final Bindings bindings;
049: private final String expr;
050: private final Class<?> type;
051: private final boolean deferred;
052:
053: private transient ExpressionNode node;
054:
055: private String structure;
056:
057: /**
058: * Create a new value expression.
059: * @param store used to get the parse tree from.
060: * @param functions the function mapper used to bind functions
061: * @param variables the variable mapper used to bind variables
062: * @param expr the expression string
063: * @param type the expected type (may be <code>null</code>)
064: */
065: public TreeValueExpression(TreeStore store,
066: FunctionMapper functions, VariableMapper variables,
067: String expr, Class<?> type) {
068: super ();
069:
070: Tree tree = store.get(expr);
071:
072: this .builder = store.getBuilder();
073: this .bindings = tree.bind(functions, variables);
074: this .expr = expr;
075: this .type = type;
076: this .node = tree.getRoot();
077: this .deferred = tree.isDeferred();
078:
079: if (type == null) {
080: throw new NullPointerException(LocalMessages
081: .get("error.value.notype"));
082: }
083: }
084:
085: private String getStructuralId() {
086: if (structure == null) {
087: structure = node.getStructuralId(bindings);
088: }
089: return structure;
090: }
091:
092: @Override
093: public Class<?> getExpectedType() {
094: return type;
095: }
096:
097: @Override
098: public String getExpressionString() {
099: return expr;
100: }
101:
102: /**
103: * Evaluates the expression as an lvalue and answers the result type.
104: * @param context used to resolve properties (<code>base.property</code> and <code>base[property]</code>)
105: * and to determine the result from the last base/property pair
106: * @return lvalue evaluation type or <code>null</code> for rvalue expressions
107: * @throws ELException if evaluation fails (e.g. property not found, type conversion failed, ...)
108: */
109: @Override
110: public Class<?> getType(ELContext context) throws ELException {
111: return node.getType(bindings, context);
112: }
113:
114: /**
115: * Evaluates the expression as an rvalue and answers the result.
116: * @param context used to resolve properties (<code>base.property</code> and <code>base[property]</code>)
117: * and to determine the result from the last base/property pair
118: * @return rvalue evaluation result
119: * @throws ELException if evaluation fails (e.g. property not found, type conversion failed, ...)
120: */
121: @Override
122: public Object getValue(ELContext context) throws ELException {
123: return node.getValue(bindings, context, type);
124: }
125:
126: /**
127: * Evaluates the expression as an lvalue and determines if {@link #setValue(ELContext, Object)}
128: * will always fail.
129: * @param context used to resolve properties (<code>base.property</code> and <code>base[property]</code>)
130: * and to determine the result from the last base/property pair
131: * @return <code>true</code> if {@link #setValue(ELContext, Object)} always fails.
132: * @throws ELException if evaluation fails (e.g. property not found, type conversion failed, ...)
133: */
134: @Override
135: public boolean isReadOnly(ELContext context) throws ELException {
136: return node.isReadOnly(bindings, context);
137: }
138:
139: /**
140: * Evaluates the expression as an lvalue and assigns the given value.
141: * @param context used to resolve properties (<code>base.property</code> and <code>base[property]</code>)
142: * and to perform the assignment to the last base/property pair
143: * @throws ELException if evaluation fails (e.g. property not found, type conversion failed, assignment failed...)
144: */
145: @Override
146: public void setValue(ELContext context, Object value)
147: throws ELException {
148: node.setValue(bindings, context, value);
149: }
150:
151: /**
152: * @return <code>true</code> if this is a literal text expression
153: */
154: @Override
155: public boolean isLiteralText() {
156: return node.isLiteralText();
157: }
158:
159: /**
160: * Answer <code>true</code> if this could be used as an lvalue.
161: * This is the case for eval expressions consisting of a simple identifier or
162: * a nonliteral prefix, followed by a sequence of property operators (<code>.</code> or <code>[]</code>)
163: */
164: public boolean isLeftValue() {
165: return node.isLeftValue();
166: }
167:
168: /**
169: * Answer <code>true</code> if this is a deferred expression (containing
170: * sub-expressions starting with <code>#{</code>)
171: */
172: public boolean isDeferred() {
173: return deferred;
174: }
175:
176: /**
177: * Expressions are compared using the concept of a <em>structural id</em>:
178: * variable and function names are anonymized such that two expressions with
179: * same tree structure will also have the same structural id and vice versa.
180: * Two value expressions are equal if
181: * <ol>
182: * <li>their structural id's are equal</li>
183: * <li>their bindings are equal</li>
184: * <li>their expected types are equal</li>
185: * </ol>
186: */
187: @Override
188: public boolean equals(Object obj) {
189: if (obj != null && obj.getClass() == getClass()) {
190: TreeValueExpression other = (TreeValueExpression) obj;
191: if (!builder.equals(other.builder)) {
192: return false;
193: }
194: if (type != other.type) {
195: return false;
196: }
197: return getStructuralId().equals(other.getStructuralId())
198: && bindings.equals(other.bindings);
199: }
200: return false;
201: }
202:
203: @Override
204: public int hashCode() {
205: return getStructuralId().hashCode();
206: }
207:
208: @Override
209: public String toString() {
210: return "TreeValueExpression(" + expr + ")";
211: }
212:
213: /**
214: * Print the parse tree.
215: * @param writer
216: */
217: public void dump(PrintWriter writer) {
218: NodePrinter.dump(writer, node);
219: }
220:
221: private void readObject(ObjectInputStream in) throws IOException,
222: ClassNotFoundException {
223: in.defaultReadObject();
224: try {
225: node = builder.build(expr).getRoot();
226: } catch (ELException e) {
227: throw new IOException(e.getMessage());
228: }
229: }
230: }
|