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