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