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