001    /*
002     * Copyright 1999-2008 Sun Microsystems, Inc.  All Rights Reserved.
003     * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004     *
005     * This code is free software; you can redistribute it and/or modify it
006     * under the terms of the GNU General Public License version 2 only, as
007     * published by the Free Software Foundation.  Sun designates this
008     * particular file as subject to the "Classpath" exception as provided
009     * by Sun in the LICENSE file that accompanied this code.
010     *
011     * This code is distributed in the hope that it will be useful, but WITHOUT
012     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014     * version 2 for more details (a copy is included in the LICENSE file that
015     * accompanied this code).
016     *
017     * You should have received a copy of the GNU General Public License version
018     * 2 along with this work; if not, write to the Free Software Foundation,
019     * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020     *
021     * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022     * CA 95054 USA or visit www.sun.com if you need additional information or
023     * have any questions.
024     */
025    
026    package com.sun.tools.javac.tree;
027    
028    import java.io.*;
029    import java.util.*;
030    
031    import com.sun.tools.javac.util.*;
032    import com.sun.tools.javac.util.List;
033    import com.sun.tools.javac.code.*;
034    
035    import com.sun.tools.javac.code.Symbol.*;
036    import com.sun.tools.javac.tree.JCTree.*;
037    
038    import static com.sun.tools.javac.code.Flags.*;
039    
040    /** Prints out a tree as an indented Java source program.
041     *
042     *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
043     *  you write code that depends on this, you do so at your own risk.
044     *  This code and its internal interfaces are subject to change or
045     *  deletion without notice.</b>
046     */
047    public class Pretty extends JCTree.Visitor {
048    
049        public Pretty(Writer out, boolean sourceOutput) {
050            this.out = out;
051            this.sourceOutput = sourceOutput;
052        }
053    
054        /** Set when we are producing source output.  If we're not
055         *  producing source output, we can sometimes give more detail in
056         *  the output even though that detail would not be valid java
057         *  soruce.
058         */
059        private final boolean sourceOutput;
060    
061        /** The output stream on which trees are printed.
062         */
063        Writer out;
064    
065        /** Indentation width (can be reassigned from outside).
066         */
067        public int width = 4;
068    
069        /** The current left margin.
070         */
071        protected int lmargin = 0;
072    
073        /** The enclosing class name.
074         */
075        Name enclClassName;
076    
077        /** A hashtable mapping trees to their documentation comments
078         *  (can be null)
079         */
080        Map<JCTree, String> docComments = null;
081    
082        /** Align code to be indented to left margin.
083         */
084        protected void align() throws IOException {
085            for (int i = 0; i < lmargin; i++) out.write(" ");
086        }
087    
088        /** Increase left margin by indentation width.
089         */
090        protected void indent() {
091            lmargin = lmargin + width;
092        }
093    
094        /** Decrease left margin by indentation width.
095         */
096        void undent() {
097            lmargin = lmargin - width;
098        }
099    
100        /** Enter a new precedence level. Emit a `(' if new precedence level
101         *  is less than precedence level so far.
102         *  @param contextPrec    The precedence level in force so far.
103         *  @param ownPrec        The new precedence level.
104         */
105        protected void open(int contextPrec, int ownPrec) throws IOException {
106            if (ownPrec < contextPrec) out.write("(");
107        }
108    
109        /** Leave precedence level. Emit a `(' if inner precedence level
110         *  is less than precedence level we revert to.
111         *  @param contextPrec    The precedence level we revert to.
112         *  @param ownPrec        The inner precedence level.
113         */
114        protected void close(int contextPrec, int ownPrec) throws IOException {
115            if (ownPrec < contextPrec) out.write(")");
116        }
117    
118        /** Print string, replacing all non-ascii character with unicode escapes.
119         */
120        public void print(Object s) throws IOException {
121            out.write(Convert.escapeUnicode(s.toString()));
122        }
123    
124        /** Print new line.
125         */
126        public void println() throws IOException {
127            out.write(lineSep);
128        }
129    
130        protected String lineSep = System.getProperty("line.separator");
131    
132        /**************************************************************************
133         * Traversal methods
134         *************************************************************************/
135    
136        /** Exception to propogate IOException through visitXXX methods */
137        protected static class UncheckedIOException extends Error {
138            static final long serialVersionUID = -4032692679158424751L;
139            public UncheckedIOException(IOException e) {
140                super(e.getMessage(), e);
141            }
142        }
143    
144        /** Visitor argument: the current precedence level.
145         */
146        int prec;
147    
148        /** Visitor method: print expression tree.
149         *  @param prec  The current precedence level.
150         */
151        public void printExpr(JCTree tree, int prec) throws IOException {
152            int prevPrec = this.prec;
153            try {
154                this.prec = prec;
155                if (tree == null) print("/*missing*/");
156                else {
157                    tree.accept(this);
158                }
159            } catch (UncheckedIOException ex) {
160                IOException e = new IOException(ex.getMessage());
161                e.initCause(ex);
162                throw e;
163            } finally {
164                this.prec = prevPrec;
165            }
166        }
167    
168        /** Derived visitor method: print expression tree at minimum precedence level
169         *  for expression.
170         */
171        public void printExpr(JCTree tree) throws IOException {
172            printExpr(tree, TreeInfo.noPrec);
173        }
174    
175        /** Derived visitor method: print statement tree.
176         */
177        public void printStat(JCTree tree) throws IOException {
178            printExpr(tree, TreeInfo.notExpression);
179        }
180    
181        /** Derived visitor method: print list of expression trees, separated by given string.
182         *  @param sep the separator string
183         */
184        public <T extends JCTree> void printExprs(List<T> trees, String sep) throws IOException {
185            if (trees.nonEmpty()) {
186                printExpr(trees.head);
187                for (List<T> l = trees.tail; l.nonEmpty(); l = l.tail) {
188                    print(sep);
189                    printExpr(l.head);
190                }
191            }
192        }
193    
194        /** Derived visitor method: print list of expression trees, separated by commas.
195         */
196        public <T extends JCTree> void printExprs(List<T> trees) throws IOException {
197            printExprs(trees, ", ");
198        }
199    
200        /** Derived visitor method: print list of statements, each on a separate line.
201         */
202        public void printStats(List<? extends JCTree> trees) throws IOException {
203            for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail) {
204                align();
205                printStat(l.head);
206                println();
207            }
208        }
209    
210        /** Print a set of modifiers.
211         */
212        public void printFlags(long flags) throws IOException {
213            if ((flags & SYNTHETIC) != 0) print("/*synthetic*/ ");
214            print(TreeInfo.flagNames(flags));
215            if ((flags & StandardFlags) != 0) print(" ");
216            if ((flags & ANNOTATION) != 0) print("@");
217        }
218    
219        public void printAnnotations(List<JCAnnotation> trees) throws IOException {
220            for (List<JCAnnotation> l = trees; l.nonEmpty(); l = l.tail) {
221                printStat(l.head);
222                println();
223                align();
224            }
225        }
226    
227        /** Print documentation comment, if it exists
228         *  @param tree    The tree for which a documentation comment should be printed.
229         */
230        public void printDocComment(JCTree tree) throws IOException {
231            if (docComments != null) {
232                String dc = docComments.get(tree);
233                if (dc != null) {
234                    print("/**"); println();
235                    int pos = 0;
236                    int endpos = lineEndPos(dc, pos);
237                    while (pos < dc.length()) {
238                        align();
239                        print(" *");
240                        if (pos < dc.length() && dc.charAt(pos) > ' ') print(" ");
241                        print(dc.substring(pos, endpos)); println();
242                        pos = endpos + 1;
243                        endpos = lineEndPos(dc, pos);
244                    }
245                    align(); print(" */"); println();
246                    align();
247                }
248            }
249        }
250    //where
251        static int lineEndPos(String s, int start) {
252            int pos = s.indexOf('\n', start);
253            if (pos < 0) pos = s.length();
254            return pos;
255        }
256    
257        /** If type parameter list is non-empty, print it enclosed in "<...>" brackets.
258         */
259        public void printTypeParameters(List<JCTypeParameter> trees) throws IOException {
260            if (trees.nonEmpty()) {
261                print("<");
262                printExprs(trees);
263                print(">");
264            }
265        }
266    
267        /** Print a block.
268         */
269        public void printBlock(List<? extends JCTree> stats) throws IOException {
270            print("{");
271            println();
272            indent();
273            printStats(stats);
274            undent();
275            align();
276            print("}");
277        }
278    
279        /** Print a block.
280         */
281        public void printEnumBody(List<JCTree> stats) throws IOException {
282            print("{");
283            println();
284            indent();
285            boolean first = true;
286            for (List<JCTree> l = stats; l.nonEmpty(); l = l.tail) {
287                if (isEnumerator(l.head)) {
288                    if (!first) {
289                        print(",");
290                        println();
291                    }
292                    align();
293                    printStat(l.head);
294                    first = false;
295                }
296            }
297            print(";");
298            println();
299            for (List<JCTree> l = stats; l.nonEmpty(); l = l.tail) {
300                if (!isEnumerator(l.head)) {
301                    align();
302                    printStat(l.head);
303                    println();
304                }
305            }
306            undent();
307            align();
308            print("}");
309        }
310    
311        /** Is the given tree an enumerator definition? */
312        boolean isEnumerator(JCTree t) {
313            return t.getTag() == JCTree.VARDEF && (((JCVariableDecl) t).mods.flags & ENUM) != 0;
314        }
315    
316        /** Print unit consisting of package clause and import statements in toplevel,
317         *  followed by class definition. if class definition == null,
318         *  print all definitions in toplevel.
319         *  @param tree     The toplevel tree
320         *  @param cdef     The class definition, which is assumed to be part of the
321         *                  toplevel tree.
322         */
323        public void printUnit(JCCompilationUnit tree, JCClassDecl cdef) throws IOException {
324            docComments = tree.docComments;
325            printDocComment(tree);
326            if (tree.pid != null) {
327                print("package ");
328                printExpr(tree.pid);
329                print(";");
330                println();
331            }
332            boolean firstImport = true;
333            for (List<JCTree> l = tree.defs;
334            l.nonEmpty() && (cdef == null || l.head.getTag() == JCTree.IMPORT);
335            l = l.tail) {
336                if (l.head.getTag() == JCTree.IMPORT) {
337                    JCImport imp = (JCImport)l.head;
338                    Name name = TreeInfo.name(imp.qualid);
339                    if (name == name.table.names.asterisk ||
340                            cdef == null ||
341                            isUsed(TreeInfo.symbol(imp.qualid), cdef)) {
342                        if (firstImport) {
343                            firstImport = false;
344                            println();
345                        }
346                        printStat(imp);
347                    }
348                } else {
349                    printStat(l.head);
350                }
351            }
352            if (cdef != null) {
353                printStat(cdef);
354                println();
355            }
356        }
357        // where
358        boolean isUsed(final Symbol t, JCTree cdef) {
359            class UsedVisitor extends TreeScanner {
360                public void scan(JCTree tree) {
361                    if (tree!=null && !result) tree.accept(this);
362                }
363                boolean result = false;
364                public void visitIdent(JCIdent tree) {
365                    if (tree.sym == t) result = true;
366                }
367            }
368            UsedVisitor v = new UsedVisitor();
369            v.scan(cdef);
370            return v.result;
371        }
372    
373        /**************************************************************************
374         * Visitor methods
375         *************************************************************************/
376    
377        public void visitTopLevel(JCCompilationUnit tree) {
378            try {
379                printUnit(tree, null);
380            } catch (IOException e) {
381                throw new UncheckedIOException(e);
382            }
383        }
384    
385        public void visitImport(JCImport tree) {
386            try {
387                print("import ");
388                if (tree.staticImport) print("static ");
389                printExpr(tree.qualid);
390                print(";");
391                println();
392            } catch (IOException e) {
393                throw new UncheckedIOException(e);
394            }
395        }
396    
397        public void visitClassDef(JCClassDecl tree) {
398            try {
399                println(); align();
400                printDocComment(tree);
401                printAnnotations(tree.mods.annotations);
402                printFlags(tree.mods.flags & ~INTERFACE);
403                Name enclClassNamePrev = enclClassName;
404                enclClassName = tree.name;
405                if ((tree.mods.flags & INTERFACE) != 0) {
406                    print("interface " + tree.name);
407                    printTypeParameters(tree.typarams);
408                    if (tree.implementing.nonEmpty()) {
409                        print(" extends ");
410                        printExprs(tree.implementing);
411                    }
412                } else {
413                    if ((tree.mods.flags & ENUM) != 0)
414                        print("enum " + tree.name);
415                    else
416                        print("class " + tree.name);
417                    printTypeParameters(tree.typarams);
418                    if (tree.extending != null) {
419                        print(" extends ");
420                        printExpr(tree.extending);
421                    }
422                    if (tree.implementing.nonEmpty()) {
423                        print(" implements ");
424                        printExprs(tree.implementing);
425                    }
426                }
427                print(" ");
428                if ((tree.mods.flags & ENUM) != 0) {
429                    printEnumBody(tree.defs);
430                } else {
431                    printBlock(tree.defs);
432                }
433                enclClassName = enclClassNamePrev;
434            } catch (IOException e) {
435                throw new UncheckedIOException(e);
436            }
437        }
438    
439        public void visitMethodDef(JCMethodDecl tree) {
440            try {
441                // when producing source output, omit anonymous constructors
442                if (tree.name == tree.name.table.names.init &&
443                        enclClassName == null &&
444                        sourceOutput) return;
445                println(); align();
446                printDocComment(tree);
447                printExpr(tree.mods);
448                printTypeParameters(tree.typarams);
449                if (tree.name == tree.name.table.names.init) {
450                    print(enclClassName != null ? enclClassName : tree.name);
451                } else {
452                    printExpr(tree.restype);
453                    print(" " + tree.name);
454                }
455                print("(");
456                printExprs(tree.params);
457                print(")");
458                if (tree.thrown.nonEmpty()) {
459                    print(" throws ");
460                    printExprs(tree.thrown);
461                }
462                if (tree.body != null) {
463                    print(" ");
464                    printStat(tree.body);
465                } else {
466                    print(";");
467                }
468            } catch (IOException e) {
469                throw new UncheckedIOException(e);
470            }
471        }
472    
473        public void visitVarDef(JCVariableDecl tree) {
474            try {
475                if (docComments != null && docComments.get(tree) != null) {
476                    println(); align();
477                }
478                printDocComment(tree);
479                if ((tree.mods.flags & ENUM) != 0) {
480                    print("/*public static final*/ ");
481                    printVarDefName(tree);
482                    if (tree.init != null) {
483                        print(" /* = ");
484                        printExpr(tree.init);
485                        print(" */");
486                    }
487                } else {
488                    printExpr(tree.mods);
489                    if ((tree.mods.flags & VARARGS) != 0) {
490                        printExpr(((JCArrayTypeTree) tree.vartype).elemtype);
491                        print("... ");
492                        printVarDefName(tree);
493                    } else {
494                        printExpr(tree.vartype);
495                        print(" ");
496                        printVarDefName(tree);
497                    }
498                    if (tree.init != null) {
499                        print(" = ");
500                        printExpr(tree.init);
501                    }
502                    if (prec == TreeInfo.notExpression) print(";");
503                }
504            } catch (IOException e) {
505                throw new UncheckedIOException(e);
506            }
507        }
508    
509        // prints the name of a variable declaration; can be overridden
510        public void printVarDefName(JCVariableDecl tree) throws IOException {
511            print(tree.name);
512        }
513    
514        public void visitSkip(JCSkip tree) {
515            try {
516                print(";");
517            } catch (IOException e) {
518                throw new UncheckedIOException(e);
519            }
520        }
521    
522        public void visitBlock(JCBlock tree) {
523            try {
524                printFlags(tree.flags);
525                printBlock(tree.stats);
526            } catch (IOException e) {
527                throw new UncheckedIOException(e);
528            }
529        }
530        
531        // mgr: staging additions
532        public void visitBracketExpr(JCBracketExpr tree) {
533            try {
534                print("<|");
535                printStat(tree.body);
536                print("|>");
537            }
538            catch (IOException e) {
539                throw new UncheckedIOException(e);
540            }
541        }
542    
543        public void visitBracketStat(JCBracketStat tree) {
544            try {
545                print("<|{ ");
546                printBlock(tree.body);
547                print("} |>");
548            }
549            catch (IOException e) {
550                throw new UncheckedIOException(e);
551            }
552        }
553    
554        public void visitEscapeExpr(JCEscapeExpr tree) {
555            try {
556                print("`(");
557                printExpr(tree.body);
558                print(")");
559            }
560            catch (IOException e) {
561                throw new UncheckedIOException(e);
562            }
563        }
564    
565        public void visitEscapeStat(JCEscapeStat tree) {
566            try {
567                print("`(");
568                printExpr(tree.body);
569                print(")");
570            }
571            catch (IOException e) {
572                throw new UncheckedIOException(e);
573            }
574        }
575    
576        public void visitDoLoop(JCDoWhileLoop tree) {
577            try {
578                print("do ");
579                printStat(tree.body);
580                align();
581                print(" while ");
582                if (tree.cond.getTag() == JCTree.PARENS) {
583                    printExpr(tree.cond);
584                } else {
585                    print("(");
586                    printExpr(tree.cond);
587                    print(")");
588                }
589                print(";");
590            } catch (IOException e) {
591                throw new UncheckedIOException(e);
592            }
593        }
594    
595        public void visitWhileLoop(JCWhileLoop tree) {
596            try {
597                print("while ");
598                if (tree.cond.getTag() == JCTree.PARENS) {
599                    printExpr(tree.cond);
600                } else {
601                    print("(");
602                    printExpr(tree.cond);
603                    print(")");
604                }
605                print(" ");
606                printStat(tree.body);
607            } catch (IOException e) {
608                throw new UncheckedIOException(e);
609            }
610        }
611    
612        public void visitForLoop(JCForLoop tree) {
613            try {
614                print("for (");
615                if (tree.init.nonEmpty()) {
616                    if (tree.init.head.getTag() == JCTree.VARDEF) {
617                        printExpr(tree.init.head);
618                        for (List<JCStatement> l = tree.init.tail; l.nonEmpty(); l = l.tail) {
619                            JCVariableDecl vdef = (JCVariableDecl)l.head;
620                            print(", " + vdef.name + " = ");
621                            printExpr(vdef.init);
622                        }
623                    } else {
624                        printExprs(tree.init);
625                    }
626                }
627                print("; ");
628                if (tree.cond != null) printExpr(tree.cond);
629                print("; ");
630                printExprs(tree.step);
631                print(") ");
632                printStat(tree.body);
633            } catch (IOException e) {
634                throw new UncheckedIOException(e);
635            }
636        }
637    
638        public void visitForeachLoop(JCEnhancedForLoop tree) {
639            try {
640                print("for (");
641                printExpr(tree.var);
642                print(" : ");
643                printExpr(tree.expr);
644                print(") ");
645                printStat(tree.body);
646            } catch (IOException e) {
647                throw new UncheckedIOException(e);
648            }
649        }
650    
651        public void visitLabelled(JCLabeledStatement tree) {
652            try {
653                print(tree.label + ": ");
654                printStat(tree.body);
655            } catch (IOException e) {
656                throw new UncheckedIOException(e);
657            }
658        }
659    
660        public void visitSwitch(JCSwitch tree) {
661            try {
662                print("switch ");
663                if (tree.selector.getTag() == JCTree.PARENS) {
664                    printExpr(tree.selector);
665                } else {
666                    print("(");
667                    printExpr(tree.selector);
668                    print(")");
669                }
670                print(" {");
671                println();
672                printStats(tree.cases);
673                align();
674                print("}");
675            } catch (IOException e) {
676                throw new UncheckedIOException(e);
677            }
678        }
679    
680        public void visitCase(JCCase tree) {
681            try {
682                if (tree.pat == null) {
683                    print("default");
684                } else {
685                    print("case ");
686                    printExpr(tree.pat);
687                }
688                print(": ");
689                println();
690                indent();
691                printStats(tree.stats);
692                undent();
693                align();
694            } catch (IOException e) {
695                throw new UncheckedIOException(e);
696            }
697        }
698    
699        public void visitSynchronized(JCSynchronized tree) {
700            try {
701                print("synchronized ");
702                if (tree.lock.getTag() == JCTree.PARENS) {
703                    printExpr(tree.lock);
704                } else {
705                    print("(");
706                    printExpr(tree.lock);
707                    print(")");
708                }
709                print(" ");
710                printStat(tree.body);
711            } catch (IOException e) {
712                throw new UncheckedIOException(e);
713            }
714        }
715    
716        public void visitTry(JCTry tree) {
717            try {
718                print("try ");
719                printStat(tree.body);
720                for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
721                    printStat(l.head);
722                }
723                if (tree.finalizer != null) {
724                    print(" finally ");
725                    printStat(tree.finalizer);
726                }
727            } catch (IOException e) {
728                throw new UncheckedIOException(e);
729            }
730        }
731    
732        public void visitCatch(JCCatch tree) {
733            try {
734                print(" catch (");
735                printExpr(tree.param);
736                print(") ");
737                printStat(tree.body);
738            } catch (IOException e) {
739                throw new UncheckedIOException(e);
740            }
741        }
742    
743        public void visitConditional(JCConditional tree) {
744            try {
745                open(prec, TreeInfo.condPrec);
746                printExpr(tree.cond, TreeInfo.condPrec);
747                print(" ? ");
748                printExpr(tree.truepart, TreeInfo.condPrec);
749                print(" : ");
750                printExpr(tree.falsepart, TreeInfo.condPrec);
751                close(prec, TreeInfo.condPrec);
752            } catch (IOException e) {
753                throw new UncheckedIOException(e);
754            }
755        }
756    
757        public void visitIf(JCIf tree) {
758            try {
759                print("if ");
760                if (tree.cond.getTag() == JCTree.PARENS) {
761                    printExpr(tree.cond);
762                } else {
763                    print("(");
764                    printExpr(tree.cond);
765                    print(")");
766                }
767                print(" ");
768                printStat(tree.thenpart);
769                if (tree.elsepart != null) {
770                    print(" else ");
771                    printStat(tree.elsepart);
772                }
773            } catch (IOException e) {
774                throw new UncheckedIOException(e);
775            }
776        }
777    
778        public void visitExec(JCExpressionStatement tree) {
779            try {
780                printExpr(tree.expr);
781                if (prec == TreeInfo.notExpression) print(";");
782            } catch (IOException e) {
783                throw new UncheckedIOException(e);
784            }
785        }
786    
787        public void visitBreak(JCBreak tree) {
788            try {
789                print("break");
790                if (tree.label != null) print(" " + tree.label);
791                print(";");
792            } catch (IOException e) {
793                throw new UncheckedIOException(e);
794            }
795        }
796    
797        public void visitContinue(JCContinue tree) {
798            try {
799                print("continue");
800                if (tree.label != null) print(" " + tree.label);
801                print(";");
802            } catch (IOException e) {
803                throw new UncheckedIOException(e);
804            }
805        }
806    
807        public void visitReturn(JCReturn tree) {
808            try {
809                print("return");
810                if (tree.expr != null) {
811                    print(" ");
812                    printExpr(tree.expr);
813                }
814                print(";");
815            } catch (IOException e) {
816                throw new UncheckedIOException(e);
817            }
818        }
819    
820        public void visitThrow(JCThrow tree) {
821            try {
822                print("throw ");
823                printExpr(tree.expr);
824                print(";");
825            } catch (IOException e) {
826                throw new UncheckedIOException(e);
827            }
828        }
829    
830        public void visitAssert(JCAssert tree) {
831            try {
832                print("assert ");
833                printExpr(tree.cond);
834                if (tree.detail != null) {
835                    print(" : ");
836                    printExpr(tree.detail);
837                }
838                print(";");
839            } catch (IOException e) {
840                throw new UncheckedIOException(e);
841            }
842        }
843    
844        public void visitApply(JCMethodInvocation tree) {
845            try {
846                if (!tree.typeargs.isEmpty()) {
847                    if (tree.meth.getTag() == JCTree.SELECT) {
848                        JCFieldAccess left = (JCFieldAccess)tree.meth;
849                        printExpr(left.selected);
850                        print(".<");
851                        printExprs(tree.typeargs);
852                        print(">" + left.name);
853                    } else {
854                        print("<");
855                        printExprs(tree.typeargs);
856                        print(">");
857                        printExpr(tree.meth);
858                    }
859                } else {
860                    printExpr(tree.meth);
861                }
862                print("(");
863                printExprs(tree.args);
864                print(")");
865            } catch (IOException e) {
866                throw new UncheckedIOException(e);
867            }
868        }
869        
870        @SuppressWarnings("unchecked")
871        public void visitNewClass(JCNewClass tree) {
872            try {
873                if (tree.encl != null) {
874                    printExpr(tree.encl);
875                    print(".");
876                }
877                print("new ");
878                if (!tree.typeargs.isEmpty()) {
879                    print("<");
880                    printExprs(tree.typeargs);
881                    print(">");
882                }
883                printExpr(tree.clazz);
884                print("(");
885                printExprs(tree.args);
886                print(")");
887                if (tree.def != null) {
888                    Name enclClassNamePrev = enclClassName;
889                    enclClassName =
890                            tree.def.name != null ? tree.def.name :
891                                tree.type != null && tree.type.tsym.name != tree.type.tsym.name.table.names.empty
892                                    ? tree.type.tsym.name : null;
893                    if ((tree.def.mods.flags & Flags.ENUM) != 0) print("/*enum*/");
894                    // do not print constructors in anonymous class creations
895                    ListBuffer<JCTree> filteredDefs = ListBuffer.lb();
896                    for(JCTree d: tree.def.defs) {
897                        if (d.getTag()==JCTree.METHODDEF) {
898                            JCMethodDecl m = (JCMethodDecl)d;
899                            if (m.name == tree.def.name.table.names.init) {
900                                // this is a constructor, filter it out
901                                continue;
902                            }
903                        }
904                        filteredDefs.add(d);
905                    }
906                    printBlock(filteredDefs.toList());
907                    enclClassName = enclClassNamePrev;
908                }
909            } catch (IOException e) {
910                throw new UncheckedIOException(e);
911            }
912        }
913    
914        public void visitNewArray(JCNewArray tree) {
915            try {
916                if (tree.elemtype != null) {
917                    print("new ");
918                    JCTree elem = tree.elemtype;
919                    if (elem instanceof JCArrayTypeTree)
920                        printBaseElementType((JCArrayTypeTree) elem);
921                    else
922                        printExpr(elem);
923                    for (List<JCExpression> l = tree.dims; l.nonEmpty(); l = l.tail) {
924                        print("[");
925                        printExpr(l.head);
926                        print("]");
927                    }
928                    if (elem instanceof JCArrayTypeTree)
929                        printBrackets((JCArrayTypeTree) elem);
930                }
931                if (tree.elems != null) {
932                    if (tree.elemtype != null) print("[]");
933                    print("{");
934                    printExprs(tree.elems);
935                    print("}");
936                }
937            } catch (IOException e) {
938                throw new UncheckedIOException(e);
939            }
940        }
941    
942        public void visitParens(JCParens tree) {
943            try {
944                print("(");
945                printExpr(tree.expr);
946                print(")");
947            } catch (IOException e) {
948                throw new UncheckedIOException(e);
949            }
950        }
951    
952        public void visitAssign(JCAssign tree) {
953            try {
954                open(prec, TreeInfo.assignPrec);
955                printExpr(tree.lhs, TreeInfo.assignPrec + 1);
956                print(" = ");
957                printExpr(tree.rhs, TreeInfo.assignPrec);
958                close(prec, TreeInfo.assignPrec);
959            } catch (IOException e) {
960                throw new UncheckedIOException(e);
961            }
962        }
963    
964        public String operatorName(int tag) {
965            switch(tag) {
966                case JCTree.POS:     return "+";
967                case JCTree.NEG:     return "-";
968                case JCTree.NOT:     return "!";
969                case JCTree.COMPL:   return "~";
970                case JCTree.PREINC:  return "++";
971                case JCTree.PREDEC:  return "--";
972                case JCTree.POSTINC: return "++";
973                case JCTree.POSTDEC: return "--";
974                case JCTree.NULLCHK: return "<*nullchk*>";
975                case JCTree.OR:      return "||";
976                case JCTree.AND:     return "&&";
977                case JCTree.EQ:      return "==";
978                case JCTree.NE:      return "!=";
979                case JCTree.LT:      return "<";
980                case JCTree.GT:      return ">";
981                case JCTree.LE:      return "<=";
982                case JCTree.GE:      return ">=";
983                case JCTree.BITOR:   return "|";
984                case JCTree.BITXOR:  return "^";
985                case JCTree.BITAND:  return "&";
986                case JCTree.SL:      return "<<";
987                case JCTree.SR:      return ">>";
988                case JCTree.USR:     return ">>>";
989                case JCTree.PLUS:    return "+";
990                case JCTree.MINUS:   return "-";
991                case JCTree.MUL:     return "*";
992                case JCTree.DIV:     return "/";
993                case JCTree.MOD:     return "%";
994                default: throw new Error();
995            }
996        }
997    
998        public void visitAssignop(JCAssignOp tree) {
999            try {
1000                open(prec, TreeInfo.assignopPrec);
1001                printExpr(tree.lhs, TreeInfo.assignopPrec + 1);
1002                print(" " + operatorName(tree.getTag() - JCTree.ASGOffset) + "= ");
1003                printExpr(tree.rhs, TreeInfo.assignopPrec);
1004                close(prec, TreeInfo.assignopPrec);
1005            } catch (IOException e) {
1006                throw new UncheckedIOException(e);
1007            }
1008        }
1009    
1010        public void visitUnary(JCUnary tree) {
1011            try {
1012                int ownprec = TreeInfo.opPrec(tree.getTag());
1013                String opname = operatorName(tree.getTag());
1014                open(prec, ownprec);
1015                if (tree.getTag() <= JCTree.PREDEC) {
1016                    print(opname);
1017                    printExpr(tree.arg, ownprec);
1018                } else {
1019                    printExpr(tree.arg, ownprec);
1020                    print(opname);
1021                }
1022                close(prec, ownprec);
1023            } catch (IOException e) {
1024                throw new UncheckedIOException(e);
1025            }
1026        }
1027    
1028        public void visitBinary(JCBinary tree) {
1029            try {
1030                int ownprec = TreeInfo.opPrec(tree.getTag());
1031                String opname = operatorName(tree.getTag());
1032                open(prec, ownprec);
1033                printExpr(tree.lhs, ownprec);
1034                print(" " + opname + " ");
1035                printExpr(tree.rhs, ownprec + 1);
1036                close(prec, ownprec);
1037            } catch (IOException e) {
1038                throw new UncheckedIOException(e);
1039            }
1040        }
1041    
1042        public void visitTypeCast(JCTypeCast tree) {
1043            try {
1044                open(prec, TreeInfo.prefixPrec);
1045                print("(");
1046                printExpr(tree.clazz);
1047                print(")");
1048                printExpr(tree.expr, TreeInfo.prefixPrec);
1049                close(prec, TreeInfo.prefixPrec);
1050            } catch (IOException e) {
1051                throw new UncheckedIOException(e);
1052            }
1053        }
1054    
1055        public void visitTypeTest(JCInstanceOf tree) {
1056            try {
1057                open(prec, TreeInfo.ordPrec);
1058                printExpr(tree.expr, TreeInfo.ordPrec);
1059                print(" instanceof ");
1060                printExpr(tree.clazz, TreeInfo.ordPrec + 1);
1061                close(prec, TreeInfo.ordPrec);
1062            } catch (IOException e) {
1063                throw new UncheckedIOException(e);
1064            }
1065        }
1066    
1067        public void visitIndexed(JCArrayAccess tree) {
1068            try {
1069                printExpr(tree.indexed, TreeInfo.postfixPrec);
1070                print("[");
1071                printExpr(tree.index);
1072                print("]");
1073            } catch (IOException e) {
1074                throw new UncheckedIOException(e);
1075            }
1076        }
1077    
1078        public void visitSelect(JCFieldAccess tree) {
1079            try {
1080                // mgr: we want to prevent the field access with no symbol to the left of the .
1081                //      example: .edu.something.or.another
1082                if ((tree.selected instanceof JCIdent) &&
1083                    (((JCIdent)tree.selected).name.toString().equals(""))) {
1084                    print(tree.name);
1085                }
1086                else {
1087                    printExpr(tree.selected, TreeInfo.postfixPrec);
1088                    print("." + tree.name);
1089                }
1090            } catch (IOException e) {
1091                throw new UncheckedIOException(e);
1092            }
1093        }
1094    
1095        public void visitIdent(JCIdent tree) {
1096            try {
1097                if (tree.genVarSym!=null) {
1098                    print("/*genVarSym="+tree.genVarSym.name+"*/");
1099                }
1100                print(tree.name);
1101            } catch (IOException e) {
1102                throw new UncheckedIOException(e);
1103            }
1104        }
1105    
1106        public void visitLiteral(JCLiteral tree) {
1107            try {
1108                switch (tree.typetag) {
1109                    case TypeTags.INT:
1110                        print(tree.value.toString());
1111                        break;
1112                    case TypeTags.LONG:
1113                        print(tree.value + "L");
1114                        break;
1115                    case TypeTags.FLOAT:
1116                        print(tree.value + "F");
1117                        break;
1118                    case TypeTags.DOUBLE:
1119                        print(tree.value.toString());
1120                        break;
1121                    case TypeTags.CHAR:
1122                        print("\'" +
1123                                Convert.quote(
1124                                String.valueOf((char)((Number)tree.value).intValue())) +
1125                                "\'");
1126                        break;
1127                    case TypeTags.BOOLEAN:
1128                        print(((Number)tree.value).intValue() == 1 ? "true" : "false");
1129                        break;
1130                    case TypeTags.BOT:
1131                        print("null");
1132                        break;
1133                    default:
1134                        print("\"" + Convert.quote(tree.value.toString()) + "\"");
1135                        break;
1136                }
1137            } catch (IOException e) {
1138                throw new UncheckedIOException(e);
1139            }
1140        }
1141    
1142        public void visitTypeIdent(JCPrimitiveTypeTree tree) {
1143            try {
1144                switch(tree.typetag) {
1145                    case TypeTags.BYTE:
1146                        print("byte");
1147                        break;
1148                    case TypeTags.CHAR:
1149                        print("char");
1150                        break;
1151                    case TypeTags.SHORT:
1152                        print("short");
1153                        break;
1154                    case TypeTags.INT:
1155                        print("int");
1156                        break;
1157                    case TypeTags.LONG:
1158                        print("long");
1159                        break;
1160                    case TypeTags.FLOAT:
1161                        print("float");
1162                        break;
1163                    case TypeTags.DOUBLE:
1164                        print("double");
1165                        break;
1166                    case TypeTags.BOOLEAN:
1167                        print("boolean");
1168                        break;
1169                    case TypeTags.VOID:
1170                        print("void");
1171                        break;
1172                    default:
1173                        print("error");
1174                        break;
1175                }
1176            } catch (IOException e) {
1177                throw new UncheckedIOException(e);
1178            }
1179        }
1180    
1181        public void visitTypeArray(JCArrayTypeTree tree) {
1182            try {
1183                printBaseElementType(tree);
1184                printBrackets(tree);
1185            } catch (IOException e) {
1186                throw new UncheckedIOException(e);
1187            }
1188        }
1189    
1190        // Prints the inner element type of a nested array
1191        private void printBaseElementType(JCArrayTypeTree tree) throws IOException {
1192            JCTree elem = tree.elemtype;
1193            while (elem instanceof JCWildcard)
1194                elem = ((JCWildcard) elem).inner;
1195            if (elem instanceof JCArrayTypeTree)
1196                printBaseElementType((JCArrayTypeTree) elem);
1197            else
1198                printExpr(elem);
1199        }
1200    
1201        // prints the brackets of a nested array in reverse order
1202        private void printBrackets(JCArrayTypeTree tree) throws IOException {
1203            JCTree elem;
1204            while (true) {
1205                elem = tree.elemtype;
1206                print("[]");
1207                if (!(elem instanceof JCArrayTypeTree)) break;
1208                tree = (JCArrayTypeTree) elem;
1209            }
1210        }
1211    
1212        public void visitTypeApply(JCTypeApply tree) {
1213            try {
1214                printExpr(tree.clazz);
1215                print("<");
1216                printExprs(tree.arguments);
1217                print(">");
1218            } catch (IOException e) {
1219                throw new UncheckedIOException(e);
1220            }
1221        }
1222    
1223        public void visitTypeParameter(JCTypeParameter tree) {
1224            try {
1225                print(tree.name);
1226                if (tree.bounds.nonEmpty()) {
1227                    print(" extends ");
1228                    printExprs(tree.bounds, " & ");
1229                }
1230            } catch (IOException e) {
1231                throw new UncheckedIOException(e);
1232            }
1233        }
1234    
1235        @Override
1236        public void visitWildcard(JCWildcard tree) {
1237            try {
1238                print(tree.kind);
1239                if (tree.kind.kind != BoundKind.UNBOUND)
1240                    printExpr(tree.inner);
1241            } catch (IOException e) {
1242                throw new UncheckedIOException(e);
1243            }
1244        }
1245    
1246        @Override
1247        public void visitTypeBoundKind(TypeBoundKind tree) {
1248            try {
1249                print(String.valueOf(tree.kind));
1250            } catch (IOException e) {
1251                throw new UncheckedIOException(e);
1252            }
1253        }
1254    
1255        public void visitErroneous(JCErroneous tree) {
1256            try {
1257                print("(ERROR)");
1258            } catch (IOException e) {
1259                throw new UncheckedIOException(e);
1260            }
1261        }
1262    
1263        public void visitLetExpr(LetExpr tree) {
1264            try {
1265                print("(let ");
1266                boolean first = true;
1267                for(JCVariableDecl decl: tree.defs) {
1268                    if (first) first = false;
1269                    else print(", ");
1270                    printExpr(decl, TreeInfo.noPrec);
1271                }
1272                print("; ");
1273                printExpr(tree.expr);
1274                print(")");
1275            } catch (IOException e) {
1276                throw new UncheckedIOException(e);
1277            }
1278        }
1279    
1280        public void visitModifiers(JCModifiers mods) {
1281            try {
1282                printAnnotations(mods.annotations);
1283                printFlags(mods.flags);
1284            } catch (IOException e) {
1285                throw new UncheckedIOException(e);
1286            }
1287        }
1288    
1289        public void visitAnnotation(JCAnnotation tree) {
1290            try {
1291                print("@");
1292                printExpr(tree.annotationType);
1293                print("(");
1294                printExprs(tree.args);
1295                print(")");
1296            } catch (IOException e) {
1297                throw new UncheckedIOException(e);
1298            }
1299        }
1300    
1301        public void visitTree(JCTree tree) {
1302            try {
1303                print("(UNKNOWN: " + tree.getClass().getName() + ")");
1304                println();
1305            } catch (IOException e) {
1306                throw new UncheckedIOException(e);
1307            }
1308        }
1309    }