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      * Get the member tree to be documented.
 508      *
 509      * @param memberTree the content tree of member to be documented
 510      * @param isLastContent true if the content to be added is the last content
 511      * @return a content tree that will be added to the class documentation
 512      */
 513     public Content getMemberTree(Content memberTree, boolean isLastContent) {
 514         if (isLastContent)
 515             return HtmlTree.LI(HtmlStyle.blockListLast, memberTree);
 516         else
 517             return HtmlTree.LI(HtmlStyle.blockList, memberTree);
 518     }
 519 
 520     /**
 521      * A HtmlTree builder for member signatures.
 522      */
 523     class MemberSignature {
 524 
 525         // The content builder
 526         private Content content = new ContentBuilder();
 527 
 528         // Position of last line separator.
 529         private int lastNewLine = 0;
 530 
 531         // Threshold for length of type parameters before switching from inline to block representation.
 532         private final static int TYPE_PARAMS_MAX_INLINE_LENGTH = 50;
 533 
 534         // Threshold for combined length of modifiers, type params and return type before breaking
 535         // it up with a line break before the return type.
 536         private final static int RETURN_TYPE_MAX_LINE_LENGTH = 50;
 537 
 538         /**
 539          * Create a new member signature tree.
 540          *
 541          * @param element The element for which to create a signature.
 542          */
 543         MemberSignature(Element element) {
 544             Content annotationInfo = writer.getAnnotationInfo(element.getAnnotationMirrors(), true);
 545             if (!annotationInfo.isEmpty()) {
 546                 content.add(HtmlTree.SPAN(HtmlStyle.annotations, annotationInfo));
 547                 lastNewLine = content.charCount();
 548             }
 549             addModifiers(element);
 550         }
 551 
 552         /**
 553          * Add the modifier for the member. The modifiers are ordered as specified
 554          * by <em>The Java Language Specification</em>.
 555          *
 556          * @param member the member for which the modifier will be added.
 557          */
 558         void addModifiers(Element member) {
 559             Set<Modifier> set = new TreeSet<>(member.getModifiers());
 560 
 561             // remove the ones we really don't need
 562             set.remove(NATIVE);
 563             set.remove(SYNCHRONIZED);
 564             set.remove(STRICTFP);
 565 
 566             // According to JLS, we should not be showing public modifier for
 567             // interface methods.
 568             if ((utils.isField(member) || utils.isMethod(member))
 569                     && ((writer instanceof ClassWriterImpl
 570                     && utils.isInterface(((ClassWriterImpl) writer).getTypeElement())  ||
 571                     writer instanceof AnnotationTypeWriterImpl) )) {
 572                 // Remove the implicit abstract and public modifiers
 573                 if (utils.isMethod(member) &&
 574                         (utils.isInterface(member.getEnclosingElement()) ||
 575                                 utils.isAnnotationType(member.getEnclosingElement()))) {
 576                     set.remove(ABSTRACT);
 577                     set.remove(PUBLIC);
 578                 }
 579                 if (!utils.isMethod(member)) {
 580                     set.remove(PUBLIC);
 581                 }
 582             }
 583             if (!set.isEmpty()) {
 584                 String mods = set.stream().map(Modifier::toString).collect(Collectors.joining(" "));
 585                 content.add(HtmlTree.SPAN(HtmlStyle.modifiers, new StringContent(mods)));
 586                 content.add(Entity.NO_BREAK_SPACE);
 587             }
 588         }
 589 
 590         /**
 591          * Add the type parameters and return type for an executable member.
 592          *
 593          * @param typeParameters the content tree containing the type parameters to add.
 594          * @param returnType the content tree containing the return type to add.
 595          */
 596         void addTypeParametersAndReturnType(Content typeParameters, Content returnType) {
 597 
 598             if (!typeParameters.isEmpty()) {
 599                 // Apply different wrapping strategies for type parameters
 600                 // depending of combined length of type parameters and return type.
 601                 int typeParamLength = typeParameters.charCount();
 602 
 603                 if (typeParamLength >= TYPE_PARAMS_MAX_INLINE_LENGTH) {
 604                     content.add(HtmlTree.SPAN(HtmlStyle.typeParametersLong, typeParameters));
 605                 } else {
 606                     content.add(HtmlTree.SPAN(HtmlStyle.typeParameters, typeParameters));
 607                 }
 608 
 609                 // count below includes modifiers plus type params added above
 610                 if (charCountFromNewline() + returnType.charCount()> RETURN_TYPE_MAX_LINE_LENGTH) {
 611                     content.add(DocletConstants.NL);
 612                     lastNewLine = content.charCount();
 613                 } else {
 614                     content.add(Entity.NO_BREAK_SPACE);
 615                 }
 616             }
 617 
 618             content.add(HtmlTree.SPAN(HtmlStyle.returnType, returnType));
 619             content.add(Entity.NO_BREAK_SPACE);
 620         }
 621 
 622         /**
 623          * Add the type information for a non-executable member.
 624          *
 625          * @param type the type of the member.
 626          */
 627         void addType(TypeMirror type) {
 628             Content typeLink = writer.getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.MEMBER, type));
 629             content.add(HtmlTree.SPAN(HtmlStyle.returnType, typeLink));
 630             content.add(Entity.NO_BREAK_SPACE);
 631         }
 632 
 633         /**
 634          * Add the name of a member.
 635          *
 636          * @param element the member being documented.
 637          */
 638         void addName(Element element) {
 639             HtmlTree nameSpan = new HtmlTree(HtmlTag.SPAN);
 640             nameSpan.setStyle(HtmlStyle.memberName);
 641             if (configuration.linksource) {
 642                 Content name = new StringContent(name(element));
 643                 writer.addSrcLink(element, name, nameSpan);
 644             } else {
 645                 nameSpan.add(name(element));
 646             }
 647             content.add(nameSpan);
 648         }
 649 
 650         /**
 651          * Add the parameter and exception information of an executable member.
 652          *
 653          * @param paramTree the content tree containing the parameter information.
 654          * @param exceptionTree the content tree containing the exception information.
 655          */
 656         void addParametersAndExceptions(Content paramTree, Content exceptionTree) {
 657             int indentSize = charCountFromNewline();
 658 
 659             if (paramTree.isEmpty()) {
 660                 content.add("()");
 661             } else {
 662                 content.add(Entity.ZERO_WIDTH_SPACE);
 663                 content.add("(");
 664                 content.add(HtmlTree.SPAN(HtmlStyle.arguments, paramTree));
 665                 paramTree.add(")");
 666             }
 667 
 668             if (!exceptionTree.isEmpty()) {
 669                 CharSequence indent = makeSpace(indentSize + 1 - 7);
 670                 content.add(DocletConstants.NL);
 671                 content.add(indent);
 672                 content.add("throws ");
 673                 content.add(HtmlTree.SPAN(HtmlStyle.exceptions, exceptionTree));
 674             }
 675         }
 676 
 677         /**
 678          * Return the number of characters of plain text content since the last newline character.
 679          *
 680          * @return the number of plain text characters since the last newline character
 681          */
 682         int charCountFromNewline() {
 683             return content.charCount() - lastNewLine;
 684         }
 685 
 686         /**
 687          * Return a HTML tree containing the member signature.
 688          *
 689          * @return the member signature
 690          */
 691         Content toContent() {
 692             return HtmlTree.DIV(HtmlStyle.memberSignature, content);
 693         }
 694 
 695     }
 696 }
--- EOF ---