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