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.var);
 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             if (tree.var != null) {
 949                 print(" ");
 950                 print(tree.var.name);
 951             }
 952         } catch (IOException e) {
 953             throw new UncheckedIOException(e);
 954         }
 955     }
 956 
 957     public void visitSynchronized(JCSynchronized tree) {
 958         try {
 959             print("synchronized ");
 960             if (tree.lock.hasTag(PARENS)) {
 961                 printExpr(tree.lock);
 962             } else {
 963                 print("(");
 964                 printExpr(tree.lock);
 965                 print(")");
 966             }
 967             print(" ");
 968             printStat(tree.body);
 969         } catch (IOException e) {
 970             throw new UncheckedIOException(e);
 971         }
 972     }
 973 
 974     public void visitTry(JCTry tree) {
 975         try {
 976             print("try ");
 977             if (tree.resources.nonEmpty()) {
 978                 print("(");
 979                 boolean first = true;
 980                 for (JCTree var : tree.resources) {
 981                     if (!first) {
 982                         println();
 983                         indent();
 984                     }
 985                     printStat(var);
 986                     first = false;
 987                 }
 988                 print(") ");
 989             }
 990             printStat(tree.body);
 991             for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
 992                 printStat(l.head);
 993             }
 994             if (tree.finalizer != null) {
 995                 print(" finally ");
 996                 printStat(tree.finalizer);
 997             }
 998         } catch (IOException e) {
 999             throw new UncheckedIOException(e);
1000         }
1001     }
1002 
1003     public void visitCatch(JCCatch tree) {
1004         try {
1005             print(" catch (");
1006             printExpr(tree.param);
1007             print(") ");
1008             printStat(tree.body);
1009         } catch (IOException e) {
1010             throw new UncheckedIOException(e);
1011         }
1012     }
1013 
1014     public void visitConditional(JCConditional tree) {
1015         try {
1016             open(prec, TreeInfo.condPrec);
1017             printExpr(tree.cond, TreeInfo.condPrec + 1);
1018             print(" ? ");
1019             printExpr(tree.truepart);
1020             print(" : ");
1021             printExpr(tree.falsepart, TreeInfo.condPrec);
1022             close(prec, TreeInfo.condPrec);
1023         } catch (IOException e) {
1024             throw new UncheckedIOException(e);
1025         }
1026     }
1027 
1028     public void visitIf(JCIf tree) {
1029         try {
1030             print("if ");
1031             if (tree.cond.hasTag(PARENS)) {
1032                 printExpr(tree.cond);
1033             } else {
1034                 print("(");
1035                 printExpr(tree.cond);
1036                 print(")");
1037             }
1038             print(" ");
1039             printStat(tree.thenpart);
1040             if (tree.elsepart != null) {
1041                 print(" else ");
1042                 printStat(tree.elsepart);
1043             }
1044         } catch (IOException e) {
1045             throw new UncheckedIOException(e);
1046         }
1047     }
1048 
1049     public void visitExec(JCExpressionStatement tree) {
1050         try {
1051             printExpr(tree.expr);
1052             if (prec == TreeInfo.notExpression) print(";");
1053         } catch (IOException e) {
1054             throw new UncheckedIOException(e);
1055         }
1056     }
1057 
1058     public void visitBreak(JCBreak tree) {
1059         try {
1060             print("break");
1061             if (tree.label != null) print(" " + tree.label);
1062             print(";");
1063         } catch (IOException e) {
1064             throw new UncheckedIOException(e);
1065         }
1066     }
1067 
1068     public void visitYield(JCYield tree) {
1069         try {
1070             print("yield");
1071             print(" ");
1072             printExpr(tree.value);
1073             print(";");
1074         } catch (IOException e) {
1075             throw new UncheckedIOException(e);
1076         }
1077     }
1078 
1079     public void visitContinue(JCContinue tree) {
1080         try {
1081             print("continue");
1082             if (tree.label != null) print(" " + tree.label);
1083             print(";");
1084         } catch (IOException e) {
1085             throw new UncheckedIOException(e);
1086         }
1087     }
1088 
1089     public void visitReturn(JCReturn tree) {
1090         try {
1091             print("return");
1092             if (tree.expr != null) {
1093                 print(" ");
1094                 printExpr(tree.expr);
1095             }
1096             print(";");
1097         } catch (IOException e) {
1098             throw new UncheckedIOException(e);
1099         }
1100     }
1101 
1102     public void visitThrow(JCThrow tree) {
1103         try {
1104             print("throw ");
1105             printExpr(tree.expr);
1106             print(";");
1107         } catch (IOException e) {
1108             throw new UncheckedIOException(e);
1109         }
1110     }
1111 
1112     public void visitAssert(JCAssert tree) {
1113         try {
1114             print("assert ");
1115             printExpr(tree.cond);
1116             if (tree.detail != null) {
1117                 print(" : ");
1118                 printExpr(tree.detail);
1119             }
1120             print(";");
1121         } catch (IOException e) {
1122             throw new UncheckedIOException(e);
1123         }
1124     }
1125 
1126     public void visitApply(JCMethodInvocation tree) {
1127         try {
1128             if (!tree.typeargs.isEmpty()) {
1129                 if (tree.meth.hasTag(SELECT)) {
1130                     JCFieldAccess left = (JCFieldAccess)tree.meth;
1131                     printExpr(left.selected);
1132                     print(".<");
1133                     printExprs(tree.typeargs);
1134                     print(">" + left.name);
1135                 } else {
1136                     print("<");
1137                     printExprs(tree.typeargs);
1138                     print(">");
1139                     printExpr(tree.meth);
1140                 }
1141             } else {
1142                 printExpr(tree.meth);
1143             }
1144             print("(");
1145             printExprs(tree.args);
1146             print(")");
1147         } catch (IOException e) {
1148             throw new UncheckedIOException(e);
1149         }
1150     }
1151 
1152     public void visitNewClass(JCNewClass tree) {
1153         try {
1154             if (tree.encl != null) {
1155                 printExpr(tree.encl);
1156                 print(".");
1157             }
1158             print("new ");
1159             if (!tree.typeargs.isEmpty()) {
1160                 print("<");
1161                 printExprs(tree.typeargs);
1162                 print(">");
1163             }
1164             if (tree.def != null && tree.def.mods.annotations.nonEmpty()) {
1165                 printTypeAnnotations(tree.def.mods.annotations);
1166             }
1167             printExpr(tree.clazz);
1168             print("(");
1169             printExprs(tree.args);
1170             print(")");
1171             if (tree.def != null) {
1172                 Name enclClassNamePrev = enclClassName;
1173                 enclClassName =
1174                         tree.def.name != null ? tree.def.name :
1175                             tree.type != null && tree.type.tsym.name != tree.type.tsym.name.table.names.empty
1176                                 ? tree.type.tsym.name : null;
1177                 if ((tree.def.mods.flags & Flags.ENUM) != 0) print("/*enum*/");
1178                 printBlock(tree.def.defs);
1179                 enclClassName = enclClassNamePrev;
1180             }
1181         } catch (IOException e) {
1182             throw new UncheckedIOException(e);
1183         }
1184     }
1185 
1186     public void visitNewArray(JCNewArray tree) {
1187         try {
1188             if (tree.elemtype != null) {
1189                 print("new ");
1190                 JCTree elem = tree.elemtype;
1191                 printBaseElementType(elem);
1192 
1193                 if (!tree.annotations.isEmpty()) {
1194                     print(' ');
1195                     printTypeAnnotations(tree.annotations);
1196                 }
1197                 if (tree.elems != null) {
1198                     print("[]");
1199                 }
1200 
1201                 int i = 0;
1202                 List<List<JCAnnotation>> da = tree.dimAnnotations;
1203                 for (List<JCExpression> l = tree.dims; l.nonEmpty(); l = l.tail) {
1204                     if (da.size() > i && !da.get(i).isEmpty()) {
1205                         print(' ');
1206                         printTypeAnnotations(da.get(i));
1207                     }
1208                     print("[");
1209                     i++;
1210                     printExpr(l.head);
1211                     print("]");
1212                 }
1213                 printBrackets(elem);
1214             }
1215             if (tree.elems != null) {
1216                 print("{");
1217                 printExprs(tree.elems);
1218                 print("}");
1219             }
1220         } catch (IOException e) {
1221             throw new UncheckedIOException(e);
1222         }
1223     }
1224 
1225     public void visitLambda(JCLambda tree) {
1226         try {
1227             print("(");
1228             if (tree.paramKind == JCLambda.ParameterKind.EXPLICIT) {
1229                 printExprs(tree.params);
1230             } else {
1231                 String sep = "";
1232                 for (JCVariableDecl param : tree.params) {
1233                     print(sep);
1234                     print(param.name);
1235                     sep = ",";
1236                 }
1237             }
1238             print(")->");
1239             printExpr(tree.body);
1240         } catch (IOException e) {
1241             throw new UncheckedIOException(e);
1242         }
1243     }
1244 
1245     public void visitParens(JCParens tree) {
1246         try {
1247             print("(");
1248             printExpr(tree.expr);
1249             print(")");
1250         } catch (IOException e) {
1251             throw new UncheckedIOException(e);
1252         }
1253     }
1254 
1255     public void visitAssign(JCAssign tree) {
1256         try {
1257             open(prec, TreeInfo.assignPrec);
1258             printExpr(tree.lhs, TreeInfo.assignPrec + 1);
1259             print(" = ");
1260             printExpr(tree.rhs, TreeInfo.assignPrec);
1261             close(prec, TreeInfo.assignPrec);
1262         } catch (IOException e) {
1263             throw new UncheckedIOException(e);
1264         }
1265     }
1266 
1267     public String operatorName(JCTree.Tag tag) {
1268         switch(tag) {
1269             case POS:     return "+";
1270             case NEG:     return "-";
1271             case NOT:     return "!";
1272             case COMPL:   return "~";
1273             case PREINC:  return "++";
1274             case PREDEC:  return "--";
1275             case POSTINC: return "++";
1276             case POSTDEC: return "--";
1277             case NULLCHK: return "<*nullchk*>";
1278             case OR:      return "||";
1279             case AND:     return "&&";
1280             case EQ:      return "==";
1281             case NE:      return "!=";
1282             case LT:      return "<";
1283             case GT:      return ">";
1284             case LE:      return "<=";
1285             case GE:      return ">=";
1286             case BITOR:   return "|";
1287             case BITXOR:  return "^";
1288             case BITAND:  return "&";
1289             case SL:      return "<<";
1290             case SR:      return ">>";
1291             case USR:     return ">>>";
1292             case PLUS:    return "+";
1293             case MINUS:   return "-";
1294             case MUL:     return "*";
1295             case DIV:     return "/";
1296             case MOD:     return "%";
1297             default: throw new Error();
1298         }
1299     }
1300 
1301     public void visitAssignop(JCAssignOp tree) {
1302         try {
1303             open(prec, TreeInfo.assignopPrec);
1304             printExpr(tree.lhs, TreeInfo.assignopPrec + 1);
1305             print(" " + operatorName(tree.getTag().noAssignOp()) + "= ");
1306             printExpr(tree.rhs, TreeInfo.assignopPrec);
1307             close(prec, TreeInfo.assignopPrec);
1308         } catch (IOException e) {
1309             throw new UncheckedIOException(e);
1310         }
1311     }
1312 
1313     public void visitUnary(JCUnary tree) {
1314         try {
1315             int ownprec = TreeInfo.opPrec(tree.getTag());
1316             String opname = operatorName(tree.getTag());
1317             open(prec, ownprec);
1318             if (!tree.getTag().isPostUnaryOp()) {
1319                 print(opname);
1320                 printExpr(tree.arg, ownprec);
1321             } else {
1322                 printExpr(tree.arg, ownprec);
1323                 print(opname);
1324             }
1325             close(prec, ownprec);
1326         } catch (IOException e) {
1327             throw new UncheckedIOException(e);
1328         }
1329     }
1330 
1331     public void visitBinary(JCBinary tree) {
1332         try {
1333             int ownprec = TreeInfo.opPrec(tree.getTag());
1334             String opname = operatorName(tree.getTag());
1335             open(prec, ownprec);
1336             printExpr(tree.lhs, ownprec);
1337             print(" " + opname + " ");
1338             printExpr(tree.rhs, ownprec + 1);
1339             close(prec, ownprec);
1340         } catch (IOException e) {
1341             throw new UncheckedIOException(e);
1342         }
1343     }
1344 
1345     public void visitTypeCast(JCTypeCast tree) {
1346         try {
1347             open(prec, TreeInfo.prefixPrec);
1348             print("(");
1349             printExpr(tree.clazz);
1350             print(")");
1351             printExpr(tree.expr, TreeInfo.prefixPrec);
1352             close(prec, TreeInfo.prefixPrec);
1353         } catch (IOException e) {
1354             throw new UncheckedIOException(e);
1355         }
1356     }
1357 
1358     public void visitTypeTest(JCInstanceOf tree) {
1359         try {
1360             open(prec, TreeInfo.ordPrec);
1361             printExpr(tree.expr, TreeInfo.ordPrec);
1362             print(" instanceof ");
1363             if (tree.pattern instanceof JCPattern) {
1364                 printPattern(tree.pattern);
1365             } else {
1366                 printExpr(tree.getType(), TreeInfo.ordPrec + 1);
1367             }
1368             close(prec, TreeInfo.ordPrec);
1369         } catch (IOException e) {
1370             throw new UncheckedIOException(e);
1371         }
1372     }
1373 
1374     public void visitIndexed(JCArrayAccess tree) {
1375         try {
1376             printExpr(tree.indexed, TreeInfo.postfixPrec);
1377             print("[");
1378             printExpr(tree.index);
1379             print("]");
1380         } catch (IOException e) {
1381             throw new UncheckedIOException(e);
1382         }
1383     }
1384 
1385     public void visitSelect(JCFieldAccess tree) {
1386         try {
1387             printExpr(tree.selected, TreeInfo.postfixPrec);
1388             print("." + tree.name);
1389         } catch (IOException e) {
1390             throw new UncheckedIOException(e);
1391         }
1392     }
1393 
1394     public void visitReference(JCMemberReference tree) {
1395         try {
1396             printExpr(tree.expr);
1397             print("::");
1398             if (tree.typeargs != null) {
1399                 print("<");
1400                 printExprs(tree.typeargs);
1401                 print(">");
1402             }
1403             print(tree.getMode() == ReferenceMode.INVOKE ? tree.name : "new");
1404         } catch (IOException e) {
1405             throw new UncheckedIOException(e);
1406         }
1407     }
1408 
1409     public void visitIdent(JCIdent tree) {
1410         try {
1411             print(tree.name);
1412         } catch (IOException e) {
1413             throw new UncheckedIOException(e);
1414         }
1415     }
1416 
1417     public void visitLiteral(JCLiteral tree) {
1418         try {
1419             switch (tree.typetag) {
1420                 case INT:
1421                     print(tree.value.toString());
1422                     break;
1423                 case LONG:
1424                     print(tree.value + "L");
1425                     break;
1426                 case FLOAT:
1427                     print(tree.value + "F");
1428                     break;
1429                 case DOUBLE:
1430                     print(tree.value.toString());
1431                     break;
1432                 case CHAR:
1433                     print("\'" +
1434                             Convert.quote(
1435                             String.valueOf((char)((Number)tree.value).intValue())) +
1436                             "\'");
1437                     break;
1438                 case BOOLEAN:
1439                     print(((Number)tree.value).intValue() == 1 ? "true" : "false");
1440                     break;
1441                 case BOT:
1442                     print("null");
1443                     break;
1444                 default:
1445                     print("\"" + Convert.quote(tree.value.toString()) + "\"");
1446                     break;
1447             }
1448         } catch (IOException e) {
1449             throw new UncheckedIOException(e);
1450         }
1451     }
1452 
1453     public void visitTypeIdent(JCPrimitiveTypeTree tree) {
1454         try {
1455             switch(tree.typetag) {
1456                 case BYTE:
1457                     print("byte");
1458                     break;
1459                 case CHAR:
1460                     print("char");
1461                     break;
1462                 case SHORT:
1463                     print("short");
1464                     break;
1465                 case INT:
1466                     print("int");
1467                     break;
1468                 case LONG:
1469                     print("long");
1470                     break;
1471                 case FLOAT:
1472                     print("float");
1473                     break;
1474                 case DOUBLE:
1475                     print("double");
1476                     break;
1477                 case BOOLEAN:
1478                     print("boolean");
1479                     break;
1480                 case VOID:
1481                     print("void");
1482                     break;
1483                 default:
1484                     print("error");
1485                     break;
1486             }
1487         } catch (IOException e) {
1488             throw new UncheckedIOException(e);
1489         }
1490     }
1491 
1492     public void visitTypeArray(JCArrayTypeTree tree) {
1493         try {
1494             printBaseElementType(tree);
1495             printBrackets(tree);
1496         } catch (IOException e) {
1497             throw new UncheckedIOException(e);
1498         }
1499     }
1500 
1501     // Prints the inner element type of a nested array
1502     private void printBaseElementType(JCTree tree) throws IOException {
1503         printExpr(TreeInfo.innermostType(tree, false));
1504     }
1505 
1506     // prints the brackets of a nested array in reverse order
1507     // tree is either JCArrayTypeTree or JCAnnotatedTypeTree
1508     private void printBrackets(JCTree tree) throws IOException {
1509         JCTree elem = tree;
1510         while (true) {
1511             if (elem.hasTag(ANNOTATED_TYPE)) {
1512                 JCAnnotatedType atype = (JCAnnotatedType) elem;
1513                 elem = atype.underlyingType;
1514                 if (elem.hasTag(TYPEARRAY)) {
1515                     print(' ');
1516                     printTypeAnnotations(atype.annotations);
1517                 }
1518             }
1519             if (elem.hasTag(TYPEARRAY)) {
1520                 print("[]");
1521                 elem = ((JCArrayTypeTree)elem).elemtype;
1522             } else {
1523                 break;
1524             }
1525         }
1526     }
1527 
1528     public void visitTypeApply(JCTypeApply tree) {
1529         try {
1530             printExpr(tree.clazz);
1531             print("<");
1532             printExprs(tree.arguments);
1533             print(">");
1534         } catch (IOException e) {
1535             throw new UncheckedIOException(e);
1536         }
1537     }
1538 
1539     public void visitTypeUnion(JCTypeUnion tree) {
1540         try {
1541             printExprs(tree.alternatives, " | ");
1542         } catch (IOException e) {
1543             throw new UncheckedIOException(e);
1544         }
1545     }
1546 
1547     public void visitTypeIntersection(JCTypeIntersection tree) {
1548         try {
1549             printExprs(tree.bounds, " & ");
1550         } catch (IOException e) {
1551             throw new UncheckedIOException(e);
1552         }
1553     }
1554 
1555     public void visitTypeParameter(JCTypeParameter tree) {
1556         try {
1557             if (tree.annotations.nonEmpty()) {
1558                 this.printTypeAnnotations(tree.annotations);
1559             }
1560             print(tree.name);
1561             if (tree.bounds.nonEmpty()) {
1562                 print(" extends ");
1563                 printExprs(tree.bounds, " & ");
1564             }
1565         } catch (IOException e) {
1566             throw new UncheckedIOException(e);
1567         }
1568     }
1569 
1570     @Override
1571     public void visitWildcard(JCWildcard tree) {
1572         try {
1573             print(tree.kind);
1574             if (tree.kind.kind != BoundKind.UNBOUND)
1575                 printExpr(tree.inner);
1576         } catch (IOException e) {
1577             throw new UncheckedIOException(e);
1578         }
1579     }
1580 
1581     @Override
1582     public void visitTypeBoundKind(TypeBoundKind tree) {
1583         try {
1584             print(String.valueOf(tree.kind));
1585         } catch (IOException e) {
1586             throw new UncheckedIOException(e);
1587         }
1588     }
1589 
1590     public void visitErroneous(JCErroneous tree) {
1591         try {
1592             print("(ERROR)");
1593         } catch (IOException e) {
1594             throw new UncheckedIOException(e);
1595         }
1596     }
1597 
1598     public void visitLetExpr(LetExpr tree) {
1599         try {
1600             print("(let " + tree.defs + " in " + tree.expr + ")");
1601         } catch (IOException e) {
1602             throw new UncheckedIOException(e);
1603         }
1604     }
1605 
1606     public void visitModifiers(JCModifiers mods) {
1607         try {
1608             printAnnotations(mods.annotations);
1609             printFlags(mods.flags);
1610         } catch (IOException e) {
1611             throw new UncheckedIOException(e);
1612         }
1613     }
1614 
1615     public void visitAnnotation(JCAnnotation tree) {
1616         try {
1617             print("@");
1618             printExpr(tree.annotationType);
1619             if (!tree.args.isEmpty()) {
1620                 print("(");
1621                 printExprs(tree.args);
1622                 print(")");
1623             }
1624         } catch (IOException e) {
1625             throw new UncheckedIOException(e);
1626         }
1627     }
1628 
1629     public void visitAnnotatedType(JCAnnotatedType tree) {
1630         try {
1631             if (tree.underlyingType.hasTag(SELECT)) {
1632                 JCFieldAccess access = (JCFieldAccess) tree.underlyingType;
1633                 printExpr(access.selected, TreeInfo.postfixPrec);
1634                 print(".");
1635                 printTypeAnnotations(tree.annotations);
1636                 print(access.name);
1637             } else if (tree.underlyingType.hasTag(TYPEARRAY)) {
1638                 printBaseElementType(tree);
1639                 printBrackets(tree);
1640             } else {
1641                 printTypeAnnotations(tree.annotations);
1642                 printExpr(tree.underlyingType);
1643             }
1644         } catch (IOException e) {
1645             throw new UncheckedIOException(e);
1646         }
1647     }
1648 
1649     public void visitTree(JCTree tree) {
1650         try {
1651             print("(UNKNOWN: " + tree.getTag() + ")");
1652             println();
1653         } catch (IOException e) {
1654             throw new UncheckedIOException(e);
1655         }
1656     }
1657 
1658 }