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