Source code: ognl/SimpleNode.java
1 //--------------------------------------------------------------------------
2 // Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 // Neither the name of the Drew Davidson nor the names of its contributors
15 // may be used to endorse or promote products derived from this software
16 // without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25 // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29 // DAMAGE.
30 //--------------------------------------------------------------------------
31 package ognl;
32
33 import java.io.*;
34
35 /**
36 * @author Luke Blanshard (blanshlu@netscape.net)
37 * @author Drew Davidson (drew@ognl.org)
38 */
39 public abstract class SimpleNode implements Node, Serializable
40 {
41 protected Node parent;
42 protected Node[] children;
43 protected int id;
44 protected OgnlParser parser;
45
46 private boolean constantValueCalculated;
47 private boolean hasConstantValue;
48 private Object constantValue;
49
50 public SimpleNode(int i) {
51 id = i;
52 }
53
54 public SimpleNode(OgnlParser p, int i) {
55 this(i);
56 parser = p;
57 }
58
59 public void jjtOpen() {
60 }
61
62 public void jjtClose() {
63 }
64
65 public void jjtSetParent(Node n) { parent = n; }
66 public Node jjtGetParent() { return parent; }
67
68 public void jjtAddChild(Node n, int i) {
69 if (children == null) {
70 children = new Node[i + 1];
71 } else if (i >= children.length) {
72 Node c[] = new Node[i + 1];
73 System.arraycopy(children, 0, c, 0, children.length);
74 children = c;
75 }
76 children[i] = n;
77 }
78
79 public Node jjtGetChild(int i) {
80 return children[i];
81 }
82
83 public int jjtGetNumChildren() {
84 return (children == null) ? 0 : children.length;
85 }
86
87 /* You can override these two methods in subclasses of SimpleNode to
88 customize the way the node appears when the tree is dumped. If
89 your output uses more than one line you should override
90 toString(String), otherwise overriding toString() is probably all
91 you need to do. */
92
93 public String toString() { return OgnlParserTreeConstants.jjtNodeName[id]; }
94
95 // OGNL additions
96
97 public String toString(String prefix) { return prefix + OgnlParserTreeConstants.jjtNodeName[id] + " " + toString(); }
98
99 /* Override this method if you want to customize how the node dumps
100 out its children. */
101
102 public void dump(PrintWriter writer, String prefix) {
103 writer.println(toString(prefix));
104 if (children != null) {
105 for (int i = 0; i < children.length; ++i) {
106 SimpleNode n = (SimpleNode)children[i];
107 if (n != null) {
108 n.dump(writer, prefix + " ");
109 }
110 }
111 }
112 }
113
114 public int getIndexInParent()
115 {
116 int result = -1;
117
118 if (parent != null) {
119 int icount = parent.jjtGetNumChildren();
120
121 for (int i = 0; i < icount; i++) {
122 if (parent.jjtGetChild(i) == this) {
123 result = i;
124 break;
125 }
126 }
127 }
128 return result;
129 }
130
131 public Node getNextSibling()
132 {
133 Node result = null;
134 int i = getIndexInParent();
135
136 if (i >= 0) {
137 int icount = parent.jjtGetNumChildren();
138
139 if (i < icount) {
140 result = parent.jjtGetChild(i + 1);
141 }
142 }
143 return result;
144 }
145
146 private static String getDepthString(int depth)
147 {
148 StringBuffer result = new StringBuffer("");
149
150 while (depth > 0) {
151 depth--;
152 result.append(" ");
153 }
154 return new String(result);
155 }
156
157 protected Object evaluateGetValueBody( OgnlContext context, Object source ) throws OgnlException
158 {
159 Object result;
160
161 context.setCurrentObject(source);
162 context.setCurrentNode(this);
163 if (!constantValueCalculated) {
164 constantValueCalculated = true;
165 hasConstantValue = isConstant(context);
166 if (hasConstantValue) {
167 constantValue = getValueBody(context, source);
168 }
169 }
170 return hasConstantValue ? constantValue : getValueBody(context, source);
171 }
172
173 protected void evaluateSetValueBody( OgnlContext context, Object target, Object value ) throws OgnlException
174 {
175 context.setCurrentObject(target);
176 context.setCurrentNode(this);
177 setValueBody(context, target, value);
178 }
179
180 public final Object getValue( OgnlContext context, Object source ) throws OgnlException
181 {
182 if (context.getTraceEvaluations()) {
183 EvaluationPool pool = OgnlRuntime.getEvaluationPool();
184 Object result = null;
185 Throwable evalException = null;
186 Evaluation evaluation = pool.create(this, source);
187
188 context.pushEvaluation(evaluation);
189 try {
190 result = evaluateGetValueBody(context, source);
191 } catch (OgnlException ex) {
192 evalException = ex;
193 throw ex;
194 } catch (RuntimeException ex) {
195 evalException = ex;
196 throw ex;
197 } finally {
198 Evaluation eval = context.popEvaluation();
199
200 eval.setResult(result);
201 if (evalException != null) {
202 eval.setException(evalException);
203 }
204 if ((evalException == null) && (context.getRootEvaluation() == null) && !context.getKeepLastEvaluation()) {
205 pool.recycleAll(eval);
206 }
207 }
208 return result;
209 } else {
210 return evaluateGetValueBody(context, source);
211 }
212 }
213
214 /** Subclasses implement this method to do the actual work of extracting the
215 appropriate value from the source object. */
216 protected abstract Object getValueBody( OgnlContext context, Object source ) throws OgnlException;
217
218 public final void setValue( OgnlContext context, Object target, Object value ) throws OgnlException
219 {
220 if (context.getTraceEvaluations()) {
221 EvaluationPool pool = OgnlRuntime.getEvaluationPool();
222 Throwable evalException = null;
223 Evaluation evaluation = pool.create(this, target, true);
224
225 context.pushEvaluation(evaluation);
226 try {
227 evaluateSetValueBody(context, target, value);
228 } catch (OgnlException ex) {
229 evalException = ex;
230 ex.setEvaluation(evaluation);
231 throw ex;
232 } catch (RuntimeException ex) {
233 evalException = ex;
234 throw ex;
235 } finally {
236 Evaluation eval = context.popEvaluation();
237
238 if (evalException != null) {
239 eval.setException(evalException);
240 }
241 if ((evalException == null) && (context.getRootEvaluation() == null) && !context.getKeepLastEvaluation()) {
242 pool.recycleAll(eval);
243 }
244 }
245 } else {
246 evaluateSetValueBody(context, target, value);
247 }
248 }
249
250 /** Subclasses implement this method to do the actual work of setting the
251 appropriate value in the target object. The default implementation
252 throws an <code>InappropriateExpressionException</code>, meaning that it
253 cannot be a set expression.
254 */
255 protected void setValueBody( OgnlContext context, Object target, Object value ) throws OgnlException
256 {
257 throw new InappropriateExpressionException( this );
258 }
259
260 /**
261 Returns true iff this node is constant without respect to the children.
262 */
263 public boolean isNodeConstant( OgnlContext context ) throws OgnlException
264 {
265 return false;
266 }
267
268 public boolean isConstant( OgnlContext context ) throws OgnlException
269 {
270 return isNodeConstant(context);
271 }
272
273 public boolean isNodeSimpleProperty( OgnlContext context ) throws OgnlException
274 {
275 return false;
276 }
277
278 public boolean isSimpleProperty( OgnlContext context ) throws OgnlException
279 {
280 return isNodeSimpleProperty(context);
281 }
282
283 public boolean isSimpleNavigationChain( OgnlContext context ) throws OgnlException
284 {
285 return isSimpleProperty(context);
286 }
287
288 /** This method may be called from subclasses' jjtClose methods. It flattens the
289 tree under this node by eliminating any children that are of the same class as
290 this node and copying their children to this node. */
291 protected void flattenTree()
292 {
293 boolean shouldFlatten = false;
294 int newSize = 0;
295
296 for ( int i=0; i < children.length; ++i )
297 if ( children[i].getClass() == getClass() ) {
298 shouldFlatten = true;
299 newSize += children[i].jjtGetNumChildren();
300 }
301 else
302 ++newSize;
303
304 if ( shouldFlatten )
305 {
306 Node[] newChildren = new Node[newSize];
307 int j = 0;
308
309 for ( int i=0; i < children.length; ++i ) {
310 Node c = children[i];
311 if ( c.getClass() == getClass() ) {
312 for ( int k=0; k < c.jjtGetNumChildren(); ++k )
313 newChildren[j++] = c.jjtGetChild(k);
314 }
315 else
316 newChildren[j++] = c;
317 }
318
319 if ( j != newSize )
320 throw new Error( "Assertion error: " + j + " != " + newSize );
321
322 this.children = newChildren;
323 }
324 }
325 }