1 /*
   2  * Copyright (c) 2015, 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 jdk.javadoc.internal.doclets.toolkit.util;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Collections;
  30 import java.util.List;
  31 
  32 import javax.lang.model.element.Element;
  33 import javax.lang.model.element.ExecutableElement;
  34 import javax.lang.model.element.ModuleElement;
  35 import javax.lang.model.element.PackageElement;
  36 import javax.lang.model.element.TypeElement;
  37 import javax.lang.model.type.TypeMirror;
  38 
  39 import com.sun.source.doctree.AttributeTree;
  40 import com.sun.source.doctree.AttributeTree.ValueKind;
  41 import com.sun.source.doctree.AuthorTree;
  42 import com.sun.source.doctree.BlockTagTree;
  43 import com.sun.source.doctree.CommentTree;
  44 import com.sun.source.doctree.DeprecatedTree;
  45 import com.sun.source.doctree.DocCommentTree;
  46 import com.sun.source.doctree.DocTree;
  47 import com.sun.source.doctree.EndElementTree;
  48 import com.sun.source.doctree.EntityTree;
  49 import com.sun.source.doctree.IdentifierTree;
  50 import com.sun.source.doctree.InlineTagTree;
  51 import com.sun.source.doctree.LinkTree;
  52 import com.sun.source.doctree.LiteralTree;
  53 import com.sun.source.doctree.ParamTree;
  54 import com.sun.source.doctree.ProvidesTree;
  55 import com.sun.source.doctree.ReferenceTree;
  56 import com.sun.source.doctree.ReturnTree;
  57 import com.sun.source.doctree.SeeTree;
  58 import com.sun.source.doctree.SerialDataTree;
  59 import com.sun.source.doctree.SerialFieldTree;
  60 import com.sun.source.doctree.SerialTree;
  61 import com.sun.source.doctree.SinceTree;
  62 import com.sun.source.doctree.StartElementTree;
  63 import com.sun.source.doctree.TextTree;
  64 import com.sun.source.doctree.ThrowsTree;
  65 import com.sun.source.doctree.UnknownBlockTagTree;
  66 import com.sun.source.doctree.UsesTree;
  67 import com.sun.source.doctree.ValueTree;
  68 import com.sun.source.doctree.VersionTree;
  69 import com.sun.source.util.DocTreePath;
  70 import com.sun.source.util.DocTrees;
  71 import com.sun.source.util.SimpleDocTreeVisitor;
  72 import com.sun.source.util.TreePath;
  73 import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
  74 
  75 import static com.sun.source.doctree.DocTree.Kind.*;
  76 
  77 /**
  78  *  A utility class.
  79  *
  80  *  <p><b>This is NOT part of any supported API.
  81  *  If you write code that depends on this, you do so at your own risk.
  82  *  This code and its internal interfaces are subject to change or
  83  *  deletion without notice.</b>
  84  */
  85 public class CommentHelper {
  86     public final TreePath path;
  87     public final DocCommentTree dctree;
  88     public final Element element;
  89     private Element overriddenElement;
  90 
  91     public static final String SPACER = " ";
  92 
  93     public CommentHelper(BaseConfiguration configuration, Element element, TreePath path, DocCommentTree dctree) {
  94         //this.configuration = configuration;
  95         this.element = element;
  96         this.path = path;
  97         this.dctree = dctree;
  98     }
  99 
 100     public void setOverrideElement(Element ove) {
 101         if (this.element == ove) {
 102             throw new AssertionError("cannot set given element as overriden element");
 103         }
 104         overriddenElement = ove;
 105     }
 106 
 107     @SuppressWarnings("fallthrough")
 108     public String getTagName(DocTree dtree) {
 109         switch (dtree.getKind()) {
 110             case AUTHOR:
 111             case DEPRECATED:
 112             case PARAM:
 113             case PROVIDES:
 114             case RETURN:
 115             case SEE:
 116             case SERIAL_DATA:
 117             case SERIAL_FIELD:
 118             case THROWS:
 119             case UNKNOWN_BLOCK_TAG:
 120             case USES:
 121             case VERSION:
 122                 return ((BlockTagTree)dtree).getTagName();
 123             case UNKNOWN_INLINE_TAG:
 124                 return ((InlineTagTree)dtree).getTagName();
 125             case ERRONEOUS:
 126                 return "erroneous";
 127             default:
 128                 return dtree.getKind().tagName;
 129         }
 130     }
 131 
 132     public boolean isTypeParameter(DocTree dtree) {
 133         if (dtree.getKind() == PARAM) {
 134             return ((ParamTree)dtree).isTypeParameter();
 135         }
 136         return false;
 137     }
 138 
 139     public String getParameterName(DocTree dtree) {
 140         if (dtree.getKind() == PARAM) {
 141             return ((ParamTree) dtree).getName().toString();
 142         } else {
 143             return null;
 144         }
 145     }
 146 
 147     Element getElement(BaseConfiguration c, ReferenceTree rtree) {
 148         // likely a synthesized tree
 149         if (path == null) {
 150             TypeMirror symbol = c.utils.getSymbol(rtree.getSignature());
 151             if (symbol == null) {
 152                 return null;
 153             }
 154             return  c.docEnv.getTypeUtils().asElement(symbol);
 155         }
 156         // case A: the element contains no comments associated and
 157         // the comments need to be copied from ancestor
 158         // case B: the element has @inheritDoc, then the ancestral comment
 159         // as appropriate has to be copied over.
 160 
 161         // Case A.
 162         if (dctree == null && overriddenElement != null) {
 163             CommentHelper ovch = c.utils.getCommentHelper(overriddenElement);
 164             return ovch.getElement(c, rtree);
 165         }
 166         if (dctree == null) {
 167             return null;
 168         }
 169         DocTreePath docTreePath = DocTreePath.getPath(path, dctree, rtree);
 170         if (docTreePath == null) {
 171             // Case B.
 172             if (overriddenElement != null) {
 173                 CommentHelper ovch = c.utils.getCommentHelper(overriddenElement);
 174                 return ovch.getElement(c, rtree);
 175             }
 176             return null;
 177         }
 178         DocTrees doctrees = c.docEnv.getDocTrees();
 179         return doctrees.getElement(docTreePath);
 180     }
 181 
 182     public Element getException(BaseConfiguration c, DocTree dtree) {
 183         if (dtree.getKind() == THROWS || dtree.getKind() == EXCEPTION) {
 184             ThrowsTree tt = (ThrowsTree)dtree;
 185             ReferenceTree exceptionName = tt.getExceptionName();
 186             return getElement(c, exceptionName);
 187         }
 188         return null;
 189     }
 190 
 191     public List<? extends DocTree> getDescription(BaseConfiguration c, DocTree dtree) {
 192         return getTags(c, dtree);
 193     }
 194 
 195     public String getText(List<? extends DocTree> list) {
 196         StringBuilder sb = new StringBuilder();
 197         for (DocTree dt : list) {
 198             sb.append(getText0(dt));
 199         }
 200         return sb.toString();
 201     }
 202 
 203     public String getText(DocTree dt) {
 204         return getText0(dt).toString();
 205     }
 206 
 207     private StringBuilder getText0(DocTree dt) {
 208         final StringBuilder sb = new StringBuilder();
 209         new SimpleDocTreeVisitor<Void, Void>() {
 210             @Override
 211             public Void visitAttribute(AttributeTree node, Void p) {
 212                 sb.append(SPACER).append(node.getName());
 213                 if (node.getValueKind() == ValueKind.EMPTY) {
 214                     return null;
 215                 }
 216 
 217                 sb.append("=");
 218                 String quote;
 219                 switch (node.getValueKind()) {
 220                     case DOUBLE:
 221                         quote = "\"";
 222                         break;
 223                     case SINGLE:
 224                         quote = "\'";
 225                         break;
 226                     default:
 227                         quote = "";
 228                         break;
 229                 }
 230                 sb.append(quote);
 231                 node.getValue().stream().forEach((dt) -> {
 232                     dt.accept(this, null);
 233                 });
 234                 sb.append(quote);
 235                 return null;
 236             }
 237 
 238             @Override
 239             public Void visitEndElement(EndElementTree node, Void p) {
 240                 sb.append("</")
 241                         .append(node.getName())
 242                         .append(">");
 243                 return null;
 244             }
 245 
 246             @Override
 247             public Void visitEntity(EntityTree node, Void p) {
 248                 sb.append(node.toString());
 249                 return null;
 250             }
 251 
 252             @Override
 253             public Void visitLink(LinkTree node, Void p) {
 254                 if (node.getReference() == null) {
 255                     return null;
 256                 }
 257 
 258                 node.getReference().accept(this, null);
 259                 node.getLabel().stream().forEach((dt) -> {
 260                     dt.accept(this, null);
 261                 });
 262                 return null;
 263             }
 264 
 265             @Override
 266             public Void visitLiteral(LiteralTree node, Void p) {
 267                 if (node.getKind() == CODE) {
 268                     sb.append("<").append(node.getKind().tagName).append(">");
 269                 }
 270                 sb.append(node.getBody().toString());
 271                 if (node.getKind() == CODE) {
 272                     sb.append("</").append(node.getKind().tagName).append(">");
 273                 }
 274                 return null;
 275             }
 276 
 277             @Override
 278             public Void visitReference(ReferenceTree node, Void p) {
 279                 sb.append(node.getSignature());
 280                 return null;
 281             }
 282 
 283             @Override
 284             public Void visitSee(SeeTree node, Void p) {
 285                 node.getReference().stream().forEach((dt) -> {
 286                     dt.accept(this, null);
 287                 });
 288                 return null;
 289             }
 290 
 291             @Override
 292             public Void visitSerial(SerialTree node, Void p) {
 293                 node.getDescription().stream().forEach((dt) -> {
 294                     dt.accept(this, null);
 295                 });
 296                 return null;
 297             }
 298 
 299             @Override
 300             public Void visitStartElement(StartElementTree node, Void p) {
 301                 sb.append("<");
 302                 sb.append(node.getName());
 303                 node.getAttributes().stream().forEach((dt) -> {
 304                     dt.accept(this, null);
 305                 });
 306                 sb.append((node.isSelfClosing() ? "/>" : ">"));
 307                 return null;
 308             }
 309 
 310             @Override
 311             public Void visitText(TextTree node, Void p) {
 312                 sb.append(node.getBody());
 313                 return null;
 314             }
 315 
 316             @Override
 317             public Void visitUnknownBlockTag(UnknownBlockTagTree node, Void p) {
 318                 node.getContent().stream().forEach((dt) -> {
 319                     dt.accept(this, null);
 320                 });
 321                 return null;
 322             }
 323 
 324             @Override
 325             public Void visitValue(ValueTree node, Void p) {
 326                 return node.getReference().accept(this, null);
 327             }
 328 
 329             @Override
 330             protected Void defaultAction(DocTree node, Void p) {
 331                 sb.append(node.toString());
 332                 return null;
 333             }
 334         }.visit(dt, null);
 335         return sb;
 336     }
 337 
 338     public String getLabel(BaseConfiguration c, DocTree dtree) {
 339         return new SimpleDocTreeVisitor<String, Void>() {
 340             @Override
 341             public String visitLink(LinkTree node, Void p) {
 342                 StringBuilder sb = new StringBuilder();
 343                 node.getLabel().stream().forEach((dt) -> {
 344                     sb.append(getText(dt));
 345                 });
 346                 return sb.toString();
 347             }
 348 
 349             @Override
 350             public String visitSee(SeeTree node, Void p) {
 351                 StringBuilder sb = new StringBuilder();
 352                 node.getReference().stream().filter((dt) -> (c.utils.isText(dt))).forEach((dt) -> {
 353                     sb.append(((TextTree)dt).getBody());
 354                 });
 355                 return sb.toString();
 356             }
 357 
 358             @Override
 359             protected String defaultAction(DocTree node, Void p) {
 360                 return "";
 361             }
 362         }.visit(dtree, null);
 363     }
 364 
 365     public TypeElement getReferencedClass(BaseConfiguration c, DocTree dtree) {
 366         Element e = getReferencedElement(c, dtree);
 367         if (e == null) {
 368             return null;
 369         } else if (c.utils.isTypeElement(e)) {
 370             return (TypeElement) e;
 371         } else if (!c.utils.isPackage(e)) {
 372             return c.utils.getEnclosingTypeElement(e);
 373         }
 374         return null;
 375     }
 376 
 377     public String getReferencedClassName(BaseConfiguration c, DocTree dtree) {
 378         Element e = getReferencedClass(c, dtree);
 379         if (e != null) {
 380             return c.utils.isTypeElement(e) ? c.utils.getSimpleName(e) : null;
 381         }
 382         String s = getReferencedSignature(dtree);
 383         if (s == null) {
 384             return null;
 385         }
 386         int n = s.indexOf("#");
 387         return (n == -1) ? s : s.substring(0, n);
 388     }
 389 
 390     public Element getReferencedMember(BaseConfiguration c, DocTree dtree) {
 391         Element e = getReferencedElement(c, dtree);
 392         if (e == null) {
 393             return null;
 394         }
 395         return (c.utils.isExecutableElement(e) || c.utils.isVariableElement(e)) ? e : null;
 396     }
 397 
 398     public String getReferencedMemberName(DocTree dtree) {
 399         String s = getReferencedSignature(dtree);
 400         if (s == null) {
 401             return null;
 402         }
 403         int n = s.indexOf("#");
 404         return (n == -1) ? null : s.substring(n + 1);
 405     }
 406 
 407     public String getReferencedMemberName(BaseConfiguration c, Element e) {
 408         if (e == null) {
 409             return null;
 410         }
 411         return c.utils.isExecutableElement(e)
 412                 ? c.utils.getSimpleName(e) + c.utils.makeSignature((ExecutableElement) e, true, true)
 413                 : c.utils.getSimpleName(e);
 414     }
 415 
 416     public PackageElement getReferencedPackage(BaseConfiguration c, DocTree dtree) {
 417         Element e = getReferencedElement(c, dtree);
 418         if (e != null) {
 419             return c.utils.containingPackage(e);
 420         }
 421         return null;
 422     }
 423 
 424     public List<? extends DocTree> getFirstSentenceTrees(BaseConfiguration c, List<? extends DocTree> body) {
 425         List<DocTree> firstSentence = c.docEnv.getDocTrees().getFirstSentence(body);
 426         return firstSentence;
 427     }
 428 
 429     public List<? extends DocTree> getFirstSentenceTrees(BaseConfiguration c, DocTree dtree) {
 430         return getFirstSentenceTrees(c, getBody(c, dtree));
 431     }
 432 
 433     private Element getReferencedElement(BaseConfiguration c, DocTree dtree) {
 434         return new SimpleDocTreeVisitor<Element, Void>() {
 435             @Override
 436             public Element visitSee(SeeTree node, Void p) {
 437                 for (DocTree dt : node.getReference()) {
 438                     return visit(dt, null);
 439                 }
 440                 return null;
 441             }
 442 
 443             @Override
 444             public Element visitLink(LinkTree node, Void p) {
 445                 return visit(node.getReference(), null);
 446             }
 447 
 448             @Override
 449             public Element visitProvides(ProvidesTree node, Void p) {
 450                 return visit(node.getServiceType(), null);
 451             }
 452 
 453             @Override
 454             public Element visitValue(ValueTree node, Void p) {
 455                 return visit(node.getReference(), null);
 456             }
 457 
 458             @Override
 459             public Element visitReference(ReferenceTree node, Void p) {
 460                 return getElement(c, node);
 461             }
 462 
 463             @Override
 464             public Element visitSerialField(SerialFieldTree node, Void p) {
 465                 return visit(node.getType(), null);
 466             }
 467 
 468             @Override
 469             public Element visitUses(UsesTree node, Void p) {
 470                 return visit(node.getServiceType(), null);
 471             }
 472 
 473             @Override
 474             protected Element defaultAction(DocTree node, Void p) {
 475                return null;
 476             }
 477         }.visit(dtree, null);
 478     }
 479 
 480     public TypeElement getServiceType(BaseConfiguration c, DocTree dtree) {
 481         Element e = getReferencedElement(c, dtree);
 482         if (e != null) {
 483             return c.utils.isTypeElement(e) ? (TypeElement) e : null;
 484         }
 485         return null;
 486     }
 487 
 488     public  String getReferencedSignature(DocTree dtree) {
 489         return new SimpleDocTreeVisitor<String, Void>() {
 490             @Override
 491             public String visitSee(SeeTree node, Void p) {
 492                 for (DocTree dt : node.getReference()) {
 493                     return visit(dt, null);
 494                 }
 495                 return null;
 496             }
 497 
 498             @Override
 499             public String visitLink(LinkTree node, Void p) {
 500                 return visit(node.getReference(), null);
 501             }
 502 
 503             @Override
 504             public String visitValue(ValueTree node, Void p) {
 505                 return visit(node.getReference(), null);
 506             }
 507 
 508             @Override
 509             public String visitReference(ReferenceTree node, Void p) {
 510                 return node.getSignature();
 511             }
 512 
 513             @Override
 514             public String visitSerialField(SerialFieldTree node, Void p) {
 515                 return visit(node.getType(), null);
 516             }
 517 
 518             @Override
 519             protected String defaultAction(DocTree node, Void p) {
 520                return null;
 521             }
 522         }.visit(dtree, null);
 523     }
 524 
 525     public List<? extends DocTree> getReference(DocTree dtree) {
 526         return dtree.getKind() == SEE ? ((SeeTree)dtree).getReference() : null;
 527     }
 528 
 529     public ReferenceTree getExceptionName(DocTree dtree) {
 530         return (dtree.getKind() == THROWS || dtree.getKind() == EXCEPTION)
 531                 ? ((ThrowsTree)dtree).getExceptionName()
 532                 : null;
 533     }
 534 
 535     public IdentifierTree getName(DocTree dtree) {
 536         switch (dtree.getKind()) {
 537             case PARAM:
 538                 return ((ParamTree)dtree).getName();
 539             case SERIAL_FIELD:
 540                 return ((SerialFieldTree)dtree).getName();
 541             default:
 542                 return null;
 543             }
 544     }
 545 
 546     public List<? extends DocTree> getTags(BaseConfiguration c, DocTree dtree) {
 547         return new SimpleDocTreeVisitor<List<? extends DocTree>, Void>() {
 548             List<? extends DocTree> asList(String content) {
 549                 List<DocTree> out = new ArrayList<>();
 550                 out.add((TextTree)c.cmtUtils.makeTextTree(content));
 551                 return out;
 552             }
 553 
 554             @Override
 555             public List<? extends DocTree> visitAuthor(AuthorTree node, Void p) {
 556                 return node.getName();
 557             }
 558 
 559             @Override
 560             public List<? extends DocTree> visitComment(CommentTree node, Void p) {
 561                 return asList(node.getBody());
 562             }
 563 
 564             @Override
 565             public List<? extends DocTree> visitDeprecated(DeprecatedTree node, Void p) {
 566                 return node.getBody();
 567             }
 568 
 569             @Override
 570             public List<? extends DocTree> visitDocComment(DocCommentTree node, Void p) {
 571                 return node.getBody();
 572             }
 573 
 574             @Override
 575             public List<? extends DocTree> visitLiteral(LiteralTree node, Void p) {
 576                 return asList(node.getBody().getBody());
 577             }
 578 
 579             @Override
 580             public List<? extends DocTree> visitProvides(ProvidesTree node, Void p) {
 581                  return node.getDescription();
 582             }
 583 
 584             @Override
 585             public List<? extends DocTree> visitSince(SinceTree node, Void p) {
 586                 return node.getBody();
 587             }
 588 
 589             @Override
 590             public List<? extends DocTree> visitText(TextTree node, Void p) {
 591                 return asList(node.getBody());
 592             }
 593 
 594             @Override
 595             public List<? extends DocTree> visitVersion(VersionTree node, Void p) {
 596                 return node.getBody();
 597             }
 598 
 599             @Override
 600             public List<? extends DocTree> visitParam(ParamTree node, Void p) {
 601                return node.getDescription();
 602             }
 603 
 604             @Override
 605             public List<? extends DocTree> visitReturn(ReturnTree node, Void p) {
 606                 return node.getDescription();
 607             }
 608 
 609             @Override
 610             public List<? extends DocTree> visitSee(SeeTree node, Void p) {
 611                 return node.getReference();
 612             }
 613 
 614             @Override
 615             public List<? extends DocTree> visitSerial(SerialTree node, Void p) {
 616                 return node.getDescription();
 617             }
 618 
 619             @Override
 620             public List<? extends DocTree> visitSerialData(SerialDataTree node, Void p) {
 621                 return node.getDescription();
 622             }
 623 
 624             @Override
 625             public List<? extends DocTree> visitSerialField(SerialFieldTree node, Void p) {
 626                 return node.getDescription();
 627             }
 628 
 629             @Override
 630             public List<? extends DocTree> visitThrows(ThrowsTree node, Void p) {
 631                  return node.getDescription();
 632             }
 633 
 634             @Override
 635             public List<? extends DocTree> visitUnknownBlockTag(UnknownBlockTagTree node, Void p) {
 636                 return node.getContent();
 637             }
 638 
 639             @Override
 640             public List<? extends DocTree> visitUses(UsesTree node, Void p) {
 641                  return node.getDescription();
 642             }
 643 
 644             @Override
 645             protected List<? extends DocTree> defaultAction(DocTree node, Void p) {
 646                return Collections.emptyList();
 647             }
 648         }.visit(dtree, null);
 649     }
 650 
 651     public List<? extends DocTree> getBody(BaseConfiguration c, DocTree dtree) {
 652         return getTags(c, dtree);
 653     }
 654 
 655     public ReferenceTree getType(DocTree dtree) {
 656         if (dtree.getKind() == SERIAL_FIELD) {
 657             return ((SerialFieldTree)dtree).getType();
 658         } else {
 659             return null;
 660         }
 661     }
 662 
 663     public DocTreePath getDocTreePath(DocTree dtree) {
 664         if (path == null || dctree == null || dtree == null)
 665             return null;
 666         return DocTreePath.getPath(path, dctree, dtree);
 667     }
 668 
 669     public Element getOverriddenElement() {
 670         return overriddenElement;
 671     }
 672 
 673 
 674     /**
 675      * For debugging purposes only. Do not rely on this for other things.
 676      * @return a string representation.
 677      */
 678     @Override
 679     public String toString() {
 680         StringBuilder sb = new StringBuilder("CommentHelper{" + "path=" + path + ", dctree=" + dctree);
 681         sb.append(", element=");
 682         sb.append(element.getEnclosingElement());
 683         sb.append("::");
 684         sb.append(element);
 685         sb.append(", overriddenElement=");
 686         if (overriddenElement != null) {
 687             sb.append(overriddenElement.getEnclosingElement());
 688             sb.append("::");
 689             sb.append(overriddenElement);
 690         } else {
 691             sb.append("<none>");
 692         }
 693         sb.append('}');
 694         return sb.toString();
 695     }
 696 }