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