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 }