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