1 /*
   2  * Copyright (c) 1997, 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.formats.html;
  27 
  28 import java.util.ArrayList;
  29 import java.util.List;
  30 import java.util.Set;
  31 import java.util.TreeSet;
  32 import java.util.stream.Collectors;
  33 
  34 import javax.lang.model.element.Element;
  35 import javax.lang.model.element.ExecutableElement;
  36 import javax.lang.model.element.Modifier;
  37 import javax.lang.model.element.TypeElement;
  38 import javax.lang.model.element.TypeParameterElement;
  39 import javax.lang.model.type.TypeMirror;
  40 
  41 import com.sun.source.doctree.DocTree;
  42 
  43 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
  44 import jdk.javadoc.internal.doclets.formats.html.markup.Entity;
  45 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
  46 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
  47 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
  48 import jdk.javadoc.internal.doclets.formats.html.markup.Links;
  49 import jdk.javadoc.internal.doclets.formats.html.markup.Table;
  50 import jdk.javadoc.internal.doclets.formats.html.markup.TableHeader;
  51 import jdk.javadoc.internal.doclets.toolkit.Content;
  52 import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter;
  53 import jdk.javadoc.internal.doclets.toolkit.Resources;
  54 import jdk.javadoc.internal.doclets.toolkit.taglets.DeprecatedTaglet;
  55 import jdk.javadoc.internal.doclets.toolkit.util.Utils;
  56 
  57 import static javax.lang.model.element.Modifier.ABSTRACT;
  58 import static javax.lang.model.element.Modifier.NATIVE;
  59 import static javax.lang.model.element.Modifier.PUBLIC;
  60 import static javax.lang.model.element.Modifier.STRICTFP;
  61 import static javax.lang.model.element.Modifier.SYNCHRONIZED;
  62 
  63 /**
  64  * The base class for member writers.
  65  *
  66  *  <p><b>This is NOT part of any supported API.
  67  *  If you write code that depends on this, you do so at your own risk.
  68  *  This code and its internal interfaces are subject to change or
  69  *  deletion without notice.</b>
  70  *
  71  * @author Robert Field
  72  * @author Atul M Dambalkar
  73  * @author Jamie Ho (Re-write)
  74  * @author Bhavesh Patel (Modified)
  75  */
  76 public abstract class AbstractMemberWriter implements MemberSummaryWriter {
  77 
  78     protected final HtmlConfiguration configuration;
  79     protected final Utils utils;
  80     protected final SubWriterHolderWriter writer;
  81     protected final Contents contents;
  82     protected final Resources resources;
  83     protected final Links links;
  84 
  85     protected final TypeElement typeElement;
  86     public final boolean nodepr;
  87 
  88     protected boolean printedSummaryHeader = false;
  89 
  90     public AbstractMemberWriter(SubWriterHolderWriter writer, TypeElement typeElement) {
  91         this.configuration = writer.configuration;
  92         this.writer = writer;
  93         this.nodepr = configuration.nodeprecated;
  94         this.typeElement = typeElement;
  95         this.utils = configuration.utils;
  96         this.contents = configuration.contents;
  97         this.resources = configuration.resources;
  98         this.links = writer.links;
  99     }
 100 
 101     public AbstractMemberWriter(SubWriterHolderWriter writer) {
 102         this(writer, null);
 103     }
 104 
 105     /*** abstracts ***/
 106 
 107     /**
 108      * Add the summary label for the member.
 109      *
 110      * @param memberTree the content tree to which the label will be added
 111      */
 112     public abstract void addSummaryLabel(Content memberTree);
 113 
 114     /**
 115      * Get the summary for the member summary table.
 116      *
 117      * @return a string for the table summary
 118      */
 119     private String getTableSummaryX() { return null; }
 120 
 121     /**
 122      * Get the summary table header for the member.
 123      *
 124      * @param member the member to be documented
 125      * @return the summary table header
 126      */
 127     public abstract TableHeader getSummaryTableHeader(Element member);
 128 
 129     private Table summaryTable;
 130 
 131     private Table getSummaryTable() {
 132         if (summaryTable == null) {
 133             summaryTable = createSummaryTable();
 134         }
 135         return summaryTable;
 136     }
 137 
 138     /**
 139      * Create the summary table for this element.
 140      * The table should be created and initialized if needed, and configured
 141      * so that it is ready to add content with {@link Table#addRow(Content[])}
 142      * and similar methods.
 143      *
 144      * @return the summary table
 145      */
 146     protected abstract Table createSummaryTable();
 147 
 148 
 149 
 150     /**
 151      * Add inherited summary label for the member.
 152      *
 153      * @param typeElement the TypeElement to which to link to
 154      * @param inheritedTree the content tree to which the inherited summary label will be added
 155      */
 156     public abstract void addInheritedSummaryLabel(TypeElement typeElement, Content inheritedTree);
 157 
 158     /**
 159      * Add the anchor for the summary section of the member.
 160      *
 161      * @param typeElement the TypeElement to be documented
 162      * @param memberTree the content tree to which the summary anchor will be added
 163      */
 164     public abstract void addSummaryAnchor(TypeElement typeElement, Content memberTree);
 165 
 166     /**
 167      * Add the anchor for the inherited summary section of the member.
 168      *
 169      * @param typeElement the TypeElement to be documented
 170      * @param inheritedTree the content tree to which the inherited summary anchor will be added
 171      */
 172     public abstract void addInheritedSummaryAnchor(TypeElement typeElement, Content inheritedTree);
 173 
 174     /**
 175      * Add the summary type for the member.
 176      *
 177      * @param member the member to be documented
 178      * @param tdSummaryType the content tree to which the type will be added
 179      */
 180     protected abstract void addSummaryType(Element member, Content tdSummaryType);
 181 
 182     /**
 183      * Add the summary link for the member.
 184      *
 185      * @param typeElement the TypeElement to be documented
 186      * @param member the member to be documented
 187      * @param tdSummary the content tree to which the link will be added
 188      */
 189     protected void addSummaryLink(TypeElement typeElement, Element member, Content tdSummary) {
 190         addSummaryLink(LinkInfoImpl.Kind.MEMBER, typeElement, member, tdSummary);
 191     }
 192 
 193     /**
 194      * Add the summary link for the member.
 195      *
 196      * @param context the id of the context where the link will be printed
 197      * @param typeElement the TypeElement to be documented
 198      * @param member the member to be documented
 199      * @param tdSummary the content tree to which the summary link will be added
 200      */
 201     protected abstract void addSummaryLink(LinkInfoImpl.Kind context,
 202             TypeElement typeElement, Element member, Content tdSummary);
 203 
 204     /**
 205      * Add the inherited summary link for the member.
 206      *
 207      * @param typeElement the TypeElement to be documented
 208      * @param member the member to be documented
 209      * @param linksTree the content tree to which the inherited summary link will be added
 210      */
 211     protected abstract void addInheritedSummaryLink(TypeElement typeElement,
 212             Element member, Content linksTree);
 213 
 214     /**
 215      * Get the deprecated link.
 216      *
 217      * @param member the member being linked to
 218      * @return a content tree representing the link
 219      */
 220     protected abstract Content getDeprecatedLink(Element member);
 221 
 222     /**
 223      * Add the member name to the content tree.
 224      *
 225      * @param name the member name to be added to the content tree.
 226      * @param htmltree the content tree to which the name will be added.
 227      */
 228     protected void addName(String name, Content htmltree) {
 229         htmltree.add(name);
 230     }
 231 
 232     /**
 233      * Add the modifier for the member. The modifiers are ordered as specified
 234      * by <em>The Java Language Specification</em>.
 235      *
 236      * @param member the member for which the modifier will be added.
 237      * @param htmltree the content tree to which the modifier information will be added.
 238      */
 239     protected void addModifiers(Element member, Content htmltree) {
 240         Set<Modifier> set = new TreeSet<>(member.getModifiers());
 241 
 242         // remove the ones we really don't need
 243         set.remove(NATIVE);
 244         set.remove(SYNCHRONIZED);
 245         set.remove(STRICTFP);
 246 
 247         // According to JLS, we should not be showing public modifier for
 248         // interface methods.
 249         if ((utils.isField(member) || utils.isMethod(member))
 250             && ((writer instanceof ClassWriterImpl
 251                  && utils.isInterface(((ClassWriterImpl) writer).getTypeElement())  ||
 252                  writer instanceof AnnotationTypeWriterImpl) )) {
 253             // Remove the implicit abstract and public modifiers
 254             if (utils.isMethod(member) &&
 255                 (utils.isInterface(member.getEnclosingElement()) ||
 256                  utils.isAnnotationType(member.getEnclosingElement()))) {
 257                 set.remove(ABSTRACT);
 258                 set.remove(PUBLIC);
 259             }
 260             if (!utils.isMethod(member)) {
 261                 set.remove(PUBLIC);
 262             }
 263         }
 264         if (!set.isEmpty()) {
 265             String mods = set.stream().map(Modifier::toString).collect(Collectors.joining(" "));
 266             htmltree.add(mods);
 267             htmltree.add(Entity.NO_BREAK_SPACE);
 268         }
 269     }
 270 
 271     protected CharSequence makeSpace(int len) {
 272         if (len <= 0) {
 273             return "";
 274         }
 275         StringBuilder sb = new StringBuilder(len);
 276         for (int i = 0; i < len; i++) {
 277             sb.append(' ');
 278         }
 279         return sb;
 280     }
 281 
 282     /**
 283      * Add the modifier and type for the member in the member summary.
 284      *
 285      * @param member the member to add the type for
 286      * @param type the type to add
 287      * @param tdSummaryType the content tree to which the modified and type will be added
 288      */
 289     protected void addModifierAndType(Element member, TypeMirror type,
 290             Content tdSummaryType) {
 291         HtmlTree code = new HtmlTree(HtmlTag.CODE);
 292         addModifier(member, code);
 293         if (type == null) {
 294             code.add(utils.isClass(member) ? "class" : "interface");
 295             code.add(Entity.NO_BREAK_SPACE);
 296         } else {
 297             List<? extends TypeParameterElement> list = utils.isExecutableElement(member)
 298                     ? ((ExecutableElement)member).getTypeParameters()
 299                     : null;
 300             if (list != null && !list.isEmpty()) {
 301                 Content typeParameters = ((AbstractExecutableMemberWriter) this)
 302                         .getTypeParameters((ExecutableElement)member);
 303                     code.add(typeParameters);
 304                 //Code to avoid ugly wrapping in member summary table.
 305                 if (typeParameters.charCount() > 10) {
 306                     code.add(new HtmlTree(HtmlTag.BR));
 307                 } else {
 308                     code.add(Entity.NO_BREAK_SPACE);
 309                 }
 310                 code.add(
 311                         writer.getLink(new LinkInfoImpl(configuration,
 312                         LinkInfoImpl.Kind.SUMMARY_RETURN_TYPE, type)));
 313             } else {
 314                 code.add(
 315                         writer.getLink(new LinkInfoImpl(configuration,
 316                         LinkInfoImpl.Kind.SUMMARY_RETURN_TYPE, type)));
 317             }
 318 
 319         }
 320         tdSummaryType.add(code);
 321     }
 322 
 323     /**
 324      * Add the modifier for the member.
 325      *
 326      * @param member the member to add the type for
 327      * @param code the content tree to which the modified will be added
 328      */
 329     private void addModifier(Element member, Content code) {
 330         if (utils.isProtected(member)) {
 331             code.add("protected ");
 332         } else if (utils.isPrivate(member)) {
 333             code.add("private ");
 334         } else if (!utils.isPublic(member)) { // Package private
 335             code.add(resources.getText("doclet.Package_private"));
 336             code.add(" ");
 337         }
 338         boolean isAnnotatedTypeElement = utils.isAnnotationType(member.getEnclosingElement());
 339         if (!isAnnotatedTypeElement && utils.isMethod(member)) {
 340             if (!utils.isInterface(member.getEnclosingElement()) && utils.isAbstract(member)) {
 341                 code.add("abstract ");
 342             }
 343             if (utils.isDefault(member)) {
 344                 code.add("default ");
 345             }
 346         }
 347         if (utils.isStatic(member)) {
 348             code.add("static ");
 349         }
 350     }
 351 
 352     /**
 353      * Add the deprecated information for the given member.
 354      *
 355      * @param member the member being documented.
 356      * @param contentTree the content tree to which the deprecated information will be added.
 357      */
 358     protected void addDeprecatedInfo(Element member, Content contentTree) {
 359         Content output = (new DeprecatedTaglet()).getTagletOutput(member,
 360             writer.getTagletWriterInstance(false));
 361         if (!output.isEmpty()) {
 362             Content deprecatedContent = output;
 363             Content div = HtmlTree.DIV(HtmlStyle.deprecationBlock, deprecatedContent);
 364             contentTree.add(div);
 365         }
 366     }
 367 
 368     /**
 369      * Add the comment for the given member.
 370      *
 371      * @param member the member being documented.
 372      * @param htmltree the content tree to which the comment will be added.
 373      */
 374     protected void addComment(Element member, Content htmltree) {
 375         if (!utils.getFullBody(member).isEmpty()) {
 376             writer.addInlineComment(member, htmltree);
 377         }
 378     }
 379 
 380     protected String name(Element member) {
 381         return utils.getSimpleName(member);
 382     }
 383 
 384     /**
 385     * Return true if the given <code>ProgramElement</code> is inherited
 386     * by the class that is being documented.
 387     *
 388     * @param ped The <code>ProgramElement</code> being checked.
 389     * return true if the <code>ProgramElement</code> is being inherited and
 390     * false otherwise.
 391      *@return true if inherited
 392     */
 393     protected boolean isInherited(Element ped){
 394         return (!utils.isPrivate(ped) &&
 395                 (!utils.isPackagePrivate(ped) ||
 396                     ped.getEnclosingElement().equals(ped.getEnclosingElement())));
 397     }
 398 
 399     /**
 400      * Add use information to the documentation tree.
 401      *
 402      * @param mems list of program elements for which the use information will be added
 403      * @param heading the section heading
 404      * @param contentTree the content tree to which the use information will be added
 405      */
 406     protected void addUseInfo(List<? extends Element> mems, Content heading, Content contentTree) {
 407         if (mems == null || mems.isEmpty()) {
 408             return;
 409         }
 410         List<? extends Element> members = mems;
 411         boolean printedUseTableHeader = false;
 412         if (members.size() > 0) {
 413             Table useTable = new Table(HtmlStyle.useSummary)
 414                     .setCaption(heading)
 415                     .setRowScopeColumn(1)
 416                     .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast);
 417             for (Element element : members) {
 418                 TypeElement te = (typeElement == null)
 419                         ? utils.getEnclosingTypeElement(element)
 420                         : typeElement;
 421                 if (!printedUseTableHeader) {
 422                     useTable.setHeader(getSummaryTableHeader(element));
 423                     printedUseTableHeader = true;
 424                 }
 425                 Content summaryType = new ContentBuilder();
 426                 addSummaryType(element, summaryType);
 427                 Content typeContent = new ContentBuilder();
 428                 if (te != null
 429                         && !utils.isConstructor(element)
 430                         && !utils.isClass(element)
 431                         && !utils.isInterface(element)
 432                         && !utils.isAnnotationType(element)) {
 433                     HtmlTree name = new HtmlTree(HtmlTag.SPAN);
 434                     name.setStyle(HtmlStyle.typeNameLabel);
 435                     name.add(name(te) + ".");
 436                     typeContent.add(name);
 437                 }
 438                 addSummaryLink(utils.isClass(element) || utils.isInterface(element)
 439                         ? LinkInfoImpl.Kind.CLASS_USE
 440                         : LinkInfoImpl.Kind.MEMBER,
 441                         te, element, typeContent);
 442                 Content desc = new ContentBuilder();
 443                 writer.addSummaryLinkComment(this, element, desc);
 444                 useTable.addRow(summaryType, typeContent, desc);
 445             }
 446             contentTree.add(useTable.toContent());
 447         }
 448     }
 449 
 450     protected void serialWarning(Element e, String key, String a1, String a2) {
 451         if (configuration.serialwarn) {
 452             configuration.messages.warning(e, key, a1, a2);
 453         }
 454     }
 455 
 456     /**
 457      * Add the member summary for the given class.
 458      *
 459      * @param tElement the class that is being documented
 460      * @param member the member being documented
 461      * @param firstSentenceTags the first sentence tags to be added to the summary
 462      */
 463     @Override
 464     public void addMemberSummary(TypeElement tElement, Element member,
 465             List<? extends DocTree> firstSentenceTags) {
 466         if (tElement != typeElement) {
 467             throw new IllegalStateException();
 468         }
 469         Table table = getSummaryTable();
 470         List<Content> rowContents = new ArrayList<>();
 471         Content summaryType = new ContentBuilder();
 472         addSummaryType(member, summaryType);
 473         if (!summaryType.isEmpty())
 474             rowContents.add(summaryType);
 475         Content summaryLink = new ContentBuilder();
 476         addSummaryLink(tElement, member, summaryLink);
 477         rowContents.add(summaryLink);
 478         Content desc = new ContentBuilder();
 479         writer.addSummaryLinkComment(this, member, firstSentenceTags, desc);
 480         rowContents.add(desc);
 481         table.addRow(member, rowContents);
 482     }
 483 
 484     /**
 485      * Add inherited member summary for the given class and member.
 486      *
 487      * @param tElement the class the inherited member belongs to
 488      * @param nestedClass the inherited member that is summarized
 489      * @param isFirst true if this is the first member in the list
 490      * @param isLast true if this is the last member in the list
 491      * @param linksTree the content tree to which the summary will be added
 492      */
 493     @Override
 494     public void addInheritedMemberSummary(TypeElement tElement,
 495             Element nestedClass, boolean isFirst, boolean isLast,
 496             Content linksTree) {
 497         writer.addInheritedMemberSummary(this, tElement, nestedClass, isFirst,
 498                 linksTree);
 499     }
 500 
 501     /**
 502      * Get the inherited summary header for the given class.
 503      *
 504      * @param tElement the class the inherited member belongs to
 505      * @return a content tree for the inherited summary header
 506      */
 507     @Override
 508     public Content getInheritedSummaryHeader(TypeElement tElement) {
 509         Content inheritedTree = writer.getMemberInheritedTree();
 510         writer.addInheritedSummaryHeader(this, tElement, inheritedTree);
 511         return inheritedTree;
 512     }
 513 
 514     /**
 515      * Get the inherited summary links tree.
 516      *
 517      * @return a content tree for the inherited summary links
 518      */
 519     @Override
 520     public Content getInheritedSummaryLinksTree() {
 521         return new HtmlTree(HtmlTag.CODE);
 522     }
 523 
 524     /**
 525      * Get the summary table tree for the given class.
 526      *
 527      * @param tElement the class for which the summary table is generated
 528      * @return a content tree for the summary table
 529      */
 530     @Override
 531     public Content getSummaryTableTree(TypeElement tElement) {
 532         if (tElement != typeElement) {
 533             throw new IllegalStateException();
 534         }
 535         Table table = getSummaryTable();
 536         if (table.needsScript()) {
 537             writer.getMainBodyScript().append(table.getScript());
 538         }
 539         return table.toContent();
 540     }
 541 
 542     /**
 543      * Get the member tree to be documented.
 544      *
 545      * @param memberTree the content tree of member to be documented
 546      * @return a content tree that will be added to the class documentation
 547      */
 548     @Override
 549     public Content getMemberTree(Content memberTree) {
 550         return writer.getMemberTree(memberTree);
 551     }
 552 
 553     /**
 554      * Get the member tree to be documented.
 555      *
 556      * @param memberTree the content tree of member to be documented
 557      * @param isLastContent true if the content to be added is the last content
 558      * @return a content tree that will be added to the class documentation
 559      */
 560     public Content getMemberTree(Content memberTree, boolean isLastContent) {
 561         if (isLastContent)
 562             return HtmlTree.LI(HtmlStyle.blockListLast, memberTree);
 563         else
 564             return HtmlTree.LI(HtmlStyle.blockList, memberTree);
 565     }
 566 }