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