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