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