001 package org.apache.commons.ognl;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import org.apache.commons.ognl.enhance.ExpressionAccessor;
023
024 import java.io.PrintWriter;
025 import java.io.Serializable;
026
027 /**
028 * @author Luke Blanshard (blanshlu@netscape.net)
029 * @author Drew Davidson (drew@ognl.org)
030 */
031 public abstract class SimpleNode
032 implements Node, Serializable
033 {
034
035 private static final long serialVersionUID = 8305393337889433901L;
036
037 protected Node parent;
038
039 protected Node[] children;
040
041 protected int id;
042
043 protected OgnlParser parser;
044
045 private boolean constantValueCalculated;
046
047 private volatile boolean hasConstantValue;
048
049 private Object constantValue;
050
051 private ExpressionAccessor accessor;
052
053 public SimpleNode( int i )
054 {
055 id = i;
056 }
057
058 public SimpleNode( OgnlParser p, int i )
059 {
060 this( i );
061 parser = p;
062 }
063
064 public void jjtOpen()
065 {
066 }
067
068 public void jjtClose()
069 {
070 }
071
072 public void jjtSetParent( Node n )
073 {
074 parent = n;
075 }
076
077 public Node jjtGetParent()
078 {
079 return parent;
080 }
081
082 public void jjtAddChild( Node n, int i )
083 {
084 if ( children == null )
085 {
086 children = new Node[i + 1];
087 }
088 else if ( i >= children.length )
089 {
090 Node c[] = new Node[i + 1];
091 System.arraycopy( children, 0, c, 0, children.length );
092 children = c;
093 }
094 children[i] = n;
095 }
096
097 public Node jjtGetChild( int i )
098 {
099 return children[i];
100 }
101
102 public int jjtGetNumChildren()
103 {
104 return ( children == null ) ? 0 : children.length;
105 }
106
107 /*
108 * You can override these two methods in subclasses of SimpleNode to customize the way the node appears when the
109 * tree is dumped. If your output uses more than one line you should override toString(String), otherwise overriding
110 * toString() is probably all you need to do.
111 */
112
113 @Override
114 public String toString()
115 {
116 final StringBuilder data = new StringBuilder();
117 try
118 {
119 accept( ToStringVisitor.INSTANCE, data );
120 }
121 catch ( OgnlException e )
122 {
123 // ignored.
124 }
125 return data.toString();
126 }
127
128 // OGNL additions
129
130 public String toString( String prefix )
131 {
132 return prefix + OgnlParserTreeConstants.jjtNodeName[id] + " " + toString();
133 }
134
135 public String toGetSourceString( OgnlContext context, Object target )
136 {
137 return toString();
138 }
139
140 public String toSetSourceString( OgnlContext context, Object target )
141 {
142 return toString();
143 }
144
145 /*
146 * Override this method if you want to customize how the node dumps out its children.
147 */
148
149 public void dump( PrintWriter writer, String prefix )
150 {
151 writer.println( toString( prefix ) );
152
153 if ( children != null )
154 {
155 for ( int i = 0; i < children.length; ++i )
156 {
157 SimpleNode n = (SimpleNode) children[i];
158 if ( n != null )
159 {
160 n.dump( writer, prefix + " " );
161 }
162 }
163 }
164 }
165
166 public int getIndexInParent()
167 {
168 int result = -1;
169
170 if ( parent != null )
171 {
172 int icount = parent.jjtGetNumChildren();
173
174 for ( int i = 0; i < icount; i++ )
175 {
176 if ( parent.jjtGetChild( i ) == this )
177 {
178 result = i;
179 break;
180 }
181 }
182 }
183
184 return result;
185 }
186
187 public Node getNextSibling()
188 {
189 Node result = null;
190 int i = getIndexInParent();
191
192 if ( i >= 0 )
193 {
194 int icount = parent.jjtGetNumChildren();
195
196 if ( i < icount )
197 {
198 result = parent.jjtGetChild( i + 1 );
199 }
200 }
201 return result;
202 }
203
204 protected Object evaluateGetValueBody( OgnlContext context, Object source )
205 throws OgnlException
206 {
207 context.setCurrentObject( source );
208 context.setCurrentNode( this );
209
210 if ( !constantValueCalculated )
211 {
212 constantValueCalculated = true;
213 boolean constant = isConstant( context );
214
215 if ( constant )
216 {
217 constantValue = getValueBody( context, source );
218 }
219
220 hasConstantValue = constant;
221 }
222
223 return hasConstantValue ? constantValue : getValueBody( context, source );
224 }
225
226 protected void evaluateSetValueBody( OgnlContext context, Object target, Object value )
227 throws OgnlException
228 {
229 context.setCurrentObject( target );
230 context.setCurrentNode( this );
231 setValueBody( context, target, value );
232 }
233
234 public final Object getValue( OgnlContext context, Object source )
235 throws OgnlException
236 {
237 Object result = null;
238
239 if ( context.getTraceEvaluations() )
240 {
241
242 EvaluationPool pool = OgnlRuntime.getEvaluationPool();
243 Throwable evalException = null;
244 Evaluation evaluation = pool.create( this, source );
245
246 context.pushEvaluation( evaluation );
247 try
248 {
249 result = evaluateGetValueBody( context, source );
250 }
251 catch ( OgnlException ex )
252 {
253 evalException = ex;
254 throw ex;
255 }
256 catch ( RuntimeException ex )
257 {
258 evalException = ex;
259 throw ex;
260 }
261 finally
262 {
263 Evaluation eval = context.popEvaluation();
264
265 eval.setResult( result );
266 if ( evalException != null )
267 {
268 eval.setException( evalException );
269 }
270 if ( ( evalException == null ) && ( context.getRootEvaluation() == null )
271 && !context.getKeepLastEvaluation() )
272 {
273 pool.recycleAll( eval );
274 }
275 }
276 }
277 else
278 {
279 result = evaluateGetValueBody( context, source );
280 }
281
282 return result;
283 }
284
285 /**
286 * Subclasses implement this method to do the actual work of extracting the appropriate value from the source
287 * object.
288 */
289 protected abstract Object getValueBody( OgnlContext context, Object source )
290 throws OgnlException;
291
292 public final void setValue( OgnlContext context, Object target, Object value )
293 throws OgnlException
294 {
295 if ( context.getTraceEvaluations() )
296 {
297 EvaluationPool pool = OgnlRuntime.getEvaluationPool();
298 Throwable evalException = null;
299 Evaluation evaluation = pool.create( this, target, true );
300
301 context.pushEvaluation( evaluation );
302 try
303 {
304 evaluateSetValueBody( context, target, value );
305 }
306 catch ( OgnlException ex )
307 {
308 evalException = ex;
309 ex.setEvaluation( evaluation );
310 throw ex;
311 }
312 catch ( RuntimeException ex )
313 {
314 evalException = ex;
315 throw ex;
316 }
317 finally
318 {
319 Evaluation eval = context.popEvaluation();
320
321 if ( evalException != null )
322 {
323 eval.setException( evalException );
324 }
325 if ( ( evalException == null ) && ( context.getRootEvaluation() == null )
326 && !context.getKeepLastEvaluation() )
327 {
328 pool.recycleAll( eval );
329 }
330 }
331 }
332 else
333 {
334 evaluateSetValueBody( context, target, value );
335 }
336 }
337
338 /**
339 * Subclasses implement this method to do the actual work of setting the appropriate value in the target object. The
340 * default implementation throws an <code>InappropriateExpressionException</code>, meaning that it cannot be a set
341 * expression.
342 */
343 protected void setValueBody( OgnlContext context, Object target, Object value )
344 throws OgnlException
345 {
346 throw new InappropriateExpressionException( this );
347 }
348
349 /** Returns true iff this node is constant without respect to the children. */
350 public boolean isNodeConstant( OgnlContext context )
351 throws OgnlException
352 {
353 return false;
354 }
355
356 public boolean isConstant( OgnlContext context )
357 throws OgnlException
358 {
359 return isNodeConstant( context );
360 }
361
362 public boolean isNodeSimpleProperty( OgnlContext context )
363 throws OgnlException
364 {
365 return false;
366 }
367
368 public boolean isSimpleProperty( OgnlContext context )
369 throws OgnlException
370 {
371 return isNodeSimpleProperty( context );
372 }
373
374 public boolean isSimpleNavigationChain( OgnlContext context )
375 throws OgnlException
376 {
377 return isSimpleProperty( context );
378 }
379
380 public boolean isEvalChain( OgnlContext context )
381 throws OgnlException
382 {
383 if ( children == null )
384 {
385 return false;
386 }
387 for ( Node child : children )
388 {
389 if ( child instanceof SimpleNode )
390 {
391 if ( ( (SimpleNode) child ).isEvalChain( context ) )
392 {
393 return true;
394 }
395 }
396 }
397 return false;
398 }
399
400 protected boolean lastChild( OgnlContext context )
401 {
402 return parent == null || context.get( "_lastChild" ) != null;
403 }
404
405 /**
406 * This method may be called from subclasses' jjtClose methods. It flattens the tree under this node by eliminating
407 * any children that are of the same class as this node and copying their children to this node.
408 */
409 protected void flattenTree()
410 {
411 boolean shouldFlatten = false;
412 int newSize = 0;
413
414 for ( Node aChildren : children )
415 {
416 if ( aChildren.getClass() == getClass() )
417 {
418 shouldFlatten = true;
419 newSize += aChildren.jjtGetNumChildren();
420 }
421 else
422 {
423 ++newSize;
424 }
425 }
426
427 if ( shouldFlatten )
428 {
429 Node[] newChildren = new Node[newSize];
430 int j = 0;
431
432 for ( Node c : children )
433 {
434 if ( c.getClass() == getClass() )
435 {
436 for ( int k = 0; k < c.jjtGetNumChildren(); ++k )
437 {
438 newChildren[j++] = c.jjtGetChild( k );
439 }
440
441 }
442 else
443 {
444 newChildren[j++] = c;
445 }
446 }
447
448 if ( j != newSize )
449 {
450 throw new Error( "Assertion error: " + j + " != " + newSize );
451 }
452
453 children = newChildren;
454 }
455 }
456
457 public ExpressionAccessor getAccessor()
458 {
459 return accessor;
460 }
461
462 public void setAccessor( ExpressionAccessor accessor )
463 {
464 this.accessor = accessor;
465 }
466 }