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