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 code true if the label should be code font.
1093      * @return the link
1094      */
1095     public Content getCrossClassLink(String qualifiedClassName, String refMemName,
1096                                     Content label, boolean strong, boolean code) {
1097         String className = "";
1098         String packageName = qualifiedClassName == null ? "" : qualifiedClassName;
1099         int periodIndex;
1100         while ((periodIndex = packageName.lastIndexOf('.')) != -1) {
1101             className = packageName.substring(periodIndex + 1, packageName.length()) +
1102                 (className.length() > 0 ? "." + className : "");
1103             Content defaultLabel = new StringContent(className);
1104             if (code)
1105                 defaultLabel = HtmlTree.CODE(defaultLabel);
1106             packageName = packageName.substring(0, periodIndex);
1107             if (getCrossPackageLink(packageName) != null) {
1108                 /*
1109                 The package exists in external documentation, so link to the external
1110                 class (assuming that it exists).  This is definitely a limitation of
1111                 the -link option.  There are ways to determine if an external package
1112                 exists, but no way to determine if the external class exists.  We just
1113                 have to assume that it does.
1114                 */
1115                 DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot,
1116                                 className + ".html", refMemName);
1117                 return Links.createLink(link,
1118                     (label == null) || label.isEmpty() ? defaultLabel : label,
1119                     strong,
1120                     resources.getText("doclet.Href_Class_Or_Interface_Title", packageName),
1121                     "");
1122             }
1123         }
1124         return null;
1125     }
1126 
1127     public boolean isClassLinkable(TypeElement typeElement) {
1128         if (utils.isIncluded(typeElement)) {
1129             return configuration.isGeneratedDoc(typeElement);
1130         }
1131         return configuration.extern.isExternal(typeElement);
1132     }
1133 
1134     public DocLink getCrossPackageLink(String pkgName) {
1135         return configuration.extern.getExternalLink(pkgName, pathToRoot,
1136             DocPaths.PACKAGE_SUMMARY.getPath());
1137     }
1138 
1139     /**
1140      * Get the class link.
1141      *
1142      * @param context the id of the context where the link will be added
1143      * @param element to link to
1144      * @return a content tree for the link
1145      */
1146     public Content getQualifiedClassLink(LinkInfoImpl.Kind context, Element element) {
1147         LinkInfoImpl linkInfoImpl = new LinkInfoImpl(configuration, context, (TypeElement)element);
1148         return getLink(linkInfoImpl.label(utils.getFullyQualifiedName(element)));
1149     }
1150 
1151     /**
1152      * Add the class link.
1153      *
1154      * @param context the id of the context where the link will be added
1155      * @param typeElement to link to
1156      * @param contentTree the content tree to which the link will be added
1157      */
1158     public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) {
1159         addPreQualifiedClassLink(context, typeElement, false, contentTree);
1160     }
1161 
1162     /**
1163      * Retrieve the class link with the package portion of the label in
1164      * plain text.  If the qualifier is excluded, it will not be included in the
1165      * link label.
1166      *
1167      * @param typeElement the class to link to.
1168      * @param isStrong true if the link should be strong.
1169      * @return the link with the package portion of the label in plain text.
1170      */
1171     public Content getPreQualifiedClassLink(LinkInfoImpl.Kind context,
1172             TypeElement typeElement, boolean isStrong) {
1173         ContentBuilder classlink = new ContentBuilder();
1174         PackageElement pkg = utils.containingPackage(typeElement);
1175         if (pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) {
1176             classlink.addContent(getEnclosingPackageName(typeElement));
1177         }
1178         classlink.addContent(getLink(new LinkInfoImpl(configuration,
1179                 context, typeElement).label(utils.getSimpleName(typeElement)).strong(isStrong)));
1180         return classlink;
1181     }
1182 
1183     /**
1184      * Add the class link with the package portion of the label in
1185      * plain text. If the qualifier is excluded, it will not be included in the
1186      * link label.
1187      *
1188      * @param context the id of the context where the link will be added
1189      * @param typeElement the class to link to
1190      * @param isStrong true if the link should be strong
1191      * @param contentTree the content tree to which the link with be added
1192      */
1193     public void addPreQualifiedClassLink(LinkInfoImpl.Kind context,
1194             TypeElement typeElement, boolean isStrong, Content contentTree) {
1195         PackageElement pkg = utils.containingPackage(typeElement);
1196         if(pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) {
1197             contentTree.addContent(getEnclosingPackageName(typeElement));
1198         }
1199         LinkInfoImpl linkinfo = new LinkInfoImpl(configuration, context, typeElement)
1200                 .label(utils.getSimpleName(typeElement))
1201                 .strong(isStrong);
1202         Content link = getLink(linkinfo);
1203         contentTree.addContent(link);
1204     }
1205 
1206     /**
1207      * Add the class link, with only class name as the strong link and prefixing
1208      * plain package name.
1209      *
1210      * @param context the id of the context where the link will be added
1211      * @param typeElement the class to link to
1212      * @param contentTree the content tree to which the link with be added
1213      */
1214     public void addPreQualifiedStrongClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) {
1215         addPreQualifiedClassLink(context, typeElement, true, contentTree);
1216     }
1217 
1218     /**
1219      * Get the link for the given member.
1220      *
1221      * @param context the id of the context where the link will be added
1222      * @param element the member being linked to
1223      * @param label the label for the link
1224      * @return a content tree for the element link
1225      */
1226     public Content getDocLink(LinkInfoImpl.Kind context, Element element, CharSequence label) {
1227         return getDocLink(context, utils.getEnclosingTypeElement(element), element,
1228                 new StringContent(label));
1229     }
1230 
1231     /**
1232      * Return the link for the given member.
1233      *
1234      * @param context the id of the context where the link will be printed.
1235      * @param element the member being linked to.
1236      * @param label the label for the link.
1237      * @param strong true if the link should be strong.
1238      * @return the link for the given member.
1239      */
1240     public Content getDocLink(LinkInfoImpl.Kind context, Element element, CharSequence label,
1241             boolean strong) {
1242         return getDocLink(context, utils.getEnclosingTypeElement(element), element, label, strong);
1243     }
1244 
1245     /**
1246      * Return the link for the given member.
1247      *
1248      * @param context the id of the context where the link will be printed.
1249      * @param typeElement the typeElement that we should link to.  This is not
1250                  necessarily equal to element.containingClass().  We may be
1251                  inheriting comments.
1252      * @param element the member being linked to.
1253      * @param label the label for the link.
1254      * @param strong true if the link should be strong.
1255      * @return the link for the given member.
1256      */
1257     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1258             CharSequence label, boolean strong) {
1259         return getDocLink(context, typeElement, element, label, strong, false);
1260     }
1261 
1262     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1263             Content label, boolean strong) {
1264         return getDocLink(context, typeElement, element, label, strong, false);
1265     }
1266 
1267    /**
1268      * Return the link for the given member.
1269      *
1270      * @param context the id of the context where the link will be printed.
1271      * @param typeElement the typeElement that we should link to.  This is not
1272                  necessarily equal to element.containingClass().  We may be
1273                  inheriting comments.
1274      * @param element the member being linked to.
1275      * @param label the label for the link.
1276      * @param strong true if the link should be strong.
1277      * @param isProperty true if the element parameter is a JavaFX property.
1278      * @return the link for the given member.
1279      */
1280     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1281             CharSequence label, boolean strong, boolean isProperty) {
1282         return getDocLink(context, typeElement, element, new StringContent(check(label)), strong, isProperty);
1283     }
1284 
1285     CharSequence check(CharSequence s) {
1286         Matcher m = IMPROPER_HTML_CHARS.matcher(s);
1287         if (m.matches()) {
1288             throw new IllegalArgumentException(s.toString());
1289         }
1290         return s;
1291     }
1292 
1293     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1294             Content label, boolean strong, boolean isProperty) {
1295         if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) {
1296             return label;
1297         } else if (utils.isExecutableElement(element)) {
1298             ExecutableElement ee = (ExecutableElement)element;
1299             return getLink(new LinkInfoImpl(configuration, context, typeElement)
1300                 .label(label)
1301                 .where(links.getName(getAnchor(ee, isProperty)))
1302                 .strong(strong));
1303         } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) {
1304             return getLink(new LinkInfoImpl(configuration, context, typeElement)
1305                 .label(label)
1306                 .where(links.getName(element.getSimpleName().toString()))
1307                 .strong(strong));
1308         } else {
1309             return label;
1310         }
1311     }
1312 
1313     /**
1314      * Return the link for the given member.
1315      *
1316      * @param context the id of the context where the link will be added
1317      * @param typeElement the typeElement that we should link to.  This is not
1318                  necessarily equal to element.containingClass().  We may be
1319                  inheriting comments
1320      * @param element the member being linked to
1321      * @param label the label for the link
1322      * @return the link for the given member
1323      */
1324     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1325             Content label) {
1326         if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) {
1327             return label;
1328         } else if (utils.isExecutableElement(element)) {
1329             ExecutableElement emd = (ExecutableElement) element;
1330             return getLink(new LinkInfoImpl(configuration, context, typeElement)
1331                 .label(label)
1332                 .where(links.getName(getAnchor(emd))));
1333         } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) {
1334             return getLink(new LinkInfoImpl(configuration, context, typeElement)
1335                 .label(label).where(links.getName(element.getSimpleName().toString())));
1336         } else {
1337             return label;
1338         }
1339     }
1340 
1341     public String getAnchor(ExecutableElement executableElement) {
1342         return getAnchor(executableElement, false);
1343     }
1344 
1345     public String getAnchor(ExecutableElement executableElement, boolean isProperty) {
1346         if (isProperty) {
1347             return executableElement.getSimpleName().toString();
1348         }
1349         String member = anchorName(executableElement);
1350         String erasedSignature = utils.makeSignature(executableElement, true, true);
1351         return member + erasedSignature;
1352     }
1353 
1354     public String anchorName(Element member) {
1355         if (member.getKind() == ElementKind.CONSTRUCTOR
1356                 && configuration.isOutputHtml5()) {
1357             return "<init>";
1358         } else {
1359             return utils.getSimpleName(member);
1360         }
1361     }
1362 
1363     public Content seeTagToContent(Element element, DocTree see) {
1364 
1365         Kind kind = see.getKind();
1366         if (!(kind == LINK || kind == SEE || kind == LINK_PLAIN)) {
1367             return new ContentBuilder();
1368         }
1369 
1370         CommentHelper ch = utils.getCommentHelper(element);
1371         String tagName = ch.getTagName(see);
1372         String seetext = replaceDocRootDir(utils.normalizeNewlines(ch.getText(see)).toString());
1373         // Check if @see is an href or "string"
1374         if (seetext.startsWith("<") || seetext.startsWith("\"")) {
1375             return new RawHtml(seetext);
1376         }
1377         boolean isLinkPlain = kind == LINK_PLAIN;
1378         Content label = plainOrCode(isLinkPlain, new RawHtml(ch.getLabel(configuration, see)));
1379 
1380         //The text from the @see tag.  We will output this text when a label is not specified.
1381         Content text = plainOrCode(kind == LINK_PLAIN, new RawHtml(seetext));
1382 
1383         TypeElement refClass = ch.getReferencedClass(configuration, see);
1384         String refClassName =  ch.getReferencedClassName(configuration, see);
1385         Element refMem =       ch.getReferencedMember(configuration, see);
1386         String refMemName =    ch.getReferencedMemberName(see);
1387 
1388         if (refMemName == null && refMem != null) {
1389             refMemName = refMem.toString();
1390         }
1391         if (refClass == null) {
1392             //@see is not referencing an included class
1393             PackageElement refPackage = ch.getReferencedPackage(configuration, see);
1394             if (refPackage != null && utils.isIncluded(refPackage)) {
1395                 //@see is referencing an included package
1396                 if (label.isEmpty())
1397                     label = plainOrCode(isLinkPlain,
1398                             new StringContent(refPackage.getQualifiedName()));
1399                 return getPackageLink(refPackage, label);
1400             } else {
1401                 // @see is not referencing an included class or package.  Check for cross links.
1402                 Content classCrossLink;
1403                 DocLink packageCrossLink = getCrossPackageLink(refClassName);
1404                 if (packageCrossLink != null) {
1405                     // Package cross link found
1406                     return Links.createLink(packageCrossLink,
1407                         (label.isEmpty() ? text : label));
1408                 } else if ((classCrossLink = getCrossClassLink(refClassName,
1409                         refMemName, label, false, !isLinkPlain)) != null) {
1410                     // Class cross link found (possibly to a member in the class)
1411                     return classCrossLink;
1412                 } else {
1413                     // No cross link found so print warning
1414                     messages.warning(ch.getDocTreePath(see),
1415                             "doclet.see.class_or_package_not_found",
1416                             "@" + tagName,
1417                             seetext);
1418                     return (label.isEmpty() ? text: label);
1419                 }
1420             }
1421         } else if (refMemName == null) {
1422             // Must be a class reference since refClass is not null and refMemName is null.
1423             if (label.isEmpty()) {
1424                 /*
1425                  * it seems to me this is the right thing to do, but it causes comparator failures.
1426                  */
1427                 if (!configuration.backwardCompatibility) {
1428                     StringContent content = utils.isEnclosingPackageIncluded(refClass)
1429                             ? new StringContent(utils.getSimpleName(refClass))
1430                             : new StringContent(utils.getFullyQualifiedName(refClass));
1431                     label = plainOrCode(isLinkPlain, content);
1432                 } else {
1433                     label = plainOrCode(isLinkPlain,
1434                             new StringContent(utils.getSimpleName(refClass)));
1435                 }
1436 
1437             }
1438             return getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, refClass)
1439                     .label(label));
1440         } else if (refMem == null) {
1441             // Must be a member reference since refClass is not null and refMemName is not null.
1442             // However, refMem is null, so this referenced member does not exist.
1443             return (label.isEmpty() ? text: label);
1444         } else {
1445             // Must be a member reference since refClass is not null and refMemName is not null.
1446             // refMem is not null, so this @see tag must be referencing a valid member.
1447             TypeElement containing = utils.getEnclosingTypeElement(refMem);
1448 
1449             // Find the enclosing type where the method is actually visible
1450             // in the inheritance hierarchy.
1451             if (refMem.getKind() == ElementKind.METHOD) {
1452                 VisibleMemberMap vmm = configuration.getVisibleMemberMap(containing,
1453                         VisibleMemberMap.Kind.METHODS);
1454                 ExecutableElement overriddenMethod = vmm.getVisibleMethod((ExecutableElement)refMem);
1455                 if (overriddenMethod != null)
1456                     containing = utils.getEnclosingTypeElement(overriddenMethod);
1457             }
1458             if (ch.getText(see).trim().startsWith("#") &&
1459                 ! (utils.isPublic(containing) || utils.isLinkable(containing))) {
1460                 // Since the link is relative and the holder is not even being
1461                 // documented, this must be an inherited link.  Redirect it.
1462                 // The current class either overrides the referenced member or
1463                 // inherits it automatically.
1464                 if (this instanceof ClassWriterImpl) {
1465                     containing = ((ClassWriterImpl) this).getTypeElement();
1466                 } else if (!utils.isPublic(containing)) {
1467                     messages.warning(
1468                         ch.getDocTreePath(see), "doclet.see.class_or_package_not_accessible",
1469                         tagName, utils.getFullyQualifiedName(containing));
1470                 } else {
1471                     messages.warning(
1472                         ch.getDocTreePath(see), "doclet.see.class_or_package_not_found",
1473                         tagName, seetext);
1474                 }
1475             }
1476             if (configuration.currentTypeElement != containing) {
1477                 refMemName = (utils.isConstructor(refMem))
1478                         ? refMemName
1479                         : utils.getSimpleName(containing) + "." + refMemName;
1480             }
1481             if (utils.isExecutableElement(refMem)) {
1482                 if (refMemName.indexOf('(') < 0) {
1483                     refMemName += utils.makeSignature((ExecutableElement)refMem, true);
1484                 }
1485             }
1486 
1487             text = plainOrCode(kind == LINK_PLAIN, new StringContent(refMemName));
1488 
1489             return getDocLink(LinkInfoImpl.Kind.SEE_TAG, containing,
1490                     refMem, (label.isEmpty() ? text: label), false);
1491         }
1492     }
1493 
1494     private Content plainOrCode(boolean plain, Content body) {
1495         return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body);
1496     }
1497 
1498     /**
1499      * Add the inline comment.
1500      *
1501      * @param element the Element for which the inline comment will be added
1502      * @param tag the inline tag to be added
1503      * @param htmltree the content tree to which the comment will be added
1504      */
1505     public void addInlineComment(Element element, DocTree tag, Content htmltree) {
1506         CommentHelper ch = utils.getCommentHelper(element);
1507         List<? extends DocTree> description = ch.getDescription(configuration, tag);
1508         addCommentTags(element, tag, description, false, false, htmltree);
1509     }
1510 
1511     /**
1512      * Get the deprecated phrase as content.
1513      *
1514      * @param e the Element for which the inline deprecated comment will be added
1515      * @return a content tree for the deprecated phrase.
1516      */
1517     public Content getDeprecatedPhrase(Element e) {
1518         return (utils.isDeprecatedForRemoval(e))
1519                 ? contents.deprecatedForRemovalPhrase
1520                 : contents.deprecatedPhrase;
1521     }
1522 
1523     /**
1524      * Add the inline deprecated comment.
1525      *
1526      * @param e the Element for which the inline deprecated comment will be added
1527      * @param tag the inline tag to be added
1528      * @param htmltree the content tree to which the comment will be added
1529      */
1530     public void addInlineDeprecatedComment(Element e, DocTree tag, Content htmltree) {
1531         CommentHelper ch = utils.getCommentHelper(e);
1532         addCommentTags(e, ch.getBody(configuration, tag), true, false, htmltree);
1533     }
1534 
1535     /**
1536      * Adds the summary content.
1537      *
1538      * @param element the Element for which the summary will be generated
1539      * @param htmltree the documentation tree to which the summary will be added
1540      */
1541     public void addSummaryComment(Element element, Content htmltree) {
1542         addSummaryComment(element, utils.getFirstSentenceTrees(element), htmltree);
1543     }
1544 
1545     /**
1546      * Adds the summary content.
1547      *
1548      * @param element the Element for which the summary will be generated
1549      * @param firstSentenceTags the first sentence tags for the doc
1550      * @param htmltree the documentation tree to which the summary will be added
1551      */
1552     public void addSummaryComment(Element element, List<? extends DocTree> firstSentenceTags, Content htmltree) {
1553         addCommentTags(element, firstSentenceTags, false, true, htmltree);
1554     }
1555 
1556     public void addSummaryDeprecatedComment(Element element, DocTree tag, Content htmltree) {
1557         CommentHelper ch = utils.getCommentHelper(element);
1558         List<? extends DocTree> body = ch.getBody(configuration, tag);
1559         addCommentTags(element, ch.getFirstSentenceTrees(configuration, body), true, true, htmltree);
1560     }
1561 
1562     /**
1563      * Adds the inline comment.
1564      *
1565      * @param element the Element for which the inline comments will be generated
1566      * @param htmltree the documentation tree to which the inline comments will be added
1567      */
1568     public void addInlineComment(Element element, Content htmltree) {
1569         addCommentTags(element, utils.getFullBody(element), false, false, htmltree);
1570     }
1571 
1572     /**
1573      * Adds the comment tags.
1574      *
1575      * @param element the Element for which the comment tags will be generated
1576      * @param tags the first sentence tags for the doc
1577      * @param depr true if it is deprecated
1578      * @param first true if the first sentence tags should be added
1579      * @param htmltree the documentation tree to which the comment tags will be added
1580      */
1581     private void addCommentTags(Element element, List<? extends DocTree> tags, boolean depr,
1582             boolean first, Content htmltree) {
1583         addCommentTags(element, null, tags, depr, first, htmltree);
1584     }
1585 
1586     /**
1587      * Adds the comment tags.
1588      *
1589      * @param element for which the comment tags will be generated
1590      * @param holderTag the block tag context for the inline tags
1591      * @param tags the first sentence tags for the doc
1592      * @param depr true if it is deprecated
1593      * @param first true if the first sentence tags should be added
1594      * @param htmltree the documentation tree to which the comment tags will be added
1595      */
1596     private void addCommentTags(Element element, DocTree holderTag, List<? extends DocTree> tags, boolean depr,
1597             boolean first, Content htmltree) {
1598         if(configuration.nocomment){
1599             return;
1600         }
1601         Content div;
1602         Content result = commentTagsToContent(null, element, tags, first);
1603         if (depr) {
1604             div = HtmlTree.DIV(HtmlStyle.deprecationComment, result);
1605             htmltree.addContent(div);
1606         }
1607         else {
1608             div = HtmlTree.DIV(HtmlStyle.block, result);
1609             htmltree.addContent(div);
1610         }
1611         if (tags.isEmpty()) {
1612             htmltree.addContent(Contents.SPACE);
1613         }
1614     }
1615 
1616     boolean ignoreNonInlineTag(DocTree dtree) {
1617         Name name = null;
1618         if (dtree.getKind() == Kind.START_ELEMENT) {
1619             StartElementTree setree = (StartElementTree)dtree;
1620             name = setree.getName();
1621         } else if (dtree.getKind() == Kind.END_ELEMENT) {
1622             EndElementTree eetree = (EndElementTree)dtree;
1623             name = eetree.getName();
1624         }
1625 
1626         if (name != null) {
1627             com.sun.tools.doclint.HtmlTag htmlTag = com.sun.tools.doclint.HtmlTag.get(name);
1628             if (htmlTag != null &&
1629                     htmlTag.blockType != com.sun.tools.doclint.HtmlTag.BlockType.INLINE) {
1630                 return true;
1631             }
1632         }
1633         return false;
1634     }
1635 
1636     boolean isAllWhiteSpace(String body) {
1637         for (int i = 0 ; i < body.length(); i++) {
1638             if (!Character.isWhitespace(body.charAt(i)))
1639                 return false;
1640         }
1641         return true;
1642     }
1643 
1644     // Notify the next DocTree handler to take necessary action
1645     private boolean commentRemoved = false;
1646 
1647     /**
1648      * Converts inline tags and text to text strings, expanding the
1649      * inline tags along the way.  Called wherever text can contain
1650      * an inline tag, such as in comments or in free-form text arguments
1651      * to non-inline tags.
1652      *
1653      * @param holderTag    specific tag where comment resides
1654      * @param element    specific element where comment resides
1655      * @param tags   array of text tags and inline tags (often alternating)
1656                present in the text of interest for this element
1657      * @param isFirstSentence  true if text is first sentence
1658      * @return a Content object
1659      */
1660     public Content commentTagsToContent(DocTree holderTag, Element element,
1661             List<? extends DocTree> tags, boolean isFirstSentence) {
1662 
1663         final Content result = new ContentBuilder() {
1664             @Override
1665             public void addContent(CharSequence text) {
1666                 super.addContent(utils.normalizeNewlines(text));
1667             }
1668         };
1669         CommentHelper ch = utils.getCommentHelper(element);
1670         // Array of all possible inline tags for this javadoc run
1671         configuration.tagletManager.checkTags(utils, element, tags, true);
1672         commentRemoved = false;
1673 
1674         for (ListIterator<? extends DocTree> iterator = tags.listIterator(); iterator.hasNext();) {
1675             boolean isFirstNode = !iterator.hasPrevious();
1676             DocTree tag = iterator.next();
1677             boolean isLastNode  = !iterator.hasNext();
1678 
1679             if (isFirstSentence) {
1680                 // Ignore block tags
1681                 if (ignoreNonInlineTag(tag))
1682                     continue;
1683 
1684                 // Ignore any trailing whitespace OR whitespace after removed html comment
1685                 if ((isLastNode || commentRemoved)
1686                         && tag.getKind() == TEXT
1687                         && isAllWhiteSpace(ch.getText(tag)))
1688                     continue;
1689 
1690                 // Ignore any leading html comments
1691                 if ((isFirstNode || commentRemoved) && tag.getKind() == COMMENT) {
1692                     commentRemoved = true;
1693                     continue;
1694                 }
1695             }
1696 
1697             boolean allDone = new SimpleDocTreeVisitor<Boolean, Content>() {
1698 
1699                 private boolean inAnAtag() {
1700                     if (utils.isStartElement(tag)) {
1701                         StartElementTree st = (StartElementTree)tag;
1702                         Name name = st.getName();
1703                         if (name != null) {
1704                             com.sun.tools.doclint.HtmlTag htag =
1705                                     com.sun.tools.doclint.HtmlTag.get(name);
1706                             return htag != null && htag.equals(com.sun.tools.doclint.HtmlTag.A);
1707                         }
1708                     }
1709                     return false;
1710                 }
1711 
1712                 @Override
1713                 public Boolean visitAttribute(AttributeTree node, Content c) {
1714                     StringBuilder sb = new StringBuilder(SPACER).append(node.getName());
1715                     if (node.getValueKind() == ValueKind.EMPTY) {
1716                         result.addContent(sb);
1717                         return false;
1718                     }
1719                     sb.append("=");
1720                     String quote;
1721                     switch (node.getValueKind()) {
1722                         case DOUBLE:
1723                             quote = "\"";
1724                             break;
1725                         case SINGLE:
1726                             quote = "\'";
1727                             break;
1728                         default:
1729                             quote = "";
1730                             break;
1731                     }
1732                     sb.append(quote);
1733                     result.addContent(sb);
1734                     Content docRootContent = new ContentBuilder();
1735 
1736                     for (DocTree dt : node.getValue()) {
1737                         if (utils.isText(dt) && inAnAtag()) {
1738                             String text = ((TextTree) dt).getBody();
1739                             if (text.startsWith("/..") && !configuration.docrootparent.isEmpty()) {
1740                                 result.addContent(configuration.docrootparent);
1741                                 docRootContent = new ContentBuilder();
1742                                 result.addContent(textCleanup(text.substring(3), isLastNode));
1743                             } else {
1744                                 if (!docRootContent.isEmpty()) {
1745                                     docRootContent = copyDocRootContent(docRootContent);
1746                                 } else {
1747                                     text = redirectRelativeLinks(element, (TextTree) dt);
1748                                 }
1749                                 result.addContent(textCleanup(text, isLastNode));
1750                             }
1751                         } else {
1752                             docRootContent = copyDocRootContent(docRootContent);
1753                             dt.accept(this, docRootContent);
1754                         }
1755                     }
1756                     copyDocRootContent(docRootContent);
1757                     result.addContent(quote);
1758                     return false;
1759                 }
1760 
1761                 @Override
1762                 public Boolean visitComment(CommentTree node, Content c) {
1763                     result.addContent(new RawHtml(node.getBody()));
1764                     return false;
1765                 }
1766 
1767                 private Content copyDocRootContent(Content content) {
1768                     if (!content.isEmpty()) {
1769                         result.addContent(content);
1770                         return new ContentBuilder();
1771                     }
1772                     return content;
1773                 }
1774 
1775                 @Override
1776                 public Boolean visitDocRoot(DocRootTree node, Content c) {
1777                     Content docRootContent = TagletWriter.getInlineTagOutput(element,
1778                             configuration.tagletManager,
1779                             holderTag,
1780                             node,
1781                             getTagletWriterInstance(isFirstSentence));
1782                     if (c != null) {
1783                         c.addContent(docRootContent);
1784                     } else {
1785                         result.addContent(docRootContent);
1786                     }
1787                     return false;
1788                 }
1789 
1790                 @Override
1791                 public Boolean visitEndElement(EndElementTree node, Content c) {
1792                     RawHtml rawHtml = new RawHtml("</" + node.getName() + ">");
1793                     result.addContent(rawHtml);
1794                     return false;
1795                 }
1796 
1797                 @Override
1798                 public Boolean visitEntity(EntityTree node, Content c) {
1799                     result.addContent(new RawHtml(node.toString()));
1800                     return false;
1801                 }
1802 
1803                 @Override
1804                 public Boolean visitErroneous(ErroneousTree node, Content c) {
1805                     messages.warning(ch.getDocTreePath(node),
1806                             "doclet.tag.invalid_usage", node);
1807                     result.addContent(new RawHtml(node.toString()));
1808                     return false;
1809                 }
1810 
1811                 @Override
1812                 public Boolean visitInheritDoc(InheritDocTree node, Content c) {
1813                     Content output = TagletWriter.getInlineTagOutput(element,
1814                             configuration.tagletManager, holderTag,
1815                             tag, getTagletWriterInstance(isFirstSentence));
1816                     result.addContent(output);
1817                     // if we obtained the first sentence successfully, nothing more to do
1818                     return (isFirstSentence && !output.isEmpty());
1819                 }
1820 
1821                 @Override
1822                 public Boolean visitIndex(IndexTree node, Content p) {
1823                     Content output = TagletWriter.getInlineTagOutput(element,
1824                             configuration.tagletManager, holderTag, tag,
1825                             getTagletWriterInstance(isFirstSentence));
1826                     if (output != null) {
1827                         result.addContent(output);
1828                     }
1829                     return false;
1830                 }
1831 
1832                 @Override
1833                 public Boolean visitLink(LinkTree node, Content c) {
1834                     // we need to pass the DocTreeImpl here, so ignore node
1835                     result.addContent(seeTagToContent(element, tag));
1836                     return false;
1837                 }
1838 
1839                 @Override
1840                 public Boolean visitLiteral(LiteralTree node, Content c) {
1841                     String s = node.getBody().toString();
1842                     Content content = new StringContent(utils.normalizeNewlines(s));
1843                     if (node.getKind() == CODE)
1844                         content = HtmlTree.CODE(content);
1845                     result.addContent(content);
1846                     return false;
1847                 }
1848 
1849                 @Override
1850                 public Boolean visitSee(SeeTree node, Content c) {
1851                     // we need to pass the DocTreeImpl here, so ignore node
1852                     result.addContent(seeTagToContent(element, tag));
1853                     return false;
1854                 }
1855 
1856                 @Override
1857                 public Boolean visitStartElement(StartElementTree node, Content c) {
1858                     String text = "<" + node.getName();
1859                     RawHtml rawHtml = new RawHtml(utils.normalizeNewlines(text));
1860                     result.addContent(rawHtml);
1861 
1862                     for (DocTree dt : node.getAttributes()) {
1863                         dt.accept(this, null);
1864                     }
1865                     result.addContent(new RawHtml(node.isSelfClosing() ? "/>" : ">"));
1866                     return false;
1867                 }
1868 
1869                 @Override
1870                 public Boolean visitSummary(SummaryTree node, Content c) {
1871                     Content output = TagletWriter.getInlineTagOutput(element,
1872                             configuration.tagletManager, holderTag, tag,
1873                             getTagletWriterInstance(isFirstSentence));
1874                     result.addContent(output);
1875                     return false;
1876                 }
1877 
1878                 private CharSequence textCleanup(String text, boolean isLast) {
1879                     return textCleanup(text, isLast, false);
1880                 }
1881 
1882                 private CharSequence textCleanup(String text, boolean isLast, boolean trimLeader) {
1883                     if (trimLeader) {
1884                         text = removeLeadingWhitespace(text);
1885                     }
1886                     if (isFirstSentence && isLast) {
1887                         text = removeTrailingWhitespace(text);
1888                     }
1889                     text = utils.replaceTabs(text);
1890                     return utils.normalizeNewlines(text);
1891                 }
1892 
1893                 @Override
1894                 public Boolean visitText(TextTree node, Content c) {
1895                     String text = node.getBody();
1896                     result.addContent(new RawHtml(textCleanup(text, isLastNode, commentRemoved)));
1897                     return false;
1898                 }
1899 
1900                 @Override
1901                 protected Boolean defaultAction(DocTree node, Content c) {
1902                     Content output = TagletWriter.getInlineTagOutput(element,
1903                             configuration.tagletManager, holderTag, tag,
1904                             getTagletWriterInstance(isFirstSentence));
1905                     if (output != null) {
1906                         result.addContent(output);
1907                     }
1908                     return false;
1909                 }
1910 
1911             }.visit(tag, null);
1912             commentRemoved = false;
1913             if (allDone)
1914                 break;
1915         }
1916         return result;
1917     }
1918 
1919     private String removeTrailingWhitespace(String text) {
1920         char[] buf = text.toCharArray();
1921         for (int i = buf.length - 1; i > 0 ; i--) {
1922             if (!Character.isWhitespace(buf[i]))
1923                 return text.substring(0, i + 1);
1924         }
1925         return text;
1926     }
1927 
1928     private String removeLeadingWhitespace(String text) {
1929         char[] buf = text.toCharArray();
1930         for (int i = 0; i < buf.length; i++) {
1931             if (!Character.isWhitespace(buf[i])) {
1932                 return text.substring(i);
1933             }
1934         }
1935         return text;
1936     }
1937 
1938     /**
1939      * Return true if relative links should not be redirected.
1940      *
1941      * @return Return true if a relative link should not be redirected.
1942      */
1943     private boolean shouldNotRedirectRelativeLinks() {
1944         return  this instanceof AnnotationTypeWriter ||
1945                 this instanceof ClassWriter ||
1946                 this instanceof PackageSummaryWriter;
1947     }
1948 
1949     /**
1950      * Suppose a piece of documentation has a relative link.  When you copy
1951      * that documentation to another place such as the index or class-use page,
1952      * that relative link will no longer work.  We should redirect those links
1953      * so that they will work again.
1954      * <p>
1955      * Here is the algorithm used to fix the link:
1956      * <p>
1957      * {@literal <relative link> => docRoot + <relative path to file> + <relative link> }
1958      * <p>
1959      * For example, suppose DocletEnvironment has this link:
1960      * {@literal <a href="package-summary.html">The package Page</a> }
1961      * <p>
1962      * If this link appeared in the index, we would redirect
1963      * the link like this:
1964      *
1965      * {@literal <a href="./com/sun/javadoc/package-summary.html">The package Page</a>}
1966      *
1967      * @param element the Element object whose documentation is being written.
1968      * @param text the text being written.
1969      *
1970      * @return the text, with all the relative links redirected to work.
1971      */
1972     private String redirectRelativeLinks(Element element, TextTree tt) {
1973         String text = tt.getBody();
1974         if (element == null || utils.isOverviewElement(element) || shouldNotRedirectRelativeLinks()) {
1975             return text;
1976         }
1977 
1978         DocPath redirectPathFromRoot = new SimpleElementVisitor9<DocPath, Void>() {
1979             @Override
1980             public DocPath visitType(TypeElement e, Void p) {
1981                 return DocPath.forPackage(utils.containingPackage(e));
1982             }
1983 
1984             @Override
1985             public DocPath visitPackage(PackageElement e, Void p) {
1986                 return DocPath.forPackage(e);
1987             }
1988 
1989             @Override
1990             public DocPath visitVariable(VariableElement e, Void p) {
1991                 return DocPath.forPackage(utils.containingPackage(e));
1992             }
1993 
1994             @Override
1995             public DocPath visitExecutable(ExecutableElement e, Void p) {
1996                 return DocPath.forPackage(utils.containingPackage(e));
1997             }
1998 
1999             @Override
2000             protected DocPath defaultAction(Element e, Void p) {
2001                 return null;
2002             }
2003         }.visit(element);
2004         if (redirectPathFromRoot == null) {
2005             return text;
2006         }
2007         String lower = Utils.toLowerCase(text);
2008         if (!(lower.startsWith("mailto:")
2009                 || lower.startsWith("http:")
2010                 || lower.startsWith("https:")
2011                 || lower.startsWith("file:"))) {
2012             text = "{@" + (new DocRootTaglet()).getName() + "}/"
2013                     + redirectPathFromRoot.resolve(text).getPath();
2014             text = replaceDocRootDir(text);
2015         }
2016         return text;
2017     }
2018 
2019     /**
2020      * According to
2021      * <cite>The Java&trade; Language Specification</cite>,
2022      * all the outer classes and static nested classes are core classes.
2023      */
2024     public boolean isCoreClass(TypeElement typeElement) {
2025         return utils.getEnclosingTypeElement(typeElement) == null || utils.isStatic(typeElement);
2026     }
2027 
2028     /**
2029      * Adds the annotation types for the given packageElement.
2030      *
2031      * @param packageElement the package to write annotations for.
2032      * @param htmltree the documentation tree to which the annotation info will be
2033      *        added
2034      */
2035     public void addAnnotationInfo(PackageElement packageElement, Content htmltree) {
2036         addAnnotationInfo(packageElement, packageElement.getAnnotationMirrors(), htmltree);
2037     }
2038 
2039     /**
2040      * Add the annotation types of the executable receiver.
2041      *
2042      * @param method the executable to write the receiver annotations for.
2043      * @param descList list of annotation description.
2044      * @param htmltree the documentation tree to which the annotation info will be
2045      *        added
2046      */
2047     public void addReceiverAnnotationInfo(ExecutableElement method, List<AnnotationMirror> descList,
2048             Content htmltree) {
2049         addAnnotationInfo(0, method, descList, false, htmltree);
2050     }
2051 
2052     /*
2053      * this is a hack to delay dealing with Annotations in the writers, the assumption
2054      * is that all necessary checks have been made to get here.
2055      */
2056     public void addReceiverAnnotationInfo(ExecutableElement method, TypeMirror rcvrTypeMirror,
2057             List<? extends AnnotationMirror> annotationMirrors, Content htmltree) {
2058         TypeMirror rcvrType = method.getReceiverType();
2059         List<? extends AnnotationMirror> annotationMirrors1 = rcvrType.getAnnotationMirrors();
2060         addAnnotationInfo(0, method, annotationMirrors1, false, htmltree);
2061     }
2062 
2063     /**
2064      * Adds the annotation types for the given element.
2065      *
2066      * @param element the package to write annotations for
2067      * @param htmltree the content tree to which the annotation types will be added
2068      */
2069     public void addAnnotationInfo(Element element, Content htmltree) {
2070         addAnnotationInfo(element, element.getAnnotationMirrors(), htmltree);
2071     }
2072 
2073     /**
2074      * Add the annotatation types for the given element and parameter.
2075      *
2076      * @param indent the number of spaces to indent the parameters.
2077      * @param element the element to write annotations for.
2078      * @param param the parameter to write annotations for.
2079      * @param tree the content tree to which the annotation types will be added
2080      */
2081     public boolean addAnnotationInfo(int indent, Element element, VariableElement param,
2082             Content tree) {
2083         return addAnnotationInfo(indent, element, param.getAnnotationMirrors(), false, tree);
2084     }
2085 
2086     /**
2087      * Adds the annotatation types for the given Element.
2088      *
2089      * @param element the element to write annotations for.
2090      * @param descList the array of {@link AnnotationDesc}.
2091      * @param htmltree the documentation tree to which the annotation info will be
2092      *        added
2093      */
2094     private void addAnnotationInfo(Element element, List<? extends AnnotationMirror> descList,
2095             Content htmltree) {
2096         addAnnotationInfo(0, element, descList, true, htmltree);
2097     }
2098 
2099     /**
2100      * Adds the annotation types for the given element.
2101      *
2102      * @param indent the number of extra spaces to indent the annotations.
2103      * @param element the element to write annotations for.
2104      * @param descList the array of {@link AnnotationDesc}.
2105      * @param htmltree the documentation tree to which the annotation info will be
2106      *        added
2107      */
2108     private boolean addAnnotationInfo(int indent, Element element,
2109             List<? extends AnnotationMirror> descList, boolean lineBreak, Content htmltree) {
2110         List<Content> annotations = getAnnotations(indent, descList, lineBreak);
2111         String sep = "";
2112         if (annotations.isEmpty()) {
2113             return false;
2114         }
2115         for (Content annotation: annotations) {
2116             htmltree.addContent(sep);
2117             htmltree.addContent(annotation);
2118             if (!lineBreak) {
2119                 sep = " ";
2120             }
2121         }
2122         return true;
2123     }
2124 
2125    /**
2126      * Return the string representations of the annotation types for
2127      * the given doc.
2128      *
2129      * @param indent the number of extra spaces to indent the annotations.
2130      * @param descList the array of {@link AnnotationDesc}.
2131      * @param linkBreak if true, add new line between each member value.
2132      * @return an array of strings representing the annotations being
2133      *         documented.
2134      */
2135     private List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList, boolean linkBreak) {
2136         return getAnnotations(indent, descList, linkBreak, true);
2137     }
2138 
2139     private List<Content> getAnnotations(int indent, AnnotationMirror amirror, boolean linkBreak) {
2140         List<AnnotationMirror> descList = new ArrayList<>();
2141         descList.add(amirror);
2142         return getAnnotations(indent, descList, linkBreak, true);
2143     }
2144 
2145     /**
2146      * Return the string representations of the annotation types for
2147      * the given doc.
2148      *
2149      * A {@code null} {@code elementType} indicates that all the
2150      * annotations should be returned without any filtering.
2151      *
2152      * @param indent the number of extra spaces to indent the annotations.
2153      * @param descList the array of {@link AnnotationDesc}.
2154      * @param linkBreak if true, add new line between each member value.
2155      * @param isJava5DeclarationLocation
2156      * @return an array of strings representing the annotations being
2157      *         documented.
2158      */
2159     public List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList,
2160             boolean linkBreak, boolean isJava5DeclarationLocation) {
2161         List<Content> results = new ArrayList<>();
2162         ContentBuilder annotation;
2163         for (AnnotationMirror aDesc : descList) {
2164             TypeElement annotationElement = (TypeElement)aDesc.getAnnotationType().asElement();
2165             // If an annotation is not documented, do not add it to the list. If
2166             // the annotation is of a repeatable type, and if it is not documented
2167             // and also if its container annotation is not documented, do not add it
2168             // to the list. If an annotation of a repeatable type is not documented
2169             // but its container is documented, it will be added to the list.
2170             if (!utils.isDocumentedAnnotation(annotationElement) &&
2171                 (!isAnnotationDocumented && !isContainerDocumented)) {
2172                 continue;
2173             }
2174             /* TODO: check logic here to correctly handle declaration
2175              * and type annotations.
2176             if  (utils.isDeclarationAnnotation(annotationElement, isJava5DeclarationLocation)) {
2177                 continue;
2178             }*/
2179             annotation = new ContentBuilder();
2180             isAnnotationDocumented = false;
2181             LinkInfoImpl linkInfo = new LinkInfoImpl(configuration,
2182                                                      LinkInfoImpl.Kind.ANNOTATION, annotationElement);
2183             Map<? extends ExecutableElement, ? extends AnnotationValue> pairs = aDesc.getElementValues();
2184             // If the annotation is synthesized, do not print the container.
2185             if (utils.configuration.workArounds.isSynthesized(aDesc)) {
2186                 for (ExecutableElement ee : pairs.keySet()) {
2187                     AnnotationValue annotationValue = pairs.get(ee);
2188                     List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2189 
2190                     new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() {
2191                         @Override
2192                         public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> p) {
2193                             p.addAll(vals);
2194                             return null;
2195                         }
2196 
2197                         @Override
2198                         protected Void defaultAction(Object o, List<AnnotationValue> p) {
2199                             p.add(annotationValue);
2200                             return null;
2201                         }
2202                     }.visit(annotationValue, annotationTypeValues);
2203 
2204                     String sep = "";
2205                     for (AnnotationValue av : annotationTypeValues) {
2206                         annotation.addContent(sep);
2207                         annotation.addContent(annotationValueToContent(av));
2208                         sep = " ";
2209                     }
2210                 }
2211             } else if (isAnnotationArray(pairs)) {
2212                 // If the container has 1 or more value defined and if the
2213                 // repeatable type annotation is not documented, do not print
2214                 // the container.
2215                 if (pairs.size() == 1 && isAnnotationDocumented) {
2216                     List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2217                     for (AnnotationValue a :  pairs.values()) {
2218                         new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() {
2219                             @Override
2220                             public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> annotationTypeValues) {
2221                                for (AnnotationValue av : vals) {
2222                                    annotationTypeValues.add(av);
2223                                }
2224                                return null;
2225                             }
2226                         }.visit(a, annotationTypeValues);
2227                     }
2228                     String sep = "";
2229                     for (AnnotationValue av : annotationTypeValues) {
2230                         annotation.addContent(sep);
2231                         annotation.addContent(annotationValueToContent(av));
2232                         sep = " ";
2233                     }
2234                 }
2235                 // If the container has 1 or more value defined and if the
2236                 // repeatable type annotation is not documented, print the container.
2237                 else {
2238                     addAnnotations(annotationElement, linkInfo, annotation, pairs,
2239                                    indent, false);
2240                 }
2241             }
2242             else {
2243                 addAnnotations(annotationElement, linkInfo, annotation, pairs,
2244                                indent, linkBreak);
2245             }
2246             annotation.addContent(linkBreak ? DocletConstants.NL : "");
2247             results.add(annotation);
2248         }
2249         return results;
2250     }
2251 
2252     /**
2253      * Add annotation to the annotation string.
2254      *
2255      * @param annotationDoc the annotation being documented
2256      * @param linkInfo the information about the link
2257      * @param annotation the annotation string to which the annotation will be added
2258      * @param pairs annotation type element and value pairs
2259      * @param indent the number of extra spaces to indent the annotations.
2260      * @param linkBreak if true, add new line between each member value
2261      */
2262     private void addAnnotations(TypeElement annotationDoc, LinkInfoImpl linkInfo,
2263         ContentBuilder annotation, Map<? extends ExecutableElement,? extends AnnotationValue>map,
2264         int indent, boolean linkBreak) {
2265         linkInfo.label = new StringContent("@");
2266         linkInfo.label.addContent(annotationDoc.getSimpleName());
2267         annotation.addContent(getLink(linkInfo));
2268         if (!map.isEmpty()) {
2269             annotation.addContent("(");
2270             boolean isFirst = true;
2271             Set<? extends ExecutableElement> keys = map.keySet();
2272             boolean multipleValues = keys.size() > 1;
2273             for (ExecutableElement element : keys) {
2274                 if (isFirst) {
2275                     isFirst = false;
2276                 } else {
2277                     annotation.addContent(",");
2278                     if (linkBreak) {
2279                         annotation.addContent(DocletConstants.NL);
2280                         int spaces = annotationDoc.getSimpleName().length() + 2;
2281                         for (int k = 0; k < (spaces + indent); k++) {
2282                             annotation.addContent(" ");
2283                         }
2284                     }
2285                 }
2286                 String simpleName = element.getSimpleName().toString();
2287                 if (multipleValues || !"value".equals(simpleName)) { // Omit "value=" where unnecessary
2288                     annotation.addContent(getDocLink(LinkInfoImpl.Kind.ANNOTATION,
2289                                                      element, simpleName, false));
2290                     annotation.addContent("=");
2291                 }
2292                 AnnotationValue annotationValue = map.get(element);
2293                 List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2294                 new SimpleAnnotationValueVisitor9<Void, AnnotationValue>() {
2295                     @Override
2296                     public Void visitArray(List<? extends AnnotationValue> vals, AnnotationValue p) {
2297                         annotationTypeValues.addAll(vals);
2298                         return null;
2299                     }
2300                     @Override
2301                     protected Void defaultAction(Object o, AnnotationValue p) {
2302                         annotationTypeValues.add(p);
2303                         return null;
2304                     }
2305                 }.visit(annotationValue, annotationValue);
2306                 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "{");
2307                 String sep = "";
2308                 for (AnnotationValue av : annotationTypeValues) {
2309                     annotation.addContent(sep);
2310                     annotation.addContent(annotationValueToContent(av));
2311                     sep = ",";
2312                 }
2313                 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "}");
2314                 isContainerDocumented = false;
2315             }
2316             annotation.addContent(")");
2317         }
2318     }
2319 
2320     /**
2321      * Check if the annotation contains an array of annotation as a value. This
2322      * check is to verify if a repeatable type annotation is present or not.
2323      *
2324      * @param pairs annotation type element and value pairs
2325      *
2326      * @return true if the annotation contains an array of annotation as a value.
2327      */
2328     private boolean isAnnotationArray(Map<? extends ExecutableElement, ? extends AnnotationValue> pairs) {
2329         AnnotationValue annotationValue;
2330         for (ExecutableElement ee : pairs.keySet()) {
2331             annotationValue = pairs.get(ee);
2332             boolean rvalue = new SimpleAnnotationValueVisitor9<Boolean, Void>() {
2333                 @Override
2334                 public Boolean visitArray(List<? extends AnnotationValue> vals, Void p) {
2335                     if (vals.size() > 1) {
2336                         if (vals.get(0) instanceof AnnotationMirror) {
2337                             isContainerDocumented = true;
2338                             return new SimpleAnnotationValueVisitor9<Boolean, Void>() {
2339                                 @Override
2340                                 public Boolean visitAnnotation(AnnotationMirror a, Void p) {
2341                                     isContainerDocumented = true;
2342                                     Element asElement = a.getAnnotationType().asElement();
2343                                     if (utils.isDocumentedAnnotation((TypeElement)asElement)) {
2344                                         isAnnotationDocumented = true;
2345                                     }
2346                                     return true;
2347                                 }
2348                                 @Override
2349                                 protected Boolean defaultAction(Object o, Void p) {
2350                                     return false;
2351                                 }
2352                             }.visit(vals.get(0));
2353                         }
2354                     }
2355                     return false;
2356                 }
2357 
2358                 @Override
2359                 protected Boolean defaultAction(Object o, Void p) {
2360                     return false;
2361                 }
2362             }.visit(annotationValue);
2363             if (rvalue) {
2364                 return true;
2365             }
2366         }
2367         return false;
2368     }
2369 
2370     private Content annotationValueToContent(AnnotationValue annotationValue) {
2371         return new SimpleAnnotationValueVisitor9<Content, Void>() {
2372 
2373             @Override
2374             public Content visitType(TypeMirror t, Void p) {
2375                 return new SimpleTypeVisitor9<Content, Void>() {
2376                     @Override
2377                     public Content visitDeclared(DeclaredType t, Void p) {
2378                         LinkInfoImpl linkInfo = new LinkInfoImpl(configuration,
2379                                 LinkInfoImpl.Kind.ANNOTATION, t);
2380                         String name = utils.isIncluded(t.asElement())
2381                                 ? t.asElement().getSimpleName().toString()
2382                                 : utils.getFullyQualifiedName(t.asElement());
2383                         linkInfo.label = new StringContent(name + utils.getDimension(t) + ".class");
2384                         return getLink(linkInfo);
2385                     }
2386                     @Override
2387                     protected Content defaultAction(TypeMirror e, Void p) {
2388                         return new StringContent(t + utils.getDimension(t) + ".class");
2389                     }
2390                 }.visit(t);
2391             }
2392             @Override
2393             public Content visitAnnotation(AnnotationMirror a, Void p) {
2394                 List<Content> list = getAnnotations(0, a, false);
2395                 ContentBuilder buf = new ContentBuilder();
2396                 for (Content c : list) {
2397                     buf.addContent(c);
2398                 }
2399                 return buf;
2400             }
2401             @Override
2402             public Content visitEnumConstant(VariableElement c, Void p) {
2403                 return getDocLink(LinkInfoImpl.Kind.ANNOTATION,
2404                         c, c.getSimpleName(), false);
2405             }
2406             @Override
2407             public Content visitArray(List<? extends AnnotationValue> vals, Void p) {
2408                 ContentBuilder buf = new ContentBuilder();
2409                 String sep = "";
2410                 for (AnnotationValue av : vals) {
2411                     buf.addContent(sep);
2412                     buf.addContent(visit(av));
2413                     sep = " ";
2414                 }
2415                 return buf;
2416             }
2417             @Override
2418             protected Content defaultAction(Object o, Void p) {
2419                 return new StringContent(annotationValue.toString());
2420             }
2421         }.visit(annotationValue);
2422     }
2423 
2424     protected TableHeader getPackageTableHeader() {
2425         return new TableHeader(contents.packageLabel, contents.descriptionLabel);
2426     }
2427 
2428     /**
2429      * Returns an HtmlTree for the SCRIPT tag.
2430      *
2431      * @return an HtmlTree for the SCRIPT tag
2432      */
2433     protected Script getWinTitleScript() {
2434         Script script = new Script();
2435         if (winTitle != null && winTitle.length() > 0) {
2436             script.append("<!--\n" +
2437                     "    try {\n" +
2438                     "        if (location.href.indexOf('is-external=true') == -1) {\n" +
2439                     "            parent.document.title=")
2440                     .appendStringLiteral(winTitle)
2441                     .append(";\n" +
2442                     "        }\n" +
2443                     "    }\n" +
2444                     "    catch(err) {\n" +
2445                     "    }\n" +
2446                     "//-->\n");
2447         }
2448         return script;
2449     }
2450 
2451     /**
2452      * Returns an HtmlTree for the BODY tag.
2453      *
2454      * @param includeScript  set true if printing windowtitle script
2455      * @param title title for the window
2456      * @return an HtmlTree for the BODY tag
2457      */
2458     public HtmlTree getBody(boolean includeScript, String title) {
2459         HtmlTree body = new HtmlTree(HtmlTag.BODY);
2460         // Set window title string which is later printed
2461         this.winTitle = title;
2462         // Don't print windowtitle script for overview-frame, allclasses-frame
2463         // and package-frame
2464         if (includeScript) {
2465             this.mainBodyScript = getWinTitleScript();
2466             body.addContent(mainBodyScript.asContent());
2467             Content noScript = HtmlTree.NOSCRIPT(HtmlTree.DIV(contents.noScriptMessage));
2468             body.addContent(noScript);
2469         }
2470         return body;
2471     }
2472 
2473     Script getMainBodyScript() {
2474         return mainBodyScript;
2475     }
2476 }