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.*; 008import java.io.*; 009 010/** 011 * Dumps the syntax tree to a Writer using the location information in 012 * each NodeToken. 013 */ 014public class TreeDumper extends DepthFirstVisitor { 015 protected PrintWriter out; 016 private int curLine = 1; 017 private int curColumn = 1; 018 private boolean startAtNextToken = false; 019 private boolean printSpecials = true; 020 021 /** 022 * The default constructor uses System.out as its output location. 023 * You may specify your own Writer or OutputStream using one of the 024 * other constructors. 025 */ 026 public TreeDumper() { out = new PrintWriter(System.out, true); } 027 public TreeDumper(Writer o) { out = new PrintWriter(o, true); } 028 public TreeDumper(OutputStream o) { out = new PrintWriter(o, true); } 029 030 /** 031 * Flushes the OutputStream or Writer that this TreeDumper is using. 032 */ 033 public void flushWriter() { out.flush(); } 034 035 /** 036 * Allows you to specify whether or not to print special tokens. 037 */ 038 public void printSpecials(boolean b) { printSpecials = b; } 039 040 /** 041 * Starts the tree dumper on the line containing the next token 042 * visited. For example, if the next token begins on line 50 and the 043 * dumper is currently on line 1 of the file, it will set its current 044 * line to 50 and continue printing from there, as opposed to 045 * printing 49 blank lines and then printing the token. 046 */ 047 public void startAtNextToken() { startAtNextToken = true; } 048 049 /** 050 * Resets the position of the output "cursor" to the first line and 051 * column. When using a dumper on a syntax tree more than once, you 052 * either need to call this method or startAtNextToken() between each 053 * dump. 054 */ 055 public void resetPosition() { curLine = curColumn = 1; } 056 057 /** 058 * Dumps the current NodeToken to the output stream being used. 059 * 060 * @throws IllegalStateException if the token position is invalid 061 * relative to the current position, i.e. its location places it 062 * before the previous token. 063 */ 064 public void visit(NodeToken n) { 065 if ( n.beginLine == -1 || n.beginColumn == -1 ) { 066 printToken(n.tokenImage); 067 return; 068 } 069 070 // 071 // Handle special tokens 072 // 073 if ( printSpecials && n.numSpecials() > 0 ) 074 for ( Enumeration<NodeToken> e = n.specialTokens.elements(); e.hasMoreElements(); ) 075 visit(e.nextElement()); 076 077 // 078 // Handle startAtNextToken option 079 // 080 if ( startAtNextToken ) { 081 curLine = n.beginLine; 082 curColumn = 1; 083 startAtNextToken = false; 084 085 if ( n.beginColumn < curColumn ) 086 out.println(); 087 } 088 089 // 090 // Check for invalid token position relative to current position. 091 // 092 if ( n.beginLine < curLine ) 093 throw new IllegalStateException("at token \"" + n.tokenImage + 094 "\", n.beginLine = " + Integer.toString(n.beginLine) + 095 ", curLine = " + Integer.toString(curLine)); 096 else if ( n.beginLine == curLine && n.beginColumn < curColumn ) 097 throw new IllegalStateException("at token \"" + n.tokenImage + 098 "\", n.beginColumn = " + 099 Integer.toString(n.beginColumn) + ", curColumn = " + 100 Integer.toString(curColumn)); 101 102 // 103 // Move output "cursor" to proper location, then print the token 104 // 105 if ( curLine < n.beginLine ) { 106 curColumn = 1; 107 for ( ; curLine < n.beginLine; ++curLine ) 108 out.println(); 109 } 110 111 for ( ; curColumn < n.beginColumn; ++curColumn ) 112 out.print(" "); 113 114 printToken(n.tokenImage); 115 } 116 117 private void printToken(String s) { 118 for ( int i = 0; i < s.length(); ++i ) { 119 if ( s.charAt(i) == '\n' ) { 120 ++curLine; 121 curColumn = 1; 122 } 123 else 124 curColumn++; 125 126 out.print(s.charAt(i)); 127 } 128 129 out.flush(); 130 } 131}