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