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