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