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