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