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