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