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