1 /*
   2  * Copyright (c) 1999, 2017, 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.IOException;
  29 import java.io.Writer;
  30 import java.util.List;
  31 
  32 import com.sun.source.doctree.*;
  33 import com.sun.source.doctree.AttributeTree.ValueKind;
  34 import com.sun.tools.javac.util.Convert;
  35 import com.sun.tools.javac.util.DefinedBy;
  36 import com.sun.tools.javac.util.DefinedBy.Api;
  37 
  38 /**
  39  * Prints out a doc comment tree.
  40  *
  41  *  <p><b>This is NOT part of any supported API.
  42  *  If you write code that depends on this, you do so at your own risk.
  43  *  This code and its internal interfaces are subject to change or
  44  *  deletion without notice.</b>
  45  */
  46 public class DocPretty implements DocTreeVisitor<Void,Void> {
  47 
  48     /**
  49      * The output stream on which trees are printed.
  50      */
  51     final Writer out;
  52 
  53     /**
  54      * The left margin.
  55      */
  56     int lmargin = 0;
  57 
  58     public DocPretty(Writer out) {
  59         this.out = out;
  60     }
  61 
  62     /** Visitor method: print expression tree.
  63      */
  64     public void print(DocTree tree) throws IOException {
  65         try {
  66             if (tree == null)
  67                 print("/*missing*/");
  68             else {
  69                 tree.accept(this, null);
  70             }
  71         } catch (UncheckedIOException ex) {
  72             throw new IOException(ex.getMessage(), ex);
  73         }
  74     }
  75 
  76     /**
  77      * Print string, replacing all non-ascii character with unicode escapes.
  78      */
  79     protected void print(Object s) throws IOException {
  80         out.write(Convert.escapeUnicode(s.toString()));
  81     }
  82 
  83     /**
  84      * Print list.
  85      */
  86     public void print(List<? extends DocTree> list) throws IOException {
  87         for (DocTree t: list) {
  88             print(t);
  89         }
  90     }
  91 
  92     /**
  93      * Print list., with separators
  94      */
  95     protected void print(List<? extends DocTree> list, String sep) throws IOException {
  96         if (list.isEmpty())
  97             return;
  98         boolean first = true;
  99         for (DocTree t: list) {
 100             if (!first)
 101                 print(sep);
 102             print(t);
 103             first = false;
 104         }
 105     }
 106 
 107     /** Print new line.
 108      */
 109     protected void println() throws IOException {
 110         out.write(lineSep);
 111     }
 112 
 113     protected void printTagName(DocTree node) throws IOException {
 114         out.write("@");
 115         out.write(node.getKind().tagName);
 116     }
 117 
 118     final String lineSep = System.getProperty("line.separator");
 119 
 120     /**************************************************************************
 121      * Traversal methods
 122      *************************************************************************/
 123 
 124     /** Exception to propagate IOException through visitXXX methods */
 125     private static class UncheckedIOException extends Error {
 126         static final long serialVersionUID = -4032692679158424751L;
 127         UncheckedIOException(IOException e) {
 128             super(e.getMessage(), e);
 129         }
 130     }
 131 
 132     @Override
 133     public Void visitAccessor(AccessorTree node, Void aVoid) {
 134         try {
 135             print("{");
 136             printTagName(node);
 137             print(node.getDescription());
 138             print("}");
 139         } catch (IOException e) {
 140             throw new UncheckedIOException(e);
 141         }
 142         return null;
 143     }
 144 
 145     @Override @DefinedBy(Api.COMPILER_TREE)
 146     public Void visitAttribute(AttributeTree node, Void p) {
 147         try {
 148             print(node.getName());
 149             String quote;
 150             switch (node.getValueKind()) {
 151                 case EMPTY:
 152                     quote = null;
 153                     break;
 154                 case UNQUOTED:
 155                     quote = "";
 156                     break;
 157                 case SINGLE:
 158                     quote = "'";
 159                     break;
 160                 case DOUBLE:
 161                     quote = "\"";
 162                     break;
 163                 default:
 164                     throw new AssertionError();
 165             }
 166             if (quote != null) {
 167                 print("=" + quote);
 168                 print(node.getValue());
 169                 print(quote);
 170             }
 171         } catch (IOException e) {
 172             throw new UncheckedIOException(e);
 173         }
 174         return null;
 175     }
 176 
 177     @Override @DefinedBy(Api.COMPILER_TREE)
 178     public Void visitAuthor(AuthorTree node, Void p) {
 179         try {
 180             printTagName(node);
 181             print(" ");
 182             print(node.getName());
 183         } catch (IOException e) {
 184             throw new UncheckedIOException(e);
 185         }
 186         return null;
 187     }
 188 
 189     @Override @DefinedBy(Api.COMPILER_TREE)
 190     public Void visitComment(CommentTree node, Void p) {
 191         try {
 192             print(node.getBody());
 193         } catch (IOException e) {
 194             throw new UncheckedIOException(e);
 195         }
 196         return null;
 197     }
 198 
 199     @Override @DefinedBy(Api.COMPILER_TREE)
 200     public Void visitDeprecated(DeprecatedTree node, Void p) {
 201         try {
 202             printTagName(node);
 203             if (!node.getBody().isEmpty()) {
 204                 print(" ");
 205                 print(node.getBody());
 206             }
 207         } catch (IOException e) {
 208             throw new UncheckedIOException(e);
 209         }
 210         return null;
 211     }
 212 
 213     @Override @DefinedBy(Api.COMPILER_TREE)
 214     public Void visitDocComment(DocCommentTree node, Void p) {
 215         try {
 216             List<? extends DocTree> b = node.getFullBody();
 217             List<? extends DocTree> t = node.getBlockTags();
 218             print(b);
 219             if (!b.isEmpty() && !t.isEmpty())
 220                 print("\n");
 221             print(t, "\n");
 222         } catch (IOException e) {
 223             throw new UncheckedIOException(e);
 224         }
 225         return null;
 226     }
 227 
 228     @Override @DefinedBy(Api.COMPILER_TREE)
 229     public Void visitDocRoot(DocRootTree node, Void p) {
 230         try {
 231             print("{");
 232             printTagName(node);
 233             print("}");
 234         } catch (IOException e) {
 235             throw new UncheckedIOException(e);
 236         }
 237         return null;
 238     }
 239 
 240     @Override @DefinedBy(Api.COMPILER_TREE)
 241     public Void visitDocType(DocTypeTree node, Void p) {
 242         try {
 243             print(node.getText());
 244         } catch (IOException e) {
 245             throw new UncheckedIOException(e);
 246         }
 247         return null;
 248     }
 249 
 250     @Override @DefinedBy(Api.COMPILER_TREE)
 251     public Void visitEndElement(EndElementTree node, Void p) {
 252         try {
 253             print("</");
 254             print(node.getName());
 255             print(">");
 256         } catch (IOException e) {
 257             throw new UncheckedIOException(e);
 258         }
 259         return null;
 260     }
 261 
 262     @Override @DefinedBy(Api.COMPILER_TREE)
 263     public Void visitEntity(EntityTree node, Void p) {
 264         try {
 265             print("&");
 266             print(node.getName());
 267             print(";");
 268         } catch (IOException e) {
 269             throw new UncheckedIOException(e);
 270         }
 271         return null;
 272     }
 273 
 274     @Override @DefinedBy(Api.COMPILER_TREE)
 275     public Void visitErroneous(ErroneousTree node, Void p) {
 276         try {
 277             print(node.getBody());
 278         } catch (IOException e) {
 279             throw new UncheckedIOException(e);
 280         }
 281         return null;
 282     }
 283 
 284     @Override @DefinedBy(Api.COMPILER_TREE)
 285     public Void visitHidden(HiddenTree node, Void p) {
 286         try {
 287             printTagName(node);
 288             if (!node.getBody().isEmpty()) {
 289                 print(" ");
 290                 print(node.getBody());
 291             }
 292         } catch (IOException e) {
 293             throw new UncheckedIOException(e);
 294         }
 295         return null;
 296     }
 297 
 298     @Override @DefinedBy(Api.COMPILER_TREE)
 299     public Void visitIdentifier(IdentifierTree node, Void p) {
 300         try {
 301             print(node.getName());
 302         } catch (IOException e) {
 303             throw new UncheckedIOException(e);
 304         }
 305         return null;
 306     }
 307 
 308     @Override @DefinedBy(Api.COMPILER_TREE)
 309     public Void visitIndex(IndexTree node, Void p) {
 310         try {
 311             print("{");
 312             printTagName(node);
 313             print(" ");
 314             print(node.getSearchTerm());
 315             if (!node.getDescription().isEmpty()) {
 316                 print(" ");
 317                 print(node.getDescription());
 318             }
 319             print("}");
 320         } catch (IOException e) {
 321             throw new UncheckedIOException(e);
 322         }
 323         return null;
 324     }
 325 
 326     @Override @DefinedBy(Api.COMPILER_TREE)
 327     public Void visitInheritDoc(InheritDocTree node, Void p) {
 328         try {
 329             print("{");
 330             printTagName(node);
 331             print("}");
 332         } catch (IOException e) {
 333             throw new UncheckedIOException(e);
 334         }
 335         return null;
 336     }
 337 
 338     @Override @DefinedBy(Api.COMPILER_TREE)
 339     public Void visitLink(LinkTree node, Void p) {
 340         try {
 341             print("{");
 342             printTagName(node);
 343             print(" ");
 344             print(node.getReference());
 345             if (!node.getLabel().isEmpty()) {
 346                 print(" ");
 347                 print(node.getLabel());
 348             }
 349             print("}");
 350         } catch (IOException e) {
 351             throw new UncheckedIOException(e);
 352         }
 353         return null;
 354     }
 355 
 356     @Override @DefinedBy(Api.COMPILER_TREE)
 357     public Void visitLiteral(LiteralTree node, Void p) {
 358         try {
 359             print("{");
 360             printTagName(node);
 361             String body = node.getBody().getBody();
 362             if (!body.isEmpty() && !Character.isWhitespace(body.charAt(0))) {
 363                 print(" ");
 364             }
 365             print(node.getBody());
 366             print("}");
 367         } catch (IOException e) {
 368             throw new UncheckedIOException(e);
 369         }
 370         return null;
 371     }
 372 
 373     @Override @DefinedBy(Api.COMPILER_TREE)
 374     public Void visitParam(ParamTree node, Void p) {
 375         try {
 376             printTagName(node);
 377             print(" ");
 378             if (node.isTypeParameter()) print("<");
 379             print(node.getName());
 380             if (node.isTypeParameter()) print(">");
 381             if (!node.getDescription().isEmpty()) {
 382                 print(" ");
 383                 print(node.getDescription());
 384             }
 385         } catch (IOException e) {
 386             throw new UncheckedIOException(e);
 387         }
 388         return null;
 389     }
 390 
 391     @Override @DefinedBy(Api.COMPILER_TREE)
 392     public Void visitProvides(ProvidesTree node, Void p) {
 393         try {
 394             printTagName(node);
 395             print(" ");
 396             print(node.getServiceType());
 397             if (!node.getDescription().isEmpty()) {
 398                 print(" ");
 399                 print(node.getDescription());
 400             }
 401         } catch (IOException e) {
 402             throw new UncheckedIOException(e);
 403         }
 404         return null;
 405     }
 406 
 407     @Override @DefinedBy(Api.COMPILER_TREE)
 408     public Void visitReference(ReferenceTree node, Void p) {
 409         try {
 410             print(node.getSignature());
 411         } catch (IOException e) {
 412             throw new UncheckedIOException(e);
 413         }
 414         return null;
 415     }
 416 
 417     @Override @DefinedBy(Api.COMPILER_TREE)
 418     public Void visitReturn(ReturnTree node, Void p) {
 419         try {
 420             printTagName(node);
 421             print(" ");
 422             print(node.getDescription());
 423         } catch (IOException e) {
 424             throw new UncheckedIOException(e);
 425         }
 426         return null;
 427     }
 428 
 429     @Override @DefinedBy(Api.COMPILER_TREE)
 430     public Void visitSee(SeeTree node, Void p) {
 431         try {
 432             printTagName(node);
 433             boolean first = true;
 434             boolean needSep = true;
 435             for (DocTree t: node.getReference()) {
 436                 if (needSep) print(" ");
 437                 needSep = (first && (t instanceof ReferenceTree));
 438                 first = false;
 439                 print(t);
 440             }
 441         } catch (IOException e) {
 442             throw new UncheckedIOException(e);
 443         }
 444         return null;
 445     }
 446 
 447     @Override @DefinedBy(Api.COMPILER_TREE)
 448     public Void visitSerial(SerialTree node, Void p) {
 449         try {
 450             printTagName(node);
 451             if (!node.getDescription().isEmpty()) {
 452                 print(" ");
 453                 print(node.getDescription());
 454             }
 455         } catch (IOException e) {
 456             throw new UncheckedIOException(e);
 457         }
 458         return null;
 459     }
 460 
 461     @Override @DefinedBy(Api.COMPILER_TREE)
 462     public Void visitSerialData(SerialDataTree node, Void p) {
 463         try {
 464             printTagName(node);
 465             if (!node.getDescription().isEmpty()) {
 466                 print(" ");
 467                 print(node.getDescription());
 468             }
 469         } catch (IOException e) {
 470             throw new UncheckedIOException(e);
 471         }
 472         return null;
 473     }
 474 
 475     @Override @DefinedBy(Api.COMPILER_TREE)
 476     public Void visitSerialField(SerialFieldTree node, Void p) {
 477         try {
 478             printTagName(node);
 479             print(" ");
 480             print(node.getName());
 481             print(" ");
 482             print(node.getType());
 483             if (!node.getDescription().isEmpty()) {
 484                 print(" ");
 485                 print(node.getDescription());
 486             }
 487         } catch (IOException e) {
 488             throw new UncheckedIOException(e);
 489         }
 490         return null;
 491     }
 492 
 493     @Override @DefinedBy(Api.COMPILER_TREE)
 494     public Void visitSince(SinceTree node, Void p) {
 495         try {
 496             printTagName(node);
 497             print(" ");
 498             print(node.getBody());
 499         } catch (IOException e) {
 500             throw new UncheckedIOException(e);
 501         }
 502         return null;
 503     }
 504 
 505     @Override @DefinedBy(Api.COMPILER_TREE)
 506     public Void visitStartElement(StartElementTree node, Void p) {
 507         try {
 508             print("<");
 509             print(node.getName());
 510             List<? extends DocTree> attrs = node.getAttributes();
 511             if (!attrs.isEmpty()) {
 512                 print(" ");
 513                 print(attrs);
 514                 DocTree last = node.getAttributes().get(attrs.size() - 1);
 515                 if (node.isSelfClosing() && last instanceof AttributeTree
 516                         && ((AttributeTree) last).getValueKind() == ValueKind.UNQUOTED)
 517                     print(" ");
 518             }
 519             if (node.isSelfClosing())
 520                 print("/");
 521             print(">");
 522         } catch (IOException e) {
 523             throw new UncheckedIOException(e);
 524         }
 525         return null;
 526     }
 527 
 528     @Override @DefinedBy(Api.COMPILER_TREE)
 529     public Void visitSummary(SummaryTree node, Void p) {
 530         try {
 531             print("{");
 532             printTagName(node);
 533             if (!node.getSummary().isEmpty()) {
 534                 print(" ");
 535                 print(node.getSummary());
 536             }
 537             print("}");
 538         } catch (IOException e) {
 539             throw new UncheckedIOException(e);
 540         }
 541         return null;
 542     }
 543 
 544     @Override @DefinedBy(Api.COMPILER_TREE)
 545     public Void visitSystemProperty(SystemPropertyTree node, Void p) {
 546         try {
 547             print("{");
 548             printTagName(node);
 549             print(" ");
 550             print(node.getPropertyName());
 551             print("}");
 552         } catch (IOException e) {
 553             throw new UncheckedIOException(e);
 554         }
 555         return null;
 556     }
 557 
 558     @Override @DefinedBy(Api.COMPILER_TREE)
 559     public Void visitText(TextTree node, Void p) {
 560         try {
 561             print(node.getBody());
 562         } catch (IOException e) {
 563             throw new UncheckedIOException(e);
 564         }
 565         return null;
 566     }
 567 
 568     @Override @DefinedBy(Api.COMPILER_TREE)
 569     public Void visitThrows(ThrowsTree node, Void p) {
 570         try {
 571             printTagName(node);
 572             print(" ");
 573             print(node.getExceptionName());
 574             if (!node.getDescription().isEmpty()) {
 575                 print(" ");
 576                 print(node.getDescription());
 577             }
 578         } catch (IOException e) {
 579             throw new UncheckedIOException(e);
 580         }
 581         return null;
 582     }
 583 
 584     @Override @DefinedBy(Api.COMPILER_TREE)
 585     public Void visitUnknownBlockTag(UnknownBlockTagTree node, Void p) {
 586         try {
 587             print("@");
 588             print(node.getTagName());
 589             print(" ");
 590             print(node.getContent());
 591         } catch (IOException e) {
 592             throw new UncheckedIOException(e);
 593         }
 594         return null;
 595     }
 596 
 597     @Override @DefinedBy(Api.COMPILER_TREE)
 598     public Void visitUnknownInlineTag(UnknownInlineTagTree node, Void p) {
 599         try {
 600             print("{");
 601             print("@");
 602             print(node.getTagName());
 603             print(" ");
 604             print(node.getContent());
 605             print("}");
 606         } catch (IOException e) {
 607             throw new UncheckedIOException(e);
 608         }
 609         return null;
 610     }
 611 
 612     @Override @DefinedBy(Api.COMPILER_TREE)
 613     public Void visitUses(UsesTree node, Void p) {
 614         try {
 615             printTagName(node);
 616             print(" ");
 617             print(node.getServiceType());
 618             if (!node.getDescription().isEmpty()) {
 619                 print(" ");
 620                 print(node.getDescription());
 621             }
 622         } catch (IOException e) {
 623             throw new UncheckedIOException(e);
 624         }
 625         return null;
 626     }
 627 
 628     @Override @DefinedBy(Api.COMPILER_TREE)
 629     public Void visitValue(ValueTree node, Void p) {
 630         try {
 631             print("{");
 632             printTagName(node);
 633             if (node.getReference() != null) {
 634                 print(" ");
 635                 print(node.getReference());
 636             }
 637             print("}");
 638         } catch (IOException e) {
 639             throw new UncheckedIOException(e);
 640         }
 641         return null;
 642     }
 643 
 644     @Override @DefinedBy(Api.COMPILER_TREE)
 645     public Void visitVersion(VersionTree node, Void p) {
 646         try {
 647             printTagName(node);
 648             print(" ");
 649             print(node.getBody());
 650         } catch (IOException e) {
 651             throw new UncheckedIOException(e);
 652         }
 653         return null;
 654     }
 655 
 656     @Override @DefinedBy(Api.COMPILER_TREE)
 657     public Void visitOther(DocTree node, Void p) {
 658         try {
 659             print("(UNKNOWN: " + node + ")");
 660             println();
 661         } catch (IOException e) {
 662             throw new UncheckedIOException(e);
 663         }
 664         return null;
 665     }
 666 }