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 visitDefaultValue(JCDefaultValue tree) {
 737         try {
 738             printExpr(tree.clazz, TreeInfo.postfixPrec);
 739             print(".default");
 740         } catch (IOException e) {
 741             throw new UncheckedIOException(e);
 742         }
 743     }
 744 
 745     public void visitDoLoop(JCDoWhileLoop tree) {
 746         try {
 747             print("do ");
 748             printStat(tree.body);
 749             align();
 750             print(" while ");
 751             if (tree.cond.hasTag(PARENS)) {
 752                 printExpr(tree.cond);
 753             } else {
 754                 print("(");
 755                 printExpr(tree.cond);
 756                 print(")");
 757             }
 758             print(";");
 759         } catch (IOException e) {
 760             throw new UncheckedIOException(e);
 761         }
 762     }
 763 
 764     public void visitWhileLoop(JCWhileLoop tree) {
 765         try {
 766             print("while ");
 767             if (tree.cond.hasTag(PARENS)) {
 768                 printExpr(tree.cond);
 769             } else {
 770                 print("(");
 771                 printExpr(tree.cond);
 772                 print(")");
 773             }
 774             print(" ");
 775             printStat(tree.body);
 776         } catch (IOException e) {
 777             throw new UncheckedIOException(e);
 778         }
 779     }
 780 
 781     public void visitWithField(JCWithField tree) {
 782         try {
 783             print("__WithField(");
 784             printExpr(tree.field);
 785             print(", ");
 786             printExpr(tree.value);
 787             print(")");
 788         } catch (IOException e) {
 789             throw new UncheckedIOException(e);
 790         }
 791     }
 792 
 793     public void visitForLoop(JCForLoop tree) {
 794         try {
 795             print("for (");
 796             if (tree.init.nonEmpty()) {
 797                 if (tree.init.head.hasTag(VARDEF)) {
 798                     printExpr(tree.init.head);
 799                     for (List<JCStatement> l = tree.init.tail; l.nonEmpty(); l = l.tail) {
 800                         JCVariableDecl vdef = (JCVariableDecl)l.head;
 801                         print(", " + vdef.name);
 802                         if (vdef.init != null) {
 803                             print(" = ");
 804                             printExpr(vdef.init);
 805                         }
 806                     }
 807                 } else {
 808                     printExprs(tree.init);
 809                 }
 810             }
 811             print("; ");
 812             if (tree.cond != null) printExpr(tree.cond);
 813             print("; ");
 814             printExprs(tree.step);
 815             print(") ");
 816             printStat(tree.body);
 817         } catch (IOException e) {
 818             throw new UncheckedIOException(e);
 819         }
 820     }
 821 
 822     public void visitForeachLoop(JCEnhancedForLoop tree) {
 823         try {
 824             print("for (");
 825             printExpr(tree.var);
 826             print(" : ");
 827             printExpr(tree.expr);
 828             print(") ");
 829             printStat(tree.body);
 830         } catch (IOException e) {
 831             throw new UncheckedIOException(e);
 832         }
 833     }
 834 
 835     public void visitLabelled(JCLabeledStatement tree) {
 836         try {
 837             print(tree.label + ": ");
 838             printStat(tree.body);
 839         } catch (IOException e) {
 840             throw new UncheckedIOException(e);
 841         }
 842     }
 843 
 844     public void visitSwitch(JCSwitch tree) {
 845         try {
 846             print("switch ");
 847             if (tree.selector.hasTag(PARENS)) {
 848                 printExpr(tree.selector);
 849             } else {
 850                 print("(");
 851                 printExpr(tree.selector);
 852                 print(")");
 853             }
 854             print(" {");
 855             println();
 856             printStats(tree.cases);
 857             align();
 858             print("}");
 859         } catch (IOException e) {
 860             throw new UncheckedIOException(e);
 861         }
 862     }
 863 
 864     public void visitCase(JCCase tree) {
 865         try {
 866             if (tree.labels.size() == 1 && tree.labels.get(0).hasTag(DEFAULTCASELABEL)) {
 867                 print("default");
 868             } else {
 869                 print("case ");
 870                 printExprs(tree.labels);
 871             }
 872             if (tree.caseKind == JCCase.STATEMENT) {
 873                 print(":");
 874                 println();
 875                 indent();
 876                 printStats(tree.stats);
 877                 undent();
 878                 align();
 879             } else {
 880                 print(" -> ");
 881                 if (tree.stats.size() == 1) {
 882                     printStat(tree.stats.head);
 883                 } else {
 884                     printBlock(tree.stats);
 885                 }
 886             }
 887         } catch (IOException e) {
 888             throw new UncheckedIOException(e);
 889         }
 890     }
 891 
 892     @Override
 893     public void visitDefaultCaseLabel(JCTree.JCDefaultCaseLabel that) {
 894         try {
 895             print("default");
 896         } catch (IOException e) {
 897             throw new UncheckedIOException(e);
 898         }
 899     }
 900 
 901     @Override
 902     public void visitConstantCaseLabel(JCConstantCaseLabel tree) {
 903         try {
 904             print(tree.expr);
 905         } catch (IOException e) {
 906             throw new UncheckedIOException(e);
 907         }
 908     }
 909 
 910     @Override
 911     public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
 912         try {
 913             print(tree.pat);
 914             if (tree.guard != null) {
 915                 print(" when ");
 916                 print(tree.guard);
 917             }
 918         } catch (IOException e) {
 919             throw new UncheckedIOException(e);
 920         }
 921     }
 922 
 923     public void visitSwitchExpression(JCSwitchExpression tree) {
 924         try {
 925             print("switch ");
 926             if (tree.selector.hasTag(PARENS)) {
 927                 printExpr(tree.selector);
 928             } else {
 929                 print("(");
 930                 printExpr(tree.selector);
 931                 print(")");
 932             }
 933             print(" {");
 934             println();
 935             printStats(tree.cases);
 936             align();
 937             print("}");
 938         } catch (IOException e) {
 939             throw new UncheckedIOException(e);
 940         }
 941     }
 942 
 943     public void visitBindingPattern(JCBindingPattern patt) {
 944         try {
 945             printExpr(patt.var);
 946         } catch (IOException e) {
 947             throw new UncheckedIOException(e);
 948         }
 949     }
 950 
 951     @Override
 952     public void visitParenthesizedPattern(JCParenthesizedPattern patt) {
 953         try {
 954             print("(");
 955             printExpr(patt.pattern);
 956             print(")");
 957         } catch (IOException e) {
 958             throw new UncheckedIOException(e);
 959         }
 960     }
 961 
 962     @Override
 963     public void visitRecordPattern(JCRecordPattern tree) {
 964         try {
 965             printExpr(tree.deconstructor);
 966             print("(");
 967             printExprs(tree.nested);
 968             print(")");
 969             if (tree.var != null) {
 970                 print(" ");
 971                 print(tree.var.name);
 972             }
 973         } catch (IOException e) {
 974             throw new UncheckedIOException(e);
 975         }
 976     }
 977 
 978     public void visitSynchronized(JCSynchronized tree) {
 979         try {
 980             print("synchronized ");
 981             if (tree.lock.hasTag(PARENS)) {
 982                 printExpr(tree.lock);
 983             } else {
 984                 print("(");
 985                 printExpr(tree.lock);
 986                 print(")");
 987             }
 988             print(" ");
 989             printStat(tree.body);
 990         } catch (IOException e) {
 991             throw new UncheckedIOException(e);
 992         }
 993     }
 994 
 995     public void visitTry(JCTry tree) {
 996         try {
 997             print("try ");
 998             if (tree.resources.nonEmpty()) {
 999                 print("(");
1000                 boolean first = true;
1001                 for (JCTree var : tree.resources) {
1002                     if (!first) {
1003                         println();
1004                         indent();
1005                     }
1006                     printStat(var);
1007                     first = false;
1008                 }
1009                 print(") ");
1010             }
1011             printStat(tree.body);
1012             for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
1013                 printStat(l.head);
1014             }
1015             if (tree.finalizer != null) {
1016                 print(" finally ");
1017                 printStat(tree.finalizer);
1018             }
1019         } catch (IOException e) {
1020             throw new UncheckedIOException(e);
1021         }
1022     }
1023 
1024     public void visitCatch(JCCatch tree) {
1025         try {
1026             print(" catch (");
1027             printExpr(tree.param);
1028             print(") ");
1029             printStat(tree.body);
1030         } catch (IOException e) {
1031             throw new UncheckedIOException(e);
1032         }
1033     }
1034 
1035     public void visitConditional(JCConditional tree) {
1036         try {
1037             open(prec, TreeInfo.condPrec);
1038             printExpr(tree.cond, TreeInfo.condPrec + 1);
1039             print(" ? ");
1040             printExpr(tree.truepart);
1041             print(" : ");
1042             printExpr(tree.falsepart, TreeInfo.condPrec);
1043             close(prec, TreeInfo.condPrec);
1044         } catch (IOException e) {
1045             throw new UncheckedIOException(e);
1046         }
1047     }
1048 
1049     public void visitIf(JCIf tree) {
1050         try {
1051             print("if ");
1052             if (tree.cond.hasTag(PARENS)) {
1053                 printExpr(tree.cond);
1054             } else {
1055                 print("(");
1056                 printExpr(tree.cond);
1057                 print(")");
1058             }
1059             print(" ");
1060             printStat(tree.thenpart);
1061             if (tree.elsepart != null) {
1062                 print(" else ");
1063                 printStat(tree.elsepart);
1064             }
1065         } catch (IOException e) {
1066             throw new UncheckedIOException(e);
1067         }
1068     }
1069 
1070     public void visitExec(JCExpressionStatement tree) {
1071         try {
1072             printExpr(tree.expr);
1073             if (prec == TreeInfo.notExpression) print(";");
1074         } catch (IOException e) {
1075             throw new UncheckedIOException(e);
1076         }
1077     }
1078 
1079     public void visitBreak(JCBreak tree) {
1080         try {
1081             print("break");
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 visitYield(JCYield tree) {
1090         try {
1091             print("yield");
1092             print(" ");
1093             printExpr(tree.value);
1094             print(";");
1095         } catch (IOException e) {
1096             throw new UncheckedIOException(e);
1097         }
1098     }
1099 
1100     public void visitContinue(JCContinue tree) {
1101         try {
1102             print("continue");
1103             if (tree.label != null) print(" " + tree.label);
1104             print(";");
1105         } catch (IOException e) {
1106             throw new UncheckedIOException(e);
1107         }
1108     }
1109 
1110     public void visitReturn(JCReturn tree) {
1111         try {
1112             print("return");
1113             if (tree.expr != null) {
1114                 print(" ");
1115                 printExpr(tree.expr);
1116             }
1117             print(";");
1118         } catch (IOException e) {
1119             throw new UncheckedIOException(e);
1120         }
1121     }
1122 
1123     public void visitThrow(JCThrow tree) {
1124         try {
1125             print("throw ");
1126             printExpr(tree.expr);
1127             print(";");
1128         } catch (IOException e) {
1129             throw new UncheckedIOException(e);
1130         }
1131     }
1132 
1133     public void visitAssert(JCAssert tree) {
1134         try {
1135             print("assert ");
1136             printExpr(tree.cond);
1137             if (tree.detail != null) {
1138                 print(" : ");
1139                 printExpr(tree.detail);
1140             }
1141             print(";");
1142         } catch (IOException e) {
1143             throw new UncheckedIOException(e);
1144         }
1145     }
1146 
1147     public void visitApply(JCMethodInvocation tree) {
1148         try {
1149             if (!tree.typeargs.isEmpty()) {
1150                 if (tree.meth.hasTag(SELECT)) {
1151                     JCFieldAccess left = (JCFieldAccess)tree.meth;
1152                     printExpr(left.selected);
1153                     print(".<");
1154                     printExprs(tree.typeargs);
1155                     print(">" + left.name);
1156                 } else {
1157                     print("<");
1158                     printExprs(tree.typeargs);
1159                     print(">");
1160                     printExpr(tree.meth);
1161                 }
1162             } else {
1163                 printExpr(tree.meth);
1164             }
1165             print("(");
1166             printExprs(tree.args);
1167             print(")");
1168         } catch (IOException e) {
1169             throw new UncheckedIOException(e);
1170         }
1171     }
1172 
1173     public void visitNewClass(JCNewClass tree) {
1174         try {
1175             if (tree.encl != null) {
1176                 printExpr(tree.encl);
1177                 print(".");
1178             }
1179             print("new ");
1180             if (!tree.typeargs.isEmpty()) {
1181                 print("<");
1182                 printExprs(tree.typeargs);
1183                 print(">");
1184             }
1185             if (tree.def != null && tree.def.mods.annotations.nonEmpty()) {
1186                 printTypeAnnotations(tree.def.mods.annotations);
1187             }
1188             printExpr(tree.clazz);
1189             print("(");
1190             printExprs(tree.args);
1191             print(")");
1192             if (tree.def != null) {
1193                 Name enclClassNamePrev = enclClassName;
1194                 enclClassName =
1195                         tree.def.name != null ? tree.def.name :
1196                             tree.type != null && tree.type.tsym.name != tree.type.tsym.name.table.names.empty
1197                                 ? tree.type.tsym.name : null;
1198                 if ((tree.def.mods.flags & Flags.ENUM) != 0) print("/*enum*/");
1199                 printBlock(tree.def.defs);
1200                 enclClassName = enclClassNamePrev;
1201             }
1202         } catch (IOException e) {
1203             throw new UncheckedIOException(e);
1204         }
1205     }
1206 
1207     public void visitNewArray(JCNewArray tree) {
1208         try {
1209             if (tree.elemtype != null) {
1210                 print("new ");
1211                 JCTree elem = tree.elemtype;
1212                 printBaseElementType(elem);
1213 
1214                 if (!tree.annotations.isEmpty()) {
1215                     print(' ');
1216                     printTypeAnnotations(tree.annotations);
1217                 }
1218                 if (tree.elems != null) {
1219                     print("[]");
1220                 }
1221 
1222                 int i = 0;
1223                 List<List<JCAnnotation>> da = tree.dimAnnotations;
1224                 for (List<JCExpression> l = tree.dims; l.nonEmpty(); l = l.tail) {
1225                     if (da.size() > i && !da.get(i).isEmpty()) {
1226                         print(' ');
1227                         printTypeAnnotations(da.get(i));
1228                     }
1229                     print("[");
1230                     i++;
1231                     printExpr(l.head);
1232                     print("]");
1233                 }
1234                 printBrackets(elem);
1235             }
1236             if (tree.elems != null) {
1237                 print("{");
1238                 printExprs(tree.elems);
1239                 print("}");
1240             }
1241         } catch (IOException e) {
1242             throw new UncheckedIOException(e);
1243         }
1244     }
1245 
1246     public void visitLambda(JCLambda tree) {
1247         try {
1248             print("(");
1249             if (tree.paramKind == JCLambda.ParameterKind.EXPLICIT) {
1250                 printExprs(tree.params);
1251             } else {
1252                 String sep = "";
1253                 for (JCVariableDecl param : tree.params) {
1254                     print(sep);
1255                     print(param.name);
1256                     sep = ",";
1257                 }
1258             }
1259             print(")->");
1260             printExpr(tree.body);
1261         } catch (IOException e) {
1262             throw new UncheckedIOException(e);
1263         }
1264     }
1265 
1266     public void visitParens(JCParens tree) {
1267         try {
1268             print("(");
1269             printExpr(tree.expr);
1270             print(")");
1271         } catch (IOException e) {
1272             throw new UncheckedIOException(e);
1273         }
1274     }
1275 
1276     public void visitAssign(JCAssign tree) {
1277         try {
1278             open(prec, TreeInfo.assignPrec);
1279             printExpr(tree.lhs, TreeInfo.assignPrec + 1);
1280             print(" = ");
1281             printExpr(tree.rhs, TreeInfo.assignPrec);
1282             close(prec, TreeInfo.assignPrec);
1283         } catch (IOException e) {
1284             throw new UncheckedIOException(e);
1285         }
1286     }
1287 
1288     public String operatorName(JCTree.Tag tag) {
1289         switch(tag) {
1290             case POS:     return "+";
1291             case NEG:     return "-";
1292             case NOT:     return "!";
1293             case COMPL:   return "~";
1294             case PREINC:  return "++";
1295             case PREDEC:  return "--";
1296             case POSTINC: return "++";
1297             case POSTDEC: return "--";
1298             case NULLCHK: return "<*nullchk*>";
1299             case OR:      return "||";
1300             case AND:     return "&&";
1301             case EQ:      return "==";
1302             case NE:      return "!=";
1303             case LT:      return "<";
1304             case GT:      return ">";
1305             case LE:      return "<=";
1306             case GE:      return ">=";
1307             case BITOR:   return "|";
1308             case BITXOR:  return "^";
1309             case BITAND:  return "&";
1310             case SL:      return "<<";
1311             case SR:      return ">>";
1312             case USR:     return ">>>";
1313             case PLUS:    return "+";
1314             case MINUS:   return "-";
1315             case MUL:     return "*";
1316             case DIV:     return "/";
1317             case MOD:     return "%";
1318             default: throw new Error();
1319         }
1320     }
1321 
1322     public void visitAssignop(JCAssignOp tree) {
1323         try {
1324             open(prec, TreeInfo.assignopPrec);
1325             printExpr(tree.lhs, TreeInfo.assignopPrec + 1);
1326             print(" " + operatorName(tree.getTag().noAssignOp()) + "= ");
1327             printExpr(tree.rhs, TreeInfo.assignopPrec);
1328             close(prec, TreeInfo.assignopPrec);
1329         } catch (IOException e) {
1330             throw new UncheckedIOException(e);
1331         }
1332     }
1333 
1334     public void visitUnary(JCUnary tree) {
1335         try {
1336             int ownprec = TreeInfo.opPrec(tree.getTag());
1337             String opname = operatorName(tree.getTag());
1338             open(prec, ownprec);
1339             if (!tree.getTag().isPostUnaryOp()) {
1340                 print(opname);
1341                 printExpr(tree.arg, ownprec);
1342             } else {
1343                 printExpr(tree.arg, ownprec);
1344                 print(opname);
1345             }
1346             close(prec, ownprec);
1347         } catch (IOException e) {
1348             throw new UncheckedIOException(e);
1349         }
1350     }
1351 
1352     public void visitBinary(JCBinary tree) {
1353         try {
1354             int ownprec = TreeInfo.opPrec(tree.getTag());
1355             String opname = operatorName(tree.getTag());
1356             open(prec, ownprec);
1357             printExpr(tree.lhs, ownprec);
1358             print(" " + opname + " ");
1359             printExpr(tree.rhs, ownprec + 1);
1360             close(prec, ownprec);
1361         } catch (IOException e) {
1362             throw new UncheckedIOException(e);
1363         }
1364     }
1365 
1366     public void visitTypeCast(JCTypeCast tree) {
1367         try {
1368             open(prec, TreeInfo.prefixPrec);
1369             print("(");
1370             printExpr(tree.clazz);
1371             print(")");
1372             printExpr(tree.expr, TreeInfo.prefixPrec);
1373             close(prec, TreeInfo.prefixPrec);
1374         } catch (IOException e) {
1375             throw new UncheckedIOException(e);
1376         }
1377     }
1378 
1379     public void visitTypeTest(JCInstanceOf tree) {
1380         try {
1381             open(prec, TreeInfo.ordPrec);
1382             printExpr(tree.expr, TreeInfo.ordPrec);
1383             print(" instanceof ");
1384             if (tree.pattern instanceof JCPattern) {
1385                 printPattern(tree.pattern);
1386             } else {
1387                 printExpr(tree.getType(), TreeInfo.ordPrec + 1);
1388             }
1389             close(prec, TreeInfo.ordPrec);
1390         } catch (IOException e) {
1391             throw new UncheckedIOException(e);
1392         }
1393     }
1394 
1395     public void visitIndexed(JCArrayAccess tree) {
1396         try {
1397             printExpr(tree.indexed, TreeInfo.postfixPrec);
1398             print("[");
1399             printExpr(tree.index);
1400             print("]");
1401         } catch (IOException e) {
1402             throw new UncheckedIOException(e);
1403         }
1404     }
1405 
1406     public void visitSelect(JCFieldAccess tree) {
1407         try {
1408             printExpr(tree.selected, TreeInfo.postfixPrec);
1409             print("." + tree.name);
1410         } catch (IOException e) {
1411             throw new UncheckedIOException(e);
1412         }
1413     }
1414 
1415     public void visitReference(JCMemberReference tree) {
1416         try {
1417             printExpr(tree.expr);
1418             print("::");
1419             if (tree.typeargs != null) {
1420                 print("<");
1421                 printExprs(tree.typeargs);
1422                 print(">");
1423             }
1424             print(tree.getMode() == ReferenceMode.INVOKE ? tree.name : "new");
1425         } catch (IOException e) {
1426             throw new UncheckedIOException(e);
1427         }
1428     }
1429 
1430     public void visitIdent(JCIdent tree) {
1431         try {
1432             print(tree.name);
1433         } catch (IOException e) {
1434             throw new UncheckedIOException(e);
1435         }
1436     }
1437 
1438     public void visitLiteral(JCLiteral tree) {
1439         try {
1440             switch (tree.typetag) {
1441                 case INT:
1442                     print(tree.value.toString());
1443                     break;
1444                 case LONG:
1445                     print(tree.value + "L");
1446                     break;
1447                 case FLOAT:
1448                     print(tree.value + "F");
1449                     break;
1450                 case DOUBLE:
1451                     print(tree.value.toString());
1452                     break;
1453                 case CHAR:
1454                     print("\'" +
1455                             Convert.quote(
1456                             String.valueOf((char)((Number)tree.value).intValue())) +
1457                             "\'");
1458                     break;
1459                 case BOOLEAN:
1460                     print(((Number)tree.value).intValue() == 1 ? "true" : "false");
1461                     break;
1462                 case BOT:
1463                     print("null");
1464                     break;
1465                 default:
1466                     print("\"" + Convert.quote(tree.value.toString()) + "\"");
1467                     break;
1468             }
1469         } catch (IOException e) {
1470             throw new UncheckedIOException(e);
1471         }
1472     }
1473 
1474     public void visitTypeIdent(JCPrimitiveTypeTree tree) {
1475         try {
1476             switch(tree.typetag) {
1477                 case BYTE:
1478                     print("byte");
1479                     break;
1480                 case CHAR:
1481                     print("char");
1482                     break;
1483                 case SHORT:
1484                     print("short");
1485                     break;
1486                 case INT:
1487                     print("int");
1488                     break;
1489                 case LONG:
1490                     print("long");
1491                     break;
1492                 case FLOAT:
1493                     print("float");
1494                     break;
1495                 case DOUBLE:
1496                     print("double");
1497                     break;
1498                 case BOOLEAN:
1499                     print("boolean");
1500                     break;
1501                 case VOID:
1502                     print("void");
1503                     break;
1504                 default:
1505                     print("error");
1506                     break;
1507             }
1508         } catch (IOException e) {
1509             throw new UncheckedIOException(e);
1510         }
1511     }
1512 
1513     public void visitTypeArray(JCArrayTypeTree tree) {
1514         try {
1515             printBaseElementType(tree);
1516             printBrackets(tree);
1517         } catch (IOException e) {
1518             throw new UncheckedIOException(e);
1519         }
1520     }
1521 
1522     // Prints the inner element type of a nested array
1523     private void printBaseElementType(JCTree tree) throws IOException {
1524         printExpr(TreeInfo.innermostType(tree, false));
1525     }
1526 
1527     // prints the brackets of a nested array in reverse order
1528     // tree is either JCArrayTypeTree or JCAnnotatedTypeTree
1529     private void printBrackets(JCTree tree) throws IOException {
1530         JCTree elem = tree;
1531         while (true) {
1532             if (elem.hasTag(ANNOTATED_TYPE)) {
1533                 JCAnnotatedType atype = (JCAnnotatedType) elem;
1534                 elem = atype.underlyingType;
1535                 if (elem.hasTag(TYPEARRAY)) {
1536                     print(' ');
1537                     printTypeAnnotations(atype.annotations);
1538                 }
1539             }
1540             if (elem.hasTag(TYPEARRAY)) {
1541                 print("[]");
1542                 elem = ((JCArrayTypeTree)elem).elemtype;
1543             } else {
1544                 break;
1545             }
1546         }
1547     }
1548 
1549     public void visitTypeApply(JCTypeApply tree) {
1550         try {
1551             printExpr(tree.clazz);
1552             print("<");
1553             printExprs(tree.arguments);
1554             print(">");
1555         } catch (IOException e) {
1556             throw new UncheckedIOException(e);
1557         }
1558     }
1559 
1560     public void visitTypeUnion(JCTypeUnion tree) {
1561         try {
1562             printExprs(tree.alternatives, " | ");
1563         } catch (IOException e) {
1564             throw new UncheckedIOException(e);
1565         }
1566     }
1567 
1568     public void visitTypeIntersection(JCTypeIntersection tree) {
1569         try {
1570             printExprs(tree.bounds, " & ");
1571         } catch (IOException e) {
1572             throw new UncheckedIOException(e);
1573         }
1574     }
1575 
1576     public void visitTypeParameter(JCTypeParameter tree) {
1577         try {
1578             if (tree.annotations.nonEmpty()) {
1579                 this.printTypeAnnotations(tree.annotations);
1580             }
1581             print(tree.name);
1582             if (tree.bounds.nonEmpty()) {
1583                 print(" extends ");
1584                 printExprs(tree.bounds, " & ");
1585             }
1586         } catch (IOException e) {
1587             throw new UncheckedIOException(e);
1588         }
1589     }
1590 
1591     @Override
1592     public void visitWildcard(JCWildcard tree) {
1593         try {
1594             print(tree.kind);
1595             if (tree.kind.kind != BoundKind.UNBOUND)
1596                 printExpr(tree.inner);
1597         } catch (IOException e) {
1598             throw new UncheckedIOException(e);
1599         }
1600     }
1601 
1602     @Override
1603     public void visitTypeBoundKind(TypeBoundKind tree) {
1604         try {
1605             print(String.valueOf(tree.kind));
1606         } catch (IOException e) {
1607             throw new UncheckedIOException(e);
1608         }
1609     }
1610 
1611     public void visitErroneous(JCErroneous tree) {
1612         try {
1613             print("(ERROR)");
1614         } catch (IOException e) {
1615             throw new UncheckedIOException(e);
1616         }
1617     }
1618 
1619     public void visitLetExpr(LetExpr tree) {
1620         try {
1621             print("(let " + tree.defs + " in " + tree.expr + ")");
1622         } catch (IOException e) {
1623             throw new UncheckedIOException(e);
1624         }
1625     }
1626 
1627     public void visitModifiers(JCModifiers mods) {
1628         try {
1629             printAnnotations(mods.annotations);
1630             printFlags(mods.flags);
1631         } catch (IOException e) {
1632             throw new UncheckedIOException(e);
1633         }
1634     }
1635 
1636     public void visitAnnotation(JCAnnotation tree) {
1637         try {
1638             print("@");
1639             printExpr(tree.annotationType);
1640             if (!tree.args.isEmpty()) {
1641                 print("(");
1642                 printExprs(tree.args);
1643                 print(")");
1644             }
1645         } catch (IOException e) {
1646             throw new UncheckedIOException(e);
1647         }
1648     }
1649 
1650     public void visitAnnotatedType(JCAnnotatedType tree) {
1651         try {
1652             if (tree.underlyingType.hasTag(SELECT)) {
1653                 JCFieldAccess access = (JCFieldAccess) tree.underlyingType;
1654                 printExpr(access.selected, TreeInfo.postfixPrec);
1655                 print(".");
1656                 printTypeAnnotations(tree.annotations);
1657                 print(access.name);
1658             } else if (tree.underlyingType.hasTag(TYPEARRAY)) {
1659                 printBaseElementType(tree);
1660                 printBrackets(tree);
1661             } else {
1662                 printTypeAnnotations(tree.annotations);
1663                 printExpr(tree.underlyingType);
1664             }
1665         } catch (IOException e) {
1666             throw new UncheckedIOException(e);
1667         }
1668     }
1669 
1670     public void visitTree(JCTree tree) {
1671         try {
1672             print("(UNKNOWN: " + tree.getTag() + ")");
1673             println();
1674         } catch (IOException e) {
1675             throw new UncheckedIOException(e);
1676         }
1677     }
1678 
1679 }