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(getDeprecatedPhrase(te));
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                HtmlTree scriptTree = HtmlTree.SCRIPT();
651                String scriptCode = "<!--\n"
652                        + "$('.navPadding').css('padding-top', $('.fixedNav').css(\"height\"));\n"
653                        + "//-->\n";
654                RawHtml scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL));
655                scriptTree.addContent(scriptContent);
656                tree.addContent(scriptTree);
657            } else {
658                subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_BOTTOM));
659                tree.addContent(subDiv);
660                tree.addContent(HtmlConstants.END_OF_BOTTOM_NAVBAR);
661            }
662            if (configuration.allowTag(HtmlTag.NAV)) {
663                htmlTree.addContent(tree);
664            }
665        }
666    }
667
668    /**
669     * Get the word "NEXT" to indicate that no link is available.  Override
670     * this method to customize next link.
671     *
672     * @return a content tree for the link
673     */
674    protected Content getNavLinkNext() {
675        return getNavLinkNext(null);
676    }
677
678    /**
679     * Get the word "PREV" to indicate that no link is available.  Override
680     * this method to customize prev link.
681     *
682     * @return a content tree for the link
683     */
684    protected Content getNavLinkPrevious() {
685        return getNavLinkPrevious(null);
686    }
687
688    /**
689     * Do nothing. This is the default method.
690     */
691    protected void addSummaryDetailLinks(Content navDiv) {
692    }
693
694    /**
695     * Get link to the "overview-summary.html" page.
696     *
697     * @return a content tree for the link
698     */
699    protected Content getNavLinkContents() {
700        Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.overviewSummary(configuration.frames)),
701                contents.overviewLabel, "", "");
702        Content li = HtmlTree.LI(linkContent);
703        return li;
704    }
705
706    /**
707     * Get link to the module summary page for the module passed.
708     *
709     * @param mdle Module to which link will be generated
710     * @return a content tree for the link
711     */
712    protected Content getNavLinkModule(ModuleElement mdle) {
713        Content linkContent = getModuleLink(mdle, contents.moduleLabel);
714        Content li = HtmlTree.LI(linkContent);
715        return li;
716    }
717
718    /**
719     * Get the word "Module", to indicate that link is not available here.
720     *
721     * @return a content tree for the link
722     */
723    protected Content getNavLinkModule() {
724        Content li = HtmlTree.LI(contents.moduleLabel);
725        return li;
726    }
727
728    /**
729     * Get link to the "package-summary.html" page for the package passed.
730     *
731     * @param pkg Package to which link will be generated
732     * @return a content tree for the link
733     */
734    protected Content getNavLinkPackage(PackageElement pkg) {
735        Content linkContent = getPackageLink(pkg, contents.packageLabel);
736        Content li = HtmlTree.LI(linkContent);
737        return li;
738    }
739
740    /**
741     * Get the word "Package" , to indicate that link is not available here.
742     *
743     * @return a content tree for the link
744     */
745    protected Content getNavLinkPackage() {
746        Content li = HtmlTree.LI(contents.packageLabel);
747        return li;
748    }
749
750    /**
751     * Get the word "Use", to indicate that link is not available.
752     *
753     * @return a content tree for the link
754     */
755    protected Content getNavLinkClassUse() {
756        Content li = HtmlTree.LI(contents.useLabel);
757        return li;
758    }
759
760    /**
761     * Get link for previous file.
762     *
763     * @param prev File name for the prev link
764     * @return a content tree for the link
765     */
766    public Content getNavLinkPrevious(DocPath prev) {
767        Content li;
768        if (prev != null) {
769            li = HtmlTree.LI(getHyperLink(prev, contents.prevLabel, "", ""));
770        }
771        else
772            li = HtmlTree.LI(contents.prevLabel);
773        return li;
774    }
775
776    /**
777     * Get link for next file.  If next is null, just print the label
778     * without linking it anywhere.
779     *
780     * @param next File name for the next link
781     * @return a content tree for the link
782     */
783    public Content getNavLinkNext(DocPath next) {
784        Content li;
785        if (next != null) {
786            li = HtmlTree.LI(getHyperLink(next, contents.nextLabel, "", ""));
787        }
788        else
789            li = HtmlTree.LI(contents.nextLabel);
790        return li;
791    }
792
793    /**
794     * Get "FRAMES" link, to switch to the frame version of the output.
795     *
796     * @param link File to be linked, "index.html"
797     * @return a content tree for the link
798     */
799    protected Content getNavShowLists(DocPath link) {
800        DocLink dl = new DocLink(link, path.getPath(), null);
801        Content framesContent = getHyperLink(dl, contents.framesLabel, "", "_top");
802        Content li = HtmlTree.LI(framesContent);
803        return li;
804    }
805
806    /**
807     * Get "FRAMES" link, to switch to the frame version of the output.
808     *
809     * @return a content tree for the link
810     */
811    protected Content getNavShowLists() {
812        return getNavShowLists(pathToRoot.resolve(DocPaths.INDEX));
813    }
814
815    /**
816     * Get "NO FRAMES" link, to switch to the non-frame version of the output.
817     *
818     * @param link File to be linked
819     * @return a content tree for the link
820     */
821    protected Content getNavHideLists(DocPath link) {
822        Content noFramesContent = getHyperLink(link, contents.noFramesLabel, "", "_top");
823        Content li = HtmlTree.LI(noFramesContent);
824        return li;
825    }
826
827    /**
828     * Get "Tree" link in the navigation bar. If there is only one package
829     * specified on the command line, then the "Tree" link will be to the
830     * only "package-tree.html" file otherwise it will be to the
831     * "overview-tree.html" file.
832     *
833     * @return a content tree for the link
834     */
835    protected Content getNavLinkTree() {
836        List<PackageElement> packages = new ArrayList<>(configuration.getSpecifiedPackageElements());
837        DocPath docPath = packages.size() == 1 && configuration.getSpecifiedTypeElements().isEmpty()
838                ? pathString(packages.get(0), DocPaths.PACKAGE_TREE)
839                : pathToRoot.resolve(DocPaths.OVERVIEW_TREE);
840        return HtmlTree.LI(getHyperLink(docPath, contents.treeLabel, "", ""));
841    }
842
843    /**
844     * Get the overview tree link for the main tree.
845     *
846     * @param label the label for the link
847     * @return a content tree for the link
848     */
849    protected Content getNavLinkMainTree(String label) {
850        Content mainTreeContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE),
851                new StringContent(label));
852        Content li = HtmlTree.LI(mainTreeContent);
853        return li;
854    }
855
856    /**
857     * Get the word "Class", to indicate that class link is not available.
858     *
859     * @return a content tree for the link
860     */
861    protected Content getNavLinkClass() {
862        Content li = HtmlTree.LI(contents.classLabel);
863        return li;
864    }
865
866    /**
867     * Get "Deprecated" API link in the navigation bar.
868     *
869     * @return a content tree for the link
870     */
871    protected Content getNavLinkDeprecated() {
872        Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.DEPRECATED_LIST),
873                contents.deprecatedLabel, "", "");
874        Content li = HtmlTree.LI(linkContent);
875        return li;
876    }
877
878    /**
879     * Get link for generated index. If the user has used "-splitindex"
880     * command line option, then link to file "index-files/index-1.html" is
881     * generated otherwise link to file "index-all.html" is generated.
882     *
883     * @return a content tree for the link
884     */
885    protected Content getNavLinkClassIndex() {
886        Content allClassesContent = getHyperLink(pathToRoot.resolve(
887                DocPaths.AllClasses(configuration.frames)),
888                contents.allClassesLabel, "", "");
889        Content li = HtmlTree.LI(allClassesContent);
890        return li;
891    }
892
893    /**
894     * Get link for generated class index.
895     *
896     * @return a content tree for the link
897     */
898    protected Content getNavLinkIndex() {
899        Content linkContent = getHyperLink(pathToRoot.resolve(
900                (configuration.splitindex
901                    ? DocPaths.INDEX_FILES.resolve(DocPaths.indexN(1))
902                    : DocPaths.INDEX_ALL)),
903            contents.indexLabel, "", "");
904        Content li = HtmlTree.LI(linkContent);
905        return li;
906    }
907
908    /**
909     * Get help file link. If user has provided a help file, then generate a
910     * link to the user given file, which is already copied to current or
911     * destination directory.
912     *
913     * @return a content tree for the link
914     */
915    protected Content getNavLinkHelp() {
916        String helpfile = configuration.helpfile;
917        DocPath helpfilenm;
918        if (helpfile.isEmpty()) {
919            helpfilenm = DocPaths.HELP_DOC;
920        } else {
921            DocFile file = DocFile.createFileForInput(configuration, helpfile);
922            helpfilenm = DocPath.create(file.getName());
923        }
924        Content linkContent = getHyperLink(pathToRoot.resolve(helpfilenm),
925                contents.helpLabel, "", "");
926        Content li = HtmlTree.LI(linkContent);
927        return li;
928    }
929
930    /**
931     * Add gap between navigation bar elements.
932     *
933     * @param liNav the content tree to which the gap will be added
934     */
935    protected void addNavGap(Content liNav) {
936        liNav.addContent(Contents.SPACE);
937        liNav.addContent("|");
938        liNav.addContent(Contents.SPACE);
939    }
940
941    /**
942     * Get summary table header.
943     *
944     * @param header the header for the table
945     * @param scope the scope of the headers
946     * @return a content tree for the header
947     */
948    public Content getSummaryTableHeader(List<String> header, String scope) {
949        Content tr = new HtmlTree(HtmlTag.TR);
950        final int size = header.size();
951        Content tableHeader;
952        if (size == 2) {
953            tableHeader = new StringContent(header.get(0));
954            tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader));
955            tableHeader = new StringContent(header.get(1));
956            tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader));
957            return tr;
958        }
959        for (int i = 0; i < size; i++) {
960            tableHeader = new StringContent(header.get(i));
961            if (i == 0)
962                tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader));
963            else if (i == 1)
964                tr.addContent(HtmlTree.TH(HtmlStyle.colSecond, scope, tableHeader));
965            else if (i == (size - 1))
966                tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader));
967            else
968                tr.addContent(HtmlTree.TH(scope, tableHeader));
969        }
970        return tr;
971    }
972
973    /**
974     * Get table caption.
975     *
976     * @param rawText the caption for the table which could be raw Html
977     * @return a content tree for the caption
978     */
979    public Content getTableCaption(Content title) {
980        Content captionSpan = HtmlTree.SPAN(title);
981        Content space = Contents.SPACE;
982        Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space);
983        Content caption = HtmlTree.CAPTION(captionSpan);
984        caption.addContent(tabSpan);
985        return caption;
986    }
987
988    /**
989     * Get the marker anchor which will be added to the documentation tree.
990     *
991     * @param anchorName the anchor name attribute
992     * @return a content tree for the marker anchor
993     */
994    public Content getMarkerAnchor(String anchorName) {
995        return getMarkerAnchor(getName(anchorName), null);
996    }
997
998    /**
999     * Get the marker anchor which will be added to the documentation tree.
1000     *
1001     * @param sectionName the section name anchor attribute for page
1002     * @return a content tree for the marker anchor
1003     */
1004    public Content getMarkerAnchor(SectionName sectionName) {
1005        return getMarkerAnchor(sectionName.getName(), null);
1006    }
1007
1008    /**
1009     * Get the marker anchor which will be added to the documentation tree.
1010     *
1011     * @param sectionName the section name anchor attribute for page
1012     * @param anchorName the anchor name combined with section name attribute for the page
1013     * @return a content tree for the marker anchor
1014     */
1015    public Content getMarkerAnchor(SectionName sectionName, String anchorName) {
1016        return getMarkerAnchor(sectionName.getName() + getName(anchorName), null);
1017    }
1018
1019    /**
1020     * Get the marker anchor which will be added to the documentation tree.
1021     *
1022     * @param anchorName the anchor name or id attribute
1023     * @param anchorContent the content that should be added to the anchor
1024     * @return a content tree for the marker anchor
1025     */
1026    public Content getMarkerAnchor(String anchorName, Content anchorContent) {
1027        if (anchorContent == null)
1028            anchorContent = new Comment(" ");
1029        Content markerAnchor = HtmlTree.A(configuration.htmlVersion, anchorName, anchorContent);
1030        return markerAnchor;
1031    }
1032
1033    /**
1034     * Returns a packagename content.
1035     *
1036     * @param packageElement the package to check
1037     * @return package name content
1038     */
1039    public Content getPackageName(PackageElement packageElement) {
1040        return packageElement == null || packageElement.isUnnamed()
1041                ? contents.defaultPackageLabel
1042                : getPackageLabel(packageElement.getQualifiedName());
1043    }
1044
1045    /**
1046     * Returns a package name label.
1047     *
1048     * @param packageName the package name
1049     * @return the package name content
1050     */
1051    public Content getPackageLabel(CharSequence packageName) {
1052        return new StringContent(packageName);
1053    }
1054
1055    /**
1056     * Return the path to the class page for a typeElement.
1057     *
1058     * @param te   TypeElement for which the path is requested.
1059     * @param name Name of the file(doesn't include path).
1060     */
1061    protected DocPath pathString(TypeElement te, DocPath name) {
1062        return pathString(utils.containingPackage(te), name);
1063    }
1064
1065    /**
1066     * Return path to the given file name in the given package. So if the name
1067     * passed is "Object.html" and the name of the package is "java.lang", and
1068     * if the relative path is "../.." then returned string will be
1069     * "../../java/lang/Object.html"
1070     *
1071     * @param packageElement Package in which the file name is assumed to be.
1072     * @param name File name, to which path string is.
1073     */
1074    protected DocPath pathString(PackageElement packageElement, DocPath name) {
1075        return pathToRoot.resolve(DocPath.forPackage(packageElement).resolve(name));
1076    }
1077
1078    /**
1079     * Given a package, return the name to be used in HTML anchor tag.
1080     * @param packageElement the package.
1081     * @return the name to be used in HTML anchor tag.
1082     */
1083    public String getPackageAnchorName(PackageElement packageElement) {
1084        return packageElement == null || packageElement.isUnnamed()
1085                ? SectionName.UNNAMED_PACKAGE_ANCHOR.getName()
1086                : utils.getPackageName(packageElement);
1087    }
1088
1089    /**
1090     * Return the link to the given package.
1091     *
1092     * @param packageElement the package to link to.
1093     * @param label the label for the link.
1094     * @return a content tree for the package link.
1095     */
1096    public Content getPackageLink(PackageElement packageElement, CharSequence label) {
1097        return getPackageLink(packageElement, new StringContent(label));
1098    }
1099
1100    public Content getPackageLink(PackageElement packageElement) {
1101        StringContent content =  packageElement.isUnnamed()
1102                ? new StringContent()
1103                : new StringContent(utils.getPackageName(packageElement));
1104        return getPackageLink(packageElement, content);
1105    }
1106
1107    /**
1108     * Return the link to the given package.
1109     *
1110     * @param packageElement the package to link to.
1111     * @param label the label for the link.
1112     * @return a content tree for the package link.
1113     */
1114    public Content getPackageLink(PackageElement packageElement, Content label) {
1115        boolean included = packageElement != null && utils.isIncluded(packageElement);
1116        if (!included) {
1117            for (PackageElement p : configuration.packages) {
1118                if (p.equals(packageElement)) {
1119                    included = true;
1120                    break;
1121                }
1122            }
1123        }
1124        if (included || packageElement == null) {
1125            return getHyperLink(pathString(packageElement, DocPaths.PACKAGE_SUMMARY),
1126                    label);
1127        } else {
1128            DocLink crossPkgLink = getCrossPackageLink(utils.getPackageName(packageElement));
1129            if (crossPkgLink != null) {
1130                return getHyperLink(crossPkgLink, label);
1131            } else {
1132                return label;
1133            }
1134        }
1135    }
1136
1137    /**
1138     * Get Module link.
1139     *
1140     * @param mdle the module being documented
1141     * @param label tag for the link
1142     * @return a content for the module link
1143     */
1144    public Content getModuleLink(ModuleElement mdle, Content label) {
1145        boolean included = utils.isIncluded(mdle);
1146        return (included)
1147                ? getHyperLink(pathToRoot.resolve(DocPaths.moduleSummary(mdle)), label, "", "")
1148                : label;
1149    }
1150
1151    public Content interfaceName(TypeElement typeElement, boolean qual) {
1152        Content name = new StringContent((qual)
1153                ? typeElement.getQualifiedName()
1154                : utils.getSimpleName(typeElement));
1155        return (utils.isInterface(typeElement)) ?  HtmlTree.SPAN(HtmlStyle.interfaceName, name) : name;
1156    }
1157
1158    /**
1159     * Add the link to the content tree.
1160     *
1161     * @param typeElement program element typeElement for which the link will be added
1162     * @param label label for the link
1163     * @param htmltree the content tree to which the link will be added
1164     */
1165    public void addSrcLink(Element typeElement, Content label, Content htmltree) {
1166        if (typeElement == null) {
1167            return;
1168        }
1169        TypeElement te = utils.getEnclosingTypeElement(typeElement);
1170        if (te == null) {
1171            // must be a typeElement since in has no containing class.
1172            te = (TypeElement) typeElement;
1173        }
1174        DocPath href = pathToRoot
1175                .resolve(DocPaths.SOURCE_OUTPUT)
1176                .resolve(DocPath.forClass(utils, te));
1177        Content linkContent = getHyperLink(href
1178                .fragment(SourceToHTMLConverter.getAnchorName(utils, typeElement)), label, "", "");
1179        htmltree.addContent(linkContent);
1180    }
1181
1182    /**
1183     * Return the link to the given class.
1184     *
1185     * @param linkInfo the information about the link.
1186     *
1187     * @return the link for the given class.
1188     */
1189    public Content getLink(LinkInfoImpl linkInfo) {
1190        LinkFactoryImpl factory = new LinkFactoryImpl(this);
1191        return factory.getLink(linkInfo);
1192    }
1193
1194    /**
1195     * Return the type parameters for the given class.
1196     *
1197     * @param linkInfo the information about the link.
1198     * @return the type for the given class.
1199     */
1200    public Content getTypeParameterLinks(LinkInfoImpl linkInfo) {
1201        LinkFactoryImpl factory = new LinkFactoryImpl(this);
1202        return factory.getTypeParameterLinks(linkInfo, false);
1203    }
1204
1205    /*************************************************************
1206     * Return a class cross link to external class documentation.
1207     * The name must be fully qualified to determine which package
1208     * the class is in.  The -link option does not allow users to
1209     * link to external classes in the "default" package.
1210     *
1211     * @param qualifiedClassName the qualified name of the external class.
1212     * @param refMemName the name of the member being referenced.  This should
1213     * be null or empty string if no member is being referenced.
1214     * @param label the label for the external link.
1215     * @param strong true if the link should be strong.
1216     * @param style the style of the link.
1217     * @param code true if the label should be code font.
1218     */
1219    public Content getCrossClassLink(String qualifiedClassName, String refMemName,
1220                                    Content label, boolean strong, String style,
1221                                    boolean code) {
1222        String className = "";
1223        String packageName = qualifiedClassName == null ? "" : qualifiedClassName;
1224        int periodIndex;
1225        while ((periodIndex = packageName.lastIndexOf('.')) != -1) {
1226            className = packageName.substring(periodIndex + 1, packageName.length()) +
1227                (className.length() > 0 ? "." + className : "");
1228            Content defaultLabel = new StringContent(className);
1229            if (code)
1230                defaultLabel = HtmlTree.CODE(defaultLabel);
1231            packageName = packageName.substring(0, periodIndex);
1232            if (getCrossPackageLink(packageName) != null) {
1233                /*
1234                The package exists in external documentation, so link to the external
1235                class (assuming that it exists).  This is definitely a limitation of
1236                the -link option.  There are ways to determine if an external package
1237                exists, but no way to determine if the external class exists.  We just
1238                have to assume that it does.
1239                */
1240                DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot,
1241                                className + ".html", refMemName);
1242                return getHyperLink(link,
1243                    (label == null) || label.isEmpty() ? defaultLabel : label,
1244                    strong, style,
1245                    configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName),
1246                    "");
1247            }
1248        }
1249        return null;
1250    }
1251
1252    public boolean isClassLinkable(TypeElement typeElement) {
1253        if (utils.isIncluded(typeElement)) {
1254            return configuration.isGeneratedDoc(typeElement);
1255        }
1256        return configuration.extern.isExternal(typeElement);
1257    }
1258
1259    public DocLink getCrossPackageLink(String pkgName) {
1260        return configuration.extern.getExternalLink(pkgName, pathToRoot,
1261            DocPaths.PACKAGE_SUMMARY.getPath());
1262    }
1263
1264    /**
1265     * Get the class link.
1266     *
1267     * @param context the id of the context where the link will be added
1268     * @param element to link to
1269     * @return a content tree for the link
1270     */
1271    public Content getQualifiedClassLink(LinkInfoImpl.Kind context, Element element) {
1272        LinkInfoImpl linkInfoImpl = new LinkInfoImpl(configuration, context, (TypeElement)element);
1273        return getLink(linkInfoImpl.label(utils.getFullyQualifiedName(element)));
1274    }
1275
1276    /**
1277     * Add the class link.
1278     *
1279     * @param context the id of the context where the link will be added
1280     * @param typeElement to link to
1281     * @param contentTree the content tree to which the link will be added
1282     */
1283    public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) {
1284        addPreQualifiedClassLink(context, typeElement, false, contentTree);
1285    }
1286
1287    /**
1288     * Retrieve the class link with the package portion of the label in
1289     * plain text.  If the qualifier is excluded, it will not be included in the
1290     * link label.
1291     *
1292     * @param typeElement the class to link to.
1293     * @param isStrong true if the link should be strong.
1294     * @return the link with the package portion of the label in plain text.
1295     */
1296    public Content getPreQualifiedClassLink(LinkInfoImpl.Kind context,
1297            TypeElement typeElement, boolean isStrong) {
1298        ContentBuilder classlink = new ContentBuilder();
1299        PackageElement pkg = utils.containingPackage(typeElement);
1300        if (pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) {
1301            classlink.addContent(getEnclosingPackageName(typeElement));
1302        }
1303        classlink.addContent(getLink(new LinkInfoImpl(configuration,
1304                context, typeElement).label(utils.getSimpleName(typeElement)).strong(isStrong)));
1305        return classlink;
1306    }
1307
1308    /**
1309     * Add the class link with the package portion of the label in
1310     * plain text. If the qualifier is excluded, it will not be included in the
1311     * link label.
1312     *
1313     * @param context the id of the context where the link will be added
1314     * @param typeElement the class to link to
1315     * @param isStrong true if the link should be strong
1316     * @param contentTree the content tree to which the link with be added
1317     */
1318    public void addPreQualifiedClassLink(LinkInfoImpl.Kind context,
1319            TypeElement typeElement, boolean isStrong, Content contentTree) {
1320        PackageElement pkg = utils.containingPackage(typeElement);
1321        if(pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) {
1322            contentTree.addContent(getEnclosingPackageName(typeElement));
1323        }
1324        LinkInfoImpl linkinfo = new LinkInfoImpl(configuration, context, typeElement)
1325                .label(utils.getSimpleName(typeElement))
1326                .strong(isStrong);
1327        Content link = getLink(linkinfo);
1328        contentTree.addContent(link);
1329    }
1330
1331    /**
1332     * Add the class link, with only class name as the strong link and prefixing
1333     * plain package name.
1334     *
1335     * @param context the id of the context where the link will be added
1336     * @param typeElement the class to link to
1337     * @param contentTree the content tree to which the link with be added
1338     */
1339    public void addPreQualifiedStrongClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) {
1340        addPreQualifiedClassLink(context, typeElement, true, contentTree);
1341    }
1342
1343    /**
1344     * Get the link for the given member.
1345     *
1346     * @param context the id of the context where the link will be added
1347     * @param element the member being linked to
1348     * @param label the label for the link
1349     * @return a content tree for the element link
1350     */
1351    public Content getDocLink(LinkInfoImpl.Kind context, Element element, CharSequence label) {
1352        return getDocLink(context, utils.getEnclosingTypeElement(element), element,
1353                new StringContent(label));
1354    }
1355
1356    /**
1357     * Return the link for the given member.
1358     *
1359     * @param context the id of the context where the link will be printed.
1360     * @param element the member being linked to.
1361     * @param label the label for the link.
1362     * @param strong true if the link should be strong.
1363     * @return the link for the given member.
1364     */
1365    public Content getDocLink(LinkInfoImpl.Kind context, Element element, CharSequence label,
1366            boolean strong) {
1367        return getDocLink(context, utils.getEnclosingTypeElement(element), element, label, strong);
1368    }
1369
1370    /**
1371     * Return the link for the given member.
1372     *
1373     * @param context the id of the context where the link will be printed.
1374     * @param typeElement the typeElement that we should link to.  This is not
1375                 necessarily equal to element.containingClass().  We may be
1376                 inheriting comments.
1377     * @param element the member being linked to.
1378     * @param label the label for the link.
1379     * @param strong true if the link should be strong.
1380     * @return the link for the given member.
1381     */
1382    public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1383            CharSequence label, boolean strong) {
1384        return getDocLink(context, typeElement, element, label, strong, false);
1385    }
1386
1387    public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1388            Content label, boolean strong) {
1389        return getDocLink(context, typeElement, element, label, strong, false);
1390    }
1391
1392   /**
1393     * Return the link for the given member.
1394     *
1395     * @param context the id of the context where the link will be printed.
1396     * @param typeElement the typeElement that we should link to.  This is not
1397                 necessarily equal to element.containingClass().  We may be
1398                 inheriting comments.
1399     * @param element the member being linked to.
1400     * @param label the label for the link.
1401     * @param strong true if the link should be strong.
1402     * @param isProperty true if the element parameter is a JavaFX property.
1403     * @return the link for the given member.
1404     */
1405    public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1406            CharSequence label, boolean strong, boolean isProperty) {
1407        return getDocLink(context, typeElement, element, new StringContent(check(label)), strong, isProperty);
1408    }
1409
1410    CharSequence check(CharSequence s) {
1411        Matcher m = IMPROPER_HTML_CHARS.matcher(s);
1412        if (m.matches()) {
1413            throw new IllegalArgumentException(s.toString());
1414        }
1415        return s;
1416    }
1417
1418    public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1419            Content label, boolean strong, boolean isProperty) {
1420        if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) {
1421            return label;
1422        } else if (utils.isExecutableElement(element)) {
1423            ExecutableElement ee = (ExecutableElement)element;
1424            return getLink(new LinkInfoImpl(configuration, context, typeElement)
1425                .label(label)
1426                .where(getName(getAnchor(ee, isProperty)))
1427                .strong(strong));
1428        } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) {
1429            return getLink(new LinkInfoImpl(configuration, context, typeElement)
1430                .label(label)
1431                .where(getName(element.getSimpleName().toString()))
1432                .strong(strong));
1433        } else {
1434            return label;
1435        }
1436    }
1437
1438    /**
1439     * Return the link for the given member.
1440     *
1441     * @param context the id of the context where the link will be added
1442     * @param typeElement the typeElement that we should link to.  This is not
1443                 necessarily equal to element.containingClass().  We may be
1444                 inheriting comments
1445     * @param element the member being linked to
1446     * @param label the label for the link
1447     * @return the link for the given member
1448     */
1449    public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
1450            Content label) {
1451        if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) {
1452            return label;
1453        } else if (utils.isExecutableElement(element)) {
1454            ExecutableElement emd = (ExecutableElement) element;
1455            return getLink(new LinkInfoImpl(configuration, context, typeElement)
1456                .label(label)
1457                .where(getName(getAnchor(emd))));
1458        } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) {
1459            return getLink(new LinkInfoImpl(configuration, context, typeElement)
1460                .label(label).where(getName(element.getSimpleName().toString())));
1461        } else {
1462            return label;
1463        }
1464    }
1465
1466    public String getAnchor(ExecutableElement executableElement) {
1467        return getAnchor(executableElement, false);
1468    }
1469
1470    public String getAnchor(ExecutableElement executableElement, boolean isProperty) {
1471        if (isProperty) {
1472            return executableElement.getSimpleName().toString();
1473        }
1474        String signature = utils.signature(executableElement);
1475        StringBuilder signatureParsed = new StringBuilder();
1476        int counter = 0;
1477        for (int i = 0; i < signature.length(); i++) {
1478            char c = signature.charAt(i);
1479            if (c == '<') {
1480                counter++;
1481            } else if (c == '>') {
1482                counter--;
1483            } else if (counter == 0) {
1484                signatureParsed.append(c);
1485            }
1486        }
1487        return utils.getSimpleName(executableElement) + signatureParsed.toString();
1488    }
1489
1490    public Content seeTagToContent(Element element, DocTree see) {
1491
1492        Kind kind = see.getKind();
1493        if (!(kind == LINK || kind == SEE || kind == LINK_PLAIN)) {
1494            return new ContentBuilder();
1495        }
1496
1497        CommentHelper ch = utils.getCommentHelper(element);
1498        String tagName = ch.getTagName(see);
1499        String seetext = replaceDocRootDir(utils.normalizeNewlines(ch.getText(see)).toString());
1500        // Check if @see is an href or "string"
1501        if (seetext.startsWith("<") || seetext.startsWith("\"")) {
1502            return new RawHtml(seetext);
1503        }
1504        boolean isLinkPlain = kind == LINK_PLAIN;
1505        Content label = plainOrCode(isLinkPlain, new RawHtml(ch.getLabel(configuration, see)));
1506
1507        //The text from the @see tag.  We will output this text when a label is not specified.
1508        Content text = plainOrCode(kind == LINK_PLAIN, new RawHtml(seetext));
1509
1510        TypeElement refClass = ch.getReferencedClass(configuration, see);
1511        String refClassName =  ch.getReferencedClassName(configuration, see);
1512        Element refMem =       ch.getReferencedMember(configuration, see);
1513        String refMemName =    ch.getReferencedMemberName(see);
1514
1515        if (refMemName == null && refMem != null) {
1516            refMemName = refMem.toString();
1517        }
1518        if (refClass == null) {
1519            //@see is not referencing an included class
1520            PackageElement refPackage = ch.getReferencedPackage(configuration, see);
1521            if (refPackage != null && utils.isIncluded(refPackage)) {
1522                //@see is referencing an included package
1523                if (label.isEmpty())
1524                    label = plainOrCode(isLinkPlain,
1525                            new StringContent(refPackage.getQualifiedName()));
1526                return getPackageLink(refPackage, label);
1527            } else {
1528                // @see is not referencing an included class or package.  Check for cross links.
1529                Content classCrossLink;
1530                DocLink packageCrossLink = getCrossPackageLink(refClassName);
1531                if (packageCrossLink != null) {
1532                    // Package cross link found
1533                    return getHyperLink(packageCrossLink,
1534                        (label.isEmpty() ? text : label));
1535                } else if ((classCrossLink = getCrossClassLink(refClassName,
1536                        refMemName, label, false, "", !isLinkPlain)) != null) {
1537                    // Class cross link found (possibly to a member in the class)
1538                    return classCrossLink;
1539                } else {
1540                    // No cross link found so print warning
1541                    messages.warning(ch.getDocTreePath(see),
1542                            "doclet.see.class_or_package_not_found",
1543                            "@" + tagName,
1544                            seetext);
1545                    return (label.isEmpty() ? text: label);
1546                }
1547            }
1548        } else if (refMemName == null) {
1549            // Must be a class reference since refClass is not null and refMemName is null.
1550            if (label.isEmpty()) {
1551                /*
1552                 * it seems to me this is the right thing to do, but it causes comparator failures.
1553                 */
1554                if (!configuration.backwardCompatibility) {
1555                    StringContent content = utils.isEnclosingPackageIncluded(refClass)
1556                            ? new StringContent(utils.getSimpleName(refClass))
1557                            : new StringContent(utils.getFullyQualifiedName(refClass));
1558                    label = plainOrCode(isLinkPlain, content);
1559                } else {
1560                    label = plainOrCode(isLinkPlain,
1561                            new StringContent(utils.getSimpleName(refClass)));
1562                }
1563
1564            }
1565            return getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, refClass)
1566                    .label(label));
1567        } else if (refMem == null) {
1568            // Must be a member reference since refClass is not null and refMemName is not null.
1569            // However, refMem is null, so this referenced member does not exist.
1570            return (label.isEmpty() ? text: label);
1571        } else {
1572            // Must be a member reference since refClass is not null and refMemName is not null.
1573            // refMem is not null, so this @see tag must be referencing a valid member.
1574            TypeElement containing = utils.getEnclosingTypeElement(refMem);
1575            if (ch.getText(see).trim().startsWith("#") &&
1576                ! (utils.isPublic(containing) || utils.isLinkable(containing))) {
1577                // Since the link is relative and the holder is not even being
1578                // documented, this must be an inherited link.  Redirect it.
1579                // The current class either overrides the referenced member or
1580                // inherits it automatically.
1581                if (this instanceof ClassWriterImpl) {
1582                    containing = ((ClassWriterImpl) this).getTypeElement();
1583                } else if (!utils.isPublic(containing)) {
1584                    messages.warning(
1585                        ch.getDocTreePath(see), "doclet.see.class_or_package_not_accessible",
1586                        tagName, utils.getFullyQualifiedName(containing));
1587                } else {
1588                    messages.warning(
1589                        ch.getDocTreePath(see), "doclet.see.class_or_package_not_found",
1590                        tagName, seetext);
1591                }
1592            }
1593            if (configuration.currentTypeElement != containing) {
1594                refMemName = (utils.isConstructor(refMem))
1595                        ? refMemName
1596                        : utils.getSimpleName(containing) + "." + refMemName;
1597            }
1598            if (utils.isExecutableElement(refMem)) {
1599                if (refMemName.indexOf('(') < 0) {
1600                    refMemName += utils.makeSignature((ExecutableElement)refMem, true);
1601                }
1602            }
1603
1604            text = plainOrCode(kind == LINK_PLAIN, new StringContent(refMemName));
1605
1606            return getDocLink(LinkInfoImpl.Kind.SEE_TAG, containing,
1607                    refMem, (label.isEmpty() ? text: label), false);
1608        }
1609    }
1610
1611    private Content plainOrCode(boolean plain, Content body) {
1612        return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body);
1613    }
1614
1615    /**
1616     * Add the inline comment.
1617     *
1618     * @param element the Element for which the inline comment will be added
1619     * @param tag the inline tag to be added
1620     * @param htmltree the content tree to which the comment will be added
1621     */
1622    public void addInlineComment(Element element, DocTree tag, Content htmltree) {
1623        CommentHelper ch = utils.getCommentHelper(element);
1624        List<? extends DocTree> description = ch.getDescription(configuration, tag);
1625        addCommentTags(element, tag, description, false, false, htmltree);
1626    }
1627
1628    /**
1629     * Get the deprecated phrase as content.
1630     *
1631     * @param e the Element for which the inline deprecated comment will be added
1632     * @return a content tree for the deprecated phrase.
1633     */
1634    public Content getDeprecatedPhrase(Element e) {
1635        return (utils.isDeprecatedForRemoval(e))
1636                ? contents.deprecatedForRemovalPhrase
1637                : contents.deprecatedPhrase;
1638    }
1639
1640    /**
1641     * Add the inline deprecated comment.
1642     *
1643     * @param e the Element for which the inline deprecated comment will be added
1644     * @param tag the inline tag to be added
1645     * @param htmltree the content tree to which the comment will be added
1646     */
1647    public void addInlineDeprecatedComment(Element e, DocTree tag, Content htmltree) {
1648        CommentHelper ch = utils.getCommentHelper(e);
1649        addCommentTags(e, ch.getBody(configuration, tag), true, false, htmltree);
1650    }
1651
1652    /**
1653     * Adds the summary content.
1654     *
1655     * @param element the Element for which the summary will be generated
1656     * @param htmltree the documentation tree to which the summary will be added
1657     */
1658    public void addSummaryComment(Element element, Content htmltree) {
1659        addSummaryComment(element, utils.getFirstSentenceTrees(element), htmltree);
1660    }
1661
1662    /**
1663     * Adds the summary content.
1664     *
1665     * @param element the Element for which the summary will be generated
1666     * @param firstSentenceTags the first sentence tags for the doc
1667     * @param htmltree the documentation tree to which the summary will be added
1668     */
1669    public void addSummaryComment(Element element, List<? extends DocTree> firstSentenceTags, Content htmltree) {
1670        addCommentTags(element, firstSentenceTags, false, true, htmltree);
1671    }
1672
1673    public void addSummaryDeprecatedComment(Element element, DocTree tag, Content htmltree) {
1674        CommentHelper ch = utils.getCommentHelper(element);
1675        List<? extends DocTree> body = ch.getBody(configuration, tag);
1676        addCommentTags(element, ch.getFirstSentenceTrees(configuration, body), true, true, htmltree);
1677    }
1678
1679    /**
1680     * Adds the inline comment.
1681     *
1682     * @param element the Element for which the inline comments will be generated
1683     * @param htmltree the documentation tree to which the inline comments will be added
1684     */
1685    public void addInlineComment(Element element, Content htmltree) {
1686        addCommentTags(element, utils.getFullBody(element), false, false, htmltree);
1687    }
1688
1689    /**
1690     * Adds the comment tags.
1691     *
1692     * @param element the Element for which the comment tags will be generated
1693     * @param tags the first sentence tags for the doc
1694     * @param depr true if it is deprecated
1695     * @param first true if the first sentence tags should be added
1696     * @param htmltree the documentation tree to which the comment tags will be added
1697     */
1698    private void addCommentTags(Element element, List<? extends DocTree> tags, boolean depr,
1699            boolean first, Content htmltree) {
1700        addCommentTags(element, null, tags, depr, first, htmltree);
1701    }
1702
1703    /**
1704     * Adds the comment tags.
1705     *
1706     * @param element for which the comment tags will be generated
1707     * @param holderTag the block tag context for the inline tags
1708     * @param tags the first sentence tags for the doc
1709     * @param depr true if it is deprecated
1710     * @param first true if the first sentence tags should be added
1711     * @param htmltree the documentation tree to which the comment tags will be added
1712     */
1713    private void addCommentTags(Element element, DocTree holderTag, List<? extends DocTree> tags, boolean depr,
1714            boolean first, Content htmltree) {
1715        if(configuration.nocomment){
1716            return;
1717        }
1718        Content div;
1719        Content result = commentTagsToContent(null, element, tags, first);
1720        if (depr) {
1721            Content italic = HtmlTree.SPAN(HtmlStyle.deprecationComment, result);
1722            div = HtmlTree.DIV(HtmlStyle.block, italic);
1723            htmltree.addContent(div);
1724        }
1725        else {
1726            div = HtmlTree.DIV(HtmlStyle.block, result);
1727            htmltree.addContent(div);
1728        }
1729        if (tags.isEmpty()) {
1730            htmltree.addContent(Contents.SPACE);
1731        }
1732    }
1733
1734    boolean ignoreNonInlineTag(DocTree dtree) {
1735        Name name = null;
1736        if (dtree.getKind() == Kind.START_ELEMENT) {
1737            StartElementTree setree = (StartElementTree)dtree;
1738            name = setree.getName();
1739        } else if (dtree.getKind() == Kind.END_ELEMENT) {
1740            EndElementTree eetree = (EndElementTree)dtree;
1741            name = eetree.getName();
1742        }
1743
1744        if (name != null) {
1745            com.sun.tools.doclint.HtmlTag htmlTag = com.sun.tools.doclint.HtmlTag.get(name);
1746            if (htmlTag != null &&
1747                    htmlTag.blockType != com.sun.tools.doclint.HtmlTag.BlockType.INLINE) {
1748                return true;
1749            }
1750        }
1751        return false;
1752    }
1753
1754    boolean isAllWhiteSpace(String body) {
1755        for (int i = 0 ; i < body.length(); i++) {
1756            if (!Character.isWhitespace(body.charAt(i)))
1757                return false;
1758        }
1759        return true;
1760    }
1761
1762    /**
1763     * Converts inline tags and text to text strings, expanding the
1764     * inline tags along the way.  Called wherever text can contain
1765     * an inline tag, such as in comments or in free-form text arguments
1766     * to non-inline tags.
1767     *
1768     * @param holderTag    specific tag where comment resides
1769     * @param element    specific element where comment resides
1770     * @param tags   array of text tags and inline tags (often alternating)
1771               present in the text of interest for this element
1772     * @param isFirstSentence  true if text is first sentence
1773     * @return a Content object
1774     */
1775    public Content commentTagsToContent(DocTree holderTag, Element element,
1776            List<? extends DocTree> tags, boolean isFirstSentence) {
1777
1778        final Content result = new ContentBuilder() {
1779            @Override
1780            public void addContent(CharSequence text) {
1781                super.addContent(utils.normalizeNewlines(text));
1782            }
1783        };
1784        CommentHelper ch = utils.getCommentHelper(element);
1785        // Array of all possible inline tags for this javadoc run
1786        configuration.tagletManager.checkTags(utils, element, tags, true);
1787        for (ListIterator<? extends DocTree> iterator = tags.listIterator(); iterator.hasNext();) {
1788            DocTree tag = iterator.next();
1789             // zap block tags
1790            if (isFirstSentence && ignoreNonInlineTag(tag))
1791                continue;
1792
1793            if (isFirstSentence && iterator.nextIndex() == tags.size() &&
1794                    (tag.getKind() == TEXT && isAllWhiteSpace(ch.getText(tag))))
1795                continue;
1796
1797            boolean allDone = new SimpleDocTreeVisitor<Boolean, Content>() {
1798                // notify the next DocTree handler to take necessary action
1799                boolean commentRemoved = false;
1800
1801                private boolean isLast(DocTree node) {
1802                    return node.equals(tags.get(tags.size() - 1));
1803                }
1804
1805                private boolean isFirst(DocTree node) {
1806                    return node.equals(tags.get(0));
1807                }
1808
1809                private boolean inAnAtag() {
1810                    if (utils.isStartElement(tag)) {
1811                        StartElementTree st = (StartElementTree)tag;
1812                        Name name = st.getName();
1813                        if (name != null) {
1814                            com.sun.tools.doclint.HtmlTag htag =
1815                                    com.sun.tools.doclint.HtmlTag.get(name);
1816                            return htag != null && htag.equals(com.sun.tools.doclint.HtmlTag.A);
1817                        }
1818                    }
1819                    return false;
1820                }
1821
1822                @Override
1823                public Boolean visitAttribute(AttributeTree node, Content c) {
1824                    StringBuilder sb = new StringBuilder(SPACER).append(node.getName());
1825                    if (node.getValueKind() == ValueKind.EMPTY) {
1826                        result.addContent(sb);
1827                        return false;
1828                    }
1829                    sb.append("=");
1830                    String quote;
1831                    switch (node.getValueKind()) {
1832                        case DOUBLE:
1833                            quote = "\"";
1834                            break;
1835                        case SINGLE:
1836                            quote = "\'";
1837                            break;
1838                        default:
1839                            quote = "";
1840                            break;
1841                    }
1842                    sb.append(quote);
1843                    result.addContent(sb);
1844                    Content docRootContent = new ContentBuilder();
1845
1846                    for (DocTree dt : node.getValue()) {
1847                        if (utils.isText(dt) && inAnAtag()) {
1848                            String text = ((TextTree) dt).getBody();
1849                            if (text.startsWith("/..") && !configuration.docrootparent.isEmpty()) {
1850                                result.addContent(configuration.docrootparent);
1851                                docRootContent = new ContentBuilder();
1852                                result.addContent(textCleanup(text.substring(3), isLast(node)));
1853                            } else {
1854                                if (!docRootContent.isEmpty()) {
1855                                    docRootContent = copyDocRootContent(docRootContent);
1856                                } else {
1857                                    text = redirectRelativeLinks(element, (TextTree) dt);
1858                                }
1859                                result.addContent(textCleanup(text, isLast(node)));
1860                            }
1861                        } else {
1862                            docRootContent = copyDocRootContent(docRootContent);
1863                            dt.accept(this, docRootContent);
1864                        }
1865                    }
1866                    copyDocRootContent(docRootContent);
1867                    result.addContent(quote);
1868                    return false;
1869                }
1870
1871                @Override
1872                public Boolean visitComment(CommentTree node, Content c) {
1873                    if (isFirstSentence && isFirst(node)) {
1874                        commentRemoved = true;
1875                        return this.visit(iterator.next(), c);
1876                    }
1877                    result.addContent(new RawHtml(node.getBody()));
1878                    return false;
1879                }
1880
1881                private Content copyDocRootContent(Content content) {
1882                    if (!content.isEmpty()) {
1883                        result.addContent(content);
1884                        return new ContentBuilder();
1885                    }
1886                    return content;
1887                }
1888
1889                @Override
1890                public Boolean visitDocRoot(DocRootTree node, Content c) {
1891                    Content docRootContent = TagletWriter.getInlineTagOutput(element,
1892                            configuration.tagletManager,
1893                            holderTag,
1894                            node,
1895                            getTagletWriterInstance(isFirstSentence));
1896                    if (c != null) {
1897                        c.addContent(docRootContent);
1898                    } else {
1899                        result.addContent(docRootContent);
1900                    }
1901                    return false;
1902                }
1903
1904                @Override
1905                public Boolean visitEndElement(EndElementTree node, Content c) {
1906                    RawHtml rawHtml = new RawHtml("</" + node.getName() + ">");
1907                    result.addContent(rawHtml);
1908                    return false;
1909                }
1910
1911                @Override
1912                public Boolean visitEntity(EntityTree node, Content c) {
1913                    result.addContent(new RawHtml(node.toString()));
1914                    return false;
1915                }
1916
1917                @Override
1918                public Boolean visitErroneous(ErroneousTree node, Content c) {
1919                    messages.warning(ch.getDocTreePath(node),
1920                            "doclet.tag.invalid_usage", node);
1921                    result.addContent(new RawHtml(node.toString()));
1922                    return false;
1923                }
1924
1925                @Override
1926                public Boolean visitInheritDoc(InheritDocTree node, Content c) {
1927                    Content output = TagletWriter.getInlineTagOutput(element,
1928                            configuration.tagletManager, holderTag,
1929                            tag, getTagletWriterInstance(isFirstSentence));
1930                    result.addContent(output);
1931                    // if we obtained the first sentence successfully, nothing more to do
1932                    return (isFirstSentence && !output.isEmpty());
1933                }
1934
1935                @Override
1936                public Boolean visitIndex(IndexTree node, Content p) {
1937                    Content output = TagletWriter.getInlineTagOutput(element,
1938                            configuration.tagletManager, holderTag, tag,
1939                            getTagletWriterInstance(isFirstSentence));
1940                    if (output != null) {
1941                        result.addContent(output);
1942                    }
1943                    return false;
1944                }
1945
1946                @Override
1947                public Boolean visitLink(LinkTree node, Content c) {
1948                    // we need to pass the DocTreeImpl here, so ignore node
1949                    result.addContent(seeTagToContent(element, tag));
1950                    return false;
1951                }
1952
1953                @Override
1954                public Boolean visitLiteral(LiteralTree node, Content c) {
1955                    String s = node.getBody().toString();
1956                    Content content = new StringContent(utils.normalizeNewlines(s));
1957                    if (node.getKind() == CODE)
1958                        content = HtmlTree.CODE(content);
1959                    result.addContent(content);
1960                    return false;
1961                }
1962
1963                @Override
1964                public Boolean visitSee(SeeTree node, Content c) {
1965                    // we need to pass the DocTreeImpl here, so ignore node
1966                    result.addContent(seeTagToContent(element, tag));
1967                    return false;
1968                }
1969
1970                @Override
1971                public Boolean visitStartElement(StartElementTree node, Content c) {
1972                    String text = "<" + node.getName();
1973                    RawHtml rawHtml = new RawHtml(utils.normalizeNewlines(text));
1974                    result.addContent(rawHtml);
1975
1976                    for (DocTree dt : node.getAttributes()) {
1977                        dt.accept(this, null);
1978                    }
1979                    result.addContent(new RawHtml(node.isSelfClosing() ? "/>" : ">"));
1980                    return false;
1981                }
1982
1983                private CharSequence textCleanup(String text, boolean isLast) {
1984                    return textCleanup(text, isLast, false);
1985                }
1986
1987                private CharSequence textCleanup(String text, boolean isLast, boolean trimLeader) {
1988                    if (trimLeader) {
1989                        text = removeLeadingWhitespace(text);
1990                    }
1991                    if (isFirstSentence && isLast) {
1992                        text = removeTrailingWhitespace(text);
1993                    }
1994                    text = utils.replaceTabs(text);
1995                    return utils.normalizeNewlines(text);
1996                }
1997
1998                @Override
1999                public Boolean visitText(TextTree node, Content c) {
2000                    String text = node.getBody();
2001                    result.addContent(new RawHtml(textCleanup(text, isLast(node), commentRemoved)));
2002                    commentRemoved = false;
2003                    return false;
2004                }
2005
2006                @Override
2007                protected Boolean defaultAction(DocTree node, Content c) {
2008                    Content output = TagletWriter.getInlineTagOutput(element,
2009                            configuration.tagletManager, holderTag, tag,
2010                            getTagletWriterInstance(isFirstSentence));
2011                    if (output != null) {
2012                        result.addContent(output);
2013                    }
2014                    return false;
2015                }
2016
2017            }.visit(tag, null);
2018            if (allDone)
2019                break;
2020        }
2021        return result;
2022    }
2023
2024    private String removeTrailingWhitespace(String text) {
2025        char[] buf = text.toCharArray();
2026        for (int i = buf.length - 1; i > 0 ; i--) {
2027            if (!Character.isWhitespace(buf[i]))
2028                return text.substring(0, i + 1);
2029        }
2030        return text;
2031    }
2032
2033    private String removeLeadingWhitespace(String text) {
2034        char[] buf = text.toCharArray();
2035        for (int i = 0; i < buf.length; i++) {
2036            if (!Character.isWhitespace(buf[i])) {
2037                return text.substring(i);
2038            }
2039        }
2040        return text;
2041    }
2042
2043    /**
2044     * Return true if relative links should not be redirected.
2045     *
2046     * @return Return true if a relative link should not be redirected.
2047     */
2048    private boolean shouldNotRedirectRelativeLinks() {
2049        return  this instanceof AnnotationTypeWriter ||
2050                this instanceof ClassWriter ||
2051                this instanceof PackageSummaryWriter;
2052    }
2053
2054    /**
2055     * Suppose a piece of documentation has a relative link.  When you copy
2056     * that documentation to another place such as the index or class-use page,
2057     * that relative link will no longer work.  We should redirect those links
2058     * so that they will work again.
2059     * <p>
2060     * Here is the algorithm used to fix the link:
2061     * <p>
2062     * {@literal <relative link> => docRoot + <relative path to file> + <relative link> }
2063     * <p>
2064     * For example, suppose DocletEnvironment has this link:
2065     * {@literal <a href="package-summary.html">The package Page</a> }
2066     * <p>
2067     * If this link appeared in the index, we would redirect
2068     * the link like this:
2069     *
2070     * {@literal <a href="./com/sun/javadoc/package-summary.html">The package Page</a>}
2071     *
2072     * @param element the Element object whose documentation is being written.
2073     * @param text the text being written.
2074     *
2075     * @return the text, with all the relative links redirected to work.
2076     */
2077    private String redirectRelativeLinks(Element element, TextTree tt) {
2078        String text = tt.getBody();
2079        if (element == null || utils.isOverviewElement(element) || shouldNotRedirectRelativeLinks()) {
2080            return text;
2081        }
2082
2083        DocPath redirectPathFromRoot = new SimpleElementVisitor9<DocPath, Void>() {
2084            @Override
2085            public DocPath visitType(TypeElement e, Void p) {
2086                return DocPath.forPackage(utils.containingPackage(e));
2087            }
2088
2089            @Override
2090            public DocPath visitPackage(PackageElement e, Void p) {
2091                return DocPath.forPackage(e);
2092            }
2093
2094            @Override
2095            public DocPath visitVariable(VariableElement e, Void p) {
2096                return DocPath.forPackage(utils.containingPackage(e));
2097            }
2098
2099            @Override
2100            public DocPath visitExecutable(ExecutableElement e, Void p) {
2101                return DocPath.forPackage(utils.containingPackage(e));
2102            }
2103
2104            @Override
2105            protected DocPath defaultAction(Element e, Void p) {
2106                return null;
2107            }
2108        }.visit(element);
2109        if (redirectPathFromRoot == null) {
2110            return text;
2111        }
2112        String lower = Utils.toLowerCase(text);
2113        if (!(lower.startsWith("mailto:")
2114                || lower.startsWith("http:")
2115                || lower.startsWith("https:")
2116                || lower.startsWith("file:"))) {
2117            text = "{@" + (new DocRootTaglet()).getName() + "}/"
2118                    + redirectPathFromRoot.resolve(text).getPath();
2119            text = replaceDocRootDir(text);
2120        }
2121        return text;
2122    }
2123
2124    static final Set<String> blockTags = new HashSet<>();
2125    static {
2126        for (HtmlTag t: HtmlTag.values()) {
2127            if (t.blockType == HtmlTag.BlockType.BLOCK)
2128                blockTags.add(t.value);
2129        }
2130    }
2131
2132    /**
2133     * Add a link to the stylesheet file.
2134     *
2135     * @param head the content tree to which the files will be added
2136     */
2137    public void addStyleSheetProperties(Content head) {
2138        String stylesheetfile = configuration.stylesheetfile;
2139        DocPath stylesheet;
2140        if (stylesheetfile.isEmpty()) {
2141            stylesheet = DocPaths.STYLESHEET;
2142        } else {
2143            DocFile file = DocFile.createFileForInput(configuration, stylesheetfile);
2144            stylesheet = DocPath.create(file.getName());
2145        }
2146        HtmlTree link = HtmlTree.LINK("stylesheet", "text/css",
2147                pathToRoot.resolve(stylesheet).getPath(),
2148                "Style");
2149        head.addContent(link);
2150        if (configuration.createindex) {
2151            HtmlTree jq_link = HtmlTree.LINK("stylesheet", "text/css",
2152                    pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(DocPaths.JQUERY_STYLESHEET_FILE)).getPath(),
2153                    "Style");
2154            head.addContent(jq_link);
2155        }
2156    }
2157
2158    /**
2159     * Add a link to the JavaScript file.
2160     *
2161     * @param head the content tree to which the files will be added
2162     */
2163    public void addScriptProperties(Content head) {
2164        HtmlTree javascript = HtmlTree.SCRIPT(pathToRoot.resolve(DocPaths.JAVASCRIPT).getPath());
2165        head.addContent(javascript);
2166        if (configuration.createindex) {
2167            if (pathToRoot != null && script != null) {
2168                String ptrPath = pathToRoot.isEmpty() ? "." : pathToRoot.getPath();
2169                script.addContent(new RawHtml("var pathtoroot = \"" + ptrPath + "/\";loadScripts(document, \'script\');"));
2170            }
2171            addJQueryFile(head, DocPaths.JSZIP_MIN);
2172            addJQueryFile(head, DocPaths.JSZIPUTILS_MIN);
2173            head.addContent(new RawHtml("<!--[if IE]>"));
2174            addJQueryFile(head, DocPaths.JSZIPUTILS_IE_MIN);
2175            head.addContent(new RawHtml("<![endif]-->"));
2176            addJQueryFile(head, DocPaths.JQUERY_JS_1_10);
2177            addJQueryFile(head, DocPaths.JQUERY_JS);
2178        }
2179    }
2180
2181    /**
2182     * Add a link to the JQuery javascript file.
2183     *
2184     * @param head the content tree to which the files will be added
2185     * @param filePath the DocPath of the file that needs to be added
2186     */
2187    private void addJQueryFile(Content head, DocPath filePath) {
2188        HtmlTree jqyeryScriptFile = HtmlTree.SCRIPT(
2189                pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(filePath)).getPath());
2190        head.addContent(jqyeryScriptFile);
2191    }
2192
2193    /**
2194     * According to
2195     * <cite>The Java&trade; Language Specification</cite>,
2196     * all the outer classes and static nested classes are core classes.
2197     */
2198    public boolean isCoreClass(TypeElement typeElement) {
2199        return utils.getEnclosingTypeElement(typeElement) == null || utils.isStatic(typeElement);
2200    }
2201
2202    /**
2203     * Adds the annotation types for the given packageElement.
2204     *
2205     * @param packageElement the package to write annotations for.
2206     * @param htmltree the documentation tree to which the annotation info will be
2207     *        added
2208     */
2209    public void addAnnotationInfo(PackageElement packageElement, Content htmltree) {
2210        addAnnotationInfo(packageElement, packageElement.getAnnotationMirrors(), htmltree);
2211    }
2212
2213    /**
2214     * Add the annotation types of the executable receiver.
2215     *
2216     * @param method the executable to write the receiver annotations for.
2217     * @param descList list of annotation description.
2218     * @param htmltree the documentation tree to which the annotation info will be
2219     *        added
2220     */
2221    public void addReceiverAnnotationInfo(ExecutableElement method, List<AnnotationMirror> descList,
2222            Content htmltree) {
2223        addAnnotationInfo(0, method, descList, false, htmltree);
2224    }
2225
2226    /*
2227     * this is a hack to delay dealing with Annotations in the writers, the assumption
2228     * is that all necessary checks have been made to get here.
2229     */
2230    public void addReceiverAnnotationInfo(ExecutableElement method, TypeMirror rcvrTypeMirror,
2231            List<? extends AnnotationMirror> annotationMirrors, Content htmltree) {
2232        TypeMirror rcvrType = method.getReceiverType();
2233        List<? extends AnnotationMirror> annotationMirrors1 = rcvrType.getAnnotationMirrors();
2234        addAnnotationInfo(0, method, annotationMirrors1, false, htmltree);
2235    }
2236
2237    /**
2238     * Adds the annotatation types for the given element.
2239     *
2240     * @param element the package to write annotations for
2241     * @param htmltree the content tree to which the annotation types will be added
2242     */
2243    public void addAnnotationInfo(Element element, Content htmltree) {
2244        addAnnotationInfo(element, element.getAnnotationMirrors(), htmltree);
2245    }
2246
2247    /**
2248     * Add the annotatation types for the given element and parameter.
2249     *
2250     * @param indent the number of spaces to indent the parameters.
2251     * @param element the element to write annotations for.
2252     * @param param the parameter to write annotations for.
2253     * @param tree the content tree to which the annotation types will be added
2254     */
2255    public boolean addAnnotationInfo(int indent, Element element, VariableElement param,
2256            Content tree) {
2257        return addAnnotationInfo(indent, element, param.getAnnotationMirrors(), false, tree);
2258    }
2259
2260    /**
2261     * Adds the annotatation types for the given Element.
2262     *
2263     * @param element the element to write annotations for.
2264     * @param descList the array of {@link AnnotationDesc}.
2265     * @param htmltree the documentation tree to which the annotation info will be
2266     *        added
2267     */
2268    private void addAnnotationInfo(Element element, List<? extends AnnotationMirror> descList,
2269            Content htmltree) {
2270        addAnnotationInfo(0, element, descList, true, htmltree);
2271    }
2272
2273    /**
2274     * Adds the annotation types for the given element.
2275     *
2276     * @param indent the number of extra spaces to indent the annotations.
2277     * @param element the element to write annotations for.
2278     * @param descList the array of {@link AnnotationDesc}.
2279     * @param htmltree the documentation tree to which the annotation info will be
2280     *        added
2281     */
2282    private boolean addAnnotationInfo(int indent, Element element,
2283            List<? extends AnnotationMirror> descList, boolean lineBreak, Content htmltree) {
2284        List<Content> annotations = getAnnotations(indent, descList, lineBreak);
2285        String sep = "";
2286        if (annotations.isEmpty()) {
2287            return false;
2288        }
2289        for (Content annotation: annotations) {
2290            htmltree.addContent(sep);
2291            htmltree.addContent(annotation);
2292            if (!lineBreak) {
2293                sep = " ";
2294            }
2295        }
2296        return true;
2297    }
2298
2299   /**
2300     * Return the string representations of the annotation types for
2301     * the given doc.
2302     *
2303     * @param indent the number of extra spaces to indent the annotations.
2304     * @param descList the array of {@link AnnotationDesc}.
2305     * @param linkBreak if true, add new line between each member value.
2306     * @return an array of strings representing the annotations being
2307     *         documented.
2308     */
2309    private List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList, boolean linkBreak) {
2310        return getAnnotations(indent, descList, linkBreak, true);
2311    }
2312
2313    private List<Content> getAnnotations(int indent, AnnotationMirror amirror, boolean linkBreak) {
2314        List<AnnotationMirror> descList = new ArrayList<>();
2315        descList.add(amirror);
2316        return getAnnotations(indent, descList, linkBreak, true);
2317    }
2318
2319    /**
2320     * Return the string representations of the annotation types for
2321     * the given doc.
2322     *
2323     * A {@code null} {@code elementType} indicates that all the
2324     * annotations should be returned without any filtering.
2325     *
2326     * @param indent the number of extra spaces to indent the annotations.
2327     * @param descList the array of {@link AnnotationDesc}.
2328     * @param linkBreak if true, add new line between each member value.
2329     * @param isJava5DeclarationLocation
2330     * @return an array of strings representing the annotations being
2331     *         documented.
2332     */
2333    public List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList,
2334            boolean linkBreak, boolean isJava5DeclarationLocation) {
2335        List<Content> results = new ArrayList<>();
2336        ContentBuilder annotation;
2337        for (AnnotationMirror aDesc : descList) {
2338            TypeElement annotationElement = (TypeElement)aDesc.getAnnotationType().asElement();
2339            // If an annotation is not documented, do not add it to the list. If
2340            // the annotation is of a repeatable type, and if it is not documented
2341            // and also if its container annotation is not documented, do not add it
2342            // to the list. If an annotation of a repeatable type is not documented
2343            // but its container is documented, it will be added to the list.
2344            if (!utils.isDocumentedAnnotation(annotationElement) &&
2345                (!isAnnotationDocumented && !isContainerDocumented)) {
2346                continue;
2347            }
2348            /* TODO: check logic here to correctly handle declaration
2349             * and type annotations.
2350            if  (utils.isDeclarationAnnotation(annotationElement, isJava5DeclarationLocation)) {
2351                continue;
2352            }*/
2353            annotation = new ContentBuilder();
2354            isAnnotationDocumented = false;
2355            LinkInfoImpl linkInfo = new LinkInfoImpl(configuration,
2356                                                     LinkInfoImpl.Kind.ANNOTATION, annotationElement);
2357            Map<? extends ExecutableElement, ? extends AnnotationValue> pairs = aDesc.getElementValues();
2358            // If the annotation is synthesized, do not print the container.
2359            if (utils.configuration.workArounds.isSynthesized(aDesc)) {
2360                for (ExecutableElement ee : pairs.keySet()) {
2361                    AnnotationValue annotationValue = pairs.get(ee);
2362                    List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2363
2364                    new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() {
2365                        @Override
2366                        public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> p) {
2367                            p.addAll(vals);
2368                            return null;
2369                        }
2370
2371                        @Override
2372                        protected Void defaultAction(Object o, List<AnnotationValue> p) {
2373                            p.add(annotationValue);
2374                            return null;
2375                        }
2376                    }.visit(annotationValue, annotationTypeValues);
2377
2378                    String sep = "";
2379                    for (AnnotationValue av : annotationTypeValues) {
2380                        annotation.addContent(sep);
2381                        annotation.addContent(annotationValueToContent(av));
2382                        sep = " ";
2383                    }
2384                }
2385            } else if (isAnnotationArray(pairs)) {
2386                // If the container has 1 or more value defined and if the
2387                // repeatable type annotation is not documented, do not print
2388                // the container.
2389                if (pairs.size() == 1 && isAnnotationDocumented) {
2390                    List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2391                    for (AnnotationValue a :  pairs.values()) {
2392                        new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() {
2393                            @Override
2394                            public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> annotationTypeValues) {
2395                               for (AnnotationValue av : vals) {
2396                                   annotationTypeValues.add(av);
2397                               }
2398                               return null;
2399                            }
2400                        }.visit(a, annotationTypeValues);
2401                    }
2402                    String sep = "";
2403                    for (AnnotationValue av : annotationTypeValues) {
2404                        annotation.addContent(sep);
2405                        annotation.addContent(annotationValueToContent(av));
2406                        sep = " ";
2407                    }
2408                }
2409                // If the container has 1 or more value defined and if the
2410                // repeatable type annotation is not documented, print the container.
2411                else {
2412                    addAnnotations(annotationElement, linkInfo, annotation, pairs,
2413                                   indent, false);
2414                }
2415            }
2416            else {
2417                addAnnotations(annotationElement, linkInfo, annotation, pairs,
2418                               indent, linkBreak);
2419            }
2420            annotation.addContent(linkBreak ? DocletConstants.NL : "");
2421            results.add(annotation);
2422        }
2423        return results;
2424    }
2425
2426    /**
2427     * Add annotation to the annotation string.
2428     *
2429     * @param annotationDoc the annotation being documented
2430     * @param linkInfo the information about the link
2431     * @param annotation the annotation string to which the annotation will be added
2432     * @param pairs annotation type element and value pairs
2433     * @param indent the number of extra spaces to indent the annotations.
2434     * @param linkBreak if true, add new line between each member value
2435     */
2436    private void addAnnotations(TypeElement annotationDoc, LinkInfoImpl linkInfo,
2437        ContentBuilder annotation, Map<? extends ExecutableElement,? extends AnnotationValue>map,
2438        int indent, boolean linkBreak) {
2439        linkInfo.label = new StringContent("@");
2440        linkInfo.label.addContent(annotationDoc.getSimpleName());
2441        annotation.addContent(getLink(linkInfo));
2442        if (!map.isEmpty()) {
2443            annotation.addContent("(");
2444            boolean isFirst = true;
2445            Set<? extends ExecutableElement> keys = map.keySet();
2446            boolean multipleValues = keys.size() > 1;
2447            for (ExecutableElement element : keys) {
2448                if (isFirst) {
2449                    isFirst = false;
2450                } else {
2451                    annotation.addContent(",");
2452                    if (linkBreak) {
2453                        annotation.addContent(DocletConstants.NL);
2454                        int spaces = annotationDoc.getSimpleName().length() + 2;
2455                        for (int k = 0; k < (spaces + indent); k++) {
2456                            annotation.addContent(" ");
2457                        }
2458                    }
2459                }
2460                String simpleName = element.getSimpleName().toString();
2461                if (multipleValues || !"value".equals(simpleName)) { // Omit "value=" where unnecessary
2462                    annotation.addContent(getDocLink(LinkInfoImpl.Kind.ANNOTATION,
2463                                                     element, simpleName, false));
2464                    annotation.addContent("=");
2465                }
2466                AnnotationValue annotationValue = map.get(element);
2467                List<AnnotationValue> annotationTypeValues = new ArrayList<>();
2468                new SimpleAnnotationValueVisitor9<Void, AnnotationValue>() {
2469                    @Override
2470                    public Void visitArray(List<? extends AnnotationValue> vals, AnnotationValue p) {
2471                        annotationTypeValues.addAll(vals);
2472                        return null;
2473                    }
2474                    @Override
2475                    protected Void defaultAction(Object o, AnnotationValue p) {
2476                        annotationTypeValues.add(p);
2477                        return null;
2478                    }
2479                }.visit(annotationValue, annotationValue);
2480                annotation.addContent(annotationTypeValues.size() == 1 ? "" : "{");
2481                String sep = "";
2482                for (AnnotationValue av : annotationTypeValues) {
2483                    annotation.addContent(sep);
2484                    annotation.addContent(annotationValueToContent(av));
2485                    sep = ",";
2486                }
2487                annotation.addContent(annotationTypeValues.size() == 1 ? "" : "}");
2488                isContainerDocumented = false;
2489            }
2490            annotation.addContent(")");
2491        }
2492    }
2493
2494    /**
2495     * Check if the annotation contains an array of annotation as a value. This
2496     * check is to verify if a repeatable type annotation is present or not.
2497     *
2498     * @param pairs annotation type element and value pairs
2499     *
2500     * @return true if the annotation contains an array of annotation as a value.
2501     */
2502    private boolean isAnnotationArray(Map<? extends ExecutableElement, ? extends AnnotationValue> pairs) {
2503        AnnotationValue annotationValue;
2504        for (ExecutableElement ee : pairs.keySet()) {
2505            annotationValue = pairs.get(ee);
2506            boolean rvalue = new SimpleAnnotationValueVisitor9<Boolean, Void>() {
2507                @Override
2508                public Boolean visitArray(List<? extends AnnotationValue> vals, Void p) {
2509                    if (vals.size() > 1) {
2510                        if (vals.get(0) instanceof AnnotationMirror) {
2511                            isContainerDocumented = true;
2512                            return new SimpleAnnotationValueVisitor9<Boolean, Void>() {
2513                                @Override
2514                                public Boolean visitAnnotation(AnnotationMirror a, Void p) {
2515                                    isContainerDocumented = true;
2516                                    Element asElement = a.getAnnotationType().asElement();
2517                                    if (utils.isDocumentedAnnotation((TypeElement)asElement)) {
2518                                        isAnnotationDocumented = true;
2519                                    }
2520                                    return true;
2521                                }
2522                                @Override
2523                                protected Boolean defaultAction(Object o, Void p) {
2524                                    return false;
2525                                }
2526                            }.visit(vals.get(0));
2527                        }
2528                    }
2529                    return false;
2530                }
2531
2532                @Override
2533                protected Boolean defaultAction(Object o, Void p) {
2534                    return false;
2535                }
2536            }.visit(annotationValue);
2537            if (rvalue) {
2538                return true;
2539            }
2540        }
2541        return false;
2542    }
2543
2544    private Content annotationValueToContent(AnnotationValue annotationValue) {
2545        return new SimpleAnnotationValueVisitor9<Content, Void>() {
2546
2547            @Override
2548            public Content visitType(TypeMirror t, Void p) {
2549                return new SimpleTypeVisitor9<Content, Void>() {
2550                    @Override
2551                    public Content visitDeclared(DeclaredType t, Void p) {
2552                        LinkInfoImpl linkInfo = new LinkInfoImpl(configuration,
2553                                LinkInfoImpl.Kind.ANNOTATION, t);
2554                        String name = utils.isIncluded(t.asElement())
2555                                ? t.asElement().getSimpleName().toString()
2556                                : utils.getFullyQualifiedName(t.asElement());
2557                        linkInfo.label = new StringContent(name + utils.getDimension(t) + ".class");
2558                        return getLink(linkInfo);
2559                    }
2560                    @Override
2561                    protected Content defaultAction(TypeMirror e, Void p) {
2562                        return new StringContent(t + utils.getDimension(t) + ".class");
2563                    }
2564                }.visit(t);
2565            }
2566            @Override
2567            public Content visitAnnotation(AnnotationMirror a, Void p) {
2568                List<Content> list = getAnnotations(0, a, false);
2569                ContentBuilder buf = new ContentBuilder();
2570                for (Content c : list) {
2571                    buf.addContent(c);
2572                }
2573                return buf;
2574            }
2575            @Override
2576            public Content visitEnumConstant(VariableElement c, Void p) {
2577                return getDocLink(LinkInfoImpl.Kind.ANNOTATION,
2578                        c, c.getSimpleName(), false);
2579            }
2580            @Override
2581            public Content visitArray(List<? extends AnnotationValue> vals, Void p) {
2582                ContentBuilder buf = new ContentBuilder();
2583                String sep = "";
2584                for (AnnotationValue av : vals) {
2585                    buf.addContent(sep);
2586                    buf.addContent(visit(av));
2587                    sep = " ";
2588                }
2589                return buf;
2590            }
2591            @Override
2592            protected Content defaultAction(Object o, Void p) {
2593                return new StringContent(annotationValue.toString());
2594            }
2595        }.visit(annotationValue);
2596    }
2597
2598    /**
2599     * Return the configuration for this doclet.
2600     *
2601     * @return the configuration for this doclet.
2602     */
2603    @Override
2604    public Configuration configuration() {
2605        return configuration;
2606    }
2607}
2608