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