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