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.StringContent;
  50 import jdk.javadoc.internal.doclets.formats.html.markup.Table;
  51 import jdk.javadoc.internal.doclets.formats.html.markup.TableHeader;
  52 import jdk.javadoc.internal.doclets.toolkit.Content;
  53 import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter;
  54 import jdk.javadoc.internal.doclets.toolkit.Resources;
  55 import jdk.javadoc.internal.doclets.toolkit.taglets.DeprecatedTaglet;
  56 import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
  57 import jdk.javadoc.internal.doclets.toolkit.util.Utils;
  58 
  59 import static javax.lang.model.element.Modifier.ABSTRACT;
  60 import static javax.lang.model.element.Modifier.NATIVE;
  61 import static javax.lang.model.element.Modifier.PUBLIC;
  62 import static javax.lang.model.element.Modifier.STRICTFP;
  63 import static javax.lang.model.element.Modifier.SYNCHRONIZED;
  64 
  65 /**
  66  * The base class for member writers.
  67  *
  68  *  <p><b>This is NOT part of any supported API.
  69  *  If you write code that depends on this, you do so at your own risk.
  70  *  This code and its internal interfaces are subject to change or
  71  *  deletion without notice.</b>
  72  *
  73  * @author Robert Field
  74  * @author Atul M Dambalkar
  75  * @author Jamie Ho (Re-write)
  76  * @author Bhavesh Patel (Modified)
  77  */
  78 public abstract class AbstractMemberWriter implements MemberSummaryWriter {
  79 
  80     protected final HtmlConfiguration configuration;
  81     protected final Utils utils;
  82     protected final SubWriterHolderWriter writer;
  83     protected final Contents contents;
  84     protected final Resources resources;
  85     protected final Links links;
  86 
  87     protected final TypeElement typeElement;
  88     public final boolean nodepr;
  89 
  90     protected boolean printedSummaryHeader = false;
  91 
  92     public AbstractMemberWriter(SubWriterHolderWriter writer, TypeElement typeElement) {
  93         this.configuration = writer.configuration;
  94         this.writer = writer;
  95         this.nodepr = configuration.nodeprecated;
  96         this.typeElement = typeElement;
  97         this.utils = configuration.utils;
  98         this.contents = configuration.contents;
  99         this.resources = configuration.resources;
 100         this.links = writer.links;
 101     }
 102 
 103     public AbstractMemberWriter(SubWriterHolderWriter writer) {
 104         this(writer, null);
 105     }
 106 
 107     /*** abstracts ***/
 108 
 109     /**
 110      * Add the summary label for the member.
 111      *
 112      * @param memberTree the content tree to which the label will be added
 113      */
 114     public abstract void addSummaryLabel(Content memberTree);
 115 
 116     /**
 117      * Get the summary for the member summary table.
 118      *
 119      * @return a string for the table summary
 120      */
 121     private String getTableSummaryX() { return null; }
 122 
 123     /**
 124      * Get the summary table header for the member.
 125      *
 126      * @param member the member to be documented
 127      * @return the summary table header
 128      */
 129     public abstract TableHeader getSummaryTableHeader(Element member);
 130 
 131     private Table summaryTable;
 132 
 133     private Table getSummaryTable() {
 134         if (summaryTable == null) {
 135             summaryTable = createSummaryTable();
 136         }
 137         return summaryTable;
 138     }
 139 
 140     /**
 141      * Create the summary table for this element.
 142      * The table should be created and initialized if needed, and configured
 143      * so that it is ready to add content with {@link Table#addRow(Content[])}
 144      * and similar methods.
 145      *
 146      * @return the summary table
 147      */
 148     protected abstract Table createSummaryTable();
 149 
 150 
 151 
 152     /**
 153      * Add inherited summary label for the member.
 154      *
 155      * @param typeElement the TypeElement to which to link to
 156      * @param inheritedTree the content tree to which the inherited summary label will be added
 157      */
 158     public abstract void addInheritedSummaryLabel(TypeElement typeElement, Content inheritedTree);
 159 
 160     /**
 161      * Add the anchor for the summary section of the member.
 162      *
 163      * @param typeElement the TypeElement to be documented
 164      * @param memberTree the content tree to which the summary anchor will be added
 165      */
 166     public abstract void addSummaryAnchor(TypeElement typeElement, Content memberTree);
 167 
 168     /**
 169      * Add the anchor for the inherited summary section of the member.
 170      *
 171      * @param typeElement the TypeElement to be documented
 172      * @param inheritedTree the content tree to which the inherited summary anchor will be added
 173      */
 174     public abstract void addInheritedSummaryAnchor(TypeElement typeElement, Content inheritedTree);
 175 
 176     /**
 177      * Add the summary type for the member.
 178      *
 179      * @param member the member to be documented
 180      * @param tdSummaryType the content tree to which the type will be added
 181      */
 182     protected abstract void addSummaryType(Element member, Content tdSummaryType);
 183 
 184     /**
 185      * Add the summary link for the member.
 186      *
 187      * @param typeElement the TypeElement to be documented
 188      * @param member the member to be documented
 189      * @param tdSummary the content tree to which the link will be added
 190      */
 191     protected void addSummaryLink(TypeElement typeElement, Element member, Content tdSummary) {
 192         addSummaryLink(LinkInfoImpl.Kind.MEMBER, typeElement, member, tdSummary);
 193     }
 194 
 195     /**
 196      * Add the summary link for the member.
 197      *
 198      * @param context the id of the context where the link will be printed
 199      * @param typeElement the TypeElement to be documented
 200      * @param member the member to be documented
 201      * @param tdSummary the content tree to which the summary link will be added
 202      */
 203     protected abstract void addSummaryLink(LinkInfoImpl.Kind context,
 204             TypeElement typeElement, Element member, Content tdSummary);
 205 
 206     /**
 207      * Add the inherited summary link for the member.
 208      *
 209      * @param typeElement the TypeElement to be documented
 210      * @param member the member to be documented
 211      * @param linksTree the content tree to which the inherited summary link will be added
 212      */
 213     protected abstract void addInheritedSummaryLink(TypeElement typeElement,
 214             Element member, Content linksTree);
 215 
 216     /**
 217      * Get the deprecated link.
 218      *
 219      * @param member the member being linked to
 220      * @return a content tree representing the link
 221      */
 222     protected abstract Content getDeprecatedLink(Element member);
 223 
 224     protected CharSequence makeSpace(int len) {
 225         if (len <= 0) {
 226             return "";
 227         }
 228         StringBuilder sb = new StringBuilder(len);
 229         for (int i = 0; i < len; i++) {
 230             sb.append(' ');
 231         }
 232         return sb;
 233     }
 234 
 235     /**
 236      * Add the modifier and type for the member in the member summary.
 237      *
 238      * @param member the member to add the type for
 239      * @param type the type to add
 240      * @param tdSummaryType the content tree to which the modified and type will be added
 241      */
 242     protected void addModifierAndType(Element member, TypeMirror type,
 243             Content tdSummaryType) {
 244         HtmlTree code = new HtmlTree(HtmlTag.CODE);
 245         addModifier(member, code);
 246         if (type == null) {
 247             code.add(utils.isClass(member) ? "class" : "interface");
 248             code.add(Entity.NO_BREAK_SPACE);
 249         } else {
 250             List<? extends TypeParameterElement> list = utils.isExecutableElement(member)
 251                     ? ((ExecutableElement)member).getTypeParameters()
 252                     : null;
 253             if (list != null && !list.isEmpty()) {
 254                 Content typeParameters = ((AbstractExecutableMemberWriter) this)
 255                         .getTypeParameters((ExecutableElement)member);
 256                     code.add(typeParameters);
 257                 //Code to avoid ugly wrapping in member summary table.
 258                 if (typeParameters.charCount() > 10) {
 259                     code.add(new HtmlTree(HtmlTag.BR));
 260                 } else {
 261                     code.add(Entity.NO_BREAK_SPACE);
 262                 }
 263                 code.add(
 264                         writer.getLink(new LinkInfoImpl(configuration,
 265                         LinkInfoImpl.Kind.SUMMARY_RETURN_TYPE, type)));
 266             } else {
 267                 code.add(
 268                         writer.getLink(new LinkInfoImpl(configuration,
 269                         LinkInfoImpl.Kind.SUMMARY_RETURN_TYPE, type)));
 270             }
 271 
 272         }
 273         tdSummaryType.add(code);
 274     }
 275 
 276     /**
 277      * Add the modifier for the member.
 278      *
 279      * @param member the member to add the type for
 280      * @param code the content tree to which the modified will be added
 281      */
 282     private void addModifier(Element member, Content code) {
 283         if (utils.isProtected(member)) {
 284             code.add("protected ");
 285         } else if (utils.isPrivate(member)) {
 286             code.add("private ");
 287         } else if (!utils.isPublic(member)) { // Package private
 288             code.add(resources.getText("doclet.Package_private"));
 289             code.add(" ");
 290         }
 291         boolean isAnnotatedTypeElement = utils.isAnnotationType(member.getEnclosingElement());
 292         if (!isAnnotatedTypeElement && utils.isMethod(member)) {
 293             if (!utils.isInterface(member.getEnclosingElement()) && utils.isAbstract(member)) {
 294                 code.add("abstract ");
 295             }
 296             if (utils.isDefault(member)) {
 297                 code.add("default ");
 298             }
 299         }
 300         if (utils.isStatic(member)) {
 301             code.add("static ");
 302         }
 303     }
 304 
 305     /**
 306      * Add the deprecated information for the given member.
 307      *
 308      * @param member the member being documented.
 309      * @param contentTree the content tree to which the deprecated information will be added.
 310      */
 311     protected void addDeprecatedInfo(Element member, Content contentTree) {
 312         Content output = (new DeprecatedTaglet()).getTagletOutput(member,
 313             writer.getTagletWriterInstance(false));
 314         if (!output.isEmpty()) {
 315             Content deprecatedContent = output;
 316             Content div = HtmlTree.DIV(HtmlStyle.deprecationBlock, deprecatedContent);
 317             contentTree.add(div);
 318         }
 319     }
 320 
 321     /**
 322      * Add the comment for the given member.
 323      *
 324      * @param member the member being documented.
 325      * @param htmltree the content tree to which the comment will be added.
 326      */
 327     protected void addComment(Element member, Content htmltree) {
 328         if (!utils.getFullBody(member).isEmpty()) {
 329             writer.addInlineComment(member, htmltree);
 330         }
 331     }
 332 
 333     protected String name(Element member) {
 334         return utils.getSimpleName(member);
 335     }
 336 
 337     /**
 338     * Return true if the given <code>ProgramElement</code> is inherited
 339     * by the class that is being documented.
 340     *
 341     * @param ped The <code>ProgramElement</code> being checked.
 342     * return true if the <code>ProgramElement</code> is being inherited and
 343     * false otherwise.
 344      *@return true if inherited
 345     */
 346     protected boolean isInherited(Element ped){
 347         return (!utils.isPrivate(ped) &&
 348                 (!utils.isPackagePrivate(ped) ||
 349                     ped.getEnclosingElement().equals(ped.getEnclosingElement())));
 350     }
 351 
 352     /**
 353      * Add use information to the documentation tree.
 354      *
 355      * @param mems list of program elements for which the use information will be added
 356      * @param heading the section heading
 357      * @param contentTree the content tree to which the use information will be added
 358      */
 359     protected void addUseInfo(List<? extends Element> mems, Content heading, Content contentTree) {
 360         if (mems == null || mems.isEmpty()) {
 361             return;
 362         }
 363         List<? extends Element> members = mems;
 364         boolean printedUseTableHeader = false;
 365         if (members.size() > 0) {
 366             Table useTable = new Table(HtmlStyle.useSummary)
 367                     .setCaption(heading)
 368                     .setRowScopeColumn(1)
 369                     .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast);
 370             for (Element element : members) {
 371                 TypeElement te = (typeElement == null)
 372                         ? utils.getEnclosingTypeElement(element)
 373                         : typeElement;
 374                 if (!printedUseTableHeader) {
 375                     useTable.setHeader(getSummaryTableHeader(element));
 376                     printedUseTableHeader = true;
 377                 }
 378                 Content summaryType = new ContentBuilder();
 379                 addSummaryType(element, summaryType);
 380                 Content typeContent = new ContentBuilder();
 381                 if (te != null
 382                         && !utils.isConstructor(element)
 383                         && !utils.isClass(element)
 384                         && !utils.isInterface(element)
 385                         && !utils.isAnnotationType(element)) {
 386                     HtmlTree name = new HtmlTree(HtmlTag.SPAN);
 387                     name.setStyle(HtmlStyle.typeNameLabel);
 388                     name.add(name(te) + ".");
 389                     typeContent.add(name);
 390                 }
 391                 addSummaryLink(utils.isClass(element) || utils.isInterface(element)
 392                         ? LinkInfoImpl.Kind.CLASS_USE
 393                         : LinkInfoImpl.Kind.MEMBER,
 394                         te, element, typeContent);
 395                 Content desc = new ContentBuilder();
 396                 writer.addSummaryLinkComment(this, element, desc);
 397                 useTable.addRow(summaryType, typeContent, desc);
 398             }
 399             contentTree.add(useTable.toContent());
 400         }
 401     }
 402 
 403     protected void serialWarning(Element e, String key, String a1, String a2) {
 404         if (configuration.serialwarn) {
 405             configuration.messages.warning(e, key, a1, a2);
 406         }
 407     }
 408 
 409     /**
 410      * Add the member summary for the given class.
 411      *
 412      * @param tElement the class that is being documented
 413      * @param member the member being documented
 414      * @param firstSentenceTags the first sentence tags to be added to the summary
 415      */
 416     @Override
 417     public void addMemberSummary(TypeElement tElement, Element member,
 418             List<? extends DocTree> firstSentenceTags) {
 419         if (tElement != typeElement) {
 420             throw new IllegalStateException();
 421         }
 422         Table table = getSummaryTable();
 423         List<Content> rowContents = new ArrayList<>();
 424         Content summaryType = new ContentBuilder();
 425         addSummaryType(member, summaryType);
 426         if (!summaryType.isEmpty())
 427             rowContents.add(summaryType);
 428         Content summaryLink = new ContentBuilder();
 429         addSummaryLink(tElement, member, summaryLink);
 430         rowContents.add(summaryLink);
 431         Content desc = new ContentBuilder();
 432         writer.addSummaryLinkComment(this, member, firstSentenceTags, desc);
 433         rowContents.add(desc);
 434         table.addRow(member, rowContents);
 435     }
 436 
 437     /**
 438      * Add inherited member summary for the given class and member.
 439      *
 440      * @param tElement the class the inherited member belongs to
 441      * @param nestedClass the inherited member that is summarized
 442      * @param isFirst true if this is the first member in the list
 443      * @param isLast true if this is the last member in the list
 444      * @param linksTree the content tree to which the summary will be added
 445      */
 446     @Override
 447     public void addInheritedMemberSummary(TypeElement tElement,
 448             Element nestedClass, boolean isFirst, boolean isLast,
 449             Content linksTree) {
 450         writer.addInheritedMemberSummary(this, tElement, nestedClass, isFirst,
 451                 linksTree);
 452     }
 453 
 454     /**
 455      * Get the inherited summary header for the given class.
 456      *
 457      * @param tElement the class the inherited member belongs to
 458      * @return a content tree for the inherited summary header
 459      */
 460     @Override
 461     public Content getInheritedSummaryHeader(TypeElement tElement) {
 462         Content inheritedTree = writer.getMemberInheritedTree();
 463         writer.addInheritedSummaryHeader(this, tElement, inheritedTree);
 464         return inheritedTree;
 465     }
 466 
 467     /**
 468      * Get the inherited summary links tree.
 469      *
 470      * @return a content tree for the inherited summary links
 471      */
 472     @Override
 473     public Content getInheritedSummaryLinksTree() {
 474         return new HtmlTree(HtmlTag.CODE);
 475     }
 476 
 477     /**
 478      * Get the summary table tree for the given class.
 479      *
 480      * @param tElement the class for which the summary table is generated
 481      * @return a content tree for the summary table
 482      */
 483     @Override
 484     public Content getSummaryTableTree(TypeElement tElement) {
 485         if (tElement != typeElement) {
 486             throw new IllegalStateException();
 487         }
 488         Table table = getSummaryTable();
 489         if (table.needsScript()) {
 490             writer.getMainBodyScript().append(table.getScript());
 491         }
 492         return table.toContent();
 493     }
 494 
 495     /**
 496      * Get the member tree to be documented.
 497      *
 498      * @param memberTree the content tree of member to be documented
 499      * @return a content tree that will be added to the class documentation
 500      */
 501     @Override
 502     public Content getMemberTree(Content memberTree) {
 503         return writer.getMemberTree(memberTree);
 504     }
 505 
 506     /**
 507      * A content builder for member signatures.
 508      */
 509     class MemberSignature {
 510 
 511         private Element element;
 512         private Content typeParameters;
 513         private Content returnType;
 514         private Content parameters;
 515         private Content exceptions;
 516 
 517         // Threshold for length of type parameters before switching from inline to block representation.
 518         private final static int TYPE_PARAMS_MAX_INLINE_LENGTH = 50;
 519 
 520         // Threshold for combined length of modifiers, type params and return type before breaking
 521         // it up with a line break before the return type.
 522         private final static int RETURN_TYPE_MAX_LINE_LENGTH = 50;
 523 
 524         /**
 525          * Create a new member signature builder.
 526          *
 527          * @param element The element for which to create a signature.
 528          */
 529         MemberSignature(Element element) {
 530             this.element = element;
 531         }
 532 
 533         /**
 534          * Add the type parameters for an executable member.
 535          *
 536          * @param typeParameters the content tree containing the type parameters to add.
 537          * @return this MemberSignature instance
 538          */
 539         MemberSignature addTypeParameters(Content typeParameters) {
 540             this.typeParameters = typeParameters;
 541             return this;
 542         }
 543 
 544         /**
 545          * Add the return type for an executable member.
 546          *
 547          * @param returnType the content tree containing the return type to add.
 548          * @return this MemberSignature instance
 549          */
 550         MemberSignature addReturnType(Content returnType) {
 551             this.returnType = returnType;
 552             return this;
 553         }
 554 
 555         /**
 556          * Add the type information for a non-executable member.
 557          *
 558          * @param type the type of the member.
 559          * @return this MemberSignature instance
 560          */
 561         MemberSignature addType(TypeMirror type) {
 562             this.returnType = writer.getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.MEMBER, type));
 563             return this;
 564         }
 565 
 566         /**
 567          * Add the parameter information of an executable member.
 568          *
 569          * @param paramTree the content tree containing the parameter information.
 570          * @return this MemberSignature instance
 571          */
 572         MemberSignature addParameters(Content paramTree) {
 573             this.parameters = paramTree;
 574             return this;
 575         }
 576 
 577         /**
 578          * Add the exception information of an executable member.
 579          *
 580          * @param exceptionTree the content tree containing the exception information
 581          * @return this MemberSignature instance
 582          */
 583         MemberSignature addExceptions(Content exceptionTree) {
 584             this.exceptions = exceptionTree;
 585             return this;
 586         }
 587 
 588         /**
 589          * Return a HTML tree containing the member signature.
 590          *
 591          * @return a HTML tree containing the member signature
 592          */
 593         Content toContent() {
 594             Content content = new ContentBuilder();
 595             // Position of last line separator.
 596             int lastLineSeparator = 0;
 597 
 598             // Annotations
 599             Content annotationInfo = writer.getAnnotationInfo(element.getAnnotationMirrors(), true);
 600             if (!annotationInfo.isEmpty()) {
 601                 content.add(HtmlTree.SPAN(HtmlStyle.annotations, annotationInfo));
 602                 lastLineSeparator = content.charCount();
 603             }
 604 
 605             // Modifiers
 606             appendModifiers(content);
 607 
 608             // Type parameters
 609             if (typeParameters != null && !typeParameters.isEmpty()) {
 610                 lastLineSeparator = appendTypeParameters(content, lastLineSeparator);
 611             }
 612 
 613             // Return type
 614             if (returnType != null) {
 615                 content.add(HtmlTree.SPAN(HtmlStyle.returnType, returnType));
 616                 content.add(Entity.NO_BREAK_SPACE);
 617             }
 618 
 619             // Name
 620             HtmlTree nameSpan = new HtmlTree(HtmlTag.SPAN);
 621             nameSpan.setStyle(HtmlStyle.memberName);
 622             if (configuration.linksource) {
 623                 Content name = new StringContent(name(element));
 624                 writer.addSrcLink(element, name, nameSpan);
 625             } else {
 626                 nameSpan.add(name(element));
 627             }
 628             content.add(nameSpan);
 629 
 630 
 631             // Parameters and exceptions
 632             if (parameters != null) {
 633                 appendParametersAndExceptions(content, lastLineSeparator);
 634             }
 635 
 636             return HtmlTree.DIV(HtmlStyle.memberSignature, content);
 637         }
 638 
 639         /**
 640          * Add the modifier for the member. The modifiers are ordered as specified
 641          * by <em>The Java Language Specification</em>.
 642          *
 643          * @param htmltree the content tree to which the modifier information will be added.
 644          */
 645         private void appendModifiers(Content htmltree) {
 646             Set<Modifier> set = new TreeSet<>(element.getModifiers());
 647 
 648             // remove the ones we really don't need
 649             set.remove(NATIVE);
 650             set.remove(SYNCHRONIZED);
 651             set.remove(STRICTFP);
 652 
 653             // According to JLS, we should not be showing public modifier for
 654             // interface methods.
 655             if ((utils.isField(element) || utils.isMethod(element))
 656                     && ((writer instanceof ClassWriterImpl
 657                     && utils.isInterface(((ClassWriterImpl) writer).getTypeElement())  ||
 658                     writer instanceof AnnotationTypeWriterImpl) )) {
 659                 // Remove the implicit abstract and public modifiers
 660                 if (utils.isMethod(element) &&
 661                         (utils.isInterface(element.getEnclosingElement()) ||
 662                                 utils.isAnnotationType(element.getEnclosingElement()))) {
 663                     set.remove(ABSTRACT);
 664                     set.remove(PUBLIC);
 665                 }
 666                 if (!utils.isMethod(element)) {
 667                     set.remove(PUBLIC);
 668                 }
 669             }
 670             if (!set.isEmpty()) {
 671                 String mods = set.stream().map(Modifier::toString).collect(Collectors.joining(" "));
 672                 htmltree.add(HtmlTree.SPAN(HtmlStyle.modifiers, new StringContent(mods)));
 673                 htmltree.add(Entity.NO_BREAK_SPACE);
 674             }
 675         }
 676 
 677         /**
 678          * Append the type parameter information to the HTML tree.
 679          *
 680          * @param htmltree the HTML tree
 681          * @param lastLineSeparator index of last line separator in HTML tree
 682          * @return the new index of the last line separator
 683          */
 684         private int appendTypeParameters(Content htmltree, int lastLineSeparator) {
 685             // Apply different wrapping strategies for type parameters
 686             // depending of combined length of type parameters and return type.
 687             int typeParamLength = typeParameters.charCount();
 688 
 689             if (typeParamLength >= TYPE_PARAMS_MAX_INLINE_LENGTH) {
 690                 htmltree.add(HtmlTree.SPAN(HtmlStyle.typeParametersLong, typeParameters));
 691             } else {
 692                 htmltree.add(HtmlTree.SPAN(HtmlStyle.typeParameters, typeParameters));
 693             }
 694 
 695             int lineLength = htmltree.charCount() - lastLineSeparator;
 696             int newLastLineSeparator = lastLineSeparator;
 697 
 698             // sum below includes length of modifiers plus type params added above
 699             if (lineLength + returnType.charCount()> RETURN_TYPE_MAX_LINE_LENGTH) {
 700                 htmltree.add(DocletConstants.NL);
 701                 newLastLineSeparator = htmltree.charCount();
 702             } else {
 703                 htmltree.add(Entity.NO_BREAK_SPACE);
 704             }
 705 
 706             return newLastLineSeparator;
 707         }
 708 
 709         /**
 710          * Append the parameters and exceptions information to the HTML tree.
 711          *
 712          * @param htmltree the HTML tree
 713          * @param lastLineSeparator the index of the last line separator in HTML tree
 714          */
 715         private void appendParametersAndExceptions(Content htmltree, int lastLineSeparator) {
 716             // Record current position for indentation of exceptions
 717             int indentSize = htmltree.charCount() - lastLineSeparator;
 718 
 719             if (parameters.isEmpty()) {
 720                 htmltree.add("()");
 721             } else {
 722                 parameters.add(")");
 723                 htmltree.add(Entity.ZERO_WIDTH_SPACE);
 724                 htmltree.add("(");
 725                 htmltree.add(HtmlTree.SPAN(HtmlStyle.arguments, parameters));
 726             }
 727 
 728             // Exceptions
 729             if (exceptions != null && !exceptions.isEmpty()) {
 730                 CharSequence indent = makeSpace(indentSize + 1 - 7);
 731                 htmltree.add(DocletConstants.NL);
 732                 htmltree.add(indent);
 733                 htmltree.add("throws ");
 734                 htmltree.add(HtmlTree.SPAN(HtmlStyle.exceptions, exceptions));
 735             }
 736         }
 737     }
 738 }