001: //--------------------------------------------------------------------------
002: // Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
003: // All rights reserved.
004: //
005: // Redistribution and use in source and binary forms, with or without
006: // modification, are permitted provided that the following conditions are
007: // met:
008: //
009: // Redistributions of source code must retain the above copyright notice,
010: // this list of conditions and the following disclaimer.
011: // Redistributions in binary form must reproduce the above copyright
012: // notice, this list of conditions and the following disclaimer in the
013: // documentation and/or other materials provided with the distribution.
014: // Neither the name of the Drew Davidson nor the names of its contributors
015: // may be used to endorse or promote products derived from this software
016: // without specific prior written permission.
017: //
018: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
019: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
020: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
021: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
022: // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
023: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
024: // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
025: // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026: // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
027: // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
028: // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
029: // DAMAGE.
030: //--------------------------------------------------------------------------
031: package ognl;
032:
033: import java.io.*;
034:
035: /**
036: * @author Luke Blanshard (blanshlu@netscape.net)
037: * @author Drew Davidson (drew@ognl.org)
038: */
039: public abstract class SimpleNode implements Node, Serializable {
040: protected Node parent;
041: protected Node[] children;
042: protected int id;
043: protected OgnlParser parser;
044:
045: private boolean constantValueCalculated;
046: private boolean hasConstantValue;
047: private Object constantValue;
048:
049: public SimpleNode(int i) {
050: id = i;
051: }
052:
053: public SimpleNode(OgnlParser p, int i) {
054: this (i);
055: parser = p;
056: }
057:
058: public void jjtOpen() {
059: }
060:
061: public void jjtClose() {
062: }
063:
064: public void jjtSetParent(Node n) {
065: parent = n;
066: }
067:
068: public Node jjtGetParent() {
069: return parent;
070: }
071:
072: public void jjtAddChild(Node n, int i) {
073: if (children == null) {
074: children = new Node[i + 1];
075: } else if (i >= children.length) {
076: Node c[] = new Node[i + 1];
077: System.arraycopy(children, 0, c, 0, children.length);
078: children = c;
079: }
080: children[i] = n;
081: }
082:
083: public Node jjtGetChild(int i) {
084: return children[i];
085: }
086:
087: public int jjtGetNumChildren() {
088: return (children == null) ? 0 : children.length;
089: }
090:
091: /* You can override these two methods in subclasses of SimpleNode to
092: customize the way the node appears when the tree is dumped. If
093: your output uses more than one line you should override
094: toString(String), otherwise overriding toString() is probably all
095: you need to do. */
096:
097: public String toString() {
098: return OgnlParserTreeConstants.jjtNodeName[id];
099: }
100:
101: // OGNL additions
102:
103: public String toString(String prefix) {
104: return prefix + OgnlParserTreeConstants.jjtNodeName[id] + " "
105: + toString();
106: }
107:
108: /* Override this method if you want to customize how the node dumps
109: out its children. */
110:
111: public void dump(PrintWriter writer, String prefix) {
112: writer.println(toString(prefix));
113: if (children != null) {
114: for (int i = 0; i < children.length; ++i) {
115: SimpleNode n = (SimpleNode) children[i];
116: if (n != null) {
117: n.dump(writer, prefix + " ");
118: }
119: }
120: }
121: }
122:
123: public int getIndexInParent() {
124: int result = -1;
125:
126: if (parent != null) {
127: int icount = parent.jjtGetNumChildren();
128:
129: for (int i = 0; i < icount; i++) {
130: if (parent.jjtGetChild(i) == this ) {
131: result = i;
132: break;
133: }
134: }
135: }
136: return result;
137: }
138:
139: public Node getNextSibling() {
140: Node result = null;
141: int i = getIndexInParent();
142:
143: if (i >= 0) {
144: int icount = parent.jjtGetNumChildren();
145:
146: if (i < icount) {
147: result = parent.jjtGetChild(i + 1);
148: }
149: }
150: return result;
151: }
152:
153: private static String getDepthString(int depth) {
154: StringBuffer result = new StringBuffer("");
155:
156: while (depth > 0) {
157: depth--;
158: result.append(" ");
159: }
160: return new String(result);
161: }
162:
163: protected Object evaluateGetValueBody(OgnlContext context,
164: Object source) throws OgnlException {
165: Object result;
166:
167: context.setCurrentObject(source);
168: context.setCurrentNode(this );
169: if (!constantValueCalculated) {
170: constantValueCalculated = true;
171: hasConstantValue = isConstant(context);
172: if (hasConstantValue) {
173: constantValue = getValueBody(context, source);
174: }
175: }
176: return hasConstantValue ? constantValue : getValueBody(context,
177: source);
178: }
179:
180: protected void evaluateSetValueBody(OgnlContext context,
181: Object target, Object value) throws OgnlException {
182: context.setCurrentObject(target);
183: context.setCurrentNode(this );
184: setValueBody(context, target, value);
185: }
186:
187: public final Object getValue(OgnlContext context, Object source)
188: throws OgnlException {
189: if (context.getTraceEvaluations()) {
190: EvaluationPool pool = OgnlRuntime.getEvaluationPool();
191: Object result = null;
192: Throwable evalException = null;
193: Evaluation evaluation = pool.create(this , source);
194:
195: context.pushEvaluation(evaluation);
196: try {
197: result = evaluateGetValueBody(context, source);
198: } catch (OgnlException ex) {
199: evalException = ex;
200: throw ex;
201: } catch (RuntimeException ex) {
202: evalException = ex;
203: throw ex;
204: } finally {
205: Evaluation eval = context.popEvaluation();
206:
207: eval.setResult(result);
208: if (evalException != null) {
209: eval.setException(evalException);
210: }
211: if ((evalException == null)
212: && (context.getRootEvaluation() == null)
213: && !context.getKeepLastEvaluation()) {
214: pool.recycleAll(eval);
215: }
216: }
217: return result;
218: } else {
219: return evaluateGetValueBody(context, source);
220: }
221: }
222:
223: /** Subclasses implement this method to do the actual work of extracting the
224: appropriate value from the source object. */
225: protected abstract Object getValueBody(OgnlContext context,
226: Object source) throws OgnlException;
227:
228: public final void setValue(OgnlContext context, Object target,
229: Object value) throws OgnlException {
230: if (context.getTraceEvaluations()) {
231: EvaluationPool pool = OgnlRuntime.getEvaluationPool();
232: Throwable evalException = null;
233: Evaluation evaluation = pool.create(this , target, true);
234:
235: context.pushEvaluation(evaluation);
236: try {
237: evaluateSetValueBody(context, target, value);
238: } catch (OgnlException ex) {
239: evalException = ex;
240: ex.setEvaluation(evaluation);
241: throw ex;
242: } catch (RuntimeException ex) {
243: evalException = ex;
244: throw ex;
245: } finally {
246: Evaluation eval = context.popEvaluation();
247:
248: if (evalException != null) {
249: eval.setException(evalException);
250: }
251: if ((evalException == null)
252: && (context.getRootEvaluation() == null)
253: && !context.getKeepLastEvaluation()) {
254: pool.recycleAll(eval);
255: }
256: }
257: } else {
258: evaluateSetValueBody(context, target, value);
259: }
260: }
261:
262: /** Subclasses implement this method to do the actual work of setting the
263: appropriate value in the target object. The default implementation
264: throws an <code>InappropriateExpressionException</code>, meaning that it
265: cannot be a set expression.
266: */
267: protected void setValueBody(OgnlContext context, Object target,
268: Object value) throws OgnlException {
269: throw new InappropriateExpressionException(this );
270: }
271:
272: /**
273: Returns true iff this node is constant without respect to the children.
274: */
275: public boolean isNodeConstant(OgnlContext context)
276: throws OgnlException {
277: return false;
278: }
279:
280: public boolean isConstant(OgnlContext context) throws OgnlException {
281: return isNodeConstant(context);
282: }
283:
284: public boolean isNodeSimpleProperty(OgnlContext context)
285: throws OgnlException {
286: return false;
287: }
288:
289: public boolean isSimpleProperty(OgnlContext context)
290: throws OgnlException {
291: return isNodeSimpleProperty(context);
292: }
293:
294: public boolean isSimpleNavigationChain(OgnlContext context)
295: throws OgnlException {
296: return isSimpleProperty(context);
297: }
298:
299: /** This method may be called from subclasses' jjtClose methods. It flattens the
300: tree under this node by eliminating any children that are of the same class as
301: this node and copying their children to this node. */
302: protected void flattenTree() {
303: boolean shouldFlatten = false;
304: int newSize = 0;
305:
306: for (int i = 0; i < children.length; ++i)
307: if (children[i].getClass() == getClass()) {
308: shouldFlatten = true;
309: newSize += children[i].jjtGetNumChildren();
310: } else
311: ++newSize;
312:
313: if (shouldFlatten) {
314: Node[] newChildren = new Node[newSize];
315: int j = 0;
316:
317: for (int i = 0; i < children.length; ++i) {
318: Node c = children[i];
319: if (c.getClass() == getClass()) {
320: for (int k = 0; k < c.jjtGetNumChildren(); ++k)
321: newChildren[j++] = c.jjtGetChild(k);
322: } else
323: newChildren[j++] = c;
324: }
325:
326: if (j != newSize)
327: throw new Error("Assertion error: " + j + " != "
328: + newSize);
329:
330: this.children = newChildren;
331: }
332: }
333: }
|