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