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