001    /**
002     *
003     * Copyright 2005 Jeremy Rayner
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     * http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     *
017     **/
018    package org.codehaus.groovy.antlr.treewalker;
019    
020    import java.io.PrintStream;
021    import java.util.Stack;
022    
023    import org.codehaus.groovy.antlr.GroovySourceAST;
024    import org.codehaus.groovy.antlr.parser.GroovyTokenTypes;
025    
026    /**
027     * An antlr AST visitor that prints groovy source code for each visited node
028     * to the supplied PrintStream.
029     *
030     * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
031     * @version $Revision: 4538 $
032     */
033    
034    public class SourcePrinter extends VisitorAdapter {
035        private String[] tokenNames;
036        private int tabLevel;
037        private int lastLinePrinted;
038        private boolean newLines;
039        protected PrintStream out;
040        private String className;
041        private Stack stack;
042        private int stringConstructorCounter;
043    
044        /**
045         * A visitor that prints groovy source code for each node visited.
046         * @param out where to print the source code to
047         * @param tokenNames an array of token names from antlr
048         */
049        public SourcePrinter(PrintStream out,String[] tokenNames) {
050            this(out,tokenNames,true);
051        }
052    
053        /**
054         * A visitor that prints groovy source code for each node visited.
055         * @param out where to print the source code to
056         * @param tokenNames an array of token names from antlr
057         * @param newLines output newline character
058         */
059        public SourcePrinter(PrintStream out,String[] tokenNames, boolean newLines) {
060            this.tokenNames = tokenNames;
061            tabLevel = 0;
062            lastLinePrinted = 0;
063            this.out = out;
064            this.newLines = newLines;
065            this.stack = new Stack();
066        }
067        
068    
069            public void visitAbstract(GroovySourceAST t, int visit) {
070                    print(t,visit,"abstract ",null,null);
071            }
072    
073            public void visitAnnotation(GroovySourceAST t, int visit) {
074                    if (visit == OPENING_VISIT) {
075                            print(t,visit,"@");
076                    }
077                    if (visit == SECOND_VISIT) {
078                            print(t,visit,"(");
079                    }
080                    if (visit == SUBSEQUENT_VISIT) {
081                            print(t,visit,", ");
082                    }
083                    if (visit == CLOSING_VISIT) {
084                            if (t.getNumberOfChildren() > 1) {
085                                    print(t,visit,") ");
086                            } else {
087                                    print(t,visit," ");
088                            }
089                    }
090    
091        }
092    
093        public void visitAnnotations(GroovySourceAST t, int visit) {
094            // do nothing
095        }
096    
097        public void visitAnnotationDef(GroovySourceAST t,int visit) {
098            print(t,visit,"@interface ",null,null);
099        }
100    
101            public void visitAnnotationFieldDef(GroovySourceAST t, int visit) {
102            print(t,visit,"() ","default ",null);
103            }
104    
105            public void visitAnnotationMemberValuePair(GroovySourceAST t, int visit) {
106                    print(t,visit," = ",null,null);
107            }
108    
109            public void visitArrayDeclarator(GroovySourceAST t, int visit) {
110                    //<ARRAY_DECLARATOR>int</ARRAY_DECLARATOR> primes = new int(<ARRAY_DECLARATOR>5</ARRAY_DECLARATOR>)
111                    if (getParentNode().getType() == GroovyTokenTypes.TYPE ||
112                                    getParentNode().getType() == GroovyTokenTypes.TYPECAST) { // ugly hack
113                            // type defintion, i.e.   int[] x;
114                            print(t,visit,null,null,"[]");
115                    } else {
116                            // usually in new, i.e.   def y = new int[5];
117                            print(t,visit,"[",null,"]");
118                    }
119            }
120    
121            public void visitAssign(GroovySourceAST t,int visit) {
122            print(t,visit," = ",null,null);
123        }
124            
125        // visitAt() ...
126        //   token type 'AT' should never be visited, as annotation definitions and usage, and
127        //   direct field access should have all moved this token out of the way. No test needed.
128    
129            //   one of the BAND tokens is actually replaced by TYPE_UPPER_BOUNDS (e.g. class Foo<T extends C & I> {T t} )
130        public void visitBand(GroovySourceAST t, int visit) {
131            print(t,visit," & ",null,null);
132        }
133    
134            public void visitBandAssign(GroovySourceAST t,int visit) {
135            print(t,visit," &= ",null,null);
136        }
137            
138        // visitBigSuffix() ...
139            //   token type BIG_SUFFIX never created/visited, NUM_BIG_INT, NUM_BIG_DECIMAL instead...    
140        
141            // visitBlock() ...
142            //   token type BLOCK never created/visited, see CLOSABLE_BLOCK etc...
143            
144            public void visitBnot(GroovySourceAST t, int visit) {
145                    print(t,visit,"~",null,null);
146            }
147            
148            // Note: old closure syntax using BOR is deprecated, and also never creates/visits a BOR node
149        public void visitBor(GroovySourceAST t, int visit) {
150            print(t,visit," | ",null,null);
151        }
152            
153            public void visitBorAssign(GroovySourceAST t,int visit) {
154            print(t,visit," |= ",null,null);
155        }
156            
157        public void visitBsr(GroovySourceAST t, int visit) {
158            print(t,visit," >>> ",null,null);
159        }
160            
161            public void visitBsrAssign(GroovySourceAST t,int visit) {
162            print(t,visit," >>>= ",null,null);
163        }
164            
165        public void visitBxor(GroovySourceAST t, int visit) {
166            print(t,visit," ^ ",null,null);
167        }
168            
169            public void visitBxorAssign(GroovySourceAST t,int visit) {
170            print(t,visit," ^= ",null,null);
171        }
172            
173        public void visitCaseGroup(GroovySourceAST t, int visit) {
174            if (visit == OPENING_VISIT) {
175                tabLevel++;
176            }
177            if (visit == CLOSING_VISIT) {
178                tabLevel--;
179            }
180        }
181    
182        public void visitClassDef(GroovySourceAST t,int visit) {
183            print(t,visit,"class ",null,null);
184    
185            if (visit == OPENING_VISIT) {
186                // store name of class away for use in constructor ident
187                className = t.childOfType(GroovyTokenTypes.IDENT).getText();
188            }
189        }
190    
191        public void visitClosedBlock(GroovySourceAST t, int visit) {
192            printUpdatingTabLevel(t,visit,"{","-> ","}");
193        }
194        
195        // visitClosureOp ...
196            //   token type CLOSABLE_BLOCK_OP never created/visited, see CLOSABLE_BLOCK...
197            
198    
199        // visitColon ...
200        //   token type COLON never created/visited, see LABELED_STAT, FOR_IN_ITERABLE, 
201        //   ASSERT, CASE, QUESTION, MAP_CONSTRUCTOR, LABELED_ARG, SPREAD_MAP_ARG
202    
203        // visitComma ...
204        //   token type COMMA never created/visited,
205        //   see TYPE_ARGUMENTS, ANNOTATION, many others ...
206        
207        public void visitCompareTo(GroovySourceAST t,int visit) {
208            print(t,visit," <=> ",null,null);
209        }
210    
211        public void visitCtorCall(GroovySourceAST t,int visit) {
212            printUpdatingTabLevel(t,visit,"this("," ",")");
213        }
214    
215        public void visitCtorIdent(GroovySourceAST t, int visit) {
216            // use name of class for constructor from the class definition
217            print(t,visit,className,null,null);
218        }
219    
220        public void visitDec(GroovySourceAST t, int visit) {
221            print(t,visit,"--",null,null);
222        }
223        
224        // visitDigit ...
225        //    never created/visited
226        
227        public void visitDiv(GroovySourceAST t, int visit) {
228            print(t,visit," / ",null,null);
229        }
230    
231            public void visitDivAssign(GroovySourceAST t,int visit) {
232            print(t,visit," /= ",null,null);
233        }
234            
235        // visitDollar ...
236        //   token type DOLLAR never created/visited, see SCOPE_ESCAPE instead
237        
238        public void visitDot(GroovySourceAST t,int visit) {
239            print(t,visit,".",null,null);
240        }
241        
242        public void visitDynamicMember(GroovySourceAST t, int visit) {
243            if (t.childOfType(GroovyTokenTypes.STRING_CONSTRUCTOR) == null) {
244                    printUpdatingTabLevel(t,visit,"(",null,")");
245            }
246        }
247        
248        public void visitElist(GroovySourceAST t,int visit) {
249            if (getParentNode().getType() == GroovyTokenTypes.ENUM_CONSTANT_DEF) {
250                    print(t,visit,"(",", ",")");
251            } else {
252                    print(t,visit,null,", ",null);
253            }
254        }
255    
256        // visitEmptyStat ...
257        //   token type EMPTY_STAT obsolete and should be removed, never visited/created
258        
259        public void visitEnumConstantDef(GroovySourceAST t,int visit) {
260            GroovySourceAST sibling = (GroovySourceAST)t.getNextSibling();
261            if (sibling != null && sibling.getType() == GroovyTokenTypes.ENUM_CONSTANT_DEF) {
262                    print(t,visit,null,null,", ");
263            }
264        }
265    
266        public void visitEnumDef(GroovySourceAST t,int visit) {
267            print(t,visit,"enum ",null,null);
268        }
269    
270        // visitEof ...
271        //   token type EOF never visited/created
272    
273        public void visitEqual(GroovySourceAST t,int visit) {
274            print(t,visit," == ",null,null);
275        }
276    
277        // visitExponent ...
278        //   token type EXPONENT only used by lexer, never visited/created
279        
280        public void visitExpr(GroovySourceAST t,int visit) {
281            // do nothing
282        }
283    
284        public void visitExtendsClause(GroovySourceAST t,int visit) {
285            if (visit == OPENING_VISIT) {
286                if (t.getNumberOfChildren() != 0) {
287                    print(t,visit," extends ");
288                }
289            }
290        }
291        
292            public void visitFinal(GroovySourceAST t, int visit) {
293            print(t,visit,"final ",null,null);
294            }
295    
296            // visitFloatSuffix ... never visited/created see NUM_DOUBLE or NUM_FLOAT instead
297            
298            public void visitForCondition(GroovySourceAST t, int visit) {
299            print(t,visit," ; ",null,null);
300        }
301            
302            // visitForEachClause ... 
303            //   FOR_EACH_CLAUSE obsolete and should be removed, never visited/created
304    
305        public void visitForInit(GroovySourceAST t, int visit) {
306            print(t,visit,"(",null,null);
307        }
308        
309        public void visitForInIterable(GroovySourceAST t, int visit) {
310            printUpdatingTabLevel(t,visit,"("," in ",") ");
311        }
312    
313        public void visitForIterator(GroovySourceAST t, int visit) {
314            print(t,visit," ; ",null,")");
315        }
316        
317        public void visitGe(GroovySourceAST t, int visit) {
318            print(t,visit," >= ",null,null);
319        }
320        
321        public void visitGt(GroovySourceAST t, int visit) {
322            print(t,visit," > ",null,null);
323        }
324    
325        public void visitIdent(GroovySourceAST t,int visit) {
326            print(t,visit,t.getText(),null,null);
327        }
328        public void visitImplementsClause(GroovySourceAST t,int visit) {
329            if (visit == OPENING_VISIT) {
330                if (t.getNumberOfChildren() != 0) {
331                    print(t,visit," implements ");
332                }
333            }
334            if (visit == CLOSING_VISIT) {
335                //space between classdef and objblock
336                print(t,visit," ");
337            }
338        }
339    
340        public void visitImplicitParameters(GroovySourceAST t, int visit) {
341            // do nothing
342        }
343    
344        public void visitImport(GroovySourceAST t,int visit) {
345            print(t,visit,"import ",null,null);
346        }
347    
348        public void visitInc(GroovySourceAST t, int visit) {
349            print(t,visit,"++",null,null);
350        }
351    
352        public void visitIndexOp(GroovySourceAST t, int visit) {
353            printUpdatingTabLevel(t,visit,"[",null,"]");
354        }
355    
356        public void visitInterfaceDef(GroovySourceAST t,int visit) {
357            print(t,visit,"interface ",null,null);
358        }
359    
360        public void visitInstanceInit(GroovySourceAST t, int visit) {
361            // do nothing
362            }
363    
364            public void visitLabeledArg(GroovySourceAST t, int visit) {
365            print(t,visit,":",null,null);
366        }
367    
368            public void visitLabeledStat(GroovySourceAST t, int visit) {
369            print(t,visit,":",null,null);
370        }
371    
372        public void visitLand(GroovySourceAST t, int visit) {
373            print(t,visit," && ",null,null);
374        }
375    
376        // visit lbrack()
377        //   token type LBRACK only used inside parser, never visited/created
378    
379        // visit lcurly()
380        //   token type LCURLY only used inside parser, never visited/created
381        
382        public void visitLe(GroovySourceAST t, int visit) {
383            print(t,visit," <= ",null,null);
384        }
385    
386        // visitLetter ...
387        //   token type LETTER only used by lexer, never visited/created
388    
389        public void visitListConstructor(GroovySourceAST t, int visit) {
390            printUpdatingTabLevel(t,visit,"[",null,"]");
391        }
392    
393        public void visitLiteralAny(GroovySourceAST t,int visit) {
394            print(t,visit,"any",null,null);
395        }
396    
397        public void visitLiteralAs(GroovySourceAST t,int visit) {
398            print(t,visit," as ",null,null);
399        }
400    
401        public void visitLiteralAssert(GroovySourceAST t,int visit) {
402            if (t.getNumberOfChildren() > 1) {
403                    print(t,visit,"assert ",null," : ");
404            } else {
405                    print(t,visit,"assert ",null,null);
406            }
407        }
408    
409        public void visitLiteralBoolean(GroovySourceAST t, int visit) {
410            print(t,visit,"boolean",null,null);
411        }
412    
413        public void visitLiteralBreak(GroovySourceAST t, int visit) {
414            print(t,visit,"break ",null,null);
415        }
416    
417        public void visitLiteralByte(GroovySourceAST t, int visit) {
418            print(t,visit,"byte",null,null);
419        }
420    
421        public void visitLiteralCase(GroovySourceAST t, int visit) {
422            print(t,visit,"case ",null,":");
423        }
424    
425        public void visitLiteralCatch(GroovySourceAST t,int visit) {
426            printUpdatingTabLevel(t,visit," catch (",null,") ");
427        }
428    
429        public void visitLiteralChar(GroovySourceAST t, int visit) {
430            print(t,visit,"char",null,null);
431        }
432    
433        // visitLiteralClass ...
434        //   token type "class" only used by parser, never visited/created directly
435    
436        public void visitLiteralContinue(GroovySourceAST t, int visit) {
437            print(t,visit,"continue ",null,null);
438        }
439    
440        // visitLiteralDef ...
441        //   token type "def" only used by parser, never visited/created directly
442    
443        public void visitLiteralDefault(GroovySourceAST t,int visit) {
444            print(t,visit,"default",null,":");
445        }
446    
447        public void visitLiteralDouble(GroovySourceAST t, int visit) {
448            print(t,visit,"double",null,null);
449        }
450    
451        // visitLiteralElse ...
452        //   token type "else" only used by parser, never visited/created directly
453    
454        // visitLiteralEnum ...
455        //   token type "enum" only used by parser, never visited/created directly
456    
457        // visitLiteralExtends
458        //   token type "extends" only used by parser, never visited/created directly
459        
460        public void visitLiteralFalse(GroovySourceAST t,int visit) {
461            print(t,visit,"false",null,null);
462        }
463    
464        public void visitLiteralFinally(GroovySourceAST t,int visit) {
465            print(t,visit,"finally ",null,null);
466        }
467        public void visitLiteralFloat(GroovySourceAST t,int visit) {
468            print(t,visit,"float",null,null);
469        }
470    
471        public void visitLiteralFor(GroovySourceAST t,int visit) {
472            print(t,visit,"for ",null,null);
473        }
474    
475        public void visitLiteralIf(GroovySourceAST t,int visit) {
476            // slightly strange as subsequent visit is done after closing visit
477            printUpdatingTabLevel(t,visit,"if ("," else ",") ");
478        }
479    
480        // visitLiteralImplements
481        //   token type "implements" only used by parser, never visited/created directly
482    
483        // visitLiteralImport
484        //   token type "import" only used by parser, never visited/created directly
485    
486        public void visitLiteralIn(GroovySourceAST t, int visit) {
487            print(t,visit," in ",null,null);
488        }
489    
490        public void visitLiteralInstanceof(GroovySourceAST t, int visit) {
491            print(t,visit," instanceof ",null,null);
492        }
493    
494        public void visitLiteralInt(GroovySourceAST t,int visit) {
495            print(t,visit,"int",null,null);
496        }
497    
498        // visitLiteralInterface
499        //   token type "interface" only used by parser, never visited/created directly
500    
501        public void visitLiteralLong(GroovySourceAST t,int visit) {
502            print(t,visit,"long",null,null);
503        }
504    
505        public void visitLiteralNative(GroovySourceAST t,int visit) {
506            print(t,visit,"native ",null,null);
507        }
508        public void visitLiteralNew(GroovySourceAST t,int visit) {
509            if (t.childOfType(GroovyTokenTypes.ARRAY_DECLARATOR) == null) {
510                    // only print parenthesis if is not of form def x = new int[5]
511                    print(t,visit,"new ","(",")");
512            } else {
513                    print(t,visit,"new ",null,null);
514            }
515        }
516    
517        public void visitLiteralNull(GroovySourceAST t, int visit) {
518            print(t,visit,"null",null,null);
519        }
520    
521        // visitLiteralPackage
522        //   token type "package" only used by parser, never visited/created directly
523    
524        public void visitLiteralPrivate(GroovySourceAST t,int visit) {
525            print(t,visit,"private ",null,null);
526        }
527    
528        public void visitLiteralProtected(GroovySourceAST t,int visit) {
529            print(t,visit,"protected ",null,null);
530        }
531    
532        public void visitLiteralPublic(GroovySourceAST t,int visit) {
533            print(t,visit,"public ",null,null);
534        }
535    
536        public void visitLiteralReturn(GroovySourceAST t, int visit) {
537            print(t,visit,"return ",null,null);
538        }
539    
540        public void visitLiteralShort(GroovySourceAST t,int visit) {
541            print(t,visit,"short",null,null);
542        }
543    
544        public void visitLiteralStatic(GroovySourceAST t, int visit) {
545            print(t,visit,"static ",null,null);
546        }
547    
548        public void visitLiteralSuper(GroovySourceAST t, int visit) {
549            // only visited when calling super() without parentheses, i.e. "super 99" is equivalent to "super(99)"
550            print(t,visit,"super",null,null);
551        }
552    
553        public void visitLiteralSwitch(GroovySourceAST t, int visit) {
554            if (visit == OPENING_VISIT) {
555                print(t,visit,"switch (");
556                tabLevel++;
557            }
558            if (visit == SUBSEQUENT_VISIT) {
559                print(t,visit,") {");
560            }
561            if (visit == CLOSING_VISIT) {
562                tabLevel--;
563                print(t,visit,"}");
564            }
565        }
566    
567        public void visitLiteralSynchronized(GroovySourceAST t,int visit) {
568            if (t.getNumberOfChildren() > 0) {
569                    print(t,visit,"synchronized (",null,") ");
570            } else {
571                    print(t,visit,"synchronized ",null,null);               
572            }
573            }
574    
575        public void visitLiteralThis(GroovySourceAST t, int visit) {
576            print(t,visit,"this",null,null);
577        }
578    
579        public void visitLiteralThreadsafe(GroovySourceAST t,int visit) {
580            print(t,visit,"threadsafe ",null,null);
581        }
582    
583        public void visitLiteralThrow(GroovySourceAST t, int visit) {
584            print(t,visit,"throw ",null,null);
585        }
586    
587        public void visitLiteralThrows(GroovySourceAST t, int visit) {
588            print(t,visit,"throws ",null,null);
589        }
590    
591        public void visitLiteralTransient(GroovySourceAST t,int visit) {
592            print(t,visit,"transient ",null,null);
593        }
594    
595        public void visitLiteralTrue(GroovySourceAST t,int visit) {
596            print(t,visit,"true",null,null);
597        }
598        public void visitLiteralTry(GroovySourceAST t,int visit) {
599            print(t,visit,"try ",null,null);
600        }
601        public void visitLiteralVoid(GroovySourceAST t,int visit) {
602            print(t,visit,"void",null,null);
603        }
604        public void visitLiteralVolatile(GroovySourceAST t,int visit) {
605            print(t,visit,"volatile ",null,null);
606        }
607        public void visitLiteralWhile(GroovySourceAST t,int visit) {
608            printUpdatingTabLevel(t,visit,"while (",null,") ");
609        }
610    
611        public void visitLiteralWith(GroovySourceAST t,int visit) {
612            printUpdatingTabLevel(t,visit,"with (",null,") ");
613        }
614        
615        public void visitLnot(GroovySourceAST t, int visit) {
616            print(t,visit,"!",null,null);
617        }
618    
619            // Note: old closure syntax using LOR is deprecated, and also never creates/visits a LOR node
620        public void visitLor(GroovySourceAST t, int visit) {
621            print(t,visit," || ",null,null);
622        }
623    
624        public void visitLt(GroovySourceAST t, int visit) {
625            print(t,visit," < ",null,null);
626        }
627    
628        public void visitMapConstructor(GroovySourceAST t, int visit) {
629            if (t.getNumberOfChildren() == 0) {
630                print(t,visit,"[:]",null,null);
631            } else {
632                printUpdatingTabLevel(t,visit,"[",null,"]");
633            }
634        }
635    
636        public void visitMemberPointer(GroovySourceAST t, int visit) {
637            print(t,visit,".&",null,null);
638        }
639    
640        public void visitMethodCall(GroovySourceAST t,int visit) {
641            if ("<command>".equals(t.getText())) {
642                    printUpdatingTabLevel(t,visit," "," ",null);
643            } else {
644                    printUpdatingTabLevel(t,visit,"("," ",")");
645            }
646        }
647        public void visitMethodDef(GroovySourceAST t,int visit) {
648            //do nothing
649        }
650        public void visitMinus(GroovySourceAST t,int visit) {
651            print(t,visit," - ",null,null);
652        }
653        public void visitMinusAssign(GroovySourceAST t, int visit) {
654            print(t,visit," -= ",null,null);
655        }
656    
657        // visitMlComment
658        //   multi-line comments are not created on the AST currently.
659    
660        public void visitMod(GroovySourceAST t, int visit) {
661            print(t,visit," % ",null,null);
662        }
663    
664        public void visitModifiers(GroovySourceAST t,int visit) {
665            //do nothing
666        }
667        public void visitModAssign(GroovySourceAST t, int visit) {
668            print(t,visit," %= ",null,null);
669        }
670    
671        // visitNls
672        //   new lines are used by parser, but are not created on the AST,
673        //   they can be implied by the source code line/column information
674    
675        // visitNullTreeLookahead
676        //   not used explicitly by parser.
677        
678        
679        public void visitNotEqual(GroovySourceAST t, int visit) {
680            print(t,visit," != ",null,null);
681        }
682    
683        public void visitNumBigDecimal(GroovySourceAST t,int visit) {
684            print(t,visit,t.getText(),null,null);
685        }
686        public void visitNumBigInt(GroovySourceAST t,int visit) {
687            print(t,visit,t.getText(),null,null);
688        }
689        public void visitNumDouble(GroovySourceAST t,int visit) {
690            print(t,visit,t.getText(),null,null);
691        }
692        public void visitNumInt(GroovySourceAST t,int visit) {
693            print(t,visit,t.getText(),null,null);
694        }
695        public void visitNumFloat(GroovySourceAST t,int visit) {
696            print(t,visit,t.getText(),null,null);
697        }
698        public void visitNumLong(GroovySourceAST t,int visit) {
699            print(t,visit,t.getText(),null,null);
700        }
701        public void visitObjblock(GroovySourceAST t,int visit) {
702            if (visit == OPENING_VISIT) {
703                tabLevel++;
704                print(t,visit,"{");
705            } else {
706                tabLevel--;
707                print(t,visit,"}");
708            }
709        }
710    
711        // visitOneNl
712        //   new lines are used by parser, but are not created on the AST,
713        //   they can be implied by the source code line/column information
714    
715        public void visitOptionalDot(GroovySourceAST t,int visit) {
716            print(t,visit,"?.",null,null);
717        }
718        
719        public void visitPackageDef(GroovySourceAST t, int visit) {
720            print(t,visit,"package ",null,null);
721        }
722    
723        public void visitParameterDef(GroovySourceAST t,int visit) {
724            //do nothing
725        }
726    
727        public void visitParameters(GroovySourceAST t,int visit) {
728            if (getParentNode().getType() == GroovyTokenTypes.CLOSABLE_BLOCK) {
729                    printUpdatingTabLevel(t,visit,null,","," ");
730            } else {
731                    printUpdatingTabLevel(t,visit,"(",", ",") ");
732            }
733        }
734    
735        public void visitPlus(GroovySourceAST t, int visit) {
736            print(t,visit," + ",null,null);
737        }
738        
739        public void visitPlusAssign(GroovySourceAST t, int visit) {
740            print(t,visit," += ",null,null);
741        }
742        public void visitPostDec(GroovySourceAST t, int visit) {
743            print(t,visit,null,null,"--");
744        }
745    
746        public void visitPostInc(GroovySourceAST t, int visit) {
747            print(t,visit,null,null,"++");
748        }
749    
750        public void visitQuestion(GroovySourceAST t, int visit) {
751            // ternary operator
752            print(t,visit,"?",":",null);
753        }
754    
755        public void visitRangeExclusive(GroovySourceAST t, int visit) {
756            print(t,visit,"..<",null,null);
757        }
758    
759        public void visitRangeInclusive(GroovySourceAST t, int visit) {
760            print(t,visit,"..",null,null);
761        }
762    
763        // visit rbrack()
764        //   token type RBRACK only used inside parser, never visited/created
765    
766        // visit rcurly()
767        //   token type RCURLY only used inside parser, never visited/created
768    
769        // visit RegexpCtorEnd
770        // visit RegexpLiteral
771        // visit RegexpSymbol
772        //    token types REGEXP_CTOR_END, REGEXP_LITERAL, REGEXP_SYMBOL only used inside lexer
773        
774        public void visitRegexFind(GroovySourceAST t, int visit) {
775            print(t,visit," =~ ",null,null);
776        }
777        public void visitRegexMatch(GroovySourceAST t, int visit) {
778            print(t,visit," ==~ ",null,null);
779        }
780        // visit rparen()
781        //   token type RPAREN only used inside parser, never visited/created
782    
783        public void visitScopeEscape(GroovySourceAST t, int visit) {
784            print(t,visit,"$",null,null);
785        }
786        public void visitSelectSlot(GroovySourceAST t, int visit) {
787            print(t,visit,"@",null,null);
788        }
789        
790        // visit semi()
791        //  SEMI only used inside parser, never visited/created (see visitForCondition(), visitForIterator())
792        
793        // visit ShComment()
794        //  never visited/created by parser
795        
796        public void visitSl(GroovySourceAST t, int visit) {
797            print(t,visit," << ",null,null);
798        }
799        public void visitSlAssign(GroovySourceAST t, int visit) {
800            print(t,visit," <<= ",null,null);
801        }
802        public void visitSlist(GroovySourceAST t,int visit) {
803            if (visit == OPENING_VISIT) {
804                tabLevel++;
805                print(t,visit,"{");
806            } else {
807                tabLevel--;
808                print(t,visit,"}");
809            }
810        }
811    
812        // visit SlComment()
813        //   never visited/created by parser
814        
815        public void visitSpreadArg(GroovySourceAST t,int visit) {
816            print(t,visit,"*",null,null);
817        }
818        
819        public void visitSpreadMapArg(GroovySourceAST t,int visit) {
820            print(t,visit,"*:",null,null);
821        }
822        
823        public void visitSr(GroovySourceAST t, int visit) {
824            print(t,visit," >> ",null,null);
825        }
826        public void visitSrAssign(GroovySourceAST t, int visit) {
827            print(t,visit," >>= ",null,null);
828        }
829    
830        public void visitStar(GroovySourceAST t,int visit) {
831            print(t,visit,"*",null,null);
832        }
833        public void visitStarAssign(GroovySourceAST t, int visit) {
834            print(t,visit," *= ",null,null);
835        }
836        public void visitStarStar(GroovySourceAST t,int visit) {
837            print(t,visit,"**",null,null);
838        }
839        public void visitStarStarAssign(GroovySourceAST t, int visit) {
840            print(t,visit," **= ",null,null);
841        }
842        
843        public void visitStaticInit(GroovySourceAST t, int visit) {
844            print(t,visit,"static ",null,null);
845        }
846        public void visitStaticImport(GroovySourceAST t,int visit) {
847            print(t,visit,"import static ",null,null);
848        }
849        public void visitStrictfp(GroovySourceAST t,int visit) {
850            print(t,visit,"strictfp ",null,null);
851        }
852    
853        // visitStringch
854        //   String characters only used by lexer, never visited/created directly
855    
856    
857        public void visitStringConstructor(GroovySourceAST t,int visit) {
858            if (visit == OPENING_VISIT) {
859                stringConstructorCounter = 0;
860                print(t,visit,"\"");
861            }
862            if (visit == SUBSEQUENT_VISIT) {
863                // every other subsequent visit use an escaping $
864                if (stringConstructorCounter % 2 == 0) {
865                   print(t,visit,"$");
866                }
867                stringConstructorCounter++;
868            }
869            if (visit == CLOSING_VISIT) {
870                print(t,visit,"\"");
871            }
872        }
873    
874        public void visitStringLiteral(GroovySourceAST t,int visit) {
875            if (visit == OPENING_VISIT) {
876                String theString = escape(t.getText());
877            if (getParentNode().getType() != GroovyTokenTypes.LABELED_ARG &&
878                getParentNode().getType() != GroovyTokenTypes.STRING_CONSTRUCTOR) {
879                    theString = "\"" + theString + "\"";
880                }
881                print(t,visit,theString);
882            }
883        }
884    
885        private String escape(String literal) {
886            literal = literal.replaceAll("\n","\\\\<<REMOVE>>n"); // can't seem to do \n in one go with Java regex
887            literal = literal.replaceAll("<<REMOVE>>","");
888            return literal;
889        }
890    
891        public void visitSuperCtorCall(GroovySourceAST t,int visit) {
892                    printUpdatingTabLevel(t,visit,"super("," ",")");
893        }
894        
895        // visit TripleDot, not used in the AST
896        
897        public void visitType(GroovySourceAST t,int visit) {
898            GroovySourceAST parent = getParentNode();
899            GroovySourceAST modifiers = parent.childOfType(GroovyTokenTypes.MODIFIERS);
900    
901            // No need to print 'def' if we already have some modifiers
902            if (modifiers == null || modifiers.getNumberOfChildren() == 0) {
903    
904                if (visit == OPENING_VISIT) {
905                    if (t.getNumberOfChildren() == 0 && 
906                                    parent.getType() != GroovyTokenTypes.PARAMETER_DEF) { // no need for 'def' if in a parameter list
907                        print(t,visit,"def");
908                    }
909                }
910                    if (visit == CLOSING_VISIT) {
911                            print(t,visit," ");
912                }
913            } else {
914                    if (visit == CLOSING_VISIT) {
915                            if (t.getNumberOfChildren() != 0) {
916                                    print(t,visit," ");
917                            }
918                    }
919            }
920        }
921        public void visitTypeArgument(GroovySourceAST t, int visit) {
922            // print nothing
923        }
924    
925        public void visitTypeArguments(GroovySourceAST t, int visit) {
926            print(t,visit,"<",", ",">");
927        }
928    
929        public void visitTypecast(GroovySourceAST t,int visit) {
930            print(t,visit,"(",null,")");
931        }
932        public void visitTypeLowerBounds(GroovySourceAST t,int visit) {
933            print(t,visit," super "," & ",null);
934        }
935        public void visitTypeParameter(GroovySourceAST t, int visit) {
936            // print nothing
937        }
938    
939        public void visitTypeParameters(GroovySourceAST t, int visit) {
940            print(t,visit,"<",", ",">");
941        }
942    
943        public void visitTypeUpperBounds(GroovySourceAST t,int visit) {
944            print(t,visit," extends "," & ",null);
945        }
946        public void visitUnaryMinus(GroovySourceAST t, int visit) {
947            print(t,visit,"-",null,null);
948        }
949        public void visitUnaryPlus(GroovySourceAST t, int visit) {
950            print(t,visit,"+",null,null);
951        }
952    
953        // visit Unused "const", "do", "goto" - unsurprisingly these are unused by the AST.
954        
955        public void visitVariableDef(GroovySourceAST t,int visit) {
956            // do nothing
957        }
958    
959        // a.k.a. "variable arity parameter" in the JLS
960        public void visitVariableParameterDef(GroovySourceAST t,int visit) {
961            print(t,visit,null,"... ",null);
962        }
963        
964        // visit Vocab - only used by Lexer
965        
966        public void visitWildcardType(GroovySourceAST t, int visit) {
967            print(t,visit,"?",null,null);
968        }
969    
970        // visit WS - only used by lexer
971        
972        
973        
974        public void visitDefault(GroovySourceAST t,int visit) {
975            if (visit == OPENING_VISIT) {
976                print(t,visit,"<" + tokenNames[t.getType()] + ">");
977                //out.print("<" + t.getType() + ">");
978            } else {
979                print(t,visit,"</" + tokenNames[t.getType()] + ">");
980                //out.print("</" + t.getType() + ">");
981            }
982        }
983    
984        protected void printUpdatingTabLevel(GroovySourceAST t,int visit,String opening, String subsequent, String closing) {
985            if (visit == OPENING_VISIT && opening != null) {
986                print(t,visit,opening);
987                tabLevel++;
988            }
989            if (visit == SUBSEQUENT_VISIT && subsequent != null) {
990                print(t,visit,subsequent);
991            }
992            if (visit == CLOSING_VISIT && closing != null) {
993                tabLevel--;
994                print(t,visit,closing);
995            }
996        }
997    
998        protected void print(GroovySourceAST t,int visit,String opening, String subsequent, String closing) {
999            if (visit == OPENING_VISIT && opening != null) {
1000                print(t,visit,opening);
1001            }
1002            if (visit == SUBSEQUENT_VISIT && subsequent != null) {
1003                print(t,visit,subsequent);
1004            }
1005            if (visit == CLOSING_VISIT && closing != null) {
1006                print(t,visit,closing);
1007            }
1008        }
1009        protected void print(GroovySourceAST t,int visit,String value) {
1010            if(visit == OPENING_VISIT) {
1011                printNewlineAndIndent(t, visit);
1012            }
1013            if (visit == CLOSING_VISIT) {
1014                printNewlineAndIndent(t, visit);
1015            }
1016            out.print(value);
1017        }
1018    
1019        protected void printNewlineAndIndent(GroovySourceAST t, int visit) {
1020            int currentLine = t.getLine();
1021            if (lastLinePrinted == 0) { lastLinePrinted = currentLine; }
1022            if (lastLinePrinted != currentLine) {
1023                if (newLines) {
1024                    if (!(visit == OPENING_VISIT && t.getType() == GroovyTokenTypes.SLIST)) {
1025                        for (int i=lastLinePrinted;i<currentLine;i++) {
1026                            out.println();
1027                        }
1028                        if (lastLinePrinted > currentLine) {
1029                            out.println();
1030                        }
1031                        if (visit == OPENING_VISIT || (visit == CLOSING_VISIT && lastLinePrinted > currentLine)) {
1032                            for (int i=0;i<tabLevel;i++) {
1033                                out.print("    ");
1034                            }
1035                        }
1036                    }
1037                }
1038                lastLinePrinted = Math.max(currentLine,lastLinePrinted);
1039            }
1040        }
1041    
1042        public void push(GroovySourceAST t) {
1043            stack.push(t);
1044        }
1045        public GroovySourceAST pop() {
1046            if (!stack.empty()) {
1047                return (GroovySourceAST) stack.pop();
1048            }
1049            return null;
1050        }
1051    
1052        private GroovySourceAST getParentNode() {
1053            Object currentNode = stack.pop();
1054            Object parentNode = stack.peek();
1055            stack.push(currentNode);
1056            return (GroovySourceAST) parentNode;
1057        }
1058    
1059    }