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 -> &lt;EOF&gt;
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 -> ( ( "&lt;" | "&gt;" | "&lt;=" | "&gt;=" ) 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 -> &lt;IDENTIFIER&gt;
328    * nodeListOptional -> ( "." &lt;IDENTIFIER&gt; )*
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 -> &lt;IDENTIFIER&gt;
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 -> &lt;INTEGER_LITERAL&gt;
380    *       | &lt;FLOATING_POINT_LITERAL&gt;
381    *       | &lt;CHARACTER_LITERAL&gt;
382    *       | &lt;STRING_LITERAL&gt;
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}