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