View Javadoc

1   package org.apache.commons.ognl;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.commons.ognl.enhance.ExpressionAccessor;
23  
24  import java.io.PrintWriter;
25  import java.io.Serializable;
26  
27  /**
28   * @author Luke Blanshard (blanshlu@netscape.net)
29   * @author Drew Davidson (drew@ognl.org)
30   */
31  public abstract class SimpleNode
32      implements Node, Serializable
33  {
34  
35      private static final long serialVersionUID = 8305393337889433901L;
36  
37      protected Node parent;
38  
39      protected Node[] children;
40  
41      protected int id;
42  
43      protected OgnlParser parser;
44  
45      private boolean constantValueCalculated;
46  
47      private volatile boolean hasConstantValue;
48  
49      private Object constantValue;
50  
51      private ExpressionAccessor accessor;
52  
53      public SimpleNode( int i )
54      {
55          id = i;
56      }
57  
58      public SimpleNode( OgnlParser p, int i )
59      {
60          this( i );
61          parser = p;
62      }
63  
64      public void jjtOpen()
65      {
66      }
67  
68      public void jjtClose()
69      {
70      }
71  
72      public void jjtSetParent( Node n )
73      {
74          parent = n;
75      }
76  
77      public Node jjtGetParent()
78      {
79          return parent;
80      }
81  
82      public void jjtAddChild( Node n, int i )
83      {
84          if ( children == null )
85          {
86              children = new Node[i + 1];
87          }
88          else if ( i >= children.length )
89          {
90              Node c[] = new Node[i + 1];
91              System.arraycopy( children, 0, c, 0, children.length );
92              children = c;
93          }
94          children[i] = n;
95      }
96  
97      public Node jjtGetChild( int i )
98      {
99          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 }