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    }