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}