1 /*
   2  * Copyright (c) 1998, 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 jdk.javadoc.internal.doclets.formats.html.markup.Head;
  29 import jdk.javadoc.internal.doclets.formats.html.markup.TableHeader;
  30 
  31 import java.util.*;
  32 import java.util.regex.Matcher;
  33 import java.util.regex.Pattern;
  34 
  35 import javax.lang.model.element.AnnotationMirror;
  36 import javax.lang.model.element.AnnotationValue;
  37 import javax.lang.model.element.Element;
  38 import javax.lang.model.element.ElementKind;
  39 import javax.lang.model.element.ExecutableElement;
  40 import javax.lang.model.element.ModuleElement;
  41 import javax.lang.model.element.Name;
  42 import javax.lang.model.element.PackageElement;
  43 import javax.lang.model.element.TypeElement;
  44 import javax.lang.model.element.VariableElement;
  45 import javax.lang.model.type.DeclaredType;
  46 import javax.lang.model.type.TypeMirror;
  47 import javax.lang.model.util.SimpleAnnotationValueVisitor9;
  48 import javax.lang.model.util.SimpleElementVisitor9;
  49 import javax.lang.model.util.SimpleTypeVisitor9;
  50 
  51 import com.sun.source.doctree.AttributeTree;
  52 import com.sun.source.doctree.AttributeTree.ValueKind;
  53 import com.sun.source.doctree.CommentTree;
  54 import com.sun.source.doctree.DocRootTree;
  55 import com.sun.source.doctree.DocTree;
  56 import com.sun.source.doctree.DocTree.Kind;
  57 import com.sun.source.doctree.EndElementTree;
  58 import com.sun.source.doctree.EntityTree;
  59 import com.sun.source.doctree.ErroneousTree;
  60 import com.sun.source.doctree.IndexTree;
  61 import com.sun.source.doctree.InheritDocTree;
  62 import com.sun.source.doctree.LinkTree;
  63 import com.sun.source.doctree.LiteralTree;
  64 import com.sun.source.doctree.SeeTree;
  65 import com.sun.source.doctree.StartElementTree;
  66 import com.sun.source.doctree.SummaryTree;
  67 import com.sun.source.doctree.TextTree;
  68 import com.sun.source.util.SimpleDocTreeVisitor;
  69 
  70 import jdk.javadoc.internal.doclets.formats.html.markup.Comment;
  71 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
  72 import jdk.javadoc.internal.doclets.formats.html.markup.DocType;
  73 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr;
  74 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants;
  75 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocWriter;
  76 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocument;
  77 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
  78 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
  79 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
  80 import jdk.javadoc.internal.doclets.formats.html.markup.Links;
  81 import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml;
  82 import jdk.javadoc.internal.doclets.formats.html.markup.Script;
  83 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
  84 import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter;
  85 import jdk.javadoc.internal.doclets.toolkit.ClassWriter;
  86 import jdk.javadoc.internal.doclets.toolkit.Content;
  87 import jdk.javadoc.internal.doclets.toolkit.Messages;
  88 import jdk.javadoc.internal.doclets.toolkit.PackageSummaryWriter;
  89 import jdk.javadoc.internal.doclets.toolkit.Resources;
  90 import jdk.javadoc.internal.doclets.toolkit.taglets.DocRootTaglet;
  91 import jdk.javadoc.internal.doclets.toolkit.taglets.TagletWriter;
  92 import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
  93 import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
  94 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
  95 import jdk.javadoc.internal.doclets.toolkit.util.DocLink;
  96 import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
  97 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
  98 import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
  99 import jdk.javadoc.internal.doclets.toolkit.util.ImplementedMethods;
 100 import jdk.javadoc.internal.doclets.toolkit.util.Utils;
 101 import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap;
 102 
 103 import static com.sun.source.doctree.DocTree.Kind.*;
 104 import static jdk.javadoc.internal.doclets.toolkit.util.CommentHelper.SPACER;
 105 
 106 
 107 /**
 108  * Class for the Html Format Code Generation specific to JavaDoc.
 109  * This Class contains methods related to the Html Code Generation which
 110  * are used extensively while generating the entire documentation.
 111  *
 112  *  <p><b>This is NOT part of any supported API.
 113  *  If you write code that depends on this, you do so at your own risk.
 114  *  This code and its internal interfaces are subject to change or
 115  *  deletion without notice.</b>
 116  *
 117  * @author Atul M Dambalkar
 118  * @author Robert Field
 119  * @author Bhavesh Patel (Modified)
 120  */
 121 public class HtmlDocletWriter extends HtmlDocWriter {
 122 
 123     /**
 124      * Relative path from the file getting generated to the destination
 125      * directory. For example, if the file getting generated is
 126      * "java/lang/Object.html", then the path to the root is "../..".
 127      * This string can be empty if the file getting generated is in
 128      * the destination directory.
 129      */
 130     public final DocPath pathToRoot;
 131 
 132     /**
 133      * Platform-independent path from the current or the
 134      * destination directory to the file getting generated.
 135      * Used when creating the file.
 136      */
 137     public final DocPath path;
 138 
 139     /**
 140      * Name of the file getting generated. If the file getting generated is
 141      * "java/lang/Object.html", then the filename is "Object.html".
 142      */
 143     public final DocPath filename;
 144 
 145     /**
 146      * The global configuration information for this run.
 147      */
 148     public final HtmlConfiguration configuration;
 149 
 150     protected final Utils utils;
 151 
 152     protected final Contents contents;
 153 
 154     protected final Messages messages;
 155 
 156     protected final Resources resources;
 157 
 158     protected final Links links;
 159 
 160     /**
 161      * To check whether annotation heading is printed or not.
 162      */
 163     protected boolean printedAnnotationHeading = false;
 164 
 165     /**
 166      * To check whether annotation field heading is printed or not.
 167      */
 168     protected boolean printedAnnotationFieldHeading = false;
 169 
 170     /**
 171      * To check whether the repeated annotations is documented or not.
 172      */
 173     private boolean isAnnotationDocumented = false;
 174 
 175     /**
 176      * To check whether the container annotations is documented or not.
 177      */
 178     private boolean isContainerDocumented = false;
 179 
 180     HtmlTree fixedNavDiv = new HtmlTree(HtmlTag.DIV);
 181 
 182     final static Pattern IMPROPER_HTML_CHARS = Pattern.compile(".*[&<>].*");
 183 
 184     /**
 185      * The window title of this file.
 186      */
 187     protected String winTitle;
 188 
 189     protected Script mainBodyScript;
 190 
 191     /**
 192      * Constructor to construct the HtmlStandardWriter object.
 193      *
 194      * @param path File to be generated.
 195      */
 196     public HtmlDocletWriter(HtmlConfiguration configuration, DocPath path) {
 197         super(configuration, path);
 198         this.configuration = configuration;
 199         this.contents = configuration.contents;
 200         this.messages = configuration.messages;
 201         this.resources = configuration.resources;
 202         this.links = configuration.links;
 203         this.utils = configuration.utils;
 204         this.path = path;
 205         this.pathToRoot = path.parent().invert();
 206         this.filename = path.basename();
 207     }
 208 
 209     /**
 210      * Replace {@docRoot} tag used in options that accept HTML text, such
 211      * as -header, -footer, -top and -bottom, and when converting a relative
 212      * HREF where commentTagsToString inserts a {@docRoot} where one was
 213      * missing.  (Also see DocRootTaglet for {@docRoot} tags in doc
 214      * comments.)
 215      * <p>
 216      * Replace {@docRoot} tag in htmlstr with the relative path to the
 217      * destination directory from the directory where the file is being
 218      * written, looping to handle all such tags in htmlstr.
 219      * <p>
 220      * For example, for "-d docs" and -header containing {@docRoot}, when
 221      * the HTML page for source file p/C1.java is being generated, the
 222      * {@docRoot} tag would be inserted into the header as "../",
 223      * the relative path from docs/p/ to docs/ (the document root).
 224      * <p>
 225      * Note: This doc comment was written with '&amp;#064;' representing '@'
 226      * to prevent the inline tag from being interpreted.
 227      */
 228     public String replaceDocRootDir(String htmlstr) {
 229         // Return if no inline tags exist
 230         int index = htmlstr.indexOf("{@");
 231         if (index < 0) {
 232             return htmlstr;
 233         }
 234         Matcher docrootMatcher = docrootPattern.matcher(htmlstr);
 235         if (!docrootMatcher.find()) {
 236             return htmlstr;
 237         }
 238         StringBuilder buf = new StringBuilder();
 239         int prevEnd = 0;
 240         do {
 241             int match = docrootMatcher.start();
 242             // append htmlstr up to start of next {@docroot}
 243             buf.append(htmlstr.substring(prevEnd, match));
 244             prevEnd = docrootMatcher.end();
 245             if (configuration.docrootparent.length() > 0 && htmlstr.startsWith("/..", prevEnd)) {
 246                 // Insert the absolute link if {@docRoot} is followed by "/..".
 247                 buf.append(configuration.docrootparent);
 248                 prevEnd += 3;
 249             } else {
 250                 // Insert relative path where {@docRoot} was located
 251                 buf.append(pathToRoot.isEmpty() ? "." : pathToRoot.getPath());
 252             }
 253             // Append slash if next character is not a slash
 254             if (prevEnd < htmlstr.length() && htmlstr.charAt(prevEnd) != '/') {
 255                 buf.append('/');
 256             }
 257         } while (docrootMatcher.find());
 258         buf.append(htmlstr.substring(prevEnd));
 259         return buf.toString();
 260     }
 261     //where:
 262         // Note: {@docRoot} is not case sensitive when passed in w/command line option:
 263         private static final Pattern docrootPattern =
 264                 Pattern.compile(Pattern.quote("{@docroot}"), Pattern.CASE_INSENSITIVE);
 265 
 266     /**
 267      * Get the script to show or hide the All classes link.
 268      *
 269      * @param id id of the element to show or hide
 270      * @return a content tree for the script
 271      */
 272     public Content getAllClassesLinkScript(String id) {
 273         Script script = new Script("<!--\n" +
 274                 "  allClassesLink = document.getElementById(")
 275                 .appendStringLiteral(id)
 276                 .append(");\n" +
 277                 "  if(window==top) {\n" +
 278                 "    allClassesLink.style.display = \"block\";\n" +
 279                 "  }\n" +
 280                 "  else {\n" +
 281                 "    allClassesLink.style.display = \"none\";\n" +
 282                 "  }\n" +
 283                 "  //-->\n");
 284         Content div = HtmlTree.DIV(script.asContent());
 285         Content div_noscript = HtmlTree.DIV(contents.noScriptMessage);
 286         Content noScript = HtmlTree.NOSCRIPT(div_noscript);
 287         div.addContent(noScript);
 288         return div;
 289     }
 290 
 291     /**
 292      * Add method information.
 293      *
 294      * @param method the method to be documented
 295      * @param dl the content tree to which the method information will be added
 296      */
 297     private void addMethodInfo(ExecutableElement method, Content dl) {
 298         TypeElement enclosing = utils.getEnclosingTypeElement(method);
 299         List<? extends TypeMirror> intfacs = enclosing.getInterfaces();
 300         ExecutableElement overriddenMethod = utils.overriddenMethod(method);
 301         // Check whether there is any implementation or overridden info to be
 302         // printed. If no overridden or implementation info needs to be
 303         // printed, do not print this section.
 304         if ((!intfacs.isEmpty()
 305                 && new ImplementedMethods(method, this.configuration).build().isEmpty() == false)
 306                 || overriddenMethod != null) {
 307             MethodWriterImpl.addImplementsInfo(this, method, dl);
 308             if (overriddenMethod != null) {
 309                 MethodWriterImpl.addOverridden(this,
 310                         utils.overriddenType(method),
 311                         overriddenMethod,
 312                         dl);
 313             }
 314         }
 315     }
 316 
 317     /**
 318      * Adds the tags information.
 319      *
 320      * @param e the Element for which the tags will be generated
 321      * @param htmltree the documentation tree to which the tags will be added
 322      */
 323     protected void addTagsInfo(Element e, Content htmltree) {
 324         if (configuration.nocomment) {
 325             return;
 326         }
 327         Content dl = new HtmlTree(HtmlTag.DL);
 328         if (utils.isExecutableElement(e) && !utils.isConstructor(e)) {
 329             addMethodInfo((ExecutableElement)e, dl);
 330         }
 331         Content output = new ContentBuilder();
 332         TagletWriter.genTagOutput(configuration.tagletManager, e,
 333             configuration.tagletManager.getCustomTaglets(e),
 334                 getTagletWriterInstance(false), output);
 335         dl.addContent(output);
 336         htmltree.addContent(dl);
 337     }
 338 
 339     /**
 340      * Check whether there are any tags for Serialization Overview
 341      * section to be printed.
 342      *
 343      * @param field the VariableElement object to check for tags.
 344      * @return true if there are tags to be printed else return false.
 345      */
 346     protected boolean hasSerializationOverviewTags(VariableElement field) {
 347         Content output = new ContentBuilder();
 348         TagletWriter.genTagOutput(configuration.tagletManager, field,
 349                 configuration.tagletManager.getCustomTaglets(field),
 350                 getTagletWriterInstance(false), output);
 351         return !output.isEmpty();
 352     }
 353 
 354     /**
 355      * Returns a TagletWriter that knows how to write HTML.
 356      *
 357      * @return a TagletWriter that knows how to write HTML.
 358      */
 359     public TagletWriter getTagletWriterInstance(boolean isFirstSentence) {
 360         return new TagletWriterImpl(this, isFirstSentence);
 361     }
 362 
 363     /**
 364      * Get Package link, with target frame.
 365      *
 366      * @param pkg The link will be to the "package-summary.html" page for this package
 367      * @param target name of the target frame
 368      * @param label tag for the link
 369      * @return a content for the target package link
 370      */
 371     public Content getTargetPackageLink(PackageElement pkg, String target,
 372             Content label) {
 373         return Links.createLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY), label, "", target);
 374     }
 375 
 376     /**
 377      * Get Module Package link, with target frame.
 378      *
 379      * @param pkg the PackageElement
 380      * @param target name of the target frame
 381      * @param label tag for the link
 382      * @param mdle the module being documented
 383      * @return a content for the target module packages link
 384      */
 385     public Content getTargetModulePackageLink(PackageElement pkg, String target,
 386             Content label, ModuleElement mdle) {
 387         return Links.createLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY),
 388                 label, "", target);
 389     }
 390 
 391     /**
 392      * Get Module link, with target frame.
 393      *
 394      * @param target name of the target frame
 395      * @param label tag for the link
 396      * @param mdle the module being documented
 397      * @return a content for the target module link
 398      */
 399     public Content getTargetModuleLink(String target, Content label, ModuleElement mdle) {
 400         return Links.createLink(pathToRoot.resolve(
 401                 DocPaths.moduleSummary(mdle)), label, "", target);
 402     }
 403 
 404     /**
 405      * Generates the HTML document tree and prints it out.
 406      *
 407      * @param metakeywords Array of String keywords for META tag. Each element
 408      *                     of the array is assigned to a separate META tag.
 409      *                     Pass in null for no array
 410      * @param includeScript true if printing windowtitle script
 411      *                      false for files that appear in the left-hand frames
 412      * @param body the body htmltree to be included in the document
 413      * @throws DocFileIOException if there is a problem writing the file
 414      */
 415     public void printHtmlDocument(List<String> metakeywords, boolean includeScript,
 416             Content body) throws DocFileIOException {
 417         DocType htmlDocType = DocType.forVersion(configuration.htmlVersion);
 418         Content htmlComment = contents.newPage;
 419         Head head = new Head(path, configuration.htmlVersion, configuration.docletVersion)
 420                 .setTimestamp(!configuration.notimestamp)
 421                 .setTitle(winTitle)
 422                 .setCharset(configuration.charset)
 423                 .addKeywords(metakeywords)
 424                 .setStylesheets(configuration.getMainStylesheet(), configuration.getAdditionalStylesheets())
 425                 .setIndex(configuration.createindex, mainBodyScript);
 426 
 427         Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(), head.toContent(), body);
 428         HtmlDocument htmlDocument = new HtmlDocument(htmlDocType, htmlComment, htmlTree);
 429         htmlDocument.write(DocFile.createFileForOutput(configuration, path));
 430     }
 431 
 432     /**
 433      * Get the window title.
 434      *
 435      * @param title the title string to construct the complete window title
 436      * @return the window title string
 437      */
 438     public String getWindowTitle(String title) {
 439         if (configuration.windowtitle.length() > 0) {
 440             title += " (" + configuration.windowtitle  + ")";
 441         }
 442         return title;
 443     }
 444 
 445     /**
 446      * Get user specified header and the footer.
 447      *
 448      * @param header if true print the user provided header else print the
 449      * user provided footer.
 450      */
 451     public Content getUserHeaderFooter(boolean header) {
 452         String content;
 453         if (header) {
 454             content = replaceDocRootDir(configuration.header);
 455         } else {
 456             if (configuration.footer.length() != 0) {
 457                 content = replaceDocRootDir(configuration.footer);
 458             } else {
 459                 content = replaceDocRootDir(configuration.header);
 460             }
 461         }
 462         Content rawContent = new RawHtml(content);
 463         return rawContent;
 464     }
 465 
 466     /**
 467      * Adds the user specified top.
 468      *
 469      * @param htmlTree the content tree to which user specified top will be added
 470      */
 471     public void addTop(Content htmlTree) {
 472         Content top = new RawHtml(replaceDocRootDir(configuration.top));
 473         fixedNavDiv.addContent(top);
 474     }
 475 
 476     /**
 477      * Adds the user specified bottom.
 478      *
 479      * @param htmlTree the content tree to which user specified bottom will be added
 480      */
 481     public void addBottom(Content htmlTree) {
 482         Content bottom = new RawHtml(replaceDocRootDir(configuration.bottom));
 483         Content small = HtmlTree.SMALL(bottom);
 484         Content p = HtmlTree.P(HtmlStyle.legalCopy, small);
 485         htmlTree.addContent(p);
 486     }
 487 
 488     /**
 489      * Adds the navigation bar for the Html page at the top and and the bottom.
 490      *
 491      * @param header If true print navigation bar at the top of the page else
 492      * @param htmlTree the HtmlTree to which the nav links will be added
 493      */
 494     protected void addNavLinks(boolean header, Content htmlTree) {
 495         if (!configuration.nonavbar) {
 496             Content tree = (configuration.allowTag(HtmlTag.NAV))
 497                     ? HtmlTree.NAV()
 498                     : htmlTree;
 499             String allClassesId = "allclasses_";
 500             HtmlTree navDiv = new HtmlTree(HtmlTag.DIV);
 501             fixedNavDiv.setStyle(HtmlStyle.fixedNav);
 502             Content skipNavLinks = configuration.getContent("doclet.Skip_navigation_links");
 503             if (header) {
 504                 fixedNavDiv.addContent(HtmlConstants.START_OF_TOP_NAVBAR);
 505                 navDiv.setStyle(HtmlStyle.topNav);
 506                 allClassesId += "navbar_top";
 507                 Content a = links.createAnchor(SectionName.NAVBAR_TOP);
 508                 //WCAG - Hyperlinks should contain text or an image with alt text - for AT tools
 509                 navDiv.addContent(a);
 510                 Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav,
 511                         Links.createLink(SectionName.SKIP_NAVBAR_TOP, skipNavLinks,
 512                         skipNavLinks.toString(), ""));
 513                 navDiv.addContent(skipLinkContent);
 514             } else {
 515                 tree.addContent(HtmlConstants.START_OF_BOTTOM_NAVBAR);
 516                 navDiv.setStyle(HtmlStyle.bottomNav);
 517                 allClassesId += "navbar_bottom";
 518                 Content a = links.createAnchor(SectionName.NAVBAR_BOTTOM);
 519                 navDiv.addContent(a);
 520                 Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav,
 521                         Links.createLink(SectionName.SKIP_NAVBAR_BOTTOM, skipNavLinks,
 522                         skipNavLinks.toString(), ""));
 523                 navDiv.addContent(skipLinkContent);
 524             }
 525             if (header) {
 526                 navDiv.addContent(links.createAnchor(SectionName.NAVBAR_TOP_FIRSTROW));
 527             } else {
 528                 navDiv.addContent(links.createAnchor(SectionName.NAVBAR_BOTTOM_FIRSTROW));
 529             }
 530             HtmlTree navList = new HtmlTree(HtmlTag.UL);
 531             navList.setStyle(HtmlStyle.navList);
 532             navList.addAttr(HtmlAttr.TITLE,
 533                             configuration.getText("doclet.Navigation"));
 534             if (configuration.createoverview) {
 535                 navList.addContent(getNavLinkContents());
 536             }
 537             if (configuration.showModules) {
 538                 if (configuration.modules.size() == 1) {
 539                     navList.addContent(getNavLinkModule(configuration.modules.first()));
 540                 } else if (!configuration.modules.isEmpty()) {
 541                     navList.addContent(getNavLinkModule());
 542                 }
 543             }
 544             if (configuration.packages.size() == 1) {
 545                 navList.addContent(getNavLinkPackage(configuration.packages.first()));
 546             } else if (!configuration.packages.isEmpty()) {
 547                 navList.addContent(getNavLinkPackage());
 548             }
 549             navList.addContent(getNavLinkClass());
 550             if(configuration.classuse) {
 551                 navList.addContent(getNavLinkClassUse());
 552             }
 553             if(configuration.createtree) {
 554                 navList.addContent(getNavLinkTree());
 555             }
 556             if(!(configuration.nodeprecated ||
 557                      configuration.nodeprecatedlist)) {
 558                 navList.addContent(getNavLinkDeprecated());
 559             }
 560             if(configuration.createindex) {
 561                 navList.addContent(getNavLinkIndex());
 562             }
 563             if (!configuration.nohelp) {
 564                 navList.addContent(getNavLinkHelp());
 565             }
 566             navDiv.addContent(navList);
 567             Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, getUserHeaderFooter(header));
 568             navDiv.addContent(aboutDiv);
 569             if (header) {
 570                 fixedNavDiv.addContent(navDiv);
 571             } else {
 572                 tree.addContent(navDiv);
 573             }
 574             Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious(), getNavLinkNext());
 575             Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav);
 576             if (configuration.frames) {
 577                 Content ulFrames = HtmlTree.UL(HtmlStyle.navList,
 578                     getNavShowLists(), getNavHideLists(filename));
 579                 subDiv.addContent(ulFrames);
 580             }
 581             HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex());
 582             ulAllClasses.addAttr(HtmlAttr.ID, allClassesId);
 583             subDiv.addContent(ulAllClasses);
 584             if (header && configuration.createindex) {
 585                 String searchValueId = "search";
 586                 String reset = "reset";
 587                 HtmlTree inputText = HtmlTree.INPUT("text", searchValueId, searchValueId);
 588                 HtmlTree inputReset = HtmlTree.INPUT(reset, reset, reset);
 589                 Content searchTxt = configuration.getContent("doclet.search");
 590                 HtmlTree liInput = HtmlTree.LI(HtmlTree.LABEL(searchValueId, searchTxt));
 591                 liInput.addContent(inputText);
 592                 liInput.addContent(inputReset);
 593                 HtmlTree ulSearch = HtmlTree.UL(HtmlStyle.navListSearch, liInput);
 594                 subDiv.addContent(ulSearch);
 595             }
 596             subDiv.addContent(getAllClassesLinkScript(allClassesId));
 597             addSummaryDetailLinks(subDiv);
 598             if (header) {
 599                 subDiv.addContent(links.createAnchor(SectionName.SKIP_NAVBAR_TOP));
 600                 fixedNavDiv.addContent(subDiv);
 601                 fixedNavDiv.addContent(HtmlConstants.END_OF_TOP_NAVBAR);
 602                 tree.addContent(fixedNavDiv);
 603                 HtmlTree paddingDiv = HtmlTree.DIV(HtmlStyle.navPadding, Contents.SPACE);
 604                 tree.addContent(paddingDiv);
 605                 Script script = new Script(
 606                         "<!--\n"
 607                         + "$('.navPadding').css('padding-top', $('.fixedNav').css(\"height\"));\n"
 608                         + "//-->\n");
 609                 tree.addContent(script.asContent());
 610             } else {
 611                 subDiv.addContent(links.createAnchor(SectionName.SKIP_NAVBAR_BOTTOM));
 612                 tree.addContent(subDiv);
 613                 tree.addContent(HtmlConstants.END_OF_BOTTOM_NAVBAR);
 614             }
 615             if (configuration.allowTag(HtmlTag.NAV)) {
 616                 htmlTree.addContent(tree);
 617             }
 618         }
 619     }
 620 
 621     /**
 622      * Get the word "NEXT" to indicate that no link is available.  Override
 623      * this method to customize next link.
 624      *
 625      * @return a content tree for the link
 626      */
 627     protected Content getNavLinkNext() {
 628         return getNavLinkNext(null);
 629     }
 630 
 631     /**
 632      * Get the word "PREV" to indicate that no link is available.  Override
 633      * this method to customize prev link.
 634      *
 635      * @return a content tree for the link
 636      */
 637     protected Content getNavLinkPrevious() {
 638         return getNavLinkPrevious(null);
 639     }
 640 
 641     /**
 642      * Do nothing. This is the default method.
 643      */
 644     protected void addSummaryDetailLinks(Content navDiv) {
 645     }
 646 
 647     /**
 648      * Get link to the "overview-summary.html" page.
 649      *
 650      * @return a content tree for the link
 651      */
 652     protected Content getNavLinkContents() {
 653         Content linkContent = Links.createLink(pathToRoot.resolve(DocPaths.overviewSummary(configuration.frames)),
 654                 contents.overviewLabel, "", "");
 655         Content li = HtmlTree.LI(linkContent);
 656         return li;
 657     }
 658 
 659     /**
 660      * Get link to the module summary page for the module passed.
 661      *
 662      * @param mdle Module to which link will be generated
 663      * @return a content tree for the link
 664      */
 665     protected Content getNavLinkModule(ModuleElement mdle) {
 666         Content linkContent = getModuleLink(mdle, contents.moduleLabel);
 667         Content li = HtmlTree.LI(linkContent);
 668         return li;
 669     }
 670 
 671     /**
 672      * Get the word "Module", to indicate that link is not available here.
 673      *
 674      * @return a content tree for the link
 675      */
 676     protected Content getNavLinkModule() {
 677         Content li = HtmlTree.LI(contents.moduleLabel);
 678         return li;
 679     }
 680 
 681     /**
 682      * Get link to the "package-summary.html" page for the package passed.
 683      *
 684      * @param pkg Package to which link will be generated
 685      * @return a content tree for the link
 686      */
 687     protected Content getNavLinkPackage(PackageElement pkg) {
 688         Content linkContent = getPackageLink(pkg, contents.packageLabel);
 689         Content li = HtmlTree.LI(linkContent);
 690         return li;
 691     }
 692 
 693     /**
 694      * Get the word "Package" , to indicate that link is not available here.
 695      *
 696      * @return a content tree for the link
 697      */
 698     protected Content getNavLinkPackage() {
 699         Content li = HtmlTree.LI(contents.packageLabel);
 700         return li;
 701     }
 702 
 703     /**
 704      * Get the word "Use", to indicate that link is not available.
 705      *
 706      * @return a content tree for the link
 707      */
 708     protected Content getNavLinkClassUse() {
 709         Content li = HtmlTree.LI(contents.useLabel);
 710         return li;
 711     }
 712 
 713     /**
 714      * Get link for previous file.
 715      *
 716      * @param prev File name for the prev link
 717      * @return a content tree for the link
 718      */
 719     public Content getNavLinkPrevious(DocPath prev) {
 720         Content li;
 721         if (prev != null) {
 722             li = HtmlTree.LI(Links.createLink(prev, contents.prevLabel, "", ""));
 723         }
 724         else
 725             li = HtmlTree.LI(contents.prevLabel);
 726         return li;
 727     }
 728 
 729     /**
 730      * Get link for next file.  If next is null, just print the label
 731      * without linking it anywhere.
 732      *
 733      * @param next File name for the next link
 734      * @return a content tree for the link
 735      */
 736     public Content getNavLinkNext(DocPath next) {
 737         Content li;
 738         if (next != null) {
 739             li = HtmlTree.LI(Links.createLink(next, contents.nextLabel, "", ""));
 740         }
 741         else
 742             li = HtmlTree.LI(contents.nextLabel);
 743         return li;
 744     }
 745 
 746     /**
 747      * Get "FRAMES" link, to switch to the frame version of the output.
 748      *
 749      * @param link File to be linked, "index.html"
 750      * @return a content tree for the link
 751      */
 752     protected Content getNavShowLists(DocPath link) {
 753         DocLink dl = new DocLink(link, path.getPath(), null);
 754         Content framesContent = Links.createLink(dl, contents.framesLabel, "", "_top");
 755         Content li = HtmlTree.LI(framesContent);
 756         return li;
 757     }
 758 
 759     /**
 760      * Get "FRAMES" link, to switch to the frame version of the output.
 761      *
 762      * @return a content tree for the link
 763      */
 764     protected Content getNavShowLists() {
 765         return getNavShowLists(pathToRoot.resolve(DocPaths.INDEX));
 766     }
 767 
 768     /**
 769      * Get "NO FRAMES" link, to switch to the non-frame version of the output.
 770      *
 771      * @param link File to be linked
 772      * @return a content tree for the link
 773      */
 774     protected Content getNavHideLists(DocPath link) {
 775         Content noFramesContent = Links.createLink(link, contents.noFramesLabel, "", "_top");
 776         Content li = HtmlTree.LI(noFramesContent);
 777         return li;
 778     }
 779 
 780     /**
 781      * Get "Tree" link in the navigation bar. If there is only one package
 782      * specified on the command line, then the "Tree" link will be to the
 783      * only "package-tree.html" file otherwise it will be to the
 784      * "overview-tree.html" file.
 785      *
 786      * @return a content tree for the link
 787      */
 788     protected Content getNavLinkTree() {
 789         List<PackageElement> packages = new ArrayList<>(configuration.getSpecifiedPackageElements());
 790         DocPath docPath = packages.size() == 1 && configuration.getSpecifiedTypeElements().isEmpty()
 791                 ? pathString(packages.get(0), DocPaths.PACKAGE_TREE)
 792                 : pathToRoot.resolve(DocPaths.OVERVIEW_TREE);
 793         return HtmlTree.LI(Links.createLink(docPath, contents.treeLabel, "", ""));
 794     }
 795 
 796     /**
 797      * Get the overview tree link for the main tree.
 798      *
 799      * @param label the label for the link
 800      * @return a content tree for the link
 801      */
 802     protected Content getNavLinkMainTree(String label) {
 803         Content mainTreeContent = Links.createLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE),
 804                 new StringContent(label));
 805         Content li = HtmlTree.LI(mainTreeContent);
 806         return li;
 807     }
 808 
 809     /**
 810      * Get the word "Class", to indicate that class link is not available.
 811      *
 812      * @return a content tree for the link
 813      */
 814     protected Content getNavLinkClass() {
 815         Content li = HtmlTree.LI(contents.classLabel);
 816         return li;
 817     }
 818 
 819     /**
 820      * Get "Deprecated" API link in the navigation bar.
 821      *
 822      * @return a content tree for the link
 823      */
 824     protected Content getNavLinkDeprecated() {
 825         Content linkContent = Links.createLink(pathToRoot.resolve(DocPaths.DEPRECATED_LIST),
 826                 contents.deprecatedLabel, "", "");
 827         Content li = HtmlTree.LI(linkContent);
 828         return li;
 829     }
 830 
 831     /**
 832      * Get link for generated index. If the user has used "-splitindex"
 833      * command line option, then link to file "index-files/index-1.html" is
 834      * generated otherwise link to file "index-all.html" is generated.
 835      *
 836      * @return a content tree for the link
 837      */
 838     protected Content getNavLinkClassIndex() {
 839         Content allClassesContent = Links.createLink(pathToRoot.resolve(
 840                 DocPaths.AllClasses(configuration.frames)),
 841                 contents.allClassesLabel, "", "");
 842         Content li = HtmlTree.LI(allClassesContent);
 843         return li;
 844     }
 845 
 846     /**
 847      * Get link for generated class index.
 848      *
 849      * @return a content tree for the link
 850      */
 851     protected Content getNavLinkIndex() {
 852         Content linkContent = Links.createLink(pathToRoot.resolve(
 853                 (configuration.splitindex
 854                     ? DocPaths.INDEX_FILES.resolve(DocPaths.indexN(1))
 855                     : DocPaths.INDEX_ALL)),
 856             contents.indexLabel, "", "");
 857         Content li = HtmlTree.LI(linkContent);
 858         return li;
 859     }
 860 
 861     /**
 862      * Get help file link. If user has provided a help file, then generate a
 863      * link to the user given file, which is already copied to current or
 864      * destination directory.
 865      *
 866      * @return a content tree for the link
 867      */
 868     protected Content getNavLinkHelp() {
 869         String helpfile = configuration.helpfile;
 870         DocPath helpfilenm;
 871         if (helpfile.isEmpty()) {
 872             helpfilenm = DocPaths.HELP_DOC;
 873         } else {
 874             DocFile file = DocFile.createFileForInput(configuration, helpfile);
 875             helpfilenm = DocPath.create(file.getName());
 876         }
 877         Content linkContent = Links.createLink(pathToRoot.resolve(helpfilenm),
 878                 contents.helpLabel, "", "");
 879         Content li = HtmlTree.LI(linkContent);
 880         return li;
 881     }
 882 
 883     /**
 884      * Add gap between navigation bar elements.
 885      *
 886      * @param liNav the content tree to which the gap will be added
 887      */
 888     protected void addNavGap(Content liNav) {
 889         liNav.addContent(Contents.SPACE);
 890         liNav.addContent("|");
 891         liNav.addContent(Contents.SPACE);
 892     }
 893 
 894     /**
 895      * Get table caption.
 896      *
 897      * @param title the content for the caption
 898      * @return a content tree for the caption
 899      */
 900     public Content getTableCaption(Content title) {
 901         Content captionSpan = HtmlTree.SPAN(title);
 902         Content space = Contents.SPACE;
 903         Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space);
 904         Content caption = HtmlTree.CAPTION(captionSpan);
 905         caption.addContent(tabSpan);
 906         return caption;
 907     }
 908 
 909     /**
 910      * Returns a packagename content.
 911      *
 912      * @param packageElement the package to check
 913      * @return package name content
 914      */
 915     public Content getPackageName(PackageElement packageElement) {
 916         return packageElement == null || packageElement.isUnnamed()
 917                 ? contents.defaultPackageLabel
 918                 : getPackageLabel(packageElement.getQualifiedName());
 919     }
 920 
 921     /**
 922      * Returns a package name label.
 923      *
 924      * @param packageName the package name
 925      * @return the package name content
 926      */
 927     public Content getPackageLabel(CharSequence packageName) {
 928         return new StringContent(packageName);
 929     }
 930 
 931     /**
 932      * Return the path to the class page for a typeElement.
 933      *
 934      * @param te   TypeElement for which the path is requested.
 935      * @param name Name of the file(doesn't include path).
 936      */
 937     protected DocPath pathString(TypeElement te, DocPath name) {
 938         return pathString(utils.containingPackage(te), name);
 939     }
 940 
 941     /**
 942      * Return path to the given file name in the given package. So if the name
 943      * passed is "Object.html" and the name of the package is "java.lang", and
 944      * if the relative path is "../.." then returned string will be
 945      * "../../java/lang/Object.html"
 946      *
 947      * @param packageElement Package in which the file name is assumed to be.
 948      * @param name File name, to which path string is.
 949      */
 950     protected DocPath pathString(PackageElement packageElement, DocPath name) {
 951         return pathToRoot.resolve(DocPath.forPackage(packageElement).resolve(name));
 952     }
 953 
 954     /**
 955      * Given a package, return the name to be used in HTML anchor tag.
 956      * @param packageElement the package.
 957      * @return the name to be used in HTML anchor tag.
 958      */
 959     public String getPackageAnchorName(PackageElement packageElement) {
 960         return packageElement == null || packageElement.isUnnamed()
 961                 ? SectionName.UNNAMED_PACKAGE_ANCHOR.getName()
 962                 : utils.getPackageName(packageElement);
 963     }
 964 
 965     /**
 966      * Return the link to the given package.
 967      *
 968      * @param packageElement the package to link to.
 969      * @param label the label for the link.
 970      * @return a content tree for the package link.
 971      */
 972     public Content getPackageLink(PackageElement packageElement, CharSequence label) {
 973         return getPackageLink(packageElement, new StringContent(label));
 974     }
 975 
 976     public Content getPackageLink(PackageElement packageElement) {
 977         StringContent content =  packageElement.isUnnamed()
 978                 ? new StringContent()
 979                 : new StringContent(utils.getPackageName(packageElement));
 980         return getPackageLink(packageElement, content);
 981     }
 982 
 983     /**
 984      * Return the link to the given package.
 985      *
 986      * @param packageElement the package to link to.
 987      * @param label the label for the link.
 988      * @return a content tree for the package link.
 989      */
 990     public Content getPackageLink(PackageElement packageElement, Content label) {
 991         boolean included = packageElement != null && utils.isIncluded(packageElement);
 992         if (!included) {
 993             for (PackageElement p : configuration.packages) {
 994                 if (p.equals(packageElement)) {
 995                     included = true;
 996                     break;
 997                 }
 998             }
 999         }
1000         if (included || packageElement == null) {
1001             return Links.createLink(pathString(packageElement, DocPaths.PACKAGE_SUMMARY),
1002                     label);
1003         } else {
1004             DocLink crossPkgLink = getCrossPackageLink(utils.getPackageName(packageElement));
1005             if (crossPkgLink != null) {
1006                 return Links.createLink(crossPkgLink, label);
1007             } else {
1008                 return label;
1009             }
1010         }
1011     }
1012 
1013     /**
1014      * Get Module link.
1015      *
1016      * @param mdle the module being documented
1017      * @param label tag for the link
1018      * @return a content for the module link
1019      */
1020     public Content getModuleLink(ModuleElement mdle, Content label) {
1021         boolean included = utils.isIncluded(mdle);
1022         return (included)
1023                 ? Links.createLink(pathToRoot.resolve(DocPaths.moduleSummary(mdle)), label, "", "")
1024                 : label;
1025     }
1026 
1027     public Content interfaceName(TypeElement typeElement, boolean qual) {
1028         Content name = new StringContent((qual)
1029                 ? typeElement.getQualifiedName()
1030                 : utils.getSimpleName(typeElement));
1031         return (utils.isInterface(typeElement)) ?  HtmlTree.SPAN(HtmlStyle.interfaceName, name) : name;
1032     }
1033 
1034     /**
1035      * Add the link to the content tree.
1036      *
1037      * @param typeElement program element typeElement for which the link will be added
1038      * @param label label for the link
1039      * @param htmltree the content tree to which the link will be added
1040      */
1041     public void addSrcLink(Element typeElement, Content label, Content htmltree) {
1042         if (typeElement == null) {
1043             return;
1044         }
1045         TypeElement te = utils.getEnclosingTypeElement(typeElement);
1046         if (te == null) {
1047             // must be a typeElement since in has no containing class.
1048             te = (TypeElement) typeElement;
1049         }
1050         DocPath href = pathToRoot
1051                 .resolve(DocPaths.SOURCE_OUTPUT)
1052                 .resolve(DocPath.forClass(utils, te));
1053         Content linkContent = Links.createLink(href
1054                 .fragment(SourceToHTMLConverter.getAnchorName(utils, typeElement)), label, "", "");
1055         htmltree.addContent(linkContent);
1056     }
1057 
1058     /**
1059      * Return the link to the given class.
1060      *
1061      * @param linkInfo the information about the link.
1062      *
1063      * @return the link for the given class.
1064      */
1065     public Content getLink(LinkInfoImpl linkInfo) {
1066         LinkFactoryImpl factory = new LinkFactoryImpl(this);
1067         return factory.getLink(linkInfo);
1068     }
1069 
1070     /**
1071      * Return the type parameters for the given class.
1072      *
1073      * @param linkInfo the information about the link.
1074      * @return the type for the given class.
1075      */
1076     public Content getTypeParameterLinks(LinkInfoImpl linkInfo) {
1077         LinkFactoryImpl factory = new LinkFactoryImpl(this);
1078         return factory.getTypeParameterLinks(linkInfo, false);
1079     }
1080 
1081     /*************************************************************
1082      * Return a class cross link to external class documentation.
1083      * The name must be fully qualified to determine which package
1084      * the class is in.  The -link option does not allow users to
1085      * link to external classes in the "default" package.
1086      *
1087      * @param qualifiedClassName the qualified name of the external class.
1088      * @param refMemName the name of the member being referenced.  This should
1089      * be null or empty string if no member is being referenced.
1090      * @param label the label for the external link.
1091      * @param strong true if the link should be strong.
1092      * @param style the style of the link.
1093      * @param code true if the label should be code font.
1094      */
1095     public Content getCrossClassLink(String qualifiedClassName, String refMemName,
1096                                     Content label, boolean strong, String style,
1097                                     boolean code) {
1098         String className = "";
1099         String packageName = qualifiedClassName == null ? "" : qualifiedClassName;
1100         int periodIndex;
1101         while ((periodIndex = packageName.lastIndexOf('.')) != -1) {
1102             className = packageName.substring(periodIndex + 1, packageName.length()) +
1103                 (className.length() > 0 ? "." + className : "");
1104             Content defaultLabel = new StringContent(className);
1105             if (code)
1106                 defaultLabel = HtmlTree.CODE(defaultLabel);
1107             packageName = packageName.substring(0, periodIndex);
1108             if (getCrossPackageLink(packageName) != null) {
1109                 /*
1110                 The package exists in external documentation, so link to the external
1111                 class (assuming that it exists).  This is definitely a limitation of
1112                 the -link option.  There are ways to determine if an external package
1113                 exists, but no way to determine if the external class exists.  We just
1114                 have to assume that it does.
1115                 */
1116                 DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot,
1117                                 className + ".html", refMemName);
1118                 return Links.createLink(link,
1119                     (label == null) || label.isEmpty() ? defaultLabel : label,
1120                     strong, style,
1121                     configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName),
1122                     "");
1123             }
1124         }
1125         return null;
1126     }
1127 
1128     public boolean isClassLinkable(TypeElement typeElement) {
1129         if (utils.isIncluded(typeElement)) {
1130             return configuration.isGeneratedDoc(typeElement);
1131         }
1132         return configuration.extern.isExternal(typeElement);
1133     }
1134 
1135     public DocLink getCrossPackageLink(String pkgName) {
1136         return configuration.extern.getExternalLink(pkgName, pathToRoot,
1137             DocPaths.PACKAGE_SUMMARY.getPath());
1138     }
1139 
1140     /**
1141      * Get the class link.
1142      *
1143      * @param context the id of the context where the link will be added
1144      * @param element to link to
1145      * @return a content tree for the link
1146      */
1147     public Content getQualifiedClassLink(LinkInfoImpl.Kind context, Element element) {
1148         LinkInfoImpl linkInfoImpl = new LinkInfoImpl(configuration, context, (TypeElement)element);
1149         return getLink(linkInfoImpl.label(utils.getFullyQualifiedName(element)));
1150     }
1151 
1152     /**
1153      * Add the class link.
1154      *
1155      * @param context the id of the context where the link will be added
1156      * @param typeElement to link to
1157      * @param contentTree the content tree to which the link will be added
1158      */
1159     public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) {
1160         addPreQualifiedClassLink(context, typeElement, false, contentTree);
1161     }
1162 
1163     /**
1164      * Retrieve the class link with the package portion of the label in
1165      * plain text.  If the qualifier is excluded, it will not be included in the
1166      * link label.
1167      *
1168      * @param typeElement the class to link to.
1169      * @param isStrong true if the link should be strong.
1170      * @return the link with the package portion of the label in plain text.
1171      */
1172     public Content getPreQualifiedClassLink(LinkInfoImpl.Kind context,
1173             TypeElement typeElement, boolean isStrong) {
1174         ContentBuilder classlink = new ContentBuilder();
1175         PackageElement pkg = utils.containingPackage(typeElement);
1176         if (pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) {
1177             classlink.addContent(getEnclosingPackageName(typeElement));
1178         }
1179         classlink.addContent(getLink(new LinkInfoImpl(configuration,
1180                 context, typeElement).label(utils.getSimpleName(typeElement)).strong(isStrong)));
1181         return classlink;
1182     }
1183 
1184     /**
1185      * Add the class link with the package portion of the label in
1186      * plain text. If the qualifier is excluded, it will not be included in the
1187      * link label.
1188      *
1189      * @param context the id of the context where the link will be added
1190      * @param typeElement the class to link to
1191      * @param isStrong true if the link should be strong
1192      * @param contentTree the content tree to which the link with be added
1193      */
1194     public void addPreQualifiedClassLink(LinkInfoImpl.Kind context,
1195             TypeElement typeElement, boolean isStrong, Content contentTree) {
1196         PackageElement pkg = utils.containingPackage(typeElement);
1197         if(pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) {
1198             contentTree.addContent(getEnclosingPackageName(typeElement));
1199         }
1200         LinkInfoImpl linkinfo = new LinkInfoImpl(configuration, context, typeElement)
1201                 .label(utils.getSimpleName(typeElement))
1202                 .strong(isStrong);
1203         Content link = getLink(linkinfo);
1204         contentTree.addContent(link);
1205     }
1206 
1207     /**
1208      * Add the class link, with only class name as the strong link and prefixing
1209      * plain package name.
1210      *
1211      * @param context the id of the context where the link will be added
1212      * @param typeElement the class to link to
1213      * @param contentTree the content tree to which the link with be added
1214      */
1215     public void addPreQualifiedStrongClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) {
1216         addPreQualifiedClassLink(context, typeElement, true, contentTree);
1217     }
1218 
1219     /**
1220      * Get the link for the given member.
1221      *
1222      * @param context the id of the context where the link will be added
1223      * @param element the member being linked to
1224      * @param label the label for the link
1225      * @return a content tree for the element link
1226      */
1227     public Content getDocLink(LinkInfoImpl.Kind context, Element element, CharSequence label) {
1228         return getDocLink(context, utils.getEnclosingTypeElement(element), element,
1229                 new StringContent(label));
1230     }
1231 
1232     /**
1233      * Return the link for the given member.
1234      *
1235      * @param context the id of the context where the link will be printed.
1236      * @param element the member being linked to.
1237      * @param label the label for the link.
1238      * @param strong true if the link should be strong.
1239      * @return the link for the given member.
1240      */
1241     public Content getDocLink(LinkInfoImpl.Kind context, Element element, CharSequence label,
1242             boolean strong) {
1243         return getDocLink(context, utils.getEnclosingTypeElement(element), element, label, strong);
1244     }
1245 
1246     /**
1247      * Return the link for the given member.
1248      *
1249      * @param context the id of the context where the link will be printed.
1250      * @param typeElement the typeElement that we should link to.  This is not
1251                  necessarily equal to element.containingClass().  We may be
1252                  inheriting comments.
1253      * @param element the member being linked to.
1254      * @param label the label for the link.
1255      * @param strong true if the link should be strong.
1256      * @return the link for the given member.
1257      */
1258     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1259             CharSequence label, boolean strong) {
1260         return getDocLink(context, typeElement, element, label, strong, false);
1261     }
1262 
1263     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1264             Content label, boolean strong) {
1265         return getDocLink(context, typeElement, element, label, strong, false);
1266     }
1267 
1268    /**
1269      * Return the link for the given member.
1270      *
1271      * @param context the id of the context where the link will be printed.
1272      * @param typeElement the typeElement that we should link to.  This is not
1273                  necessarily equal to element.containingClass().  We may be
1274                  inheriting comments.
1275      * @param element the member being linked to.
1276      * @param label the label for the link.
1277      * @param strong true if the link should be strong.
1278      * @param isProperty true if the element parameter is a JavaFX property.
1279      * @return the link for the given member.
1280      */
1281     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1282             CharSequence label, boolean strong, boolean isProperty) {
1283         return getDocLink(context, typeElement, element, new StringContent(check(label)), strong, isProperty);
1284     }
1285 
1286     CharSequence check(CharSequence s) {
1287         Matcher m = IMPROPER_HTML_CHARS.matcher(s);
1288         if (m.matches()) {
1289             throw new IllegalArgumentException(s.toString());
1290         }
1291         return s;
1292     }
1293 
1294     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1295             Content label, boolean strong, boolean isProperty) {
1296         if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) {
1297             return label;
1298         } else if (utils.isExecutableElement(element)) {
1299             ExecutableElement ee = (ExecutableElement)element;
1300             return getLink(new LinkInfoImpl(configuration, context, typeElement)
1301                 .label(label)
1302                 .where(links.getName(getAnchor(ee, isProperty)))
1303                 .strong(strong));
1304         } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) {
1305             return getLink(new LinkInfoImpl(configuration, context, typeElement)
1306                 .label(label)
1307                 .where(links.getName(element.getSimpleName().toString()))
1308                 .strong(strong));
1309         } else {
1310             return label;
1311         }
1312     }
1313 
1314     /**
1315      * Return the link for the given member.
1316      *
1317      * @param context the id of the context where the link will be added
1318      * @param typeElement the typeElement that we should link to.  This is not
1319                  necessarily equal to element.containingClass().  We may be
1320                  inheriting comments
1321      * @param element the member being linked to
1322      * @param label the label for the link
1323      * @return the link for the given member
1324      */
1325     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1326             Content label) {
1327         if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) {
1328             return label;
1329         } else if (utils.isExecutableElement(element)) {
1330             ExecutableElement emd = (ExecutableElement) element;
1331             return getLink(new LinkInfoImpl(configuration, context, typeElement)
1332                 .label(label)
1333                 .where(links.getName(getAnchor(emd))));
1334         } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) {
1335             return getLink(new LinkInfoImpl(configuration, context, typeElement)
1336                 .label(label).where(links.getName(element.getSimpleName().toString())));
1337         } else {
1338             return label;
1339         }
1340     }
1341 
1342     public String getAnchor(ExecutableElement executableElement) {
1343         return getAnchor(executableElement, false);
1344     }
1345 
1346     public String getAnchor(ExecutableElement executableElement, boolean isProperty) {
1347         if (isProperty) {
1348             return executableElement.getSimpleName().toString();
1349         }
1350         String member = anchorName(executableElement);
1351         String erasedSignature = utils.makeSignature(executableElement, true, true);
1352         return member + erasedSignature;
1353     }
1354 
1355     public String anchorName(Element member) {
1356         if (member.getKind() == ElementKind.CONSTRUCTOR
1357                 && configuration.isOutputHtml5()) {
1358             return "<init>";
1359         } else {
1360             return utils.getSimpleName(member);
1361         }
1362     }
1363 
1364     public Content seeTagToContent(Element element, DocTree see) {
1365 
1366         Kind kind = see.getKind();
1367         if (!(kind == LINK || kind == SEE || kind == LINK_PLAIN)) {
1368             return new ContentBuilder();
1369         }
1370 
1371         CommentHelper ch = utils.getCommentHelper(element);
1372         String tagName = ch.getTagName(see);
1373         String seetext = replaceDocRootDir(utils.normalizeNewlines(ch.getText(see)).toString());
1374         // Check if @see is an href or "string"
1375         if (seetext.startsWith("<") || seetext.startsWith("\"")) {
1376             return new RawHtml(seetext);
1377         }
1378         boolean isLinkPlain = kind == LINK_PLAIN;
1379         Content label = plainOrCode(isLinkPlain, new RawHtml(ch.getLabel(configuration, see)));
1380 
1381         //The text from the @see tag.  We will output this text when a label is not specified.
1382         Content text = plainOrCode(kind == LINK_PLAIN, new RawHtml(seetext));
1383 
1384         TypeElement refClass = ch.getReferencedClass(configuration, see);
1385         String refClassName =  ch.getReferencedClassName(configuration, see);
1386         Element refMem =       ch.getReferencedMember(configuration, see);
1387         String refMemName =    ch.getReferencedMemberName(see);
1388 
1389         if (refMemName == null && refMem != null) {
1390             refMemName = refMem.toString();
1391         }
1392         if (refClass == null) {
1393             //@see is not referencing an included class
1394             PackageElement refPackage = ch.getReferencedPackage(configuration, see);
1395             if (refPackage != null && utils.isIncluded(refPackage)) {
1396                 //@see is referencing an included package
1397                 if (label.isEmpty())
1398                     label = plainOrCode(isLinkPlain,
1399                             new StringContent(refPackage.getQualifiedName()));
1400                 return getPackageLink(refPackage, label);
1401             } else {
1402                 // @see is not referencing an included class or package.  Check for cross links.
1403                 Content classCrossLink;
1404                 DocLink packageCrossLink = getCrossPackageLink(refClassName);
1405                 if (packageCrossLink != null) {
1406                     // Package cross link found
1407                     return Links.createLink(packageCrossLink,
1408                         (label.isEmpty() ? text : label));
1409                 } else if ((classCrossLink = getCrossClassLink(refClassName,
1410                         refMemName, label, false, "", !isLinkPlain)) != null) {
1411                     // Class cross link found (possibly to a member in the class)
1412                     return classCrossLink;
1413                 } else {
1414                     // No cross link found so print warning
1415                     messages.warning(ch.getDocTreePath(see),
1416                             "doclet.see.class_or_package_not_found",
1417                             "@" + tagName,
1418                             seetext);
1419                     return (label.isEmpty() ? text: label);
1420                 }
1421             }
1422         } else if (refMemName == null) {
1423             // Must be a class reference since refClass is not null and refMemName is null.
1424             if (label.isEmpty()) {
1425                 /*
1426                  * it seems to me this is the right thing to do, but it causes comparator failures.
1427                  */
1428                 if (!configuration.backwardCompatibility) {
1429                     StringContent content = utils.isEnclosingPackageIncluded(refClass)
1430                             ? new StringContent(utils.getSimpleName(refClass))
1431                             : new StringContent(utils.getFullyQualifiedName(refClass));
1432                     label = plainOrCode(isLinkPlain, content);
1433                 } else {
1434                     label = plainOrCode(isLinkPlain,
1435                             new StringContent(utils.getSimpleName(refClass)));
1436                 }
1437 
1438             }
1439             return getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, refClass)
1440                     .label(label));
1441         } else if (refMem == null) {
1442             // Must be a member reference since refClass is not null and refMemName is not null.
1443             // However, refMem is null, so this referenced member does not exist.
1444             return (label.isEmpty() ? text: label);
1445         } else {
1446             // Must be a member reference since refClass is not null and refMemName is not null.
1447             // refMem is not null, so this @see tag must be referencing a valid member.
1448             TypeElement containing = utils.getEnclosingTypeElement(refMem);
1449 
1450             // Find the enclosing type where the method is actually visible
1451             // in the inheritance hierarchy.
1452             if (refMem.getKind() == ElementKind.METHOD) {
1453                 VisibleMemberMap vmm = configuration.getVisibleMemberMap(containing,
1454                         VisibleMemberMap.Kind.METHODS);
1455                 ExecutableElement overriddenMethod = vmm.getVisibleMethod((ExecutableElement)refMem);
1456                 if (overriddenMethod != null)
1457                     containing = utils.getEnclosingTypeElement(overriddenMethod);
1458             }
1459             if (ch.getText(see).trim().startsWith("#") &&
1460                 ! (utils.isPublic(containing) || utils.isLinkable(containing))) {
1461                 // Since the link is relative and the holder is not even being
1462                 // documented, this must be an inherited link.  Redirect it.
1463                 // The current class either overrides the referenced member or
1464                 // inherits it automatically.
1465                 if (this instanceof ClassWriterImpl) {
1466                     containing = ((ClassWriterImpl) this).getTypeElement();
1467                 } else if (!utils.isPublic(containing)) {
1468                     messages.warning(
1469                         ch.getDocTreePath(see), "doclet.see.class_or_package_not_accessible",
1470                         tagName, utils.getFullyQualifiedName(containing));
1471                 } else {
1472                     messages.warning(
1473                         ch.getDocTreePath(see), "doclet.see.class_or_package_not_found",
1474                         tagName, seetext);
1475                 }
1476             }
1477             if (configuration.currentTypeElement != containing) {
1478                 refMemName = (utils.isConstructor(refMem))
1479                         ? refMemName
1480                         : utils.getSimpleName(containing) + "." + refMemName;
1481             }
1482             if (utils.isExecutableElement(refMem)) {
1483                 if (refMemName.indexOf('(') < 0) {
1484                     refMemName += utils.makeSignature((ExecutableElement)refMem, true);
1485                 }
1486             }
1487 
1488             text = plainOrCode(kind == LINK_PLAIN, new StringContent(refMemName));
1489 
1490             return getDocLink(LinkInfoImpl.Kind.SEE_TAG, containing,
1491                     refMem, (label.isEmpty() ? text: label), false);
1492         }
1493     }
1494 
1495     private Content plainOrCode(boolean plain, Content body) {
1496         return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body);
1497     }
1498 
1499     /**
1500      * Add the inline comment.
1501      *
1502      * @param element the Element for which the inline comment will be added
1503      * @param tag the inline tag to be added
1504      * @param htmltree the content tree to which the comment will be added
1505      */
1506     public void addInlineComment(Element element, DocTree tag, Content htmltree) {
1507         CommentHelper ch = utils.getCommentHelper(element);
1508         List<? extends DocTree> description = ch.getDescription(configuration, tag);
1509         addCommentTags(element, tag, description, false, false, htmltree);
1510     }
1511 
1512     /**
1513      * Get the deprecated phrase as content.
1514      *
1515      * @param e the Element for which the inline deprecated comment will be added
1516      * @return a content tree for the deprecated phrase.
1517      */
1518     public Content getDeprecatedPhrase(Element e) {
1519         return (utils.isDeprecatedForRemoval(e))
1520                 ? contents.deprecatedForRemovalPhrase
1521                 : contents.deprecatedPhrase;
1522     }
1523 
1524     /**
1525      * Add the inline deprecated comment.
1526      *
1527      * @param e the Element for which the inline deprecated comment will be added
1528      * @param tag the inline tag to be added
1529      * @param htmltree the content tree to which the comment will be added
1530      */
1531     public void addInlineDeprecatedComment(Element e, DocTree tag, Content htmltree) {
1532         CommentHelper ch = utils.getCommentHelper(e);
1533         addCommentTags(e, ch.getBody(configuration, tag), true, false, htmltree);
1534     }
1535 
1536     /**
1537      * Adds the summary content.
1538      *
1539      * @param element the Element for which the summary will be generated
1540      * @param htmltree the documentation tree to which the summary will be added
1541      */
1542     public void addSummaryComment(Element element, Content htmltree) {
1543         addSummaryComment(element, utils.getFirstSentenceTrees(element), htmltree);
1544     }
1545 
1546     /**
1547      * Adds the summary content.
1548      *
1549      * @param element the Element for which the summary will be generated
1550      * @param firstSentenceTags the first sentence tags for the doc
1551      * @param htmltree the documentation tree to which the summary will be added
1552      */
1553     public void addSummaryComment(Element element, List<? extends DocTree> firstSentenceTags, Content htmltree) {
1554         addCommentTags(element, firstSentenceTags, false, true, htmltree);
1555     }
1556 
1557     public void addSummaryDeprecatedComment(Element element, DocTree tag, Content htmltree) {
1558         CommentHelper ch = utils.getCommentHelper(element);
1559         List<? extends DocTree> body = ch.getBody(configuration, tag);
1560         addCommentTags(element, ch.getFirstSentenceTrees(configuration, body), true, true, htmltree);
1561     }
1562 
1563     /**
1564      * Adds the inline comment.
1565      *
1566      * @param element the Element for which the inline comments will be generated
1567      * @param htmltree the documentation tree to which the inline comments will be added
1568      */
1569     public void addInlineComment(Element element, Content htmltree) {
1570         addCommentTags(element, utils.getFullBody(element), false, false, htmltree);
1571     }
1572 
1573     /**
1574      * Adds the comment tags.
1575      *
1576      * @param element the Element for which the comment tags will be generated
1577      * @param tags the first sentence tags for the doc
1578      * @param depr true if it is deprecated
1579      * @param first true if the first sentence tags should be added
1580      * @param htmltree the documentation tree to which the comment tags will be added
1581      */
1582     private void addCommentTags(Element element, List<? extends DocTree> tags, boolean depr,
1583             boolean first, Content htmltree) {
1584         addCommentTags(element, null, tags, depr, first, htmltree);
1585     }
1586 
1587     /**
1588      * Adds the comment tags.
1589      *
1590      * @param element for which the comment tags will be generated
1591      * @param holderTag the block tag context for the inline tags
1592      * @param tags the first sentence tags for the doc
1593      * @param depr true if it is deprecated
1594      * @param first true if the first sentence tags should be added
1595      * @param htmltree the documentation tree to which the comment tags will be added
1596      */
1597     private void addCommentTags(Element element, DocTree holderTag, List<? extends DocTree> tags, boolean depr,
1598             boolean first, Content htmltree) {
1599         if(configuration.nocomment){
1600             return;
1601         }
1602         Content div;
1603         Content result = commentTagsToContent(null, element, tags, first);
1604         if (depr) {
1605             div = HtmlTree.DIV(HtmlStyle.deprecationComment, result);
1606             htmltree.addContent(div);
1607         }
1608         else {
1609             div = HtmlTree.DIV(HtmlStyle.block, result);
1610             htmltree.addContent(div);
1611         }
1612         if (tags.isEmpty()) {
1613             htmltree.addContent(Contents.SPACE);
1614         }
1615     }
1616 
1617     boolean ignoreNonInlineTag(DocTree dtree) {
1618         Name name = null;
1619         if (dtree.getKind() == Kind.START_ELEMENT) {
1620             StartElementTree setree = (StartElementTree)dtree;
1621             name = setree.getName();
1622         } else if (dtree.getKind() == Kind.END_ELEMENT) {
1623             EndElementTree eetree = (EndElementTree)dtree;
1624             name = eetree.getName();
1625         }
1626 
1627         if (name != null) {
1628             com.sun.tools.doclint.HtmlTag htmlTag = com.sun.tools.doclint.HtmlTag.get(name);
1629             if (htmlTag != null &&
1630                     htmlTag.blockType != com.sun.tools.doclint.HtmlTag.BlockType.INLINE) {
1631                 return true;
1632             }
1633         }
1634         return false;
1635     }
1636 
1637     boolean isAllWhiteSpace(String body) {
1638         for (int i = 0 ; i < body.length(); i++) {
1639             if (!Character.isWhitespace(body.charAt(i)))
1640                 return false;
1641         }
1642         return true;
1643     }
1644 
1645     // Notify the next DocTree handler to take necessary action
1646     private boolean commentRemoved = false;
1647 
1648     /**
1649      * Converts inline tags and text to text strings, expanding the
1650      * inline tags along the way.  Called wherever text can contain
1651      * an inline tag, such as in comments or in free-form text arguments
1652      * to non-inline tags.
1653      *
1654      * @param holderTag    specific tag where comment resides
1655      * @param element    specific element where comment resides
1656      * @param tags   array of text tags and inline tags (often alternating)
1657                present in the text of interest for this element
1658      * @param isFirstSentence  true if text is first sentence
1659      * @return a Content object
1660      */
1661     public Content commentTagsToContent(DocTree holderTag, Element element,
1662             List<? extends DocTree> tags, boolean isFirstSentence) {
1663 
1664         final Content result = new ContentBuilder() {
1665             @Override
1666             public void addContent(CharSequence text) {
1667                 super.addContent(utils.normalizeNewlines(text));
1668             }
1669         };
1670         CommentHelper ch = utils.getCommentHelper(element);
1671         // Array of all possible inline tags for this javadoc run
1672         configuration.tagletManager.checkTags(utils, element, tags, true);
1673         commentRemoved = false;
1674 
1675         for (ListIterator<? extends DocTree> iterator = tags.listIterator(); iterator.hasNext();) {
1676             boolean isFirstNode = !iterator.hasPrevious();
1677             DocTree tag = iterator.next();
1678             boolean isLastNode  = !iterator.hasNext();
1679 
1680             if (isFirstSentence) {
1681                 // Ignore block tags
1682                 if (ignoreNonInlineTag(tag))
1683                     continue;
1684 
1685                 // Ignore any trailing whitespace OR whitespace after removed html comment
1686                 if ((isLastNode || commentRemoved)
1687                         && tag.getKind() == TEXT
1688                         && isAllWhiteSpace(ch.getText(tag)))
1689                     continue;
1690 
1691                 // Ignore any leading html comments
1692                 if ((isFirstNode || commentRemoved) && tag.getKind() == COMMENT) {
1693                     commentRemoved = true;
1694                     continue;
1695                 }
1696             }
1697 
1698             boolean allDone = new SimpleDocTreeVisitor<Boolean, Content>() {
1699 
1700                 private boolean inAnAtag() {
1701                     if (utils.isStartElement(tag)) {
1702                         StartElementTree st = (StartElementTree)tag;
1703                         Name name = st.getName();
1704                         if (name != null) {
1705                             com.sun.tools.doclint.HtmlTag htag =
1706                                     com.sun.tools.doclint.HtmlTag.get(name);
1707                             return htag != null && htag.equals(com.sun.tools.doclint.HtmlTag.A);
1708                         }
1709                     }
1710                     return false;
1711                 }
1712 
1713                 @Override
1714                 public Boolean visitAttribute(AttributeTree node, Content c) {
1715                     StringBuilder sb = new StringBuilder(SPACER).append(node.getName());
1716                     if (node.getValueKind() == ValueKind.EMPTY) {
1717                         result.addContent(sb);
1718                         return false;
1719                     }
1720                     sb.append("=");
1721                     String quote;
1722                     switch (node.getValueKind()) {
1723                         case DOUBLE:
1724                             quote = "\"";
1725                             break;
1726                         case SINGLE:
1727                             quote = "\'";
1728                             break;
1729                         default:
1730                             quote = "";
1731                             break;
1732                     }
1733                     sb.append(quote);
1734                     result.addContent(sb);
1735                     Content docRootContent = new ContentBuilder();
1736 
1737                     for (DocTree dt : node.getValue()) {
1738                         if (utils.isText(dt) && inAnAtag()) {
1739                             String text = ((TextTree) dt).getBody();
1740                             if (text.startsWith("/..") && !configuration.docrootparent.isEmpty()) {
1741                                 result.addContent(configuration.docrootparent);
1742                                 docRootContent = new ContentBuilder();
1743                                 result.addContent(textCleanup(text.substring(3), isLastNode));
1744                             } else {
1745                                 if (!docRootContent.isEmpty()) {
1746                                     docRootContent = copyDocRootContent(docRootContent);
1747                                 } else {
1748                                     text = redirectRelativeLinks(element, (TextTree) dt);
1749                                 }
1750                                 result.addContent(textCleanup(text, isLastNode));
1751                             }
1752                         } else {
1753                             docRootContent = copyDocRootContent(docRootContent);
1754                             dt.accept(this, docRootContent);
1755                         }
1756                     }
1757                     copyDocRootContent(docRootContent);
1758                     result.addContent(quote);
1759                     return false;
1760                 }
1761 
1762                 @Override
1763                 public Boolean visitComment(CommentTree node, Content c) {
1764                     result.addContent(new RawHtml(node.getBody()));
1765                     return false;
1766                 }
1767 
1768                 private Content copyDocRootContent(Content content) {
1769                     if (!content.isEmpty()) {
1770                         result.addContent(content);
1771                         return new ContentBuilder();
1772                     }
1773                     return content;
1774                 }
1775 
1776                 @Override
1777                 public Boolean visitDocRoot(DocRootTree node, Content c) {
1778                     Content docRootContent = TagletWriter.getInlineTagOutput(element,
1779                             configuration.tagletManager,
1780                             holderTag,
1781                             node,
1782                             getTagletWriterInstance(isFirstSentence));
1783                     if (c != null) {
1784                         c.addContent(docRootContent);
1785                     } else {
1786                         result.addContent(docRootContent);
1787                     }
1788                     return false;
1789                 }
1790 
1791                 @Override
1792                 public Boolean visitEndElement(EndElementTree node, Content c) {
1793                     RawHtml rawHtml = new RawHtml("</" + node.getName() + ">");
1794                     result.addContent(rawHtml);
1795                     return false;
1796                 }
1797 
1798                 @Override
1799                 public Boolean visitEntity(EntityTree node, Content c) {
1800                     result.addContent(new RawHtml(node.toString()));
1801                     return false;
1802                 }
1803 
1804                 @Override
1805                 public Boolean visitErroneous(ErroneousTree node, Content c) {
1806                     messages.warning(ch.getDocTreePath(node),
1807                             "doclet.tag.invalid_usage", node);
1808                     result.addContent(new RawHtml(node.toString()));
1809                     return false;
1810                 }
1811 
1812                 @Override
1813                 public Boolean visitInheritDoc(InheritDocTree node, Content c) {
1814                     Content output = TagletWriter.getInlineTagOutput(element,
1815                             configuration.tagletManager, holderTag,
1816                             tag, getTagletWriterInstance(isFirstSentence));
1817                     result.addContent(output);
1818                     // if we obtained the first sentence successfully, nothing more to do
1819                     return (isFirstSentence && !output.isEmpty());
1820                 }
1821 
1822                 @Override
1823                 public Boolean visitIndex(IndexTree node, Content p) {
1824                     Content output = TagletWriter.getInlineTagOutput(element,
1825                             configuration.tagletManager, holderTag, tag,
1826                             getTagletWriterInstance(isFirstSentence));
1827                     if (output != null) {
1828                         result.addContent(output);
1829                     }
1830                     return false;
1831                 }
1832 
1833                 @Override
1834                 public Boolean visitLink(LinkTree node, Content c) {
1835                     // we need to pass the DocTreeImpl here, so ignore node
1836                     result.addContent(seeTagToContent(element, tag));
1837                     return false;
1838                 }
1839 
1840                 @Override
1841                 public Boolean visitLiteral(LiteralTree node, Content c) {
1842                     String s = node.getBody().toString();
1843                     Content content = new StringContent(utils.normalizeNewlines(s));
1844                     if (node.getKind() == CODE)
1845                         content = HtmlTree.CODE(content);
1846                     result.addContent(content);
1847                     return false;
1848                 }
1849 
1850                 @Override
1851                 public Boolean visitSee(SeeTree node, Content c) {
1852                     // we need to pass the DocTreeImpl here, so ignore node
1853                     result.addContent(seeTagToContent(element, tag));
1854                     return false;
1855                 }
1856 
1857                 @Override
1858                 public Boolean visitStartElement(StartElementTree node, Content c) {
1859                     String text = "<" + node.getName();
1860                     RawHtml rawHtml = new RawHtml(utils.normalizeNewlines(text));
1861                     result.addContent(rawHtml);
1862 
1863                     for (DocTree dt : node.getAttributes()) {
1864                         dt.accept(this, null);
1865                     }
1866                     result.addContent(new RawHtml(node.isSelfClosing() ? "/>" : ">"));
1867                     return false;
1868                 }
1869 
1870                 @Override
1871                 public Boolean visitSummary(SummaryTree node, Content c) {
1872                     Content output = TagletWriter.getInlineTagOutput(element,
1873                             configuration.tagletManager, holderTag, tag,
1874                             getTagletWriterInstance(isFirstSentence));
1875                     result.addContent(output);
1876                     return false;
1877                 }
1878 
1879                 private CharSequence textCleanup(String text, boolean isLast) {
1880                     return textCleanup(text, isLast, false);
1881                 }
1882 
1883                 private CharSequence textCleanup(String text, boolean isLast, boolean trimLeader) {
1884                     if (trimLeader) {
1885                         text = removeLeadingWhitespace(text);
1886                     }
1887                     if (isFirstSentence && isLast) {
1888                         text = removeTrailingWhitespace(text);
1889                     }
1890                     text = utils.replaceTabs(text);
1891                     return utils.normalizeNewlines(text);
1892                 }
1893 
1894                 @Override
1895                 public Boolean visitText(TextTree node, Content c) {
1896                     String text = node.getBody();
1897                     result.addContent(new RawHtml(textCleanup(text, isLastNode, commentRemoved)));
1898                     return false;
1899                 }
1900 
1901                 @Override
1902                 protected Boolean defaultAction(DocTree node, Content c) {
1903                     Content output = TagletWriter.getInlineTagOutput(element,
1904                             configuration.tagletManager, holderTag, tag,
1905                             getTagletWriterInstance(isFirstSentence));
1906                     if (output != null) {
1907                         result.addContent(output);
1908                     }
1909                     return false;
1910                 }
1911 
1912             }.visit(tag, null);
1913             commentRemoved = false;
1914             if (allDone)
1915                 break;
1916         }
1917         return result;
1918     }
1919 
1920     private String removeTrailingWhitespace(String text) {
1921         char[] buf = text.toCharArray();
1922         for (int i = buf.length - 1; i > 0 ; i--) {
1923             if (!Character.isWhitespace(buf[i]))
1924                 return text.substring(0, i + 1);
1925         }
1926         return text;
1927     }
1928 
1929     private String removeLeadingWhitespace(String text) {
1930         char[] buf = text.toCharArray();
1931         for (int i = 0; i < buf.length; i++) {
1932             if (!Character.isWhitespace(buf[i])) {
1933                 return text.substring(i);
1934             }
1935         }
1936         return text;
1937     }
1938 
1939     /**
1940      * Return true if relative links should not be redirected.
1941      *
1942      * @return Return true if a relative link should not be redirected.
1943      */
1944     private boolean shouldNotRedirectRelativeLinks() {
1945         return  this instanceof AnnotationTypeWriter ||
1946                 this instanceof ClassWriter ||
1947                 this instanceof PackageSummaryWriter;
1948     }
1949 
1950     /**
1951      * Suppose a piece of documentation has a relative link.  When you copy
1952      * that documentation to another place such as the index or class-use page,
1953      * that relative link will no longer work.  We should redirect those links
1954      * so that they will work again.
1955      * <p>
1956      * Here is the algorithm used to fix the link:
1957      * <p>
1958      * {@literal <relative link> => docRoot + <relative path to file> + <relative link> }
1959      * <p>
1960      * For example, suppose DocletEnvironment has this link:
1961      * {@literal <a href="package-summary.html">The package Page</a> }
1962      * <p>
1963      * If this link appeared in the index, we would redirect
1964      * the link like this:
1965      *
1966      * {@literal <a href="./com/sun/javadoc/package-summary.html">The package Page</a>}
1967      *
1968      * @param element the Element object whose documentation is being written.
1969      * @param text the text being written.
1970      *
1971      * @return the text, with all the relative links redirected to work.
1972      */
1973     private String redirectRelativeLinks(Element element, TextTree tt) {
1974         String text = tt.getBody();
1975         if (element == null || utils.isOverviewElement(element) || shouldNotRedirectRelativeLinks()) {
1976             return text;
1977         }
1978 
1979         DocPath redirectPathFromRoot = new SimpleElementVisitor9<DocPath, Void>() {
1980             @Override
1981             public DocPath visitType(TypeElement e, Void p) {
1982                 return DocPath.forPackage(utils.containingPackage(e));
1983             }
1984 
1985             @Override
1986             public DocPath visitPackage(PackageElement e, Void p) {
1987                 return DocPath.forPackage(e);
1988             }
1989 
1990             @Override
1991             public DocPath visitVariable(VariableElement e, Void p) {
1992                 return DocPath.forPackage(utils.containingPackage(e));
1993             }
1994 
1995             @Override
1996             public DocPath visitExecutable(ExecutableElement e, Void p) {
1997                 return DocPath.forPackage(utils.containingPackage(e));
1998             }
1999 
2000             @Override
2001             protected DocPath defaultAction(Element e, Void p) {
2002                 return null;
2003             }
2004         }.visit(element);
2005         if (redirectPathFromRoot == null) {
2006             return text;
2007         }
2008         String lower = Utils.toLowerCase(text);
2009         if (!(lower.startsWith("mailto:")
2010                 || lower.startsWith("http:")
2011                 || lower.startsWith("https:")
2012                 || lower.startsWith("file:"))) {
2013             text = "{@" + (new DocRootTaglet()).getName() + "}/"
2014                     + redirectPathFromRoot.resolve(text).getPath();
2015             text = replaceDocRootDir(text);
2016         }
2017         return text;
2018     }
2019 
2020     /**
2021      * According to
2022      * <cite>The Java&trade; Language Specification</cite>,
2023      * all the outer classes and static nested classes are core classes.
2024      */
2025     public boolean isCoreClass(TypeElement typeElement) {
2026         return utils.getEnclosingTypeElement(typeElement) == null || utils.isStatic(typeElement);
2027     }
2028 
2029     /**
2030      * Adds the annotation types for the given packageElement.
2031      *
2032      * @param packageElement the package to write annotations for.
2033      * @param htmltree the documentation tree to which the annotation info will be
2034      *        added
2035      */
2036     public void addAnnotationInfo(PackageElement packageElement, Content htmltree) {
2037         addAnnotationInfo(packageElement, packageElement.getAnnotationMirrors(), htmltree);
2038     }
2039 
2040     /**
2041      * Add the annotation types of the executable receiver.
2042      *
2043      * @param method the executable to write the receiver annotations for.
2044      * @param descList list of annotation description.
2045      * @param htmltree the documentation tree to which the annotation info will be
2046      *        added
2047      */
2048     public void addReceiverAnnotationInfo(ExecutableElement method, List<AnnotationMirror> descList,
2049             Content htmltree) {
2050         addAnnotationInfo(0, method, descList, false, htmltree);
2051     }
2052 
2053     /*
2054      * this is a hack to delay dealing with Annotations in the writers, the assumption
2055      * is that all necessary checks have been made to get here.
2056      */
2057     public void addReceiverAnnotationInfo(ExecutableElement method, TypeMirror rcvrTypeMirror,
2058             List<? extends AnnotationMirror> annotationMirrors, Content htmltree) {
2059         TypeMirror rcvrType = method.getReceiverType();
2060         List<? extends AnnotationMirror> annotationMirrors1 = rcvrType.getAnnotationMirrors();
2061         addAnnotationInfo(0, method, annotationMirrors1, false, htmltree);
2062     }
2063 
2064     /**
2065      * Adds the annotation types for the given element.
2066      *
2067      * @param element the package to write annotations for
2068      * @param htmltree the content tree to which the annotation types will be added
2069      */
2070     public void addAnnotationInfo(Element element, Content htmltree) {
2071         addAnnotationInfo(element, element.getAnnotationMirrors(), htmltree);
2072     }
2073 
2074     /**
2075      * Add the annotatation types for the given element and parameter.
2076      *
2077      * @param indent the number of spaces to indent the parameters.
2078      * @param element the element to write annotations for.
2079      * @param param the parameter to write annotations for.
2080      * @param tree the content tree to which the annotation types will be added
2081      */
2082     public boolean addAnnotationInfo(int indent, Element element, VariableElement param,
2083             Content tree) {
2084         return addAnnotationInfo(indent, element, param.getAnnotationMirrors(), false, tree);
2085     }
2086 
2087     /**
2088      * Adds the annotatation types for the given Element.
2089      *
2090      * @param element the element to write annotations for.
2091      * @param descList the array of {@link AnnotationDesc}.
2092      * @param htmltree the documentation tree to which the annotation info will be
2093      *        added
2094      */
2095     private void addAnnotationInfo(Element element, List<? extends AnnotationMirror> descList,
2096             Content htmltree) {
2097         addAnnotationInfo(0, element, descList, true, htmltree);
2098     }
2099 
2100     /**
2101      * Adds the annotation types for the given element.
2102      *
2103      * @param indent the number of extra spaces to indent the annotations.
2104      * @param element the element to write annotations for.
2105      * @param descList the array of {@link AnnotationDesc}.
2106      * @param htmltree the documentation tree to which the annotation info will be
2107      *        added
2108      */
2109     private boolean addAnnotationInfo(int indent, Element element,
2110             List<? extends AnnotationMirror> descList, boolean lineBreak, Content htmltree) {
2111         List<Content> annotations = getAnnotations(indent, descList, lineBreak);
2112         String sep = "";
2113         if (annotations.isEmpty()) {
2114             return false;
2115         }
2116         for (Content annotation: annotations) {
2117             htmltree.addContent(sep);
2118             htmltree.addContent(annotation);
2119             if (!lineBreak) {
2120                 sep = " ";
2121             }
2122         }
2123         return true;
2124     }
2125 
2126    /**
2127      * Return the string representations of the annotation types for
2128      * the given doc.
2129      *
2130      * @param indent the number of extra spaces to indent the annotations.
2131      * @param descList the array of {@link AnnotationDesc}.
2132      * @param linkBreak if true, add new line between each member value.
2133      * @return an array of strings representing the annotations being
2134      *         documented.
2135      */
2136     private List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList, boolean linkBreak) {
2137         return getAnnotations(indent, descList, linkBreak, true);
2138     }
2139 
2140     private List<Content> getAnnotations(int indent, AnnotationMirror amirror, boolean linkBreak) {
2141         List<AnnotationMirror> descList = new ArrayList<>();
2142         descList.add(amirror);
2143         return getAnnotations(indent, descList, linkBreak, true);
2144     }
2145 
2146     /**
2147      * Return the string representations of the annotation types for
2148      * the given doc.
2149      *
2150      * A {@code null} {@code elementType} indicates that all the
2151      * annotations should be returned without any filtering.
2152      *
2153      * @param indent the number of extra spaces to indent the annotations.
2154      * @param descList the array of {@link AnnotationDesc}.
2155      * @param linkBreak if true, add new line between each member value.
2156      * @param isJava5DeclarationLocation
2157      * @return an array of strings representing the annotations being
2158      *         documented.
2159      */
2160     public List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList,
2161             boolean linkBreak, boolean isJava5DeclarationLocation) {
2162         List<Content> results = new ArrayList<>();
2163         ContentBuilder annotation;
2164         for (AnnotationMirror aDesc : descList) {
2165             TypeElement annotationElement = (TypeElement)aDesc.getAnnotationType().asElement();
2166             // If an annotation is not documented, do not add it to the list. If
2167             // the annotation is of a repeatable type, and if it is not documented
2168             // and also if its container annotation is not documented, do not add it
2169             // to the list. If an annotation of a repeatable type is not documented
2170             // but its container is documented, it will be added to the list.
2171             if (!utils.isDocumentedAnnotation(annotationElement) &&
2172                 (!isAnnotationDocumented && !isContainerDocumented)) {
2173                 continue;
2174             }
2175             /* TODO: check logic here to correctly handle declaration
2176              * and type annotations.
2177             if  (utils.isDeclarationAnnotation(annotationElement, isJava5DeclarationLocation)) {
2178                 continue;
2179             }*/
2180             annotation = new ContentBuilder();
2181             isAnnotationDocumented = false;
2182             LinkInfoImpl linkInfo = new LinkInfoImpl(configuration,
2183                                                      LinkInfoImpl.Kind.ANNOTATION, annotationElement);
2184             Map<? extends ExecutableElement, ? extends AnnotationValue> pairs = aDesc.getElementValues();
2185             // If the annotation is synthesized, do not print the container.
2186             if (utils.configuration.workArounds.isSynthesized(aDesc)) {
2187                 for (ExecutableElement ee : pairs.keySet()) {
2188                     AnnotationValue annotationValue = pairs.get(ee);
2189                     List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2190 
2191                     new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() {
2192                         @Override
2193                         public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> p) {
2194                             p.addAll(vals);
2195                             return null;
2196                         }
2197 
2198                         @Override
2199                         protected Void defaultAction(Object o, List<AnnotationValue> p) {
2200                             p.add(annotationValue);
2201                             return null;
2202                         }
2203                     }.visit(annotationValue, annotationTypeValues);
2204 
2205                     String sep = "";
2206                     for (AnnotationValue av : annotationTypeValues) {
2207                         annotation.addContent(sep);
2208                         annotation.addContent(annotationValueToContent(av));
2209                         sep = " ";
2210                     }
2211                 }
2212             } else if (isAnnotationArray(pairs)) {
2213                 // If the container has 1 or more value defined and if the
2214                 // repeatable type annotation is not documented, do not print
2215                 // the container.
2216                 if (pairs.size() == 1 && isAnnotationDocumented) {
2217                     List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2218                     for (AnnotationValue a :  pairs.values()) {
2219                         new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() {
2220                             @Override
2221                             public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> annotationTypeValues) {
2222                                for (AnnotationValue av : vals) {
2223                                    annotationTypeValues.add(av);
2224                                }
2225                                return null;
2226                             }
2227                         }.visit(a, annotationTypeValues);
2228                     }
2229                     String sep = "";
2230                     for (AnnotationValue av : annotationTypeValues) {
2231                         annotation.addContent(sep);
2232                         annotation.addContent(annotationValueToContent(av));
2233                         sep = " ";
2234                     }
2235                 }
2236                 // If the container has 1 or more value defined and if the
2237                 // repeatable type annotation is not documented, print the container.
2238                 else {
2239                     addAnnotations(annotationElement, linkInfo, annotation, pairs,
2240                                    indent, false);
2241                 }
2242             }
2243             else {
2244                 addAnnotations(annotationElement, linkInfo, annotation, pairs,
2245                                indent, linkBreak);
2246             }
2247             annotation.addContent(linkBreak ? DocletConstants.NL : "");
2248             results.add(annotation);
2249         }
2250         return results;
2251     }
2252 
2253     /**
2254      * Add annotation to the annotation string.
2255      *
2256      * @param annotationDoc the annotation being documented
2257      * @param linkInfo the information about the link
2258      * @param annotation the annotation string to which the annotation will be added
2259      * @param pairs annotation type element and value pairs
2260      * @param indent the number of extra spaces to indent the annotations.
2261      * @param linkBreak if true, add new line between each member value
2262      */
2263     private void addAnnotations(TypeElement annotationDoc, LinkInfoImpl linkInfo,
2264         ContentBuilder annotation, Map<? extends ExecutableElement,? extends AnnotationValue>map,
2265         int indent, boolean linkBreak) {
2266         linkInfo.label = new StringContent("@");
2267         linkInfo.label.addContent(annotationDoc.getSimpleName());
2268         annotation.addContent(getLink(linkInfo));
2269         if (!map.isEmpty()) {
2270             annotation.addContent("(");
2271             boolean isFirst = true;
2272             Set<? extends ExecutableElement> keys = map.keySet();
2273             boolean multipleValues = keys.size() > 1;
2274             for (ExecutableElement element : keys) {
2275                 if (isFirst) {
2276                     isFirst = false;
2277                 } else {
2278                     annotation.addContent(",");
2279                     if (linkBreak) {
2280                         annotation.addContent(DocletConstants.NL);
2281                         int spaces = annotationDoc.getSimpleName().length() + 2;
2282                         for (int k = 0; k < (spaces + indent); k++) {
2283                             annotation.addContent(" ");
2284                         }
2285                     }
2286                 }
2287                 String simpleName = element.getSimpleName().toString();
2288                 if (multipleValues || !"value".equals(simpleName)) { // Omit "value=" where unnecessary
2289                     annotation.addContent(getDocLink(LinkInfoImpl.Kind.ANNOTATION,
2290                                                      element, simpleName, false));
2291                     annotation.addContent("=");
2292                 }
2293                 AnnotationValue annotationValue = map.get(element);
2294                 List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2295                 new SimpleAnnotationValueVisitor9<Void, AnnotationValue>() {
2296                     @Override
2297                     public Void visitArray(List<? extends AnnotationValue> vals, AnnotationValue p) {
2298                         annotationTypeValues.addAll(vals);
2299                         return null;
2300                     }
2301                     @Override
2302                     protected Void defaultAction(Object o, AnnotationValue p) {
2303                         annotationTypeValues.add(p);
2304                         return null;
2305                     }
2306                 }.visit(annotationValue, annotationValue);
2307                 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "{");
2308                 String sep = "";
2309                 for (AnnotationValue av : annotationTypeValues) {
2310                     annotation.addContent(sep);
2311                     annotation.addContent(annotationValueToContent(av));
2312                     sep = ",";
2313                 }
2314                 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "}");
2315                 isContainerDocumented = false;
2316             }
2317             annotation.addContent(")");
2318         }
2319     }
2320 
2321     /**
2322      * Check if the annotation contains an array of annotation as a value. This
2323      * check is to verify if a repeatable type annotation is present or not.
2324      *
2325      * @param pairs annotation type element and value pairs
2326      *
2327      * @return true if the annotation contains an array of annotation as a value.
2328      */
2329     private boolean isAnnotationArray(Map<? extends ExecutableElement, ? extends AnnotationValue> pairs) {
2330         AnnotationValue annotationValue;
2331         for (ExecutableElement ee : pairs.keySet()) {
2332             annotationValue = pairs.get(ee);
2333             boolean rvalue = new SimpleAnnotationValueVisitor9<Boolean, Void>() {
2334                 @Override
2335                 public Boolean visitArray(List<? extends AnnotationValue> vals, Void p) {
2336                     if (vals.size() > 1) {
2337                         if (vals.get(0) instanceof AnnotationMirror) {
2338                             isContainerDocumented = true;
2339                             return new SimpleAnnotationValueVisitor9<Boolean, Void>() {
2340                                 @Override
2341                                 public Boolean visitAnnotation(AnnotationMirror a, Void p) {
2342                                     isContainerDocumented = true;
2343                                     Element asElement = a.getAnnotationType().asElement();
2344                                     if (utils.isDocumentedAnnotation((TypeElement)asElement)) {
2345                                         isAnnotationDocumented = true;
2346                                     }
2347                                     return true;
2348                                 }
2349                                 @Override
2350                                 protected Boolean defaultAction(Object o, Void p) {
2351                                     return false;
2352                                 }
2353                             }.visit(vals.get(0));
2354                         }
2355                     }
2356                     return false;
2357                 }
2358 
2359                 @Override
2360                 protected Boolean defaultAction(Object o, Void p) {
2361                     return false;
2362                 }
2363             }.visit(annotationValue);
2364             if (rvalue) {
2365                 return true;
2366             }
2367         }
2368         return false;
2369     }
2370 
2371     private Content annotationValueToContent(AnnotationValue annotationValue) {
2372         return new SimpleAnnotationValueVisitor9<Content, Void>() {
2373 
2374             @Override
2375             public Content visitType(TypeMirror t, Void p) {
2376                 return new SimpleTypeVisitor9<Content, Void>() {
2377                     @Override
2378                     public Content visitDeclared(DeclaredType t, Void p) {
2379                         LinkInfoImpl linkInfo = new LinkInfoImpl(configuration,
2380                                 LinkInfoImpl.Kind.ANNOTATION, t);
2381                         String name = utils.isIncluded(t.asElement())
2382                                 ? t.asElement().getSimpleName().toString()
2383                                 : utils.getFullyQualifiedName(t.asElement());
2384                         linkInfo.label = new StringContent(name + utils.getDimension(t) + ".class");
2385                         return getLink(linkInfo);
2386                     }
2387                     @Override
2388                     protected Content defaultAction(TypeMirror e, Void p) {
2389                         return new StringContent(t + utils.getDimension(t) + ".class");
2390                     }
2391                 }.visit(t);
2392             }
2393             @Override
2394             public Content visitAnnotation(AnnotationMirror a, Void p) {
2395                 List<Content> list = getAnnotations(0, a, false);
2396                 ContentBuilder buf = new ContentBuilder();
2397                 for (Content c : list) {
2398                     buf.addContent(c);
2399                 }
2400                 return buf;
2401             }
2402             @Override
2403             public Content visitEnumConstant(VariableElement c, Void p) {
2404                 return getDocLink(LinkInfoImpl.Kind.ANNOTATION,
2405                         c, c.getSimpleName(), false);
2406             }
2407             @Override
2408             public Content visitArray(List<? extends AnnotationValue> vals, Void p) {
2409                 ContentBuilder buf = new ContentBuilder();
2410                 String sep = "";
2411                 for (AnnotationValue av : vals) {
2412                     buf.addContent(sep);
2413                     buf.addContent(visit(av));
2414                     sep = " ";
2415                 }
2416                 return buf;
2417             }
2418             @Override
2419             protected Content defaultAction(Object o, Void p) {
2420                 return new StringContent(annotationValue.toString());
2421             }
2422         }.visit(annotationValue);
2423     }
2424 
2425     protected TableHeader getPackageTableHeader() {
2426         return new TableHeader(contents.packageLabel, contents.descriptionLabel);
2427     }
2428 
2429     /**
2430      * Returns an HtmlTree for the SCRIPT tag.
2431      *
2432      * @return an HtmlTree for the SCRIPT tag
2433      */
2434     protected Script getWinTitleScript() {
2435         Script script = new Script();
2436         if (winTitle != null && winTitle.length() > 0) {
2437             script.append("<!--\n" +
2438                     "    try {\n" +
2439                     "        if (location.href.indexOf('is-external=true') == -1) {\n" +
2440                     "            parent.document.title=")
2441                     .appendStringLiteral(winTitle)
2442                     .append(";\n" +
2443                     "        }\n" +
2444                     "    }\n" +
2445                     "    catch(err) {\n" +
2446                     "    }\n" +
2447                     "//-->\n");
2448         }
2449         return script;
2450     }
2451 
2452     /**
2453      * Returns an HtmlTree for the BODY tag.
2454      *
2455      * @param includeScript  set true if printing windowtitle script
2456      * @param title title for the window
2457      * @return an HtmlTree for the BODY tag
2458      */
2459     public HtmlTree getBody(boolean includeScript, String title) {
2460         HtmlTree body = new HtmlTree(HtmlTag.BODY);
2461         // Set window title string which is later printed
2462         this.winTitle = title;
2463         // Don't print windowtitle script for overview-frame, allclasses-frame
2464         // and package-frame
2465         if (includeScript) {
2466             this.mainBodyScript = getWinTitleScript();
2467             body.addContent(mainBodyScript.asContent());
2468             Content noScript = HtmlTree.NOSCRIPT(HtmlTree.DIV(contents.noScriptMessage));
2469             body.addContent(noScript);
2470         }
2471         return body;
2472     }
2473 
2474     Script getMainBodyScript() {
2475         return mainBodyScript;
2476     }
2477 }