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