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    
022    import org.codehaus.groovy.antlr.GroovySourceAST;
023    import org.codehaus.groovy.antlr.parser.GroovyTokenTypes;
024    
025    /**
026     * An antlr AST visitor that prints groovy source code for each visited node
027     * to the supplied PrintStream.
028     *
029     * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
030     * @version $Revision: 1.12 $
031     */
032    
033    public class SourcePrinter extends VisitorAdapter {
034        private String[] tokenNames;
035        private int tabLevel;
036        private int lastLinePrinted;
037        private boolean newLines;
038        protected PrintStream out;
039        private String className;
040    
041        /**
042         * A visitor that prints groovy source code for each node visited.
043         * @param out where to print the source code to
044         * @param tokenNames an array of token names from antlr
045         */
046        public SourcePrinter(PrintStream out,String[] tokenNames) {
047            this(out,tokenNames,true);
048        }
049    
050        /**
051         * A visitor that prints groovy source code for each node visited.
052         * @param out where to print the source code to
053         * @param tokenNames an array of token names from antlr
054         * @param newLines output newline character
055         */
056        public SourcePrinter(PrintStream out,String[] tokenNames, boolean newLines) {
057            this.tokenNames = tokenNames;
058            tabLevel = 0;
059            lastLinePrinted = 0;
060            this.out = out;
061            this.newLines = newLines;
062        }
063    
064        public void visitAnnotation(GroovySourceAST t, int visit) {
065            print(t,visit,"@",null,null);
066        }
067    
068        public void visitAnnotations(GroovySourceAST t, int visit) {
069            if (t.getNumberOfChildren() > 0) {
070                //todo - default line below is just a placeholder
071                visitDefault(t,visit);
072            }
073        }
074    
075        public void visitAssign(GroovySourceAST t,int visit) {
076            print(t,visit," = ",null,null);
077        }
078    
079        public void visitCaseGroup(GroovySourceAST t, int visit) {
080            if (visit == OPENING_VISIT) {
081                tabLevel++;
082            }
083            if (visit == CLOSING_VISIT) {
084                tabLevel--;
085            }
086        }
087    
088        public void visitClassDef(GroovySourceAST t,int visit) {
089            print(t,visit,"class ",null,null);
090    
091            if (visit == OPENING_VISIT) {
092                // store name of class away for use in constructor ident
093                className = t.childOfType(GroovyTokenTypes.IDENT).getText();
094            }
095        }
096    
097        public void visitClosedBlock(GroovySourceAST t, int visit) {
098            printUpdatingTabLevel(t,visit,"{"," -> ","}");
099        }
100        public void visitCtorIdent(GroovySourceAST t, int visit) {
101            // use name of class for constructor from the class definition
102            print(t,visit,className,null,null);
103        }
104        public void visitDot(GroovySourceAST t,int visit) {
105            print(t,visit,".",null,null);
106        }
107        public void visitElist(GroovySourceAST t,int visit) {
108            print(t,visit,null,", ",null);
109        }
110    
111        public void visitEqual(GroovySourceAST t,int visit) {
112            print(t,visit," == ",null,null);
113        }
114    
115        public void visitExpr(GroovySourceAST t,int visit) {
116        }
117    
118        public void visitExtendsClause(GroovySourceAST t,int visit) {
119            if (visit == OPENING_VISIT) {
120                if (t.getNumberOfChildren() != 0) {
121                    print(t,visit," extends ");
122                }
123            }
124        }
125    
126        public void visitForInIterable(GroovySourceAST t, int visit) {
127            printUpdatingTabLevel(t,visit,"("," in ",") ");
128        }
129    
130        public void visitGt(GroovySourceAST t, int visit) {
131            print(t,visit," > ",null,null);
132        }
133    
134        public void visitIdent(GroovySourceAST t,int visit) {
135            print(t,visit,t.getText(),null,null);
136        }
137        public void visitImplementsClause(GroovySourceAST t,int visit) {
138            if (visit == OPENING_VISIT) {
139                if (t.getNumberOfChildren() != 0) {
140                    print(t,visit," implements ");
141                }
142            }
143            if (visit == CLOSING_VISIT) {
144                //space between classdef and objblock
145                print(t,visit," ");
146            }
147        }
148    
149        public void visitImplicitParameters(GroovySourceAST t, int visit) {
150        }
151    
152        public void visitImport(GroovySourceAST t,int visit) {
153            print(t,visit,"import ",null,null);
154        }
155    
156        public void visitIndexOp(GroovySourceAST t, int visit) {
157            printUpdatingTabLevel(t,visit,"[",null,"]");
158        }
159    
160        public void visitLabeledArg(GroovySourceAST t, int visit) {
161            print(t,visit,":",null,null);
162        }
163    
164        public void visitLand(GroovySourceAST t, int visit) {
165            print(t,visit," && ",null,null);
166        }
167    
168        public void visitListConstructor(GroovySourceAST t, int visit) {
169            printUpdatingTabLevel(t,visit,"[",null,"]");
170        }
171    
172        public void visitLiteralAssert(GroovySourceAST t,int visit) {
173            print(t,visit,"assert ",null,null);
174        }
175    
176        public void visitLiteralBoolean(GroovySourceAST t, int visit) {
177            print(t,visit,"boolean",null,null);
178        }
179    
180        public void visitLiteralBreak(GroovySourceAST t, int visit) {
181            print(t,visit,"break",null,null);
182        }
183    
184        public void visitLiteralCase(GroovySourceAST t, int visit) {
185            print(t,visit,"case ",null,":");
186        }
187    
188        public void visitLiteralCatch(GroovySourceAST t,int visit) {
189            printUpdatingTabLevel(t,visit," catch (",null,") ");
190        }
191        public void visitLiteralFalse(GroovySourceAST t,int visit) {
192            print(t,visit,"false",null,null);
193        }
194    
195        public void visitLiteralFloat(GroovySourceAST t,int visit) {
196            print(t,visit,"float",null,null);
197        }
198    
199        public void visitLiteralFor(GroovySourceAST t,int visit) {
200            print(t,visit,"for ",null,null);
201        }
202    
203        public void visitLiteralIf(GroovySourceAST t,int visit) {
204            // slightly strange as subsequent visit is done after closing visit
205            printUpdatingTabLevel(t,visit,"if ("," else ",") ");
206        }
207    
208        public void visitLiteralInstanceof(GroovySourceAST t, int visit) {
209            print(t,visit," instanceof ",null,null);
210        }
211    
212        public void visitLiteralInt(GroovySourceAST t,int visit) {
213            print(t,visit,"int",null,null);
214        }
215    
216        public void visitLiteralNew(GroovySourceAST t,int visit) {
217            print(t,visit,"new ","(",")");
218        }
219    
220        public void visitLiteralNull(GroovySourceAST t, int visit) {
221            print(t,visit,"null",null,null);
222        }
223    
224        public void visitLiteralPrivate(GroovySourceAST t,int visit) {
225            print(t,visit,"private ",null,null);
226        }
227    
228        public void visitLiteralProtected(GroovySourceAST t,int visit) {
229            print(t,visit,"protected ",null,null);
230        }
231    
232        public void visitLiteralPublic(GroovySourceAST t,int visit) {
233            print(t,visit,"public ",null,null);
234        }
235    
236        public void visitLiteralReturn(GroovySourceAST t, int visit) {
237            print(t,visit,"return ",null,null);
238        }
239    
240        public void visitLiteralStatic(GroovySourceAST t, int visit) {
241            print(t,visit,"static ",null,null);
242        }
243    
244        public void visitLiteralSwitch(GroovySourceAST t, int visit) {
245            if (visit == OPENING_VISIT) {
246                print(t,visit,"switch (");
247                tabLevel++;
248            }
249            if (visit == SUBSEQUENT_VISIT) {
250                print(t,visit,") {");
251            }
252            if (visit == CLOSING_VISIT) {
253                tabLevel--;
254                print(t,visit,"}");
255            }
256        }
257    
258        public void visitLiteralThis(GroovySourceAST t, int visit) {
259            print(t,visit,"this",null,null);
260        }
261    
262        public void visitLiteralThrow(GroovySourceAST t, int visit) {
263            print(t,visit,"throw ",null,null);
264        }
265    
266        public void visitLiteralTrue(GroovySourceAST t,int visit) {
267            print(t,visit,"true",null,null);
268        }
269        public void visitLiteralTry(GroovySourceAST t,int visit) {
270            print(t,visit,"try ",null,null);
271        }
272        public void visitLiteralVoid(GroovySourceAST t,int visit) {
273            print(t,visit,"void",null,null);
274        }
275        public void visitLiteralWhile(GroovySourceAST t,int visit) {
276            printUpdatingTabLevel(t,visit,"while (",null,") ");
277        }
278    
279        public void visitLnot(GroovySourceAST t, int visit) {
280            print(t,visit,"!",null,null);
281        }
282    
283        public void visitLt(GroovySourceAST t, int visit) {
284            print(t,visit," < ",null,null);
285        }
286    
287        public void visitMapConstructor(GroovySourceAST t, int visit) {
288            if (t.getNumberOfChildren() == 0) {
289                print(t,visit,"[:]",null,null);
290            } else {
291                printUpdatingTabLevel(t,visit,"[",null,"]");
292            }
293        }
294    
295        public void visitMemberPointer(GroovySourceAST t, int visit) {
296            print(t,visit,".&",null,null);
297        }
298    
299        public void visitMethodCall(GroovySourceAST t,int visit) {
300            printUpdatingTabLevel(t,visit,"("," ",")");
301        }
302        public void visitMinus(GroovySourceAST t,int visit) {
303            print(t,visit," - ",null,null);
304        }
305        public void visitMethodDef(GroovySourceAST t,int visit) {
306            //do nothing
307        }
308        public void visitModifiers(GroovySourceAST t,int visit) {
309            //do nothing
310        }
311    
312        public void visitNotEqual(GroovySourceAST t, int visit) {
313            print(t,visit," != ",null,null);
314        }
315    
316        public void visitNumInt(GroovySourceAST t,int visit) {
317            print(t,visit,t.getText(),null,null);
318        }
319        public void visitNumFloat(GroovySourceAST t,int visit) {
320            print(t,visit,t.getText(),null,null);
321        }
322        public void visitObjblock(GroovySourceAST t,int visit) {
323            if (visit == OPENING_VISIT) {
324                tabLevel++;
325                print(t,visit,"{");
326            } else {
327                tabLevel--;
328                print(t,visit,"}");
329            }
330        }
331    
332        public void visitPackageDef(GroovySourceAST t, int visit) {
333            print(t,visit,"package ",null,null);
334        }
335    
336        public void visitParameterDef(GroovySourceAST t,int visit) {
337            //do nothing
338        }
339    
340        public void visitParameters(GroovySourceAST t,int visit) {
341            printUpdatingTabLevel(t,visit,"(",", ",") ");
342        }
343    
344        public void visitPlus(GroovySourceAST t, int visit) {
345            print(t,visit," + ",null,null);
346        }
347    
348        public void visitQuestion(GroovySourceAST t, int visit) {
349            // ternary operator
350            print(t,visit,"?",":",null);
351        }
352    
353        public void visitRangeExclusive(GroovySourceAST t, int visit) {
354            print(t,visit,"..<",null,null);
355        }
356    
357        public void visitRangeInclusive(GroovySourceAST t, int visit) {
358            print(t,visit,"..",null,null);
359        }
360    
361        public void visitSlist(GroovySourceAST t,int visit) {
362            if (visit == OPENING_VISIT) {
363                tabLevel++;
364                print(t,visit,"{");
365            } else {
366                tabLevel--;
367                print(t,visit,"}");
368            }
369        }
370    
371        public void visitStar(GroovySourceAST t,int visit) {
372            print(t,visit,"*",null,null);
373        }
374        public void visitStringConstructor(GroovySourceAST t,int visit) {
375            print(t,visit,null," + ",null); // string concatenate, so ("abc$foo") becomes ("abc" + foo) for now (todo)
376        }
377    
378        public void visitStringLiteral(GroovySourceAST t,int visit) {
379            print(t,visit,"\"" + escape(t.getText()) + "\"",null,null);
380        }
381    
382        private String escape(String literal) {
383            literal = literal.replaceAll("\n","\\\\<<REMOVE>>n"); // can't seem to do \n in one go with Java regex
384            literal = literal.replaceAll("<<REMOVE>>","");
385            return literal;
386        }
387    
388        public void visitType(GroovySourceAST t,int visit) {
389            if (visit == OPENING_VISIT) {
390                if (t.getNumberOfChildren() == 0) {
391                    print(t,visit,"def");
392                }
393            }
394            if (visit == CLOSING_VISIT) {
395                print(t,visit," ");
396            }
397        }
398    
399        public void visitTypecast(GroovySourceAST t,int visit) {
400            print(t,visit,"(",null,")");
401        }
402    
403        public void visitVariableDef(GroovySourceAST t,int visit) {
404            // do nothing
405        }
406    
407        public void visitDefault(GroovySourceAST t,int visit) {
408            if (visit == OPENING_VISIT) {
409                print(t,visit,"<" + tokenNames[t.getType()] + ">");
410                //out.print("<" + t.getType() + ">");
411            } else {
412                print(t,visit,"</" + tokenNames[t.getType()] + ">");
413                //out.print("</" + t.getType() + ">");
414            }
415        }
416        protected void printUpdatingTabLevel(GroovySourceAST t,int visit,String opening, String subsequent, String closing) {
417            if (visit == OPENING_VISIT && opening != null) {
418                print(t,visit,opening);
419                tabLevel++;
420            }
421            if (visit == SUBSEQUENT_VISIT && subsequent != null) {
422                print(t,visit,subsequent);
423            }
424            if (visit == CLOSING_VISIT && closing != null) {
425                tabLevel--;
426                print(t,visit,closing);
427            }
428        }
429    
430        protected void print(GroovySourceAST t,int visit,String opening, String subsequent, String closing) {
431            if (visit == OPENING_VISIT && opening != null) {
432                print(t,visit,opening);
433            }
434            if (visit == SUBSEQUENT_VISIT && subsequent != null) {
435                print(t,visit,subsequent);
436            }
437            if (visit == CLOSING_VISIT && closing != null) {
438                print(t,visit,closing);
439            }
440        }
441        protected void print(GroovySourceAST t,int visit,String value) {
442            if(visit == OPENING_VISIT) {
443                printNewlineAndIndent(t, visit);
444            }
445            if (visit == CLOSING_VISIT) {
446                printNewlineAndIndent(t, visit);
447            }
448            out.print(value);
449        }
450    
451        protected void printNewlineAndIndent(GroovySourceAST t, int visit) {
452            int currentLine = t.getLine();
453            if (lastLinePrinted == 0) { lastLinePrinted = currentLine; }
454            if (lastLinePrinted != currentLine) {
455                if (newLines) {
456                    if (!(visit == OPENING_VISIT && t.getType() == GroovyTokenTypes.SLIST)) {
457                        for (int i=lastLinePrinted;i<currentLine;i++) {
458                            out.println();
459                        }
460                        if (lastLinePrinted > currentLine) {
461                            out.println();
462                        }
463                        if (visit == OPENING_VISIT || (visit == CLOSING_VISIT && lastLinePrinted > currentLine)) {
464                            for (int i=0;i<tabLevel;i++) {
465                                out.print("    ");
466                            }
467                        }
468                    }
469                }
470                lastLinePrinted = Math.max(currentLine,lastLinePrinted);
471            }
472        }
473    }