001// 002// Generated by JTB 1.3.2 003// 004package com.randomnoun.common.jexl.visitor; 005 006import com.randomnoun.common.jexl.ast.*; 007import java.util.*; 008 009/** 010 * A skeleton output formatter for your language grammar. Using the 011 * add() method along with force(), indent(), and outdent(), you can 012 * easily specify how this visitor will format the given syntax tree. 013 * See the JTB documentation for more details. 014 * 015 * Pass your syntax tree to this visitor, and then to the TreeDumper 016 * visitor in order to "pretty print" your tree. 017 */ 018public class TreeFormatter extends DepthFirstVisitor { 019 private Vector<FormatCommand> cmdQueue = new Vector<FormatCommand>(); 020 private boolean lineWrap; 021 private int wrapWidth; 022 private int indentAmt; 023 private int curLine = 1; 024 private int curColumn = 1; 025 private int curIndent = 0; 026 027 /** 028 * The default constructor assumes an indentation amount of 3 spaces 029 * and no line-wrap. You may alternately use the other constructor to 030 * specify your own indentation amount and line width. 031 */ 032 public TreeFormatter() { this(3, 0); } 033 034 /** 035 * This constructor accepts an indent amount and a line width which is 036 * used to wrap long lines. If a token's beginColumn value is greater 037 * than the specified wrapWidth, it will be moved to the next line and 038 * indented one extra level. To turn off line-wrapping, specify a 039 * wrapWidth of 0. 040 * 041 * @param indentAmt Amount of spaces per indentation level. 042 * @param wrapWidth Wrap lines longer than wrapWidth. 0 for no wrap. 043 */ 044 public TreeFormatter(int indentAmt, int wrapWidth) { 045 this.indentAmt = indentAmt; 046 this.wrapWidth = wrapWidth; 047 048 if ( wrapWidth > 0 ) 049 lineWrap = true; 050 else 051 lineWrap = false; 052 } 053 054 /** 055 * Accepts a NodeListInterface object and performs an optional format 056 * command between each node in the list (but not after the last node). 057 */ 058 protected void processList(NodeListInterface n) { 059 processList(n, null); 060 } 061 062 protected void processList(NodeListInterface n, FormatCommand cmd) { 063 for ( Enumeration<Node> e = n.elements(); e.hasMoreElements(); ) { 064 e.nextElement().accept(this); 065 if ( cmd != null && e.hasMoreElements() ) 066 cmdQueue.addElement(cmd); 067 } 068 } 069 070 /** 071 * A Force command inserts a line break and indents the next line to 072 * the current indentation level. Use "add(force());". 073 */ 074 protected FormatCommand force() { return force(1); } 075 protected FormatCommand force(int i) { 076 return new FormatCommand(FormatCommand.FORCE, i); 077 } 078 079 /** 080 * An Indent command increases the indentation level by one (or a 081 * user-specified amount). Use "add(indent());". 082 */ 083 protected FormatCommand indent() { return indent(1); } 084 protected FormatCommand indent(int i) { 085 return new FormatCommand(FormatCommand.INDENT, i); 086 } 087 088 /** 089 * An Outdent command is the reverse of the Indent command: it reduces 090 * the indentation level. Use "add(outdent());". 091 */ 092 protected FormatCommand outdent() { return outdent(1); } 093 protected FormatCommand outdent(int i) { 094 return new FormatCommand(FormatCommand.OUTDENT, i); 095 } 096 097 /** 098 * A Space command simply adds one or a user-specified number of 099 * spaces between tokens. Use "add(space());". 100 */ 101 protected FormatCommand space() { return space(1); } 102 protected FormatCommand space(int i) { 103 return new FormatCommand(FormatCommand.SPACE, i); 104 } 105 106 /** 107 * Use this method to add FormatCommands to the command queue to be 108 * executed when the next token in the tree is visited. 109 */ 110 protected void add(FormatCommand cmd) { 111 cmdQueue.addElement(cmd); 112 } 113 114 /** 115 * Executes the commands waiting in the command queue, then inserts the 116 * proper location information into the current NodeToken. 117 * 118 * If there are any special tokens preceding this token, they will be 119 * given the current location information. The token will follow on 120 * the next line, at the proper indentation level. If this is not the 121 * behavior you want from special tokens, feel free to modify this 122 * method. 123 */ 124 public void visit(NodeToken n) { 125 for ( Enumeration<FormatCommand> e = cmdQueue.elements(); e.hasMoreElements(); ) { 126 FormatCommand cmd = e.nextElement(); 127 switch ( cmd.getCommand() ) { 128 case FormatCommand.FORCE : 129 curLine += cmd.getNumCommands(); 130 curColumn = curIndent + 1; 131 break; 132 case FormatCommand.INDENT : 133 curIndent += indentAmt * cmd.getNumCommands(); 134 break; 135 case FormatCommand.OUTDENT : 136 if ( curIndent >= indentAmt ) 137 curIndent -= indentAmt * cmd.getNumCommands(); 138 break; 139 case FormatCommand.SPACE : 140 curColumn += cmd.getNumCommands(); 141 break; 142 default : 143 throw new TreeFormatterException( 144 "Invalid value in command queue."); 145 } 146 } 147 148 cmdQueue.removeAllElements(); 149 150 // 151 // Handle all special tokens preceding this NodeToken 152 // 153 if ( n.numSpecials() > 0 ) 154 for ( Enumeration<NodeToken> e = n.specialTokens.elements(); 155 e.hasMoreElements(); ) { 156 NodeToken special = e.nextElement(); 157 158 // 159 // -Place the token. 160 // -Move cursor to next line after the special token. 161 // -Don't update curColumn--want to keep current indent level. 162 // 163 placeToken(special, curLine, curColumn); 164 curLine = special.endLine + 1; 165 } 166 167 placeToken(n, curLine, curColumn); 168 curLine = n.endLine; 169 curColumn = n.endColumn; 170 } 171 172 /** 173 * Inserts token location (beginLine, beginColumn, endLine, endColumn) 174 * information into the NodeToken. Takes into account line-wrap. 175 * Does not update curLine and curColumn. 176 */ 177 private void placeToken(NodeToken n, int line, int column) { 178 int length = n.tokenImage.length(); 179 180 // 181 // Find beginning of token. Only line-wrap for single-line tokens 182 // 183 if ( !lineWrap || n.tokenImage.indexOf('\n') != -1 || 184 column + length <= wrapWidth ) 185 n.beginColumn = column; 186 else { 187 ++line; 188 column = curIndent + indentAmt + 1; 189 n.beginColumn = column; 190 } 191 192 n.beginLine = line; 193 194 // 195 // Find end of token; don't count \n if it's the last character 196 // 197 for ( int i = 0; i < length; ++i ) { 198 if ( n.tokenImage.charAt(i) == '\n' && i < length - 1 ) { 199 ++line; 200 column = 1; 201 } 202 else 203 ++column; 204 } 205 206 n.endLine = line; 207 n.endColumn = column; 208 } 209 210 // 211 // User-generated visitor methods below 212 // 213 214 /** 215 * <PRE> 216 * expression -> Expression() 217 * nodeToken -> <EOF> 218 * </PRE> 219 */ 220 public void visit(TopLevelExpression n) { 221 n.expression.accept(this); 222 n.nodeToken.accept(this); 223 } 224 225 /** 226 * <PRE> 227 * conditionalAndExpression -> ConditionalAndExpression() 228 * nodeListOptional -> ( "||" ConditionalAndExpression() )* 229 * </PRE> 230 */ 231 public void visit(Expression n) { 232 n.conditionalAndExpression.accept(this); 233 if ( n.nodeListOptional.present() ) { 234 processList(n.nodeListOptional); 235 } 236 } 237 238 /** 239 * <PRE> 240 * equalityExpression -> EqualityExpression() 241 * nodeListOptional -> ( "&&" EqualityExpression() )* 242 * </PRE> 243 */ 244 public void visit(ConditionalAndExpression n) { 245 n.equalityExpression.accept(this); 246 if ( n.nodeListOptional.present() ) { 247 processList(n.nodeListOptional); 248 } 249 } 250 251 /** 252 * <PRE> 253 * relationalExpression -> RelationalExpression() 254 * nodeListOptional -> ( ( "==" | "!=" ) RelationalExpression() )* 255 * </PRE> 256 */ 257 public void visit(EqualityExpression n) { 258 n.relationalExpression.accept(this); 259 if ( n.nodeListOptional.present() ) { 260 processList(n.nodeListOptional); 261 } 262 } 263 264 /** 265 * <PRE> 266 * additiveExpression -> AdditiveExpression() 267 * nodeListOptional -> ( ( "<" | ">" | "<=" | ">=" ) AdditiveExpression() )* 268 * </PRE> 269 */ 270 public void visit(RelationalExpression n) { 271 n.additiveExpression.accept(this); 272 if ( n.nodeListOptional.present() ) { 273 processList(n.nodeListOptional); 274 } 275 } 276 277 /** 278 * <PRE> 279 * multiplicativeExpression -> MultiplicativeExpression() 280 * nodeListOptional -> ( ( "+" | "-" ) MultiplicativeExpression() )* 281 * </PRE> 282 */ 283 public void visit(AdditiveExpression n) { 284 n.multiplicativeExpression.accept(this); 285 if ( n.nodeListOptional.present() ) { 286 processList(n.nodeListOptional); 287 } 288 } 289 290 /** 291 * <PRE> 292 * unaryExpression -> UnaryExpression() 293 * nodeListOptional -> ( ( "*" | "/" | "%" ) UnaryExpression() )* 294 * </PRE> 295 */ 296 public void visit(MultiplicativeExpression n) { 297 n.unaryExpression.accept(this); 298 if ( n.nodeListOptional.present() ) { 299 processList(n.nodeListOptional); 300 } 301 } 302 303 /** 304 * <PRE> 305 * nodeChoice -> ( "~" | "!" | "-" ) UnaryExpression() 306 * | PrimaryExpression() 307 * </PRE> 308 */ 309 public void visit(UnaryExpression n) { 310 n.nodeChoice.accept(this); 311 } 312 313 /** 314 * <PRE> 315 * nodeChoice -> FunctionCall() 316 * | Name() 317 * | Literal() 318 * | "(" Expression() ")" 319 * </PRE> 320 */ 321 public void visit(PrimaryExpression n) { 322 n.nodeChoice.accept(this); 323 } 324 325 /** 326 * <PRE> 327 * nodeToken -> <IDENTIFIER> 328 * nodeListOptional -> ( "." <IDENTIFIER> )* 329 * </PRE> 330 */ 331 public void visit(Name n) { 332 n.nodeToken.accept(this); 333 if ( n.nodeListOptional.present() ) { 334 processList(n.nodeListOptional); 335 } 336 } 337 338 /** 339 * <PRE> 340 * nodeToken -> <IDENTIFIER> 341 * arguments -> Arguments() 342 * </PRE> 343 */ 344 public void visit(FunctionCall n) { 345 n.nodeToken.accept(this); 346 n.arguments.accept(this); 347 } 348 349 /** 350 * <PRE> 351 * nodeToken -> "(" 352 * nodeOptional -> [ ArgumentList() ] 353 * nodeToken1 -> ")" 354 * </PRE> 355 */ 356 public void visit(Arguments n) { 357 n.nodeToken.accept(this); 358 if ( n.nodeOptional.present() ) { 359 n.nodeOptional.accept(this); 360 } 361 n.nodeToken1.accept(this); 362 } 363 364 /** 365 * <PRE> 366 * expression -> Expression() 367 * nodeListOptional -> ( "," Expression() )* 368 * </PRE> 369 */ 370 public void visit(ArgumentList n) { 371 n.expression.accept(this); 372 if ( n.nodeListOptional.present() ) { 373 processList(n.nodeListOptional); 374 } 375 } 376 377 /** 378 * <PRE> 379 * nodeChoice -> <INTEGER_LITERAL> 380 * | <FLOATING_POINT_LITERAL> 381 * | <CHARACTER_LITERAL> 382 * | <STRING_LITERAL> 383 * | BooleanLiteral() 384 * | NullLiteral() 385 * </PRE> 386 */ 387 public void visit(Literal n) { 388 n.nodeChoice.accept(this); 389 } 390 391 /** 392 * <PRE> 393 * nodeChoice -> "true" 394 * | "false" 395 * </PRE> 396 */ 397 public void visit(BooleanLiteral n) { 398 n.nodeChoice.accept(this); 399 } 400 401 /** 402 * <PRE> 403 * nodeToken -> "null" 404 * </PRE> 405 */ 406 public void visit(NullLiteral n) { 407 n.nodeToken.accept(this); 408 } 409 410} 411 412class FormatCommand { 413 public static final int FORCE = 0; 414 public static final int INDENT = 1; 415 public static final int OUTDENT = 2; 416 public static final int SPACE = 3; 417 418 private int command; 419 private int numCommands; 420 421 FormatCommand(int command, int numCommands) { 422 this.command = command; 423 this.numCommands = numCommands; 424 } 425 426 public int getCommand() { return command; } 427 public int getNumCommands() { return numCommands; } 428 public void setCommand(int i) { command = i; } 429 public void setNumCommands(int i) { numCommands = i; } 430} 431 432class TreeFormatterException extends RuntimeException { 433 TreeFormatterException() { super(); } 434 TreeFormatterException(String s) { super(s); } 435}