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 java.math.BigDecimal;
023    import java.math.BigInteger;
024    
025    /**
026     * A NodeVisitor implementation which will build a String representation of the AST tree.
027     * <p/>
028     * This class is meant to be used by SimpleNode.toString(), but you may use it to
029     *
030     * @since 4.0
031     * @author Daniel Pitts
032     */
033    public class ToStringVisitor
034        implements NodeVisitor<StringBuilder, StringBuilder>
035    {
036        static final ToStringVisitor INSTANCE = new ToStringVisitor();
037    
038        public StringBuilder visit( ASTSequence node, StringBuilder data )
039        {
040            return commaSeparatedChildren( node, data );
041        }
042    
043        private StringBuilder commaSeparatedChildren( SimpleNode node, StringBuilder data )
044        {
045            if ( ( node.children != null ) )
046            {
047                for ( int i = 0; i < node.children.length; ++i )
048                {
049                    if ( i > 0 )
050                    {
051                        data.append( ", " );
052                    }
053                    recurse( node.children[i], data );
054                }
055            }
056            return data;
057        }
058    
059        public StringBuilder visit( ASTAssign node, StringBuilder data )
060        {
061            return concatInfix( node, " = ", data );
062        }
063    
064        public StringBuilder visit( ASTTest node, StringBuilder data )
065        {
066            return visitExpressionNode( node, data );
067        }
068    
069        private StringBuilder visitExpressionNode( ExpressionNode node, StringBuilder data )
070        {
071            if ( node.parent != null )
072            {
073                data.append( "(" );
074            }
075    
076            if ( ( node.children != null ) && ( node.children.length > 0 ) )
077            {
078                for ( int i = 0; i < node.children.length; ++i )
079                {
080                    if ( i > 0 )
081                    {
082                        data.append( " " ).append( node.getExpressionOperator( i ) ).append( " " );
083                    }
084                    recurse( node.children[i], data );
085                }
086            }
087    
088            if ( node.parent != null )
089            {
090                data.append( ')' );
091            }
092            return data;
093        }
094    
095        public StringBuilder visit( ASTOr node, StringBuilder data )
096        {
097            return visitExpressionNode( node, data );
098        }
099    
100        public StringBuilder visit( ASTAnd node, StringBuilder data )
101        {
102            return visitExpressionNode( node, data );
103        }
104    
105        public StringBuilder visit( ASTBitOr node, StringBuilder data )
106        {
107            return visitExpressionNode( node, data );
108        }
109    
110        public StringBuilder visit( ASTXor node, StringBuilder data )
111        {
112            return visitExpressionNode( node, data );
113        }
114    
115        public StringBuilder visit( ASTBitAnd node, StringBuilder data )
116        {
117            return visitExpressionNode( node, data );
118        }
119    
120        public StringBuilder visit( ASTEq node, StringBuilder data )
121        {
122            return visitExpressionNode( node, data );
123        }
124    
125        public StringBuilder visit( ASTNotEq node, StringBuilder data )
126        {
127            return visitExpressionNode( node, data );
128        }
129    
130        public StringBuilder visit( ASTLess node, StringBuilder data )
131        {
132            return visitExpressionNode( node, data );
133        }
134    
135        public StringBuilder visit( ASTGreater node, StringBuilder data )
136        {
137            return visitExpressionNode( node, data );
138        }
139    
140        public StringBuilder visit( ASTLessEq node, StringBuilder data )
141        {
142            return visitExpressionNode( node, data );
143        }
144    
145        public StringBuilder visit( ASTGreaterEq node, StringBuilder data )
146        {
147            return visitExpressionNode( node, data );
148        }
149    
150        public StringBuilder visit( ASTIn node, StringBuilder data )
151        {
152            final String infix = " in ";
153            return concatInfix( node, infix, data );
154        }
155    
156        private StringBuilder concatInfix( SimpleNode node, String infix, StringBuilder data )
157        {
158            return concatInfix( node.children[0], infix, node.children[1], data );
159        }
160    
161        private StringBuilder concatInfix( Node left, String infix, Node right, StringBuilder data )
162        {
163            recurse( left, data ).append( infix );
164            return recurse( right, data );
165        }
166    
167        public StringBuilder visit( ASTNotIn node, StringBuilder data )
168        {
169            return concatInfix( node, " not in ", data );
170        }
171    
172        public StringBuilder visit( ASTShiftLeft node, StringBuilder data )
173        {
174            return visitExpressionNode( node, data );
175        }
176    
177        public StringBuilder visit( ASTShiftRight node, StringBuilder data )
178        {
179            return visitExpressionNode( node, data );
180        }
181    
182        public StringBuilder visit( ASTUnsignedShiftRight node, StringBuilder data )
183        {
184            return visitExpressionNode( node, data );
185        }
186    
187        public StringBuilder visit( ASTAdd node, StringBuilder data )
188        {
189            return visitExpressionNode( node, data );
190        }
191    
192        public StringBuilder visit( ASTSubtract node, StringBuilder data )
193        {
194            return visitExpressionNode( node, data );
195        }
196    
197        public StringBuilder visit( ASTMultiply node, StringBuilder data )
198        {
199            return visitExpressionNode( node, data );
200        }
201    
202        public StringBuilder visit( ASTDivide node, StringBuilder data )
203        {
204            return visitExpressionNode( node, data );
205        }
206    
207        public StringBuilder visit( ASTRemainder node, StringBuilder data )
208        {
209            return visitExpressionNode( node, data );
210        }
211    
212        public StringBuilder visit( ASTNegate node, StringBuilder data )
213        {
214            return appendPrefixed( "-", node, data );
215        }
216    
217        public StringBuilder visit( ASTBitNegate node, StringBuilder data )
218        {
219            return appendPrefixed( "~", node, data );
220        }
221    
222        private StringBuilder appendPrefixed( String prefix, SimpleNode node, StringBuilder data )
223        {
224            data.append( prefix );
225            return recurse( node.children[0], data );
226        }
227    
228        public StringBuilder visit( ASTNot node, StringBuilder data )
229        {
230            return visitExpressionNode( node, data );
231        }
232    
233        public StringBuilder visit( ASTInstanceof node, StringBuilder data )
234        {
235            return recurse( node.children[0], data ).append( " instanceof " ).append( node.getTargetType() );
236        }
237    
238        public StringBuilder visit( ASTChain node, StringBuilder data )
239        {
240    
241            if ( ( node.children != null ) && ( node.children.length > 0 ) )
242            {
243                for ( int i = 0; i < node.children.length; i++ )
244                {
245                    if ( i > 0 )
246                    {
247                        if ( !( node.children[i] instanceof ASTProperty )
248                            || !( (ASTProperty) node.children[i] ).isIndexedAccess() )
249                        {
250                            data.append( "." );
251                        }
252                    }
253                    recurse( node.children[i], data );
254                }
255            }
256            return data;
257        }
258    
259        public StringBuilder visit( ASTEval node, StringBuilder data )
260        {
261            data.append( "(" );
262            concatInfix( node, ")(", data );
263            return data.append( ")" );
264        }
265    
266        public StringBuilder visit( ASTConst node, StringBuilder data )
267        {
268            final Object value = node.getValue();
269            if ( value == null )
270            {
271                data.append( "null" );
272            }
273            else
274            {
275                if ( value instanceof String )
276                {
277                    data.append( '\"' ).append( OgnlOps.getEscapeString( value.toString() ) ).append( '\"' );
278                }
279                else
280                {
281                    if ( value instanceof Character )
282                    {
283                        data.append( '\'' ).append( OgnlOps.getEscapedChar( (Character) value ) ).append( '\'' );
284                    }
285                    else
286                    {
287                        if ( value instanceof Node )
288                        {
289                            data.append( ":[ " );
290                            recurse( (Node) value, data );
291                            data.append( " ]" );
292                        }
293                        else
294                        {
295                            data.append( value );
296                            if ( value instanceof Long )
297                            {
298                                data.append( 'L' );
299                            }
300                            else if ( value instanceof BigDecimal )
301                            {
302                                data.append( 'B' );
303                            }
304                            else if ( value instanceof BigInteger )
305                            {
306                                data.append( 'H' );
307                            }
308                        }
309                    }
310                }
311            }
312            return data;
313        }
314    
315        public StringBuilder visit( ASTThisVarRef node, StringBuilder data )
316        {
317            return data.append( "#this" );
318        }
319    
320        public StringBuilder visit( ASTRootVarRef node, StringBuilder data )
321        {
322            return data.append( "#root" );
323        }
324    
325        public StringBuilder visit( ASTVarRef node, StringBuilder data )
326        {
327            return data.append( "#" ).append( node.getName() );
328        }
329    
330        public StringBuilder visit( ASTList node, StringBuilder data )
331        {
332            return wrappedCommaSeparatedChildren( "{ ", node, " }", data );
333        }
334    
335        public StringBuilder visit( ASTMap node, StringBuilder data )
336        {
337            data.append( "#" );
338    
339            if ( node.getClassName() != null )
340            {
341                data.append( "@" ).append( node.getClassName() ).append( "@" );
342            }
343    
344            data.append( "{ " );
345            for ( int i = 0; i < node.jjtGetNumChildren(); ++i )
346            {
347                ASTKeyValue kv = (ASTKeyValue) node.children[i];
348    
349                if ( i > 0 )
350                {
351                    data.append( ", " );
352                }
353                concatInfix( kv.getKey(), " : ", kv.getValue(), data );
354            }
355            return data.append( " }" );
356        }
357    
358        public StringBuilder visit( ASTKeyValue node, StringBuilder data )
359        {
360            return concatInfix( node.getKey(), " -> ", node.getValue(), data );
361        }
362    
363        public StringBuilder visit( ASTStaticField node, StringBuilder data )
364        {
365            return data.append( "@" ).append( node.getClassName() ).append( "@" ).append( node.getFieldName() );
366        }
367    
368        public StringBuilder visit( ASTCtor node, StringBuilder data )
369        {
370            data.append( "new " ).append( node.getClassName() );
371    
372            if ( node.isArray() )
373            {
374                if ( node.children[0] instanceof ASTConst )
375                {
376                    indexedChild( node, data );
377                }
378                else
379                {
380                    appendPrefixed( "[] ", node, data );
381                }
382            }
383            else
384            {
385                wrappedCommaSeparatedChildren( "(", node, ")", data );
386            }
387            return data;
388        }
389    
390        private StringBuilder wrappedCommaSeparatedChildren( String prefix, SimpleNode node, String suffix,
391                                                             StringBuilder data )
392        {
393            data.append( prefix );
394            return commaSeparatedChildren( node, data ).append( suffix );
395        }
396    
397        public StringBuilder visit( ASTProperty node, StringBuilder data )
398        {
399            if ( node.isIndexedAccess() )
400            {
401                indexedChild( node, data );
402            }
403            else
404            {
405                data.append( ( (ASTConst) node.children[0] ).getValue() );
406            }
407            return data;
408        }
409    
410        private StringBuilder indexedChild( SimpleNode node, StringBuilder data )
411        {
412            return surroundedNode( "[", node.children[0], "]", data );
413        }
414    
415        public StringBuilder visit( ASTStaticMethod node, StringBuilder data )
416        {
417            data.append( "@" ).append( node.getClassName() ).append( "@" ).append( node.getMethodName() );
418            return wrappedCommaSeparatedChildren( "(", node, ")", data );
419        }
420    
421        public StringBuilder visit( ASTMethod node, StringBuilder data )
422        {
423            data.append( node.getMethodName() );
424            return wrappedCommaSeparatedChildren( "(", node, ")", data );
425        }
426    
427        public StringBuilder visit( ASTProject node, StringBuilder data )
428        {
429            return surroundedNode( "{ ", node.children[0], " }", data );
430        }
431    
432        private StringBuilder surroundedNode( String open, Node inner, String close, StringBuilder data )
433        {
434            data.append( open );
435            return recurse( inner, data ).append( close );
436        }
437    
438        public StringBuilder visit( ASTSelect node, StringBuilder data )
439        {
440            return surroundedNode( "{? ", node.children[0], " }", data );
441        }
442    
443        public StringBuilder visit( ASTSelectFirst node, StringBuilder data )
444        {
445            return surroundedNode( "{^ ", node.children[0], " }", data );
446        }
447    
448        public StringBuilder visit( ASTSelectLast node, StringBuilder data )
449        {
450            return surroundedNode( "{$ ", node.children[0], " }", data );
451        }
452    
453        private StringBuilder recurse( Node child, StringBuilder data )
454        {
455            try
456            {
457                return child == null ? data.append( "null" ) : child.accept( this, data );
458            }
459            catch ( OgnlException e )
460            {
461                // This should never happen, but delegate it on just in case.
462                throw new RuntimeException( e );
463            }
464        }
465    
466    }