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