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 }