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