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