1 /*
   2  * Copyright (c) 1999, 2011, 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 import java.util.*;
  30 
  31 import com.sun.tools.javac.util.*;
  32 import com.sun.tools.javac.util.List;
  33 import com.sun.tools.javac.code.*;
  34 
  35 import com.sun.tools.javac.code.Symbol.*;
  36 import com.sun.tools.javac.tree.JCTree.*;
  37 
  38 import static com.sun.tools.javac.code.Flags.*;
  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 hashtable mapping trees to their documentation comments
  78      *  (can be null)
  79      */
  80     Map<JCTree, String> docComments = null;
  81 
  82     /** Align code to be indented to left margin.
  83      */
  84     void align() throws IOException {
  85         for (int i = 0; i < lmargin; i++) out.write(" ");
  86     }
  87 
  88     /** Increase left margin by indentation width.
  89      */
  90     void indent() {
  91         lmargin = lmargin + width;
  92     }
  93 
  94     /** Decrease left margin by indentation width.
  95      */
  96     void undent() {
  97         lmargin = lmargin - width;
  98     }
  99 
 100     /** Enter a new precedence level. Emit a `(' if new precedence level
 101      *  is less than precedence level so far.
 102      *  @param contextPrec    The precedence level in force so far.
 103      *  @param ownPrec        The new precedence level.
 104      */
 105     void open(int contextPrec, int ownPrec) throws IOException {
 106         if (ownPrec < contextPrec) out.write("(");
 107     }
 108 
 109     /** Leave precedence level. Emit a `(' if inner precedence level
 110      *  is less than precedence level we revert to.
 111      *  @param contextPrec    The precedence level we revert to.
 112      *  @param ownPrec        The inner precedence level.
 113      */
 114     void close(int contextPrec, int ownPrec) throws IOException {
 115         if (ownPrec < contextPrec) out.write(")");
 116     }
 117 
 118     /** Print string, replacing all non-ascii character with unicode escapes.
 119      */
 120     public void print(Object s) throws IOException {
 121         out.write(Convert.escapeUnicode(s.toString()));
 122     }
 123 
 124     /** Print new line.
 125      */
 126     public void println() throws IOException {
 127         out.write(lineSep);
 128     }
 129 
 130     String lineSep = System.getProperty("line.separator");
 131 
 132     /**************************************************************************
 133      * Traversal methods
 134      *************************************************************************/
 135 
 136     /** Exception to propogate IOException through visitXXX methods */
 137     private static class UncheckedIOException extends Error {
 138         static final long serialVersionUID = -4032692679158424751L;
 139         UncheckedIOException(IOException e) {
 140             super(e.getMessage(), e);
 141         }
 142     }
 143 
 144     /** Visitor argument: the current precedence level.
 145      */
 146     int prec;
 147 
 148     /** Visitor method: print expression tree.
 149      *  @param prec  The current precedence level.
 150      */
 151     public void printExpr(JCTree tree, int prec) throws IOException {
 152         int prevPrec = this.prec;
 153         try {
 154             this.prec = prec;
 155             if (tree == null) print("/*missing*/");
 156             else {
 157                 tree.accept(this);
 158             }
 159         } catch (UncheckedIOException ex) {
 160             IOException e = new IOException(ex.getMessage());
 161             e.initCause(ex);
 162             throw e;
 163         } finally {
 164             this.prec = prevPrec;
 165         }
 166     }
 167 
 168     /** Derived visitor method: print expression tree at minimum precedence level
 169      *  for expression.
 170      */
 171     public void printExpr(JCTree tree) throws IOException {
 172         printExpr(tree, TreeInfo.noPrec);
 173     }
 174 
 175     /** Derived visitor method: print statement tree.
 176      */
 177     public void printStat(JCTree tree) throws IOException {
 178         printExpr(tree, TreeInfo.notExpression);
 179     }
 180 
 181     /** Derived visitor method: print list of expression trees, separated by given string.
 182      *  @param sep the separator string
 183      */
 184     public <T extends JCTree> void printExprs(List<T> trees, String sep) throws IOException {
 185         if (trees.nonEmpty()) {
 186             printExpr(trees.head);
 187             for (List<T> l = trees.tail; l.nonEmpty(); l = l.tail) {
 188                 print(sep);
 189                 printExpr(l.head);
 190             }
 191         }
 192     }
 193 
 194     /** Derived visitor method: print list of expression trees, separated by commas.
 195      */
 196     public <T extends JCTree> void printExprs(List<T> trees) throws IOException {
 197         printExprs(trees, ", ");
 198     }
 199 
 200     /** Derived visitor method: print list of statements, each on a separate line.
 201      */
 202     public void printStats(List<? extends JCTree> trees) throws IOException {
 203         for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail) {
 204             align();
 205             printStat(l.head);
 206             println();
 207         }
 208     }
 209 
 210     /** Print a set of modifiers.
 211      */
 212     public void printFlags(long flags) throws IOException {
 213         if ((flags & SYNTHETIC) != 0) print("/*synthetic*/ ");
 214         print(TreeInfo.flagNames(flags));
 215         if ((flags & StandardFlags) != 0) print(" ");
 216         if ((flags & ANNOTATION) != 0) print("@");
 217     }
 218 
 219     public void printAnnotations(List<JCAnnotation> trees) throws IOException {
 220         for (List<JCAnnotation> l = trees; l.nonEmpty(); l = l.tail) {
 221             printStat(l.head);
 222             println();
 223             align();
 224         }
 225     }
 226 
 227     /** Print documentation comment, if it exists
 228      *  @param tree    The tree for which a documentation comment should be printed.
 229      */
 230     public void printDocComment(JCTree tree) throws IOException {
 231         if (docComments != null) {
 232             String dc = docComments.get(tree);
 233             if (dc != null) {
 234                 print("/**"); println();
 235                 int pos = 0;
 236                 int endpos = lineEndPos(dc, pos);
 237                 while (pos < dc.length()) {
 238                     align();
 239                     print(" *");
 240                     if (pos < dc.length() && dc.charAt(pos) > ' ') print(" ");
 241                     print(dc.substring(pos, endpos)); println();
 242                     pos = endpos + 1;
 243                     endpos = lineEndPos(dc, pos);
 244                 }
 245                 align(); print(" */"); println();
 246                 align();
 247             }
 248         }
 249     }
 250 //where
 251     static int lineEndPos(String s, int start) {
 252         int pos = s.indexOf('\n', start);
 253         if (pos < 0) pos = s.length();
 254         return pos;
 255     }
 256 
 257     /** If type parameter list is non-empty, print it enclosed in "<...>" brackets.
 258      */
 259     public void printTypeParameters(List<JCTypeParameter> trees) throws IOException {
 260         if (trees.nonEmpty()) {
 261             print("<");
 262             printExprs(trees);
 263             print(">");
 264         }
 265     }
 266 
 267     /** Print a block.
 268      */
 269     public void printBlock(List<? extends JCTree> stats) throws IOException {
 270         print("{");
 271         println();
 272         indent();
 273         printStats(stats);
 274         undent();
 275         align();
 276         print("}");
 277     }
 278 
 279     /** Print a block.
 280      */
 281     public void printEnumBody(List<JCTree> stats) throws IOException {
 282         print("{");
 283         println();
 284         indent();
 285         boolean first = true;
 286         for (List<JCTree> l = stats; l.nonEmpty(); l = l.tail) {
 287             if (isEnumerator(l.head)) {
 288                 if (!first) {
 289                     print(",");
 290                     println();
 291                 }
 292                 align();
 293                 printStat(l.head);
 294                 first = false;
 295             }
 296         }
 297         print(";");
 298         println();
 299         for (List<JCTree> l = stats; l.nonEmpty(); l = l.tail) {
 300             if (!isEnumerator(l.head)) {
 301                 align();
 302                 printStat(l.head);
 303                 println();
 304             }
 305         }
 306         undent();
 307         align();
 308         print("}");
 309     }
 310 
 311     /** Is the given tree an enumerator definition? */
 312     boolean isEnumerator(JCTree t) {
 313         return t.getTag() == JCTree.VARDEF && (((JCVariableDecl) t).mods.flags & ENUM) != 0;
 314     }
 315 
 316     /** Print unit consisting of package clause and import statements in toplevel,
 317      *  followed by class definition. if class definition == null,
 318      *  print all definitions in toplevel.
 319      *  @param tree     The toplevel tree
 320      *  @param cdef     The class definition, which is assumed to be part of the
 321      *                  toplevel tree.
 322      */
 323     public void printUnit(JCCompilationUnit tree, JCClassDecl cdef) throws IOException {
 324         docComments = tree.docComments;
 325         printDocComment(tree);
 326         if (tree.pid != null) {
 327             print("package ");
 328             printExpr(tree.pid);
 329             print(";");
 330             println();
 331         }
 332         boolean firstImport = true;
 333         for (List<JCTree> l = tree.defs;
 334         l.nonEmpty() && (cdef == null || l.head.getTag() == JCTree.IMPORT);
 335         l = l.tail) {
 336             if (l.head.getTag() == JCTree.IMPORT) {
 337                 JCImport imp = (JCImport)l.head;
 338                 Name name = TreeInfo.name(imp.qualid);
 339                 if (name == name.table.names.asterisk ||
 340                         cdef == null ||
 341                         isUsed(TreeInfo.symbol(imp.qualid), cdef)) {
 342                     if (firstImport) {
 343                         firstImport = false;
 344                         println();
 345                     }
 346                     printStat(imp);
 347                 }
 348             } else {
 349                 printStat(l.head);
 350             }
 351         }
 352         if (cdef != null) {
 353             printStat(cdef);
 354             println();
 355         }
 356     }
 357     // where
 358     boolean isUsed(final Symbol t, JCTree cdef) {
 359         class UsedVisitor extends TreeScanner {
 360             public void scan(JCTree tree) {
 361                 if (tree!=null && !result) tree.accept(this);
 362             }
 363             boolean result = false;
 364             public void visitIdent(JCIdent tree) {
 365                 if (tree.sym == t) result = true;
 366             }
 367         }
 368         UsedVisitor v = new UsedVisitor();
 369         v.scan(cdef);
 370         return v.result;
 371     }
 372 
 373     /**************************************************************************
 374      * Visitor methods
 375      *************************************************************************/
 376 
 377     public void visitTopLevel(JCCompilationUnit tree) {
 378         try {
 379             printUnit(tree, null);
 380         } catch (IOException e) {
 381             throw new UncheckedIOException(e);
 382         }
 383     }
 384 
 385     public void visitImport(JCImport tree) {
 386         try {
 387             print("import ");
 388             if (tree.staticImport) print("static ");
 389             printExpr(tree.qualid);
 390             print(";");
 391             println();
 392         } catch (IOException e) {
 393             throw new UncheckedIOException(e);
 394         }
 395     }
 396 
 397     public void visitClassDef(JCClassDecl tree) {
 398         try {
 399             println(); align();
 400             printDocComment(tree);
 401             printAnnotations(tree.mods.annotations);
 402             printFlags(tree.mods.flags & ~INTERFACE);
 403             Name enclClassNamePrev = enclClassName;
 404             enclClassName = tree.name;
 405             if ((tree.mods.flags & INTERFACE) != 0) {
 406                 print("interface " + tree.name);
 407                 printTypeParameters(tree.typarams);
 408                 if (tree.implementing.nonEmpty()) {
 409                     print(" extends ");
 410                     printExprs(tree.implementing);
 411                 }
 412             } else {
 413                 if ((tree.mods.flags & ENUM) != 0)
 414                     print("enum " + tree.name);
 415                 else
 416                     print("class " + tree.name);
 417                 printTypeParameters(tree.typarams);
 418                 if (tree.extending != null) {
 419                     print(" extends ");
 420                     printExpr(tree.extending);
 421                 }
 422                 if (tree.implementing.nonEmpty()) {
 423                     print(" implements ");
 424                     printExprs(tree.implementing);
 425                 }
 426             }
 427             print(" ");
 428             if ((tree.mods.flags & ENUM) != 0) {
 429                 printEnumBody(tree.defs);
 430             } else {
 431                 printBlock(tree.defs);
 432             }
 433             enclClassName = enclClassNamePrev;
 434         } catch (IOException e) {
 435             throw new UncheckedIOException(e);
 436         }
 437     }
 438 
 439     public void visitMethodDef(JCMethodDecl tree) {
 440         try {
 441             // when producing source output, omit anonymous constructors
 442             if (tree.name == tree.name.table.names.init &&
 443                     enclClassName == null &&
 444                     sourceOutput) return;
 445             println(); align();
 446             printDocComment(tree);
 447             printExpr(tree.mods);
 448             printTypeParameters(tree.typarams);
 449             if (tree.name == tree.name.table.names.init) {
 450                 print(enclClassName != null ? enclClassName : tree.name);
 451             } else {
 452                 printExpr(tree.restype);
 453                 print(" " + tree.name);
 454             }
 455             print("(");
 456             printExprs(tree.params);
 457             print(")");
 458             if (tree.thrown.nonEmpty()) {
 459                 print(" throws ");
 460                 printExprs(tree.thrown);
 461             }
 462             if (tree.defaultValue != null) {
 463                 print(" default ");
 464                 printExpr(tree.defaultValue);
 465             }
 466             if (tree.body != null) {
 467                 print(" ");
 468                 printStat(tree.body);
 469             } else {
 470                 print(";");
 471             }
 472         } catch (IOException e) {
 473             throw new UncheckedIOException(e);
 474         }
 475     }
 476 
 477     public void visitVarDef(JCVariableDecl tree) {
 478         try {
 479             if (docComments != null && docComments.get(tree) != null) {
 480                 println(); align();
 481             }
 482             printDocComment(tree);
 483             if ((tree.mods.flags & ENUM) != 0) {
 484                 print("/*public static final*/ ");
 485                 print(tree.name);
 486                 if (tree.init != null) {
 487                     if (sourceOutput && tree.init.getTag() == JCTree.NEWCLASS) {
 488                         print(" /*enum*/ ");
 489                         JCNewClass init = (JCNewClass) tree.init;
 490                         if (init.args != null && init.args.nonEmpty()) {
 491                             print("(");
 492                             print(init.args);
 493                             print(")");
 494                         }
 495                         if (init.def != null && init.def.defs != null) {
 496                             print(" ");
 497                             printBlock(init.def.defs);
 498                         }
 499                         return;
 500                     }
 501                     print(" /* = ");
 502                     printExpr(tree.init);
 503                     print(" */");
 504                 }
 505             } else {
 506                 printExpr(tree.mods);
 507                 if ((tree.mods.flags & VARARGS) != 0) {
 508                     printExpr(((JCArrayTypeTree) tree.vartype).elemtype);
 509                     print("... " + tree.name);
 510                 } else {
 511                     printExpr(tree.vartype);
 512                     print(" " + tree.name);
 513                 }
 514                 if (tree.init != null) {
 515                     print(" = ");
 516                     printExpr(tree.init);
 517                 }
 518                 if (prec == TreeInfo.notExpression) print(";");
 519             }
 520         } catch (IOException e) {
 521             throw new UncheckedIOException(e);
 522         }
 523     }
 524 
 525     public void visitSkip(JCSkip tree) {
 526         try {
 527             print(";");
 528         } catch (IOException e) {
 529             throw new UncheckedIOException(e);
 530         }
 531     }
 532 
 533     public void visitBlock(JCBlock tree) {
 534         try {
 535             printFlags(tree.flags);
 536             printBlock(tree.stats);
 537         } catch (IOException e) {
 538             throw new UncheckedIOException(e);
 539         }
 540     }
 541 
 542     public void visitDoLoop(JCDoWhileLoop tree) {
 543         try {
 544             print("do ");
 545             printStat(tree.body);
 546             align();
 547             print(" while ");
 548             if (tree.cond.getTag() == JCTree.PARENS) {
 549                 printExpr(tree.cond);
 550             } else {
 551                 print("(");
 552                 printExpr(tree.cond);
 553                 print(")");
 554             }
 555             print(";");
 556         } catch (IOException e) {
 557             throw new UncheckedIOException(e);
 558         }
 559     }
 560 
 561     public void visitWhileLoop(JCWhileLoop tree) {
 562         try {
 563             print("while ");
 564             if (tree.cond.getTag() == JCTree.PARENS) {
 565                 printExpr(tree.cond);
 566             } else {
 567                 print("(");
 568                 printExpr(tree.cond);
 569                 print(")");
 570             }
 571             print(" ");
 572             printStat(tree.body);
 573         } catch (IOException e) {
 574             throw new UncheckedIOException(e);
 575         }
 576     }
 577 
 578     public void visitForLoop(JCForLoop tree) {
 579         try {
 580             print("for (");
 581             if (tree.init.nonEmpty()) {
 582                 if (tree.init.head.getTag() == JCTree.VARDEF) {
 583                     printExpr(tree.init.head);
 584                     for (List<JCStatement> l = tree.init.tail; l.nonEmpty(); l = l.tail) {
 585                         JCVariableDecl vdef = (JCVariableDecl)l.head;
 586                         print(", " + vdef.name + " = ");
 587                         printExpr(vdef.init);
 588                     }
 589                 } else {
 590                     printExprs(tree.init);
 591                 }
 592             }
 593             print("; ");
 594             if (tree.cond != null) printExpr(tree.cond);
 595             print("; ");
 596             printExprs(tree.step);
 597             print(") ");
 598             printStat(tree.body);
 599         } catch (IOException e) {
 600             throw new UncheckedIOException(e);
 601         }
 602     }
 603 
 604     public void visitForeachLoop(JCEnhancedForLoop tree) {
 605         try {
 606             print("for (");
 607             printExpr(tree.var);
 608             print(" : ");
 609             printExpr(tree.expr);
 610             print(") ");
 611             printStat(tree.body);
 612         } catch (IOException e) {
 613             throw new UncheckedIOException(e);
 614         }
 615     }
 616 
 617     public void visitLabelled(JCLabeledStatement tree) {
 618         try {
 619             print(tree.label + ": ");
 620             printStat(tree.body);
 621         } catch (IOException e) {
 622             throw new UncheckedIOException(e);
 623         }
 624     }
 625 
 626     public void visitSwitch(JCSwitch tree) {
 627         try {
 628             print("switch ");
 629             if (tree.selector.getTag() == JCTree.PARENS) {
 630                 printExpr(tree.selector);
 631             } else {
 632                 print("(");
 633                 printExpr(tree.selector);
 634                 print(")");
 635             }
 636             print(" {");
 637             println();
 638             printStats(tree.cases);
 639             align();
 640             print("}");
 641         } catch (IOException e) {
 642             throw new UncheckedIOException(e);
 643         }
 644     }
 645 
 646     public void visitCase(JCCase tree) {
 647         try {
 648             if (tree.pat == null) {
 649                 print("default");
 650             } else {
 651                 print("case ");
 652                 printExpr(tree.pat);
 653             }
 654             print(": ");
 655             println();
 656             indent();
 657             printStats(tree.stats);
 658             undent();
 659             align();
 660         } catch (IOException e) {
 661             throw new UncheckedIOException(e);
 662         }
 663     }
 664 
 665     public void visitSynchronized(JCSynchronized tree) {
 666         try {
 667             print("synchronized ");
 668             if (tree.lock.getTag() == JCTree.PARENS) {
 669                 printExpr(tree.lock);
 670             } else {
 671                 print("(");
 672                 printExpr(tree.lock);
 673                 print(")");
 674             }
 675             print(" ");
 676             printStat(tree.body);
 677         } catch (IOException e) {
 678             throw new UncheckedIOException(e);
 679         }
 680     }
 681 
 682     public void visitTry(JCTry tree) {
 683         try {
 684             print("try ");
 685             if (tree.resources.nonEmpty()) {
 686                 print("(");
 687                 boolean first = true;
 688                 for (JCTree var : tree.resources) {
 689                     if (!first) {
 690                         println();
 691                         indent();
 692                     }
 693                     printStat(var);
 694                     first = false;
 695                 }
 696                 print(") ");
 697             }
 698             printStat(tree.body);
 699             for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
 700                 printStat(l.head);
 701             }
 702             if (tree.finalizer != null) {
 703                 print(" finally ");
 704                 printStat(tree.finalizer);
 705             }
 706         } catch (IOException e) {
 707             throw new UncheckedIOException(e);
 708         }
 709     }
 710 
 711     public void visitCatch(JCCatch tree) {
 712         try {
 713             print(" catch (");
 714             printExpr(tree.param);
 715             print(") ");
 716             printStat(tree.body);
 717         } catch (IOException e) {
 718             throw new UncheckedIOException(e);
 719         }
 720     }
 721 
 722     public void visitConditional(JCConditional tree) {
 723         try {
 724             open(prec, TreeInfo.condPrec);
 725             printExpr(tree.cond, TreeInfo.condPrec);
 726             print(" ? ");
 727             printExpr(tree.truepart, TreeInfo.condPrec);
 728             print(" : ");
 729             printExpr(tree.falsepart, TreeInfo.condPrec);
 730             close(prec, TreeInfo.condPrec);
 731         } catch (IOException e) {
 732             throw new UncheckedIOException(e);
 733         }
 734     }
 735 
 736     public void visitIf(JCIf tree) {
 737         try {
 738             print("if ");
 739             if (tree.cond.getTag() == JCTree.PARENS) {
 740                 printExpr(tree.cond);
 741             } else {
 742                 print("(");
 743                 printExpr(tree.cond);
 744                 print(")");
 745             }
 746             print(" ");
 747             printStat(tree.thenpart);
 748             if (tree.elsepart != null) {
 749                 print(" else ");
 750                 printStat(tree.elsepart);
 751             }
 752         } catch (IOException e) {
 753             throw new UncheckedIOException(e);
 754         }
 755     }
 756 
 757     public void visitExec(JCExpressionStatement tree) {
 758         try {
 759             printExpr(tree.expr);
 760             if (prec == TreeInfo.notExpression) print(";");
 761         } catch (IOException e) {
 762             throw new UncheckedIOException(e);
 763         }
 764     }
 765 
 766     public void visitBreak(JCBreak tree) {
 767         try {
 768             print("break");
 769             if (tree.label != null) print(" " + tree.label);
 770             print(";");
 771         } catch (IOException e) {
 772             throw new UncheckedIOException(e);
 773         }
 774     }
 775 
 776     public void visitContinue(JCContinue tree) {
 777         try {
 778             print("continue");
 779             if (tree.label != null) print(" " + tree.label);
 780             print(";");
 781         } catch (IOException e) {
 782             throw new UncheckedIOException(e);
 783         }
 784     }
 785 
 786     public void visitReturn(JCReturn tree) {
 787         try {
 788             print("return");
 789             if (tree.expr != null) {
 790                 print(" ");
 791                 printExpr(tree.expr);
 792             }
 793             print(";");
 794         } catch (IOException e) {
 795             throw new UncheckedIOException(e);
 796         }
 797     }
 798 
 799     public void visitThrow(JCThrow tree) {
 800         try {
 801             print("throw ");
 802             printExpr(tree.expr);
 803             print(";");
 804         } catch (IOException e) {
 805             throw new UncheckedIOException(e);
 806         }
 807     }
 808 
 809     public void visitAssert(JCAssert tree) {
 810         try {
 811             print("assert ");
 812             printExpr(tree.cond);
 813             if (tree.detail != null) {
 814                 print(" : ");
 815                 printExpr(tree.detail);
 816             }
 817             print(";");
 818         } catch (IOException e) {
 819             throw new UncheckedIOException(e);
 820         }
 821     }
 822 
 823     public void visitApply(JCMethodInvocation tree) {
 824         try {
 825             if (!tree.typeargs.isEmpty()) {
 826                 if (tree.meth.getTag() == JCTree.SELECT) {
 827                     JCFieldAccess left = (JCFieldAccess)tree.meth;
 828                     printExpr(left.selected);
 829                     print(".<");
 830                     printExprs(tree.typeargs);
 831                     print(">" + left.name);
 832                 } else {
 833                     print("<");
 834                     printExprs(tree.typeargs);
 835                     print(">");
 836                     printExpr(tree.meth);
 837                 }
 838             } else {
 839                 printExpr(tree.meth);
 840             }
 841             print("(");
 842             printExprs(tree.args);
 843             print(")");
 844         } catch (IOException e) {
 845             throw new UncheckedIOException(e);
 846         }
 847     }
 848 
 849     public void visitNewClass(JCNewClass tree) {
 850         try {
 851             if (tree.encl != null) {
 852                 printExpr(tree.encl);
 853                 print(".");
 854             }
 855             print("new ");
 856             if (!tree.typeargs.isEmpty()) {
 857                 print("<");
 858                 printExprs(tree.typeargs);
 859                 print(">");
 860             }
 861             printExpr(tree.clazz);
 862             print("(");
 863             printExprs(tree.args);
 864             print(")");
 865             if (tree.def != null) {
 866                 Name enclClassNamePrev = enclClassName;
 867                 enclClassName =
 868                         tree.def.name != null ? tree.def.name :
 869                             tree.type != null && tree.type.tsym.name != tree.type.tsym.name.table.names.empty
 870                                 ? tree.type.tsym.name : null;
 871                 if ((tree.def.mods.flags & Flags.ENUM) != 0) print("/*enum*/");
 872                 printBlock(tree.def.defs);
 873                 enclClassName = enclClassNamePrev;
 874             }
 875         } catch (IOException e) {
 876             throw new UncheckedIOException(e);
 877         }
 878     }
 879 
 880     public void visitNewArray(JCNewArray tree) {
 881         try {
 882             if (tree.elemtype != null) {
 883                 print("new ");
 884                 JCTree elem = tree.elemtype;
 885                 if (elem.getTag() == JCTree.TYPEARRAY)
 886                     printBaseElementType((JCArrayTypeTree) elem);
 887                 else
 888                     printExpr(elem);
 889                 for (List<JCExpression> l = tree.dims; l.nonEmpty(); l = l.tail) {
 890                     print("[");
 891                     printExpr(l.head);
 892                     print("]");
 893                 }
 894                 if (elem instanceof JCArrayTypeTree)
 895                     printBrackets((JCArrayTypeTree) elem);
 896             }
 897             if (tree.elems != null) {
 898                 if (tree.elemtype != null) print("[]");
 899                 print("{");
 900                 printExprs(tree.elems);
 901                 print("}");
 902             }
 903         } catch (IOException e) {
 904             throw new UncheckedIOException(e);
 905         }
 906     }
 907 
 908     public void visitParens(JCParens tree) {
 909         try {
 910             print("(");
 911             printExpr(tree.expr);
 912             print(")");
 913         } catch (IOException e) {
 914             throw new UncheckedIOException(e);
 915         }
 916     }
 917 
 918     public void visitAssign(JCAssign tree) {
 919         try {
 920             open(prec, TreeInfo.assignPrec);
 921             printExpr(tree.lhs, TreeInfo.assignPrec + 1);
 922             print(" = ");
 923             printExpr(tree.rhs, TreeInfo.assignPrec);
 924             close(prec, TreeInfo.assignPrec);
 925         } catch (IOException e) {
 926             throw new UncheckedIOException(e);
 927         }
 928     }
 929 
 930     public String operatorName(int tag) {
 931         switch(tag) {
 932             case JCTree.POS:     return "+";
 933             case JCTree.NEG:     return "-";
 934             case JCTree.NOT:     return "!";
 935             case JCTree.COMPL:   return "~";
 936             case JCTree.PREINC:  return "++";
 937             case JCTree.PREDEC:  return "--";
 938             case JCTree.POSTINC: return "++";
 939             case JCTree.POSTDEC: return "--";
 940             case JCTree.NULLCHK: return "<*nullchk*>";
 941             case JCTree.OR:      return "||";
 942             case JCTree.AND:     return "&&";
 943             case JCTree.EQ:      return "==";
 944             case JCTree.NE:      return "!=";
 945             case JCTree.LT:      return "<";
 946             case JCTree.GT:      return ">";
 947             case JCTree.LE:      return "<=";
 948             case JCTree.GE:      return ">=";
 949             case JCTree.BITOR:   return "|";
 950             case JCTree.BITXOR:  return "^";
 951             case JCTree.BITAND:  return "&";
 952             case JCTree.SL:      return "<<";
 953             case JCTree.SR:      return ">>";
 954             case JCTree.USR:     return ">>>";
 955             case JCTree.PLUS:    return "+";
 956             case JCTree.MINUS:   return "-";
 957             case JCTree.MUL:     return "*";
 958             case JCTree.DIV:     return "/";
 959             case JCTree.MOD:     return "%";
 960             default: throw new Error();
 961         }
 962     }
 963 
 964     public void visitAssignop(JCAssignOp tree) {
 965         try {
 966             open(prec, TreeInfo.assignopPrec);
 967             printExpr(tree.lhs, TreeInfo.assignopPrec + 1);
 968             print(" " + operatorName(tree.getTag() - JCTree.ASGOffset) + "= ");
 969             printExpr(tree.rhs, TreeInfo.assignopPrec);
 970             close(prec, TreeInfo.assignopPrec);
 971         } catch (IOException e) {
 972             throw new UncheckedIOException(e);
 973         }
 974     }
 975 
 976     public void visitUnary(JCUnary tree) {
 977         try {
 978             int ownprec = TreeInfo.opPrec(tree.getTag());
 979             String opname = operatorName(tree.getTag());
 980             open(prec, ownprec);
 981             if (tree.getTag() <= JCTree.PREDEC) {
 982                 print(opname);
 983                 printExpr(tree.arg, ownprec);
 984             } else {
 985                 printExpr(tree.arg, ownprec);
 986                 print(opname);
 987             }
 988             close(prec, ownprec);
 989         } catch (IOException e) {
 990             throw new UncheckedIOException(e);
 991         }
 992     }
 993 
 994     public void visitBinary(JCBinary tree) {
 995         try {
 996             int ownprec = TreeInfo.opPrec(tree.getTag());
 997             String opname = operatorName(tree.getTag());
 998             open(prec, ownprec);
 999             printExpr(tree.lhs, ownprec);
1000             print(" " + opname + " ");
1001             printExpr(tree.rhs, ownprec + 1);
1002             close(prec, ownprec);
1003         } catch (IOException e) {
1004             throw new UncheckedIOException(e);
1005         }
1006     }
1007 
1008     public void visitTypeCast(JCTypeCast tree) {
1009         try {
1010             open(prec, TreeInfo.prefixPrec);
1011             print("(");
1012             printExpr(tree.clazz);
1013             print(")");
1014             printExpr(tree.expr, TreeInfo.prefixPrec);
1015             close(prec, TreeInfo.prefixPrec);
1016         } catch (IOException e) {
1017             throw new UncheckedIOException(e);
1018         }
1019     }
1020 
1021     public void visitTypeTest(JCInstanceOf tree) {
1022         try {
1023             open(prec, TreeInfo.ordPrec);
1024             printExpr(tree.expr, TreeInfo.ordPrec);
1025             print(" instanceof ");
1026             printExpr(tree.clazz, TreeInfo.ordPrec + 1);
1027             close(prec, TreeInfo.ordPrec);
1028         } catch (IOException e) {
1029             throw new UncheckedIOException(e);
1030         }
1031     }
1032 
1033     public void visitIndexed(JCArrayAccess tree) {
1034         try {
1035             printExpr(tree.indexed, TreeInfo.postfixPrec);
1036             print("[");
1037             printExpr(tree.index);
1038             print("]");
1039         } catch (IOException e) {
1040             throw new UncheckedIOException(e);
1041         }
1042     }
1043 
1044     public void visitSelect(JCFieldAccess tree) {
1045         try {
1046             printExpr(tree.selected, TreeInfo.postfixPrec);
1047             print("." + tree.name);
1048         } catch (IOException e) {
1049             throw new UncheckedIOException(e);
1050         }
1051     }
1052 
1053     public void visitIdent(JCIdent tree) {
1054         try {
1055             print(tree.name);
1056         } catch (IOException e) {
1057             throw new UncheckedIOException(e);
1058         }
1059     }
1060 
1061     public void visitLiteral(JCLiteral tree) {
1062         try {
1063             switch (tree.typetag) {
1064                 case TypeTags.INT:
1065                     print(tree.value.toString());
1066                     break;
1067                 case TypeTags.LONG:
1068                     print(tree.value + "L");
1069                     break;
1070                 case TypeTags.FLOAT:
1071                     print(tree.value + "F");
1072                     break;
1073                 case TypeTags.DOUBLE:
1074                     print(tree.value.toString());
1075                     break;
1076                 case TypeTags.CHAR:
1077                     print("\'" +
1078                             Convert.quote(
1079                             String.valueOf((char)((Number)tree.value).intValue())) +
1080                             "\'");
1081                     break;
1082                 case TypeTags.BOOLEAN:
1083                     print(((Number)tree.value).intValue() == 1 ? "true" : "false");
1084                     break;
1085                 case TypeTags.BOT:
1086                     print("null");
1087                     break;
1088                 default:
1089                     print("\"" + Convert.quote(tree.value.toString()) + "\"");
1090                     break;
1091             }
1092         } catch (IOException e) {
1093             throw new UncheckedIOException(e);
1094         }
1095     }
1096 
1097     public void visitTypeIdent(JCPrimitiveTypeTree tree) {
1098         try {
1099             switch(tree.typetag) {
1100                 case TypeTags.BYTE:
1101                     print("byte");
1102                     break;
1103                 case TypeTags.CHAR:
1104                     print("char");
1105                     break;
1106                 case TypeTags.SHORT:
1107                     print("short");
1108                     break;
1109                 case TypeTags.INT:
1110                     print("int");
1111                     break;
1112                 case TypeTags.LONG:
1113                     print("long");
1114                     break;
1115                 case TypeTags.FLOAT:
1116                     print("float");
1117                     break;
1118                 case TypeTags.DOUBLE:
1119                     print("double");
1120                     break;
1121                 case TypeTags.BOOLEAN:
1122                     print("boolean");
1123                     break;
1124                 case TypeTags.VOID:
1125                     print("void");
1126                     break;
1127                 default:
1128                     print("error");
1129                     break;
1130             }
1131         } catch (IOException e) {
1132             throw new UncheckedIOException(e);
1133         }
1134     }
1135 
1136     public void visitTypeArray(JCArrayTypeTree tree) {
1137         try {
1138             printBaseElementType(tree);
1139             printBrackets(tree);
1140         } catch (IOException e) {
1141             throw new UncheckedIOException(e);
1142         }
1143     }
1144 
1145     // Prints the inner element type of a nested array
1146     private void printBaseElementType(JCTree tree) throws IOException {
1147         printExpr(TreeInfo.innermostType(tree));
1148     }
1149 
1150     // prints the brackets of a nested array in reverse order
1151     private void printBrackets(JCArrayTypeTree tree) throws IOException {
1152         JCTree elem;
1153         while (true) {
1154             elem = tree.elemtype;
1155             print("[]");
1156             if (elem.getTag() != JCTree.TYPEARRAY) break;
1157             tree = (JCArrayTypeTree) elem;
1158         }
1159     }
1160 
1161     public void visitTypeApply(JCTypeApply tree) {
1162         try {
1163             printExpr(tree.clazz);
1164             print("<");
1165             printExprs(tree.arguments);
1166             print(">");
1167         } catch (IOException e) {
1168             throw new UncheckedIOException(e);
1169         }
1170     }
1171 
1172     public void visitTypeDisjunction(JCTypeDisjunction tree) {
1173         try {
1174             printExprs(tree.alternatives, " | ");
1175         } catch (IOException e) {
1176             throw new UncheckedIOException(e);
1177         }
1178     }
1179 
1180     public void visitTypeParameter(JCTypeParameter tree) {
1181         try {
1182             print(tree.name);
1183             if (tree.bounds.nonEmpty()) {
1184                 print(" extends ");
1185                 printExprs(tree.bounds, " & ");
1186             }
1187         } catch (IOException e) {
1188             throw new UncheckedIOException(e);
1189         }
1190     }
1191 
1192     @Override
1193     public void visitWildcard(JCWildcard tree) {
1194         try {
1195             print(tree.kind);
1196             if (tree.kind.kind != BoundKind.UNBOUND)
1197                 printExpr(tree.inner);
1198         } catch (IOException e) {
1199             throw new UncheckedIOException(e);
1200         }
1201     }
1202 
1203     @Override
1204     public void visitTypeBoundKind(TypeBoundKind tree) {
1205         try {
1206             print(String.valueOf(tree.kind));
1207         } catch (IOException e) {
1208             throw new UncheckedIOException(e);
1209         }
1210     }
1211 
1212     public void visitErroneous(JCErroneous tree) {
1213         try {
1214             print("(ERROR)");
1215         } catch (IOException e) {
1216             throw new UncheckedIOException(e);
1217         }
1218     }
1219 
1220     public void visitLetExpr(LetExpr tree) {
1221         try {
1222             print("(let " + tree.defs + " in " + tree.expr + ")");
1223         } catch (IOException e) {
1224             throw new UncheckedIOException(e);
1225         }
1226     }
1227 
1228     public void visitModifiers(JCModifiers mods) {
1229         try {
1230             printAnnotations(mods.annotations);
1231             printFlags(mods.flags);
1232         } catch (IOException e) {
1233             throw new UncheckedIOException(e);
1234         }
1235     }
1236 
1237     public void visitAnnotation(JCAnnotation tree) {
1238         try {
1239             print("@");
1240             printExpr(tree.annotationType);
1241             print("(");
1242             printExprs(tree.args);
1243             print(")");
1244         } catch (IOException e) {
1245             throw new UncheckedIOException(e);
1246         }
1247     }
1248 
1249     public void visitTree(JCTree tree) {
1250         try {
1251             print("(UNKNOWN: " + tree + ")");
1252             println();
1253         } catch (IOException e) {
1254             throw new UncheckedIOException(e);
1255         }
1256     }
1257 
1258 }