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.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 overridden 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             // NOTE: this code path only supports module/package/type signatures
 152             //       and not member signatures. For more complete support,
 153             //       set a suitable path and avoid this branch.
 154             TypeMirror symbol = c.utils.getSymbol(rtree.getSignature());
 155             if (symbol == null) {
 156                 return null;
 157             }
 158             return  c.docEnv.getTypeUtils().asElement(symbol);
 159         }
 160         // case A: the element contains no comments associated and
 161         // the comments need to be copied from ancestor
 162         // case B: the element has @inheritDoc, then the ancestral comment
 163         // as appropriate has to be copied over.
 164 
 165         // Case A.
 166         if (dctree == null && overriddenElement != null) {
 167             CommentHelper ovch = c.utils.getCommentHelper(overriddenElement);
 168             return ovch.getElement(c, rtree);
 169         }
 170         if (dctree == null) {
 171             return null;
 172         }
 173         DocTreePath docTreePath = DocTreePath.getPath(path, dctree, rtree);
 174         if (docTreePath == null) {
 175             // Case B.
 176             if (overriddenElement != null) {
 177                 CommentHelper ovch = c.utils.getCommentHelper(overriddenElement);
 178                 return ovch.getElement(c, rtree);
 179             }
 180             return null;
 181         }
 182         DocTrees doctrees = c.docEnv.getDocTrees();
 183         return doctrees.getElement(docTreePath);
 184     }
 185 
 186     public Element getException(BaseConfiguration c, DocTree dtree) {
 187         if (dtree.getKind() == THROWS || dtree.getKind() == EXCEPTION) {
 188             ThrowsTree tt = (ThrowsTree)dtree;
 189             ReferenceTree exceptionName = tt.getExceptionName();
 190             return getElement(c, exceptionName);
 191         }
 192         return null;
 193     }
 194 
 195     public List<? extends DocTree> getDescription(BaseConfiguration c, DocTree dtree) {
 196         return getTags(c, dtree);
 197     }
 198 
 199     public String getText(List<? extends DocTree> list) {
 200         StringBuilder sb = new StringBuilder();
 201         for (DocTree dt : list) {
 202             sb.append(getText0(dt));
 203         }
 204         return sb.toString();
 205     }
 206 
 207     public String getText(DocTree dt) {
 208         return getText0(dt).toString();
 209     }
 210 
 211     private StringBuilder getText0(DocTree dt) {
 212         final StringBuilder sb = new StringBuilder();
 213         new SimpleDocTreeVisitor<Void, Void>() {
 214             @Override
 215             public Void visitAttribute(AttributeTree node, Void p) {
 216                 sb.append(SPACER).append(node.getName());
 217                 if (node.getValueKind() == ValueKind.EMPTY) {
 218                     return null;
 219                 }
 220 
 221                 sb.append("=");
 222                 String quote;
 223                 switch (node.getValueKind()) {
 224                     case DOUBLE:
 225                         quote = "\"";
 226                         break;
 227                     case SINGLE:
 228                         quote = "\'";
 229                         break;
 230                     default:
 231                         quote = "";
 232                         break;
 233                 }
 234                 sb.append(quote);
 235                 node.getValue().stream().forEach((dt) -> {
 236                     dt.accept(this, null);
 237                 });
 238                 sb.append(quote);
 239                 return null;
 240             }
 241 
 242             @Override
 243             public Void visitEndElement(EndElementTree node, Void p) {
 244                 sb.append("</")
 245                         .append(node.getName())
 246                         .append(">");
 247                 return null;
 248             }
 249 
 250             @Override
 251             public Void visitEntity(EntityTree node, Void p) {
 252                 sb.append(node.toString());
 253                 return null;
 254             }
 255 
 256             @Override
 257             public Void visitLink(LinkTree node, Void p) {
 258                 if (node.getReference() == null) {
 259                     return null;
 260                 }
 261 
 262                 node.getReference().accept(this, null);
 263                 node.getLabel().stream().forEach((dt) -> {
 264                     dt.accept(this, null);
 265                 });
 266                 return null;
 267             }
 268 
 269             @Override
 270             public Void visitLiteral(LiteralTree node, Void p) {
 271                 if (node.getKind() == CODE) {
 272                     sb.append("<").append(node.getKind().tagName).append(">");
 273                 }
 274                 sb.append(node.getBody().toString());
 275                 if (node.getKind() == CODE) {
 276                     sb.append("</").append(node.getKind().tagName).append(">");
 277                 }
 278                 return null;
 279             }
 280 
 281             @Override
 282             public Void visitReference(ReferenceTree node, Void p) {
 283                 sb.append(node.getSignature());
 284                 return null;
 285             }
 286 
 287             @Override
 288             public Void visitSee(SeeTree node, Void p) {
 289                 node.getReference().stream().forEach((dt) -> {
 290                     dt.accept(this, null);
 291                 });
 292                 return null;
 293             }
 294 
 295             @Override
 296             public Void visitSerial(SerialTree node, Void p) {
 297                 node.getDescription().stream().forEach((dt) -> {
 298                     dt.accept(this, null);
 299                 });
 300                 return null;
 301             }
 302 
 303             @Override
 304             public Void visitStartElement(StartElementTree node, Void p) {
 305                 sb.append("<");
 306                 sb.append(node.getName());
 307                 node.getAttributes().stream().forEach((dt) -> {
 308                     dt.accept(this, null);
 309                 });
 310                 sb.append((node.isSelfClosing() ? "/>" : ">"));
 311                 return null;
 312             }
 313 
 314             @Override
 315             public Void visitText(TextTree node, Void p) {
 316                 sb.append(node.getBody());
 317                 return null;
 318             }
 319 
 320             @Override
 321             public Void visitUnknownBlockTag(UnknownBlockTagTree node, Void p) {
 322                 node.getContent().stream().forEach((dt) -> {
 323                     dt.accept(this, null);
 324                 });
 325                 return null;
 326             }
 327 
 328             @Override
 329             public Void visitValue(ValueTree node, Void p) {
 330                 return node.getReference().accept(this, null);
 331             }
 332 
 333             @Override
 334             protected Void defaultAction(DocTree node, Void p) {
 335                 sb.append(node.toString());
 336                 return null;
 337             }
 338         }.visit(dt, null);
 339         return sb;
 340     }
 341 
 342     public String getLabel(BaseConfiguration c, DocTree dtree) {
 343         return new SimpleDocTreeVisitor<String, Void>() {
 344             @Override
 345             public String visitLink(LinkTree node, Void p) {
 346                 StringBuilder sb = new StringBuilder();
 347                 node.getLabel().stream().forEach((dt) -> {
 348                     sb.append(getText(dt));
 349                 });
 350                 return sb.toString();
 351             }
 352 
 353             @Override
 354             public String visitSee(SeeTree node, Void p) {
 355                 StringBuilder sb = new StringBuilder();
 356                 node.getReference().stream().filter((dt) -> (c.utils.isText(dt))).forEach((dt) -> {
 357                     sb.append(((TextTree)dt).getBody());
 358                 });
 359                 return sb.toString();
 360             }
 361 
 362             @Override
 363             protected String defaultAction(DocTree node, Void p) {
 364                 return "";
 365             }
 366         }.visit(dtree, null);
 367     }
 368 
 369     public TypeElement getReferencedClass(BaseConfiguration c, DocTree dtree) {
 370         Element e = getReferencedElement(c, dtree);
 371         if (e == null) {
 372             return null;
 373         } else if (c.utils.isTypeElement(e)) {
 374             return (TypeElement) e;
 375         } else if (!c.utils.isPackage(e)) {
 376             return c.utils.getEnclosingTypeElement(e);
 377         }
 378         return null;
 379     }
 380 
 381     public String getReferencedClassName(BaseConfiguration c, DocTree dtree) {
 382         Element e = getReferencedClass(c, dtree);
 383         if (e != null) {
 384             return c.utils.isTypeElement(e) ? c.utils.getSimpleName(e) : null;
 385         }
 386         String s = getReferencedSignature(dtree);
 387         if (s == null) {
 388             return null;
 389         }
 390         int n = s.indexOf("#");
 391         return (n == -1) ? s : s.substring(0, n);
 392     }
 393 
 394     public Element getReferencedMember(BaseConfiguration c, DocTree dtree) {
 395         Element e = getReferencedElement(c, dtree);
 396         if (e == null) {
 397             return null;
 398         }
 399         return (c.utils.isExecutableElement(e) || c.utils.isVariableElement(e)) ? e : null;
 400     }
 401 
 402     public String getReferencedMemberName(DocTree dtree) {
 403         String s = getReferencedSignature(dtree);
 404         if (s == null) {
 405             return null;
 406         }
 407         int n = s.indexOf("#");
 408         return (n == -1) ? null : s.substring(n + 1);
 409     }
 410 
 411     public String getReferencedMemberName(BaseConfiguration c, Element e) {
 412         if (e == null) {
 413             return null;
 414         }
 415         return c.utils.isExecutableElement(e)
 416                 ? c.utils.getSimpleName(e) + c.utils.makeSignature((ExecutableElement) e, true, true)
 417                 : c.utils.getSimpleName(e);
 418     }
 419 
 420     public PackageElement getReferencedPackage(BaseConfiguration c, DocTree dtree) {
 421         Element e = getReferencedElement(c, dtree);
 422         if (e != null) {
 423             return c.utils.containingPackage(e);
 424         }
 425         return null;
 426     }
 427 
 428     public List<? extends DocTree> getFirstSentenceTrees(BaseConfiguration c, List<? extends DocTree> body) {
 429         List<DocTree> firstSentence = c.docEnv.getDocTrees().getFirstSentence(body);
 430         return firstSentence;
 431     }
 432 
 433     public List<? extends DocTree> getFirstSentenceTrees(BaseConfiguration c, DocTree dtree) {
 434         return getFirstSentenceTrees(c, getBody(c, dtree));
 435     }
 436 
 437     private Element getReferencedElement(BaseConfiguration c, DocTree dtree) {
 438         return new SimpleDocTreeVisitor<Element, Void>() {
 439             @Override
 440             public Element visitSee(SeeTree node, Void p) {
 441                 for (DocTree dt : node.getReference()) {
 442                     return visit(dt, null);
 443                 }
 444                 return null;
 445             }
 446 
 447             @Override
 448             public Element visitLink(LinkTree node, Void p) {
 449                 return visit(node.getReference(), null);
 450             }
 451 
 452             @Override
 453             public Element visitProvides(ProvidesTree node, Void p) {
 454                 return visit(node.getServiceType(), null);
 455             }
 456 
 457             @Override
 458             public Element visitValue(ValueTree node, Void p) {
 459                 return visit(node.getReference(), null);
 460             }
 461 
 462             @Override
 463             public Element visitReference(ReferenceTree node, Void p) {
 464                 return getElement(c, node);
 465             }
 466 
 467             @Override
 468             public Element visitSerialField(SerialFieldTree node, Void p) {
 469                 return visit(node.getType(), null);
 470             }
 471 
 472             @Override
 473             public Element visitUses(UsesTree node, Void p) {
 474                 return visit(node.getServiceType(), null);
 475             }
 476 
 477             @Override
 478             protected Element defaultAction(DocTree node, Void p) {
 479                return null;
 480             }
 481         }.visit(dtree, null);
 482     }
 483 
 484     public TypeElement getServiceType(BaseConfiguration c, DocTree dtree) {
 485         Element e = getReferencedElement(c, dtree);
 486         if (e != null) {
 487             return c.utils.isTypeElement(e) ? (TypeElement) e : null;
 488         }
 489         return null;
 490     }
 491 
 492     public  String getReferencedSignature(DocTree dtree) {
 493         return new SimpleDocTreeVisitor<String, Void>() {
 494             @Override
 495             public String visitSee(SeeTree node, Void p) {
 496                 for (DocTree dt : node.getReference()) {
 497                     return visit(dt, null);
 498                 }
 499                 return null;
 500             }
 501 
 502             @Override
 503             public String visitLink(LinkTree node, Void p) {
 504                 return visit(node.getReference(), null);
 505             }
 506 
 507             @Override
 508             public String visitValue(ValueTree node, Void p) {
 509                 return visit(node.getReference(), null);
 510             }
 511 
 512             @Override
 513             public String visitReference(ReferenceTree node, Void p) {
 514                 return node.getSignature();
 515             }
 516 
 517             @Override
 518             public String visitSerialField(SerialFieldTree node, Void p) {
 519                 return visit(node.getType(), null);
 520             }
 521 
 522             @Override
 523             protected String defaultAction(DocTree node, Void p) {
 524                return null;
 525             }
 526         }.visit(dtree, null);
 527     }
 528 
 529     public List<? extends DocTree> getReference(DocTree dtree) {
 530         return dtree.getKind() == SEE ? ((SeeTree)dtree).getReference() : null;
 531     }
 532 
 533     public ReferenceTree getExceptionName(DocTree dtree) {
 534         return (dtree.getKind() == THROWS || dtree.getKind() == EXCEPTION)
 535                 ? ((ThrowsTree)dtree).getExceptionName()
 536                 : null;
 537     }
 538 
 539     public IdentifierTree getName(DocTree dtree) {
 540         switch (dtree.getKind()) {
 541             case PARAM:
 542                 return ((ParamTree)dtree).getName();
 543             case SERIAL_FIELD:
 544                 return ((SerialFieldTree)dtree).getName();
 545             default:
 546                 return null;
 547             }
 548     }
 549 
 550     public List<? extends DocTree> getTags(BaseConfiguration c, DocTree dtree) {
 551         return new SimpleDocTreeVisitor<List<? extends DocTree>, Void>() {
 552             List<? extends DocTree> asList(String content) {
 553                 List<DocTree> out = new ArrayList<>();
 554                 out.add(c.cmtUtils.makeTextTree(content));
 555                 return out;
 556             }
 557 
 558             @Override
 559             public List<? extends DocTree> visitAuthor(AuthorTree node, Void p) {
 560                 return node.getName();
 561             }
 562 
 563             @Override
 564             public List<? extends DocTree> visitComment(CommentTree node, Void p) {
 565                 return asList(node.getBody());
 566             }
 567 
 568             @Override
 569             public List<? extends DocTree> visitDeprecated(DeprecatedTree node, Void p) {
 570                 return node.getBody();
 571             }
 572 
 573             @Override
 574             public List<? extends DocTree> visitDocComment(DocCommentTree node, Void p) {
 575                 return node.getBody();
 576             }
 577 
 578             @Override
 579             public List<? extends DocTree> visitLiteral(LiteralTree node, Void p) {
 580                 return asList(node.getBody().getBody());
 581             }
 582 
 583             @Override
 584             public List<? extends DocTree> visitProvides(ProvidesTree node, Void p) {
 585                  return node.getDescription();
 586             }
 587 
 588             @Override
 589             public List<? extends DocTree> visitSince(SinceTree node, Void p) {
 590                 return node.getBody();
 591             }
 592 
 593             @Override
 594             public List<? extends DocTree> visitText(TextTree node, Void p) {
 595                 return asList(node.getBody());
 596             }
 597 
 598             @Override
 599             public List<? extends DocTree> visitVersion(VersionTree node, Void p) {
 600                 return node.getBody();
 601             }
 602 
 603             @Override
 604             public List<? extends DocTree> visitParam(ParamTree node, Void p) {
 605                return node.getDescription();
 606             }
 607 
 608             @Override
 609             public List<? extends DocTree> visitReturn(ReturnTree node, Void p) {
 610                 return node.getDescription();
 611             }
 612 
 613             @Override
 614             public List<? extends DocTree> visitSee(SeeTree node, Void p) {
 615                 return node.getReference();
 616             }
 617 
 618             @Override
 619             public List<? extends DocTree> visitSerial(SerialTree node, Void p) {
 620                 return node.getDescription();
 621             }
 622 
 623             @Override
 624             public List<? extends DocTree> visitSerialData(SerialDataTree node, Void p) {
 625                 return node.getDescription();
 626             }
 627 
 628             @Override
 629             public List<? extends DocTree> visitSerialField(SerialFieldTree node, Void p) {
 630                 return node.getDescription();
 631             }
 632 
 633             @Override
 634             public List<? extends DocTree> visitThrows(ThrowsTree node, Void p) {
 635                  return node.getDescription();
 636             }
 637 
 638             @Override
 639             public List<? extends DocTree> visitUnknownBlockTag(UnknownBlockTagTree node, Void p) {
 640                 return node.getContent();
 641             }
 642 
 643             @Override
 644             public List<? extends DocTree> visitUses(UsesTree node, Void p) {
 645                  return node.getDescription();
 646             }
 647 
 648             @Override
 649             protected List<? extends DocTree> defaultAction(DocTree node, Void p) {
 650                return Collections.emptyList();
 651             }
 652         }.visit(dtree, null);
 653     }
 654 
 655     public List<? extends DocTree> getBody(BaseConfiguration c, DocTree dtree) {
 656         return getTags(c, dtree);
 657     }
 658 
 659     public ReferenceTree getType(DocTree dtree) {
 660         if (dtree.getKind() == SERIAL_FIELD) {
 661             return ((SerialFieldTree)dtree).getType();
 662         } else {
 663             return null;
 664         }
 665     }
 666 
 667     public DocTreePath getDocTreePath(DocTree dtree) {
 668         if (path == null || dctree == null || dtree == null)
 669             return null;
 670         return DocTreePath.getPath(path, dctree, dtree);
 671     }
 672 
 673     public Element getOverriddenElement() {
 674         return overriddenElement;
 675     }
 676 
 677 
 678     /**
 679      * For debugging purposes only. Do not rely on this for other things.
 680      * @return a string representation.
 681      */
 682     @Override
 683     public String toString() {
 684         StringBuilder sb = new StringBuilder("CommentHelper{" + "path=" + path + ", dctree=" + dctree);
 685         sb.append(", element=");
 686         sb.append(element.getEnclosingElement());
 687         sb.append("::");
 688         sb.append(element);
 689         sb.append(", overriddenElement=");
 690         if (overriddenElement != null) {
 691             sb.append(overriddenElement.getEnclosingElement());
 692             sb.append("::");
 693             sb.append(overriddenElement);
 694         } else {
 695             sb.append("<none>");
 696         }
 697         sb.append('}');
 698         return sb.toString();
 699     }
 700 }