1 /*
   2  * Copyright (c) 1997, 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.formats.html;
  27 
  28 import java.util.*;
  29 import java.util.stream.Collectors;
  30 
  31 import javax.lang.model.element.Element;
  32 import javax.lang.model.element.ExecutableElement;
  33 import javax.lang.model.element.Modifier;
  34 import javax.lang.model.element.TypeElement;
  35 import javax.lang.model.element.TypeParameterElement;
  36 import javax.lang.model.type.TypeMirror;
  37 
  38 import com.sun.source.doctree.DocTree;
  39 import jdk.javadoc.internal.doclets.formats.html.TableHeader;
  40 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr;
  41 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants;
  42 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
  43 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
  44 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
  45 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
  46 import jdk.javadoc.internal.doclets.toolkit.Content;
  47 import jdk.javadoc.internal.doclets.toolkit.Resources;
  48 import jdk.javadoc.internal.doclets.toolkit.taglets.DeprecatedTaglet;
  49 import jdk.javadoc.internal.doclets.toolkit.util.MethodTypes;
  50 import jdk.javadoc.internal.doclets.toolkit.util.Utils;
  51 import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap;
  52 
  53 import static javax.lang.model.element.Modifier.*;
  54 
  55 /**
  56  * The base class for member writers.
  57  *
  58  *  <p><b>This is NOT part of any supported API.
  59  *  If you write code that depends on this, you do so at your own risk.
  60  *  This code and its internal interfaces are subject to change or
  61  *  deletion without notice.</b>
  62  *
  63  * @author Robert Field
  64  * @author Atul M Dambalkar
  65  * @author Jamie Ho (Re-write)
  66  * @author Bhavesh Patel (Modified)
  67  */
  68 public abstract class AbstractMemberWriter {
  69 
  70     protected final HtmlConfiguration configuration;
  71     protected final Utils utils;
  72     protected final SubWriterHolderWriter writer;
  73     protected final Contents contents;
  74     protected final Resources resources;
  75 
  76     protected final TypeElement typeElement;
  77     protected Map<String, Integer> typeMap = new LinkedHashMap<>();
  78     protected Set<MethodTypes> methodTypes = EnumSet.noneOf(MethodTypes.class);
  79     private int methodTypesOr = 0;
  80     public final boolean nodepr;
  81 
  82     protected boolean printedSummaryHeader = false;
  83 
  84     public AbstractMemberWriter(SubWriterHolderWriter writer, TypeElement typeElement) {
  85         this.configuration = writer.configuration;
  86         this.writer = writer;
  87         this.nodepr = configuration.nodeprecated;
  88         this.typeElement = typeElement;
  89         this.utils = configuration.utils;
  90         this.contents = configuration.contents;
  91         this.resources = configuration.resources;
  92     }
  93 
  94     public AbstractMemberWriter(SubWriterHolderWriter writer) {
  95         this(writer, null);
  96     }
  97 
  98     /*** abstracts ***/
  99 
 100     /**
 101      * Add the summary label for the member.
 102      *
 103      * @param memberTree the content tree to which the label will be added
 104      */
 105     public abstract void addSummaryLabel(Content memberTree);
 106 
 107     /**
 108      * Get the summary for the member summary table.
 109      *
 110      * @return a string for the table summary
 111      */
 112     public abstract String getTableSummary();
 113 
 114     /**
 115      * Get the caption for the member summary table.
 116      *
 117      * @return a string for the table caption
 118      */
 119     public abstract Content getCaption();
 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     /**
 130      * Add inherited summary label for the member.
 131      *
 132      * @param typeElement the TypeElement to which to link to
 133      * @param inheritedTree the content tree to which the inherited summary label will be added
 134      */
 135     public abstract void addInheritedSummaryLabel(TypeElement typeElement, Content inheritedTree);
 136 
 137     /**
 138      * Add the anchor for the summary section of the member.
 139      *
 140      * @param typeElement the TypeElement to be documented
 141      * @param memberTree the content tree to which the summary anchor will be added
 142      */
 143     public abstract void addSummaryAnchor(TypeElement typeElement, Content memberTree);
 144 
 145     /**
 146      * Add the anchor for the inherited summary section of the member.
 147      *
 148      * @param typeElement the TypeElement to be documented
 149      * @param inheritedTree the content tree to which the inherited summary anchor will be added
 150      */
 151     public abstract void addInheritedSummaryAnchor(TypeElement typeElement, Content inheritedTree);
 152 
 153     /**
 154      * Add the summary type for the member.
 155      *
 156      * @param member the member to be documented
 157      * @param tdSummaryType the content tree to which the type will be added
 158      */
 159     protected abstract void addSummaryType(Element member, Content tdSummaryType);
 160 
 161     /**
 162      * Add the summary link for the member.
 163      *
 164      * @param typeElement the TypeElement to be documented
 165      * @param member the member to be documented
 166      * @param tdSummary the content tree to which the link will be added
 167      */
 168     protected void addSummaryLink(TypeElement typeElement, Element member, Content tdSummary) {
 169         addSummaryLink(LinkInfoImpl.Kind.MEMBER, typeElement, member, tdSummary);
 170     }
 171 
 172     /**
 173      * Add the summary link for the member.
 174      *
 175      * @param context the id of the context where the link will be printed
 176      * @param typeElement the TypeElement to be documented
 177      * @param member the member to be documented
 178      * @param tdSummary the content tree to which the summary link will be added
 179      */
 180     protected abstract void addSummaryLink(LinkInfoImpl.Kind context,
 181             TypeElement typeElement, Element member, Content tdSummary);
 182 
 183     /**
 184      * Add the inherited summary link for the member.
 185      *
 186      * @param typeElement the TypeElement to be documented
 187      * @param member the member to be documented
 188      * @param linksTree the content tree to which the inherited summary link will be added
 189      */
 190     protected abstract void addInheritedSummaryLink(TypeElement typeElement,
 191             Element member, Content linksTree);
 192 
 193     /**
 194      * Get the deprecated link.
 195      *
 196      * @param member the member being linked to
 197      * @return a content tree representing the link
 198      */
 199     protected abstract Content getDeprecatedLink(Element member);
 200 
 201     /**
 202      * Get the navigation summary link.
 203      *
 204      * @param typeElement the TypeElement to be documented
 205      * @param link true if its a link else the label to be printed
 206      * @return a content tree for the navigation summary link.
 207      */
 208     protected abstract Content getNavSummaryLink(TypeElement typeElement, boolean link);
 209 
 210     /**
 211      * Add the navigation detail link.
 212      *
 213      * @param link true if its a link else the label to be printed
 214      * @param liNav the content tree to which the navigation detail link will be added
 215      */
 216     protected abstract void addNavDetailLink(boolean link, Content liNav);
 217 
 218     /**
 219      * Add the member name to the content tree.
 220      *
 221      * @param name the member name to be added to the content tree.
 222      * @param htmltree the content tree to which the name will be added.
 223      */
 224     protected void addName(String name, Content htmltree) {
 225         htmltree.addContent(name);
 226     }
 227 
 228     /**
 229      * Add the modifier for the member. The modifiers are ordered as specified
 230      * by <em>The Java Language Specification</em>.
 231      *
 232      * @param member the member for which teh modifier will be added.
 233      * @param htmltree the content tree to which the modifier information will be added.
 234      */
 235     protected void addModifiers(Element member, Content htmltree) {
 236         Set<Modifier> set = new TreeSet<>(member.getModifiers());
 237 
 238         // remove the ones we really don't need
 239         set.remove(NATIVE);
 240         set.remove(SYNCHRONIZED);
 241         set.remove(STRICTFP);
 242 
 243         // According to JLS, we should not be showing public modifier for
 244         // interface methods.
 245         if ((utils.isField(member) || utils.isMethod(member))
 246             && ((writer instanceof ClassWriterImpl
 247                  && utils.isInterface(((ClassWriterImpl) writer).getTypeElement())  ||
 248                  writer instanceof AnnotationTypeWriterImpl) )) {
 249             // Remove the implicit abstract and public modifiers
 250             if (utils.isMethod(member) &&
 251                 (utils.isInterface(member.getEnclosingElement()) ||
 252                  utils.isAnnotationType(member.getEnclosingElement()))) {
 253                 set.remove(ABSTRACT);
 254                 set.remove(PUBLIC);
 255             }
 256             if (!utils.isMethod(member)) {
 257                 set.remove(PUBLIC);
 258             }
 259         }
 260         if (!set.isEmpty()) {
 261             String mods = set.stream().map(Modifier::toString).collect(Collectors.joining(" "));
 262             htmltree.addContent(mods);
 263             htmltree.addContent(Contents.SPACE);
 264         }
 265     }
 266 
 267     protected CharSequence makeSpace(int len) {
 268         if (len <= 0) {
 269             return "";
 270         }
 271         StringBuilder sb = new StringBuilder(len);
 272         for (int i = 0; i < len; i++) {
 273             sb.append(' ');
 274         }
 275         return sb;
 276     }
 277 
 278     /**
 279      * Add the modifier and type for the member in the member summary.
 280      *
 281      * @param member the member to add the type for
 282      * @param type the type to add
 283      * @param tdSummaryType the content tree to which the modified and type will be added
 284      */
 285     protected void addModifierAndType(Element member, TypeMirror type,
 286             Content tdSummaryType) {
 287         HtmlTree code = new HtmlTree(HtmlTag.CODE);
 288         addModifier(member, code);
 289         if (type == null) {
 290             code.addContent(utils.isClass(member) ? "class" : "interface");
 291             code.addContent(Contents.SPACE);
 292         } else {
 293             List<? extends TypeParameterElement> list = utils.isExecutableElement(member)
 294                     ? ((ExecutableElement)member).getTypeParameters()
 295                     : null;
 296             if (list != null && !list.isEmpty()) {
 297                 Content typeParameters = ((AbstractExecutableMemberWriter) this)
 298                         .getTypeParameters((ExecutableElement)member);
 299                     code.addContent(typeParameters);
 300                 //Code to avoid ugly wrapping in member summary table.
 301                 if (typeParameters.charCount() > 10) {
 302                     code.addContent(new HtmlTree(HtmlTag.BR));
 303                 } else {
 304                     code.addContent(Contents.SPACE);
 305                 }
 306                 code.addContent(
 307                         writer.getLink(new LinkInfoImpl(configuration,
 308                         LinkInfoImpl.Kind.SUMMARY_RETURN_TYPE, type)));
 309             } else {
 310                 code.addContent(
 311                         writer.getLink(new LinkInfoImpl(configuration,
 312                         LinkInfoImpl.Kind.SUMMARY_RETURN_TYPE, type)));
 313             }
 314 
 315         }
 316         tdSummaryType.addContent(code);
 317     }
 318 
 319     /**
 320      * Add the modifier for the member.
 321      *
 322      * @param member the member to add the type for
 323      * @param code the content tree to which the modified will be added
 324      */
 325     private void addModifier(Element member, Content code) {
 326         if (utils.isProtected(member)) {
 327             code.addContent("protected ");
 328         } else if (utils.isPrivate(member)) {
 329             code.addContent("private ");
 330         } else if (!utils.isPublic(member)) { // Package private
 331             code.addContent(configuration.getText("doclet.Package_private"));
 332             code.addContent(" ");
 333         }
 334         boolean isAnnotatedTypeElement = utils.isAnnotationType(member.getEnclosingElement());
 335         if (!isAnnotatedTypeElement && utils.isMethod(member)) {
 336             if (!utils.isInterface(member.getEnclosingElement()) && utils.isAbstract(member)) {
 337                 code.addContent("abstract ");
 338             }
 339             if (utils.isDefault(member)) {
 340                 code.addContent("default ");
 341             }
 342         }
 343         if (utils.isStatic(member)) {
 344             code.addContent("static ");
 345         }
 346     }
 347 
 348     /**
 349      * Add the deprecated information for the given member.
 350      *
 351      * @param member the member being documented.
 352      * @param contentTree the content tree to which the deprecated information will be added.
 353      */
 354     protected void addDeprecatedInfo(Element member, Content contentTree) {
 355         Content output = (new DeprecatedTaglet()).getTagletOutput(member,
 356             writer.getTagletWriterInstance(false));
 357         if (!output.isEmpty()) {
 358             Content deprecatedContent = output;
 359             Content div = HtmlTree.DIV(HtmlStyle.deprecationBlock, deprecatedContent);
 360             contentTree.addContent(div);
 361         }
 362     }
 363 
 364     /**
 365      * Add the comment for the given member.
 366      *
 367      * @param member the member being documented.
 368      * @param htmltree the content tree to which the comment will be added.
 369      */
 370     protected void addComment(Element member, Content htmltree) {
 371         if (!utils.getFullBody(member).isEmpty()) {
 372             writer.addInlineComment(member, htmltree);
 373         }
 374     }
 375 
 376     protected String name(Element member) {
 377         return utils.getSimpleName(member);
 378     }
 379 
 380     /**
 381      * Get the header for the section.
 382      *
 383      * @param member the member being documented.
 384      * @return a header content for the section.
 385      */
 386     protected Content getHead(Element member) {
 387         Content memberContent = new StringContent(name(member));
 388         Content heading = HtmlTree.HEADING(HtmlConstants.MEMBER_HEADING, memberContent);
 389         return heading;
 390     }
 391 
 392     /**
 393     * Return true if the given <code>ProgramElement</code> is inherited
 394     * by the class that is being documented.
 395     *
 396     * @param ped The <code>ProgramElement</code> being checked.
 397     * return true if the <code>ProgramElement</code> is being inherited and
 398     * false otherwise.
 399      *@return true if inherited
 400     */
 401     protected boolean isInherited(Element ped){
 402         return (!utils.isPrivate(ped) &&
 403                 (!utils.isPackagePrivate(ped) ||
 404                     ped.getEnclosingElement().equals(ped.getEnclosingElement())));
 405     }
 406 
 407     /**
 408      * Add use information to the documentation tree.
 409      *
 410      * @param mems list of program elements for which the use information will be added
 411      * @param heading the section heading
 412      * @param tableSummary the summary for the use table
 413      * @param contentTree the content tree to which the use information will be added
 414      */
 415     protected void addUseInfo(List<? extends Element> mems,
 416             Content heading, String tableSummary, Content contentTree) {
 417         if (mems == null || mems.isEmpty()) {
 418             return;
 419         }
 420         List<? extends Element> members = mems;
 421         boolean printedUseTableHeader = false;
 422         if (members.size() > 0) {
 423             Content caption = writer.getTableCaption(heading);
 424             Content table = (configuration.isOutputHtml5())
 425                     ? HtmlTree.TABLE(HtmlStyle.useSummary, caption)
 426                     : HtmlTree.TABLE(HtmlStyle.useSummary, tableSummary, caption);
 427             Content tbody = new HtmlTree(HtmlTag.TBODY);
 428             boolean altColor = true;
 429             for (Element element : members) {
 430                 TypeElement te = utils.getEnclosingTypeElement(element);
 431                 if (!printedUseTableHeader) {
 432                     table.addContent(getSummaryTableHeader(element).toContent());
 433                     printedUseTableHeader = true;
 434                 }
 435                 HtmlTree tr = new HtmlTree(HtmlTag.TR);
 436                 tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
 437                 altColor = !altColor;
 438                 HtmlTree tdFirst = new HtmlTree(HtmlTag.TD);
 439                 tdFirst.addStyle(HtmlStyle.colFirst);
 440                 writer.addSummaryType(this, element, tdFirst);
 441                 tr.addContent(tdFirst);
 442                 HtmlTree thType = new HtmlTree(HtmlTag.TH);
 443                 thType.addStyle(HtmlStyle.colSecond);
 444                 thType.addAttr(HtmlAttr.SCOPE, "row");
 445                 if (te != null
 446                         && !utils.isConstructor(element)
 447                         && !utils.isClass(element)
 448                         && !utils.isInterface(element)
 449                         && !utils.isAnnotationType(element)) {
 450                     HtmlTree name = new HtmlTree(HtmlTag.SPAN);
 451                     name.addStyle(HtmlStyle.typeNameLabel);
 452                     name.addContent(name(te) + ".");
 453                     thType.addContent(name);
 454                 }
 455                 addSummaryLink(utils.isClass(element) || utils.isInterface(element)
 456                         ? LinkInfoImpl.Kind.CLASS_USE
 457                         : LinkInfoImpl.Kind.MEMBER,
 458                         te, element, thType);
 459                 tr.addContent(thType);
 460                 HtmlTree tdDesc = new HtmlTree(HtmlTag.TD);
 461                 tdDesc.addStyle(HtmlStyle.colLast);
 462                 writer.addSummaryLinkComment(this, element, tdDesc);
 463                 tr.addContent(tdDesc);
 464                 tbody.addContent(tr);
 465             }
 466             table.addContent(tbody);
 467             contentTree.addContent(table);
 468         }
 469     }
 470 
 471     /**
 472      * Add the navigation detail link.
 473      *
 474      * @param members the members to be linked
 475      * @param liNav the content tree to which the navigation detail link will be added
 476      */
 477     protected void addNavDetailLink(SortedSet<Element> members, Content liNav) {
 478         addNavDetailLink(!members.isEmpty(), liNav);
 479     }
 480 
 481     /**
 482      * Add the navigation summary link.
 483      *
 484      * @param members members to be linked
 485      * @param visibleMemberMap the visible inherited members map
 486      * @param liNav the content tree to which the navigation summary link will be added
 487      */
 488     protected void addNavSummaryLink(SortedSet<? extends Element> members,
 489             VisibleMemberMap visibleMemberMap, Content liNav) {
 490         if (!members.isEmpty()) {
 491             liNav.addContent(getNavSummaryLink(null, true));
 492             return;
 493         }
 494 
 495         TypeElement superClass = utils.getSuperClass(typeElement);
 496         while (superClass != null) {
 497             if (visibleMemberMap.hasMembers(superClass)) {
 498                 liNav.addContent(getNavSummaryLink(superClass, true));
 499                 return;
 500             }
 501             superClass = utils.getSuperClass(superClass);
 502         }
 503         liNav.addContent(getNavSummaryLink(null, false));
 504     }
 505 
 506     protected void serialWarning(Element e, String key, String a1, String a2) {
 507         if (configuration.serialwarn) {
 508             configuration.messages.warning(e, key, a1, a2);
 509         }
 510     }
 511 
 512     /**
 513      * Add the member summary for the given class.
 514      *
 515      * @param tElement the class that is being documented
 516      * @param member the member being documented
 517      * @param firstSentenceTags the first sentence tags to be added to the summary
 518      * @param tableContents the list of contents to which the documentation will be added
 519      * @param counter the counter for determining id and style for the table row
 520      */
 521     public void addMemberSummary(TypeElement tElement, Element member,
 522             List<? extends DocTree> firstSentenceTags, List<Content> tableContents, int counter) {
 523         HtmlTree tdSummaryType = new HtmlTree(HtmlTag.TD);
 524         tdSummaryType.addStyle(HtmlStyle.colFirst);
 525         writer.addSummaryType(this, member, tdSummaryType);
 526         HtmlTree tr = HtmlTree.TR(tdSummaryType);
 527         HtmlTree thSummaryLink = new HtmlTree(HtmlTag.TH);
 528         setSummaryColumnStyleAndScope(thSummaryLink);
 529         addSummaryLink(tElement, member, thSummaryLink);
 530         tr.addContent(thSummaryLink);
 531         HtmlTree tdDesc = new HtmlTree(HtmlTag.TD);
 532         tdDesc.addStyle(HtmlStyle.colLast);
 533         writer.addSummaryLinkComment(this, member, firstSentenceTags, tdDesc);
 534         tr.addContent(tdDesc);
 535         if (utils.isMethod(member) && !utils.isAnnotationType(member) && !utils.isProperty(name(member))) {
 536             int methodType = utils.isStatic(member) ? MethodTypes.STATIC.tableTabs().value() :
 537                     MethodTypes.INSTANCE.tableTabs().value();
 538             if (utils.isInterface(member.getEnclosingElement())) {
 539                 methodType = utils.isAbstract(member)
 540                         ? methodType | MethodTypes.ABSTRACT.tableTabs().value()
 541                         : methodType | MethodTypes.DEFAULT.tableTabs().value();
 542             } else {
 543                 methodType = utils.isAbstract(member)
 544                         ? methodType | MethodTypes.ABSTRACT.tableTabs().value()
 545                         : methodType | MethodTypes.CONCRETE.tableTabs().value();
 546             }
 547             if (utils.isDeprecated(member) || utils.isDeprecated(typeElement)) {
 548                 methodType = methodType | MethodTypes.DEPRECATED.tableTabs().value();
 549             }
 550             methodTypesOr = methodTypesOr | methodType;
 551             String tableId = "i" + counter;
 552             typeMap.put(tableId, methodType);
 553             tr.addAttr(HtmlAttr.ID, tableId);
 554         }
 555         if (counter%2 == 0)
 556             tr.addStyle(HtmlStyle.altColor);
 557         else
 558             tr.addStyle(HtmlStyle.rowColor);
 559         tableContents.add(tr);
 560     }
 561 
 562     /**
 563      * Generate the method types set and return true if the method summary table
 564      * needs to show tabs.
 565      *
 566      * @return true if the table should show tabs
 567      */
 568     public boolean showTabs() {
 569         int value;
 570         for (MethodTypes type : EnumSet.allOf(MethodTypes.class)) {
 571             value = type.tableTabs().value();
 572             if ((value & methodTypesOr) == value) {
 573                 methodTypes.add(type);
 574             }
 575         }
 576         boolean showTabs = methodTypes.size() > 1;
 577         if (showTabs) {
 578             methodTypes.add(MethodTypes.ALL);
 579         }
 580         return showTabs;
 581     }
 582 
 583     /**
 584      * Set the style and scope attribute for the summary column.
 585      *
 586      * @param thTree the column for which the style and scope attribute will be set
 587      */
 588     public void setSummaryColumnStyleAndScope(HtmlTree thTree) {
 589         thTree.addStyle(HtmlStyle.colSecond);
 590         thTree.addAttr(HtmlAttr.SCOPE, "row");
 591     }
 592 
 593     /**
 594      * Add inherited member summary for the given class and member.
 595      *
 596      * @param tElement the class the inherited member belongs to
 597      * @param nestedClass the inherited member that is summarized
 598      * @param isFirst true if this is the first member in the list
 599      * @param isLast true if this is the last member in the list
 600      * @param linksTree the content tree to which the summary will be added
 601      */
 602     public void addInheritedMemberSummary(TypeElement tElement,
 603             Element nestedClass, boolean isFirst, boolean isLast,
 604             Content linksTree) {
 605         writer.addInheritedMemberSummary(this, tElement, nestedClass, isFirst,
 606                 linksTree);
 607     }
 608 
 609     /**
 610      * Get the inherited summary header for the given class.
 611      *
 612      * @param tElement the class the inherited member belongs to
 613      * @return a content tree for the inherited summary header
 614      */
 615     public Content getInheritedSummaryHeader(TypeElement tElement) {
 616         Content inheritedTree = writer.getMemberTreeHeader();
 617         writer.addInheritedSummaryHeader(this, tElement, inheritedTree);
 618         return inheritedTree;
 619     }
 620 
 621     /**
 622      * Get the inherited summary links tree.
 623      *
 624      * @return a content tree for the inherited summary links
 625      */
 626     public Content getInheritedSummaryLinksTree() {
 627         return new HtmlTree(HtmlTag.CODE);
 628     }
 629 
 630     /**
 631      * Get the summary table tree for the given class.
 632      *
 633      * @param tElement the class for which the summary table is generated
 634      * @param tableContents list of contents to be displayed in the summary table
 635      * @return a content tree for the summary table
 636      */
 637     public Content getSummaryTableTree(TypeElement tElement, List<Content> tableContents) {
 638         return writer.getSummaryTableTree(this, tElement, tableContents, showTabs());
 639     }
 640 
 641     /**
 642      * Get the member tree to be documented.
 643      *
 644      * @param memberTree the content tree of member to be documented
 645      * @return a content tree that will be added to the class documentation
 646      */
 647     public Content getMemberTree(Content memberTree) {
 648         return writer.getMemberTree(memberTree);
 649     }
 650 
 651     /**
 652      * Get the member tree to be documented.
 653      *
 654      * @param memberTree the content tree of member to be documented
 655      * @param isLastContent true if the content to be added is the last content
 656      * @return a content tree that will be added to the class documentation
 657      */
 658     public Content getMemberTree(Content memberTree, boolean isLastContent) {
 659         if (isLastContent)
 660             return HtmlTree.UL(HtmlStyle.blockListLast, memberTree);
 661         else
 662             return HtmlTree.UL(HtmlStyle.blockList, memberTree);
 663     }
 664 }