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