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