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