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