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 @DefinedBy(Api.COMPILER_TREE)
 133     public Void visitAttribute(AttributeTree node, Void p) {
 134         try {
 135             print(node.getName());
 136             String quote;
 137             switch (node.getValueKind()) {
 138                 case EMPTY:
 139                     quote = null;
 140                     break;
 141                 case UNQUOTED:
 142                     quote = "";
 143                     break;
 144                 case SINGLE:
 145                     quote = "'";
 146                     break;
 147                 case DOUBLE:
 148                     quote = "\"";
 149                     break;
 150                 default:
 151                     throw new AssertionError();
 152             }
 153             if (quote != null) {
 154                 print("=" + quote);
 155                 print(node.getValue());
 156                 print(quote);
 157             }
 158         } catch (IOException e) {
 159             throw new UncheckedIOException(e);
 160         }
 161         return null;
 162     }
 163 
 164     @Override @DefinedBy(Api.COMPILER_TREE)
 165     public Void visitAuthor(AuthorTree node, Void p) {
 166         try {
 167             printTagName(node);
 168             print(" ");
 169             print(node.getName());
 170         } catch (IOException e) {
 171             throw new UncheckedIOException(e);
 172         }
 173         return null;
 174     }
 175 
 176     @Override @DefinedBy(Api.COMPILER_TREE)
 177     public Void visitComment(CommentTree node, Void p) {
 178         try {
 179             print(node.getBody());
 180         } catch (IOException e) {
 181             throw new UncheckedIOException(e);
 182         }
 183         return null;
 184     }
 185 
 186     @Override @DefinedBy(Api.COMPILER_TREE)
 187     public Void visitDeprecated(DeprecatedTree node, Void p) {
 188         try {
 189             printTagName(node);
 190             if (!node.getBody().isEmpty()) {
 191                 print(" ");
 192                 print(node.getBody());
 193             }
 194         } catch (IOException e) {
 195             throw new UncheckedIOException(e);
 196         }
 197         return null;
 198     }
 199 
 200     @Override @DefinedBy(Api.COMPILER_TREE)
 201     public Void visitDocComment(DocCommentTree node, Void p) {
 202         try {
 203             List<? extends DocTree> b = node.getFullBody();
 204             List<? extends DocTree> t = node.getBlockTags();
 205             print(b);
 206             if (!b.isEmpty() && !t.isEmpty())
 207                 print("\n");
 208             print(t, "\n");
 209         } catch (IOException e) {
 210             throw new UncheckedIOException(e);
 211         }
 212         return null;
 213     }
 214 
 215     @Override @DefinedBy(Api.COMPILER_TREE)
 216     public Void visitDocRoot(DocRootTree node, Void p) {
 217         try {
 218             print("{");
 219             printTagName(node);
 220             print("}");
 221         } catch (IOException e) {
 222             throw new UncheckedIOException(e);
 223         }
 224         return null;
 225     }
 226 
 227     @Override @DefinedBy(Api.COMPILER_TREE)
 228     public Void visitDocType(DocTypeTree node, Void p) {
 229         try {
 230             print(node.getText());
 231         } catch (IOException e) {
 232             throw new UncheckedIOException(e);
 233         }
 234         return null;
 235     }
 236 
 237     @Override @DefinedBy(Api.COMPILER_TREE)
 238     public Void visitEndElement(EndElementTree node, Void p) {
 239         try {
 240             print("</");
 241             print(node.getName());
 242             print(">");
 243         } catch (IOException e) {
 244             throw new UncheckedIOException(e);
 245         }
 246         return null;
 247     }
 248 
 249     @Override @DefinedBy(Api.COMPILER_TREE)
 250     public Void visitEntity(EntityTree node, Void p) {
 251         try {
 252             print("&");
 253             print(node.getName());
 254             print(";");
 255         } catch (IOException e) {
 256             throw new UncheckedIOException(e);
 257         }
 258         return null;
 259     }
 260 
 261     @Override @DefinedBy(Api.COMPILER_TREE)
 262     public Void visitErroneous(ErroneousTree node, Void p) {
 263         try {
 264             print(node.getBody());
 265         } catch (IOException e) {
 266             throw new UncheckedIOException(e);
 267         }
 268         return null;
 269     }
 270 
 271     @Override @DefinedBy(Api.COMPILER_TREE)
 272     public Void visitHidden(HiddenTree node, Void p) {
 273         try {
 274             printTagName(node);
 275             if (!node.getBody().isEmpty()) {
 276                 print(" ");
 277                 print(node.getBody());
 278             }
 279         } catch (IOException e) {
 280             throw new UncheckedIOException(e);
 281         }
 282         return null;
 283     }
 284 
 285     @Override @DefinedBy(Api.COMPILER_TREE)
 286     public Void visitIdentifier(IdentifierTree node, Void p) {
 287         try {
 288             print(node.getName());
 289         } catch (IOException e) {
 290             throw new UncheckedIOException(e);
 291         }
 292         return null;
 293     }
 294 
 295     @Override @DefinedBy(Api.COMPILER_TREE)
 296     public Void visitIndex(IndexTree node, Void p) {
 297         try {
 298             print("{");
 299             printTagName(node);
 300             print(" ");
 301             print(node.getSearchTerm());
 302             if (!node.getDescription().isEmpty()) {
 303                 print(" ");
 304                 print(node.getDescription());
 305             }
 306             print("}");
 307         } catch (IOException e) {
 308             throw new UncheckedIOException(e);
 309         }
 310         return null;
 311     }
 312 
 313     @Override @DefinedBy(Api.COMPILER_TREE)
 314     public Void visitInheritDoc(InheritDocTree node, Void p) {
 315         try {
 316             print("{");
 317             printTagName(node);
 318             print("}");
 319         } catch (IOException e) {
 320             throw new UncheckedIOException(e);
 321         }
 322         return null;
 323     }
 324 
 325     @Override @DefinedBy(Api.COMPILER_TREE)
 326     public Void visitLink(LinkTree node, Void p) {
 327         try {
 328             print("{");
 329             printTagName(node);
 330             print(" ");
 331             print(node.getReference());
 332             if (!node.getLabel().isEmpty()) {
 333                 print(" ");
 334                 print(node.getLabel());
 335             }
 336             print("}");
 337         } catch (IOException e) {
 338             throw new UncheckedIOException(e);
 339         }
 340         return null;
 341     }
 342 
 343     @Override @DefinedBy(Api.COMPILER_TREE)
 344     public Void visitLiteral(LiteralTree node, Void p) {
 345         try {
 346             print("{");
 347             printTagName(node);
 348             String body = node.getBody().getBody();
 349             if (!body.isEmpty() && !Character.isWhitespace(body.charAt(0))) {
 350                 print(" ");
 351             }
 352             print(node.getBody());
 353             print("}");
 354         } catch (IOException e) {
 355             throw new UncheckedIOException(e);
 356         }
 357         return null;
 358     }
 359 
 360     @Override @DefinedBy(Api.COMPILER_TREE)
 361     public Void visitParam(ParamTree node, Void p) {
 362         try {
 363             printTagName(node);
 364             print(" ");
 365             if (node.isTypeParameter()) print("<");
 366             print(node.getName());
 367             if (node.isTypeParameter()) print(">");
 368             if (!node.getDescription().isEmpty()) {
 369                 print(" ");
 370                 print(node.getDescription());
 371             }
 372         } catch (IOException e) {
 373             throw new UncheckedIOException(e);
 374         }
 375         return null;
 376     }
 377 
 378     @Override @DefinedBy(Api.COMPILER_TREE)
 379     public Void visitProvides(ProvidesTree node, Void p) {
 380         try {
 381             printTagName(node);
 382             print(" ");
 383             print(node.getServiceType());
 384             if (!node.getDescription().isEmpty()) {
 385                 print(" ");
 386                 print(node.getDescription());
 387             }
 388         } catch (IOException e) {
 389             throw new UncheckedIOException(e);
 390         }
 391         return null;
 392     }
 393 
 394     @Override @DefinedBy(Api.COMPILER_TREE)
 395     public Void visitReference(ReferenceTree node, Void p) {
 396         try {
 397             print(node.getSignature());
 398         } catch (IOException e) {
 399             throw new UncheckedIOException(e);
 400         }
 401         return null;
 402     }
 403 
 404     @Override @DefinedBy(Api.COMPILER_TREE)
 405     public Void visitReturn(ReturnTree node, Void p) {
 406         try {
 407             printTagName(node);
 408             print(" ");
 409             print(node.getDescription());
 410         } catch (IOException e) {
 411             throw new UncheckedIOException(e);
 412         }
 413         return null;
 414     }
 415 
 416     @Override @DefinedBy(Api.COMPILER_TREE)
 417     public Void visitSee(SeeTree node, Void p) {
 418         try {
 419             printTagName(node);
 420             boolean first = true;
 421             boolean needSep = true;
 422             for (DocTree t: node.getReference()) {
 423                 if (needSep) print(" ");
 424                 needSep = (first && (t instanceof ReferenceTree));
 425                 first = false;
 426                 print(t);
 427             }
 428         } catch (IOException e) {
 429             throw new UncheckedIOException(e);
 430         }
 431         return null;
 432     }
 433 
 434     @Override @DefinedBy(Api.COMPILER_TREE)
 435     public Void visitSerial(SerialTree node, Void p) {
 436         try {
 437             printTagName(node);
 438             if (!node.getDescription().isEmpty()) {
 439                 print(" ");
 440                 print(node.getDescription());
 441             }
 442         } catch (IOException e) {
 443             throw new UncheckedIOException(e);
 444         }
 445         return null;
 446     }
 447 
 448     @Override @DefinedBy(Api.COMPILER_TREE)
 449     public Void visitSerialData(SerialDataTree node, Void p) {
 450         try {
 451             printTagName(node);
 452             if (!node.getDescription().isEmpty()) {
 453                 print(" ");
 454                 print(node.getDescription());
 455             }
 456         } catch (IOException e) {
 457             throw new UncheckedIOException(e);
 458         }
 459         return null;
 460     }
 461 
 462     @Override @DefinedBy(Api.COMPILER_TREE)
 463     public Void visitSerialField(SerialFieldTree node, Void p) {
 464         try {
 465             printTagName(node);
 466             print(" ");
 467             print(node.getName());
 468             print(" ");
 469             print(node.getType());
 470             if (!node.getDescription().isEmpty()) {
 471                 print(" ");
 472                 print(node.getDescription());
 473             }
 474         } catch (IOException e) {
 475             throw new UncheckedIOException(e);
 476         }
 477         return null;
 478     }
 479 
 480     @Override @DefinedBy(Api.COMPILER_TREE)
 481     public Void visitSince(SinceTree node, Void p) {
 482         try {
 483             printTagName(node);
 484             print(" ");
 485             print(node.getBody());
 486         } catch (IOException e) {
 487             throw new UncheckedIOException(e);
 488         }
 489         return null;
 490     }
 491 
 492     @Override @DefinedBy(Api.COMPILER_TREE)
 493     public Void visitStartElement(StartElementTree node, Void p) {
 494         try {
 495             print("<");
 496             print(node.getName());
 497             List<? extends DocTree> attrs = node.getAttributes();
 498             if (!attrs.isEmpty()) {
 499                 print(" ");
 500                 print(attrs);
 501                 DocTree last = node.getAttributes().get(attrs.size() - 1);
 502                 if (node.isSelfClosing() && last instanceof AttributeTree
 503                         && ((AttributeTree) last).getValueKind() == ValueKind.UNQUOTED)
 504                     print(" ");
 505             }
 506             if (node.isSelfClosing())
 507                 print("/");
 508             print(">");
 509         } catch (IOException e) {
 510             throw new UncheckedIOException(e);
 511         }
 512         return null;
 513     }
 514 
 515     @Override @DefinedBy(Api.COMPILER_TREE)
 516     public Void visitSummary(SummaryTree node, Void p) {
 517         try {
 518             print("{");
 519             printTagName(node);
 520             if (!node.getSummary().isEmpty()) {
 521                 print(" ");
 522                 print(node.getSummary());
 523             }
 524             print("}");
 525         } catch (IOException e) {
 526             throw new UncheckedIOException(e);
 527         }
 528         return null;
 529     }
 530 
 531     @Override @DefinedBy(Api.COMPILER_TREE)
 532     public Void visitSystemProperty(SystemPropertyTree node, Void p) {
 533         try {
 534             print("{");
 535             printTagName(node);
 536             print(" ");
 537             print(node.getPropertyName());
 538             print("}");
 539         } catch (IOException e) {
 540             throw new UncheckedIOException(e);
 541         }
 542         return null;
 543     }
 544 
 545     @Override @DefinedBy(Api.COMPILER_TREE)
 546     public Void visitText(TextTree node, Void p) {
 547         try {
 548             print(node.getBody());
 549         } catch (IOException e) {
 550             throw new UncheckedIOException(e);
 551         }
 552         return null;
 553     }
 554 
 555     @Override @DefinedBy(Api.COMPILER_TREE)
 556     public Void visitThrows(ThrowsTree node, Void p) {
 557         try {
 558             printTagName(node);
 559             print(" ");
 560             print(node.getExceptionName());
 561             if (!node.getDescription().isEmpty()) {
 562                 print(" ");
 563                 print(node.getDescription());
 564             }
 565         } catch (IOException e) {
 566             throw new UncheckedIOException(e);
 567         }
 568         return null;
 569     }
 570 
 571     @Override @DefinedBy(Api.COMPILER_TREE)
 572     public Void visitUnknownBlockTag(UnknownBlockTagTree node, Void p) {
 573         try {
 574             print("@");
 575             print(node.getTagName());
 576             print(" ");
 577             print(node.getContent());
 578         } catch (IOException e) {
 579             throw new UncheckedIOException(e);
 580         }
 581         return null;
 582     }
 583 
 584     @Override @DefinedBy(Api.COMPILER_TREE)
 585     public Void visitUnknownInlineTag(UnknownInlineTagTree node, Void p) {
 586         try {
 587             print("{");
 588             print("@");
 589             print(node.getTagName());
 590             print(" ");
 591             print(node.getContent());
 592             print("}");
 593         } catch (IOException e) {
 594             throw new UncheckedIOException(e);
 595         }
 596         return null;
 597     }
 598 
 599     @Override @DefinedBy(Api.COMPILER_TREE)
 600     public Void visitUses(UsesTree node, Void p) {
 601         try {
 602             printTagName(node);
 603             print(" ");
 604             print(node.getServiceType());
 605             if (!node.getDescription().isEmpty()) {
 606                 print(" ");
 607                 print(node.getDescription());
 608             }
 609         } catch (IOException e) {
 610             throw new UncheckedIOException(e);
 611         }
 612         return null;
 613     }
 614 
 615     @Override @DefinedBy(Api.COMPILER_TREE)
 616     public Void visitValue(ValueTree node, Void p) {
 617         try {
 618             print("{");
 619             printTagName(node);
 620             if (node.getReference() != null) {
 621                 print(" ");
 622                 print(node.getReference());
 623             }
 624             print("}");
 625         } catch (IOException e) {
 626             throw new UncheckedIOException(e);
 627         }
 628         return null;
 629     }
 630 
 631     @Override @DefinedBy(Api.COMPILER_TREE)
 632     public Void visitVersion(VersionTree node, Void p) {
 633         try {
 634             printTagName(node);
 635             print(" ");
 636             print(node.getBody());
 637         } catch (IOException e) {
 638             throw new UncheckedIOException(e);
 639         }
 640         return null;
 641     }
 642 
 643     @Override @DefinedBy(Api.COMPILER_TREE)
 644     public Void visitOther(DocTree node, Void p) {
 645         try {
 646             print("(UNKNOWN: " + node + ")");
 647             println();
 648         } catch (IOException e) {
 649             throw new UncheckedIOException(e);
 650         }
 651         return null;
 652     }
 653 }