1 /*
2  * Copyright (c) 1998, 2018, 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 
26 package jdk.javadoc.internal.doclets.formats.html;
27 
28 import jdk.javadoc.internal.doclets.formats.html.markup.Head;
29 import jdk.javadoc.internal.doclets.formats.html.markup.TableHeader;
30 
31 import java.util.*;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34 
35 import javax.lang.model.element.AnnotationMirror;
36 import javax.lang.model.element.AnnotationValue;
37 import javax.lang.model.element.Element;
38 import javax.lang.model.element.ElementKind;
39 import javax.lang.model.element.ExecutableElement;
40 import javax.lang.model.element.ModuleElement;
41 import javax.lang.model.element.Name;
42 import javax.lang.model.element.PackageElement;
43 import javax.lang.model.element.TypeElement;
44 import javax.lang.model.element.VariableElement;
45 import javax.lang.model.type.DeclaredType;
46 import javax.lang.model.type.TypeMirror;
47 import javax.lang.model.util.SimpleAnnotationValueVisitor9;
48 import javax.lang.model.util.SimpleElementVisitor9;
49 import javax.lang.model.util.SimpleTypeVisitor9;
50 
51 import com.sun.source.doctree.AttributeTree;
52 import com.sun.source.doctree.AttributeTree.ValueKind;
53 import com.sun.source.doctree.CommentTree;
54 import com.sun.source.doctree.DocRootTree;
55 import com.sun.source.doctree.DocTree;
56 import com.sun.source.doctree.DocTree.Kind;
57 import com.sun.source.doctree.EndElementTree;
58 import com.sun.source.doctree.EntityTree;
59 import com.sun.source.doctree.ErroneousTree;
60 import com.sun.source.doctree.IndexTree;
61 import com.sun.source.doctree.InheritDocTree;
62 import com.sun.source.doctree.LinkTree;
63 import com.sun.source.doctree.LiteralTree;
64 import com.sun.source.doctree.SeeTree;
65 import com.sun.source.doctree.StartElementTree;
66 import com.sun.source.doctree.SummaryTree;
67 import com.sun.source.doctree.TextTree;
68 import com.sun.source.util.SimpleDocTreeVisitor;
69 
70 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
71 import jdk.javadoc.internal.doclets.formats.html.markup.DocType;
72 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocument;
73 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
74 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
75 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
76 import jdk.javadoc.internal.doclets.formats.html.markup.Links;
77 import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml;
78 import jdk.javadoc.internal.doclets.formats.html.markup.Script;
79 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
80 import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter;
81 import jdk.javadoc.internal.doclets.toolkit.ClassWriter;
82 import jdk.javadoc.internal.doclets.toolkit.Content;
83 import jdk.javadoc.internal.doclets.toolkit.Messages;
84 import jdk.javadoc.internal.doclets.toolkit.PackageSummaryWriter;
85 import jdk.javadoc.internal.doclets.toolkit.Resources;
86 import jdk.javadoc.internal.doclets.toolkit.taglets.DocRootTaglet;
87 import jdk.javadoc.internal.doclets.toolkit.taglets.TagletWriter;
88 import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
89 import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
90 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
91 import jdk.javadoc.internal.doclets.toolkit.util.DocLink;
92 import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
93 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
94 import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
95 import jdk.javadoc.internal.doclets.toolkit.util.Utils;
96 import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable;
97 
98 import static com.sun.source.doctree.DocTree.Kind.*;
99 import 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  */
116 public class HtmlDocletWriter {
117 
118     /**
119      * Relative path from the file getting generated to the destination
120      * directory. For example, if the file getting generated is
121      * "java/lang/Object.html", then the path to the root is "../..".
122      * This string can be empty if the file getting generated is in
123      * the destination directory.
124      */
125     public final DocPath pathToRoot;
126 
127     /**
128      * Platform-independent path from the current or the
129      * destination directory to the file getting generated.
130      * Used when creating the file.
131      */
132     public final DocPath path;
133 
134     /**
135      * Name of the file getting generated. If the file getting generated is
136      * "java/lang/Object.html", then the filename is "Object.html".
137      */
138     public final DocPath filename;
139 
140     /**
141      * The global configuration information for this run.
142      */
143     public final HtmlConfiguration configuration;
144 
145     protected final Utils utils;
146 
147     protected final Contents contents;
148 
149     protected final Messages messages;
150 
151     protected final Resources resources;
152 
153     protected final Links links;
154 
155     protected final DocPaths docPaths;
156 
157     /**
158      * To check whether annotation heading is printed or not.
159      */
160     protected boolean printedAnnotationHeading = false;
161 
162     /**
163      * To check whether annotation field heading is printed or not.
164      */
165     protected boolean printedAnnotationFieldHeading = false;
166 
167     /**
168      * To check whether the repeated annotations is documented or not.
169      */
170     private boolean isAnnotationDocumented = false;
171 
172     /**
173      * To check whether the container annotations is documented or not.
174      */
175     private boolean isContainerDocumented = false;
176 
177     HtmlTree fixedNavDiv = new HtmlTree(HtmlTag.DIV);
178 
179     /**
180      * The window title of this file.
181      */
182     protected String winTitle;
183 
184     protected Script mainBodyScript;
185 
186     /**
187      * Constructor to construct the HtmlStandardWriter object.
188      *
189      * @param configuration the configuration for this doclet
190      * @param path the file to be generated.
191      */
HtmlDocletWriter(HtmlConfiguration configuration, DocPath path)192     public HtmlDocletWriter(HtmlConfiguration configuration, DocPath path) {
193         this.configuration = configuration;
194         this.contents = configuration.contents;
195         this.messages = configuration.messages;
196         this.resources = configuration.resources;
197         this.links = new Links(path, configuration.htmlVersion);
198         this.utils = configuration.utils;
199         this.path = path;
200         this.pathToRoot = path.parent().invert();
201         this.filename = path.basename();
202         this.docPaths = configuration.docPaths;
203 
204         messages.notice("doclet.Generating_0",
205             DocFile.createFileForOutput(configuration, path).getPath());
206     }
207 
208     /**
209      * Replace {&#064;docRoot} tag used in options that accept HTML text, such
210      * as -header, -footer, -top and -bottom, and when converting a relative
211      * HREF where commentTagsToString inserts a {&#064;docRoot} where one was
212      * missing.  (Also see DocRootTaglet for {&#064;docRoot} tags in doc
213      * comments.)
214      * <p>
215      * Replace {&#064;docRoot} tag in htmlstr with the relative path to the
216      * destination directory from the directory where the file is being
217      * written, looping to handle all such tags in htmlstr.
218      * <p>
219      * For example, for "-d docs" and -header containing {&#064;docRoot}, when
220      * the HTML page for source file p/C1.java is being generated, the
221      * {&#064;docRoot} tag would be inserted into the header as "../",
222      * the relative path from docs/p/ to docs/ (the document root).
223      * <p>
224      * Note: This doc comment was written with '&amp;#064;' representing '@'
225      * to prevent the inline tag from being interpreted.
226      */
replaceDocRootDir(String htmlstr)227     public String replaceDocRootDir(String htmlstr) {
228         // Return if no inline tags exist
229         int index = htmlstr.indexOf("{@");
230         if (index < 0) {
231             return htmlstr;
232         }
233         Matcher docrootMatcher = docrootPattern.matcher(htmlstr);
234         if (!docrootMatcher.find()) {
235             return htmlstr;
236         }
237         StringBuilder buf = new StringBuilder();
238         int prevEnd = 0;
239         do {
240             int match = docrootMatcher.start();
241             // append htmlstr up to start of next {@docroot}
242             buf.append(htmlstr.substring(prevEnd, match));
243             prevEnd = docrootMatcher.end();
244             if (configuration.docrootparent.length() > 0 && htmlstr.startsWith("/..", prevEnd)) {
245                 // Insert the absolute link if {@docRoot} is followed by "/..".
246                 buf.append(configuration.docrootparent);
247                 prevEnd += 3;
248             } else {
249                 // Insert relative path where {@docRoot} was located
250                 buf.append(pathToRoot.isEmpty() ? "." : pathToRoot.getPath());
251             }
252             // Append slash if next character is not a slash
253             if (prevEnd < htmlstr.length() && htmlstr.charAt(prevEnd) != '/') {
254                 buf.append('/');
255             }
256         } while (docrootMatcher.find());
257         buf.append(htmlstr.substring(prevEnd));
258         return buf.toString();
259     }
260     //where:
261         // Note: {@docRoot} is not case sensitive when passed in w/command line option:
262         private static final Pattern docrootPattern =
263                 Pattern.compile(Pattern.quote("{@docroot}"), Pattern.CASE_INSENSITIVE);
264 
265     /**
266      * Get the script to show or hide the All classes link.
267      *
268      * @param id id of the element to show or hide
269      * @return a content tree for the script
270      */
getAllClassesLinkScript(String id)271     public Content getAllClassesLinkScript(String id) {
272         Script script = new Script("<!--\n" +
273                 "  allClassesLink = document.getElementById(")
274                 .appendStringLiteral(id)
275                 .append(");\n" +
276                 "  if(window==top) {\n" +
277                 "    allClassesLink.style.display = \"block\";\n" +
278                 "  }\n" +
279                 "  else {\n" +
280                 "    allClassesLink.style.display = \"none\";\n" +
281                 "  }\n" +
282                 "  //-->\n");
283         Content div = HtmlTree.DIV(script.asContent());
284         Content div_noscript = HtmlTree.DIV(contents.noScriptMessage);
285         Content noScript = HtmlTree.NOSCRIPT(div_noscript);
286         div.addContent(noScript);
287         return div;
288     }
289 
290     /**
291      * Add method information.
292      *
293      * @param method the method to be documented
294      * @param dl the content tree to which the method information will be added
295      */
addMethodInfo(ExecutableElement method, Content dl)296     private void addMethodInfo(ExecutableElement method, Content dl) {
297         TypeElement enclosing = utils.getEnclosingTypeElement(method);
298         List<? extends TypeMirror> intfacs = enclosing.getInterfaces();
299         ExecutableElement overriddenMethod = utils.overriddenMethod(method);
300         VisibleMemberTable vmt = configuration.getVisibleMemberTable(enclosing);
301         // Check whether there is any implementation or overridden info to be
302         // printed. If no overridden or implementation info needs to be
303         // printed, do not print this section.
304         if ((!intfacs.isEmpty()
305                 && vmt.getImplementedMethods(method).isEmpty() == false)
306                 || overriddenMethod != null) {
307             MethodWriterImpl.addImplementsInfo(this, method, dl);
308             if (overriddenMethod != null) {
309                 MethodWriterImpl.addOverridden(this,
310                         utils.overriddenType(method),
311                         overriddenMethod,
312                         dl);
313             }
314         }
315     }
316 
317     /**
318      * Adds the tags information.
319      *
320      * @param e the Element for which the tags will be generated
321      * @param htmltree the documentation tree to which the tags will be added
322      */
addTagsInfo(Element e, Content htmltree)323     protected void addTagsInfo(Element e, Content htmltree) {
324         if (configuration.nocomment) {
325             return;
326         }
327         Content dl = new HtmlTree(HtmlTag.DL);
328         if (utils.isExecutableElement(e) && !utils.isConstructor(e)) {
329             addMethodInfo((ExecutableElement)e, dl);
330         }
331         Content output = new ContentBuilder();
332         TagletWriter.genTagOutput(configuration.tagletManager, e,
333             configuration.tagletManager.getBlockTaglets(e),
334                 getTagletWriterInstance(false), output);
335         dl.addContent(output);
336         htmltree.addContent(dl);
337     }
338 
339     /**
340      * Check whether there are any tags for Serialization Overview
341      * section to be printed.
342      *
343      * @param field the VariableElement object to check for tags.
344      * @return true if there are tags to be printed else return false.
345      */
hasSerializationOverviewTags(VariableElement field)346     protected boolean hasSerializationOverviewTags(VariableElement field) {
347         Content output = new ContentBuilder();
348         TagletWriter.genTagOutput(configuration.tagletManager, field,
349                 configuration.tagletManager.getBlockTaglets(field),
350                 getTagletWriterInstance(false), output);
351         return !output.isEmpty();
352     }
353 
354     /**
355      * Returns a TagletWriter that knows how to write HTML.
356      *
357      * @return a TagletWriter that knows how to write HTML.
358      */
getTagletWriterInstance(boolean isFirstSentence)359     public TagletWriter getTagletWriterInstance(boolean isFirstSentence) {
360         return new TagletWriterImpl(this, isFirstSentence);
361     }
362 
363     /**
364      * Get Package link, with target frame.
365      *
366      * @param pkg The link will be to the "package-summary.html" page for this package
367      * @param target name of the target frame
368      * @param label tag for the link
369      * @return a content for the target package link
370      */
getTargetPackageLink(PackageElement pkg, String target, Content label)371     public Content getTargetPackageLink(PackageElement pkg, String target,
372             Content label) {
373         return links.createLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY), label, "", target);
374     }
375 
376     /**
377      * Get Module Package link, with target frame.
378      *
379      * @param pkg the PackageElement
380      * @param target name of the target frame
381      * @param label tag for the link
382      * @param mdle the module being documented
383      * @return a content for the target module packages link
384      */
getTargetModulePackageLink(PackageElement pkg, String target, Content label, ModuleElement mdle)385     public Content getTargetModulePackageLink(PackageElement pkg, String target,
386             Content label, ModuleElement mdle) {
387         return links.createLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY),
388                 label, "", target);
389     }
390 
391     /**
392      * Get Module link, with target frame.
393      *
394      * @param target name of the target frame
395      * @param label tag for the link
396      * @param mdle the module being documented
397      * @return a content for the target module link
398      */
getTargetModuleLink(String target, Content label, ModuleElement mdle)399     public Content getTargetModuleLink(String target, Content label, ModuleElement mdle) {
400         return links.createLink(pathToRoot.resolve(
401                 docPaths.moduleSummary(mdle)), label, "", target);
402     }
403 
404     /**
405      * Generates the HTML document tree and prints it out.
406      *
407      * @param metakeywords Array of String keywords for META tag. Each element
408      *                     of the array is assigned to a separate META tag.
409      *                     Pass in null for no array
410      * @param includeScript true if printing windowtitle script
411      *                      false for files that appear in the left-hand frames
412      * @param body the body htmltree to be included in the document
413      * @throws DocFileIOException if there is a problem writing the file
414      */
printHtmlDocument(List<String> metakeywords, boolean includeScript, Content body)415     public void printHtmlDocument(List<String> metakeywords, boolean includeScript,
416             Content body) throws DocFileIOException {
417         DocType htmlDocType = DocType.forVersion(configuration.htmlVersion);
418         Content htmlComment = contents.newPage;
419         Head head = new Head(path, configuration.htmlVersion, configuration.docletVersion)
420                 .setTimestamp(!configuration.notimestamp)
421                 .setTitle(winTitle)
422                 .setCharset(configuration.charset)
423                 .addKeywords(metakeywords)
424                 .setStylesheets(configuration.getMainStylesheet(), configuration.getAdditionalStylesheets())
425                 .setUseModuleDirectories(configuration.useModuleDirectories)
426                 .setIndex(configuration.createindex, mainBodyScript);
427 
428         Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(), head.toContent(), body);
429         HtmlDocument htmlDocument = new HtmlDocument(htmlDocType, htmlComment, htmlTree);
430         htmlDocument.write(DocFile.createFileForOutput(configuration, path));
431     }
432 
433     /**
434      * Get the window title.
435      *
436      * @param title the title string to construct the complete window title
437      * @return the window title string
438      */
getWindowTitle(String title)439     public String getWindowTitle(String title) {
440         if (configuration.windowtitle.length() > 0) {
441             title += " (" + configuration.windowtitle  + ")";
442         }
443         return title;
444     }
445 
446     /**
447      * Get user specified header and the footer.
448      *
449      * @param header if true print the user provided header else print the
450      * user provided footer.
451      */
getUserHeaderFooter(boolean header)452     public Content getUserHeaderFooter(boolean header) {
453         String content;
454         if (header) {
455             content = replaceDocRootDir(configuration.header);
456         } else {
457             if (configuration.footer.length() != 0) {
458                 content = replaceDocRootDir(configuration.footer);
459             } else {
460                 content = replaceDocRootDir(configuration.header);
461             }
462         }
463         Content rawContent = new RawHtml(content);
464         return rawContent;
465     }
466 
467     /**
468      * Adds the user specified top.
469      *
470      * @param htmlTree the content tree to which user specified top will be added
471      */
addTop(Content htmlTree)472     public void addTop(Content htmlTree) {
473         Content top = new RawHtml(replaceDocRootDir(configuration.top));
474         fixedNavDiv.addContent(top);
475     }
476 
477     /**
478      * Adds the user specified bottom.
479      *
480      * @param htmlTree the content tree to which user specified bottom will be added
481      */
addBottom(Content htmlTree)482     public void addBottom(Content htmlTree) {
483         Content bottom = new RawHtml(replaceDocRootDir(configuration.bottom));
484         Content small = HtmlTree.SMALL(bottom);
485         Content p = HtmlTree.P(HtmlStyle.legalCopy, small);
486         htmlTree.addContent(p);
487     }
488 
489     /**
490      * Get the overview tree link for the main tree.
491      *
492      * @param label the label for the link
493      * @return a content tree for the link
494      */
getNavLinkMainTree(String label)495     protected Content getNavLinkMainTree(String label) {
496         Content mainTreeContent = links.createLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE),
497                 new StringContent(label));
498         Content li = HtmlTree.LI(mainTreeContent);
499         return li;
500     }
501 
502     /**
503      * Get table caption.
504      *
505      * @param title the content for the caption
506      * @return a content tree for the caption
507      */
getTableCaption(Content title)508     public Content getTableCaption(Content title) {
509         Content captionSpan = HtmlTree.SPAN(title);
510         Content space = Contents.SPACE;
511         Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space);
512         Content caption = HtmlTree.CAPTION(captionSpan);
513         caption.addContent(tabSpan);
514         return caption;
515     }
516 
517     /**
518      * Returns a packagename content.
519      *
520      * @param packageElement the package to check
521      * @return package name content
522      */
getPackageName(PackageElement packageElement)523     public Content getPackageName(PackageElement packageElement) {
524         return packageElement == null || packageElement.isUnnamed()
525                 ? contents.defaultPackageLabel
526                 : getPackageLabel(packageElement.getQualifiedName());
527     }
528 
529     /**
530      * Returns a package name label.
531      *
532      * @param packageName the package name
533      * @return the package name content
534      */
getPackageLabel(CharSequence packageName)535     public Content getPackageLabel(CharSequence packageName) {
536         return new StringContent(packageName);
537     }
538 
539     /**
540      * Return the path to the class page for a typeElement.
541      *
542      * @param te   TypeElement for which the path is requested.
543      * @param name Name of the file(doesn't include path).
544      */
pathString(TypeElement te, DocPath name)545     protected DocPath pathString(TypeElement te, DocPath name) {
546         return pathString(utils.containingPackage(te), name);
547     }
548 
549     /**
550      * Return path to the given file name in the given package. So if the name
551      * passed is "Object.html" and the name of the package is "java.lang", and
552      * if the relative path is "../.." then returned string will be
553      * "../../java/lang/Object.html"
554      *
555      * @param packageElement Package in which the file name is assumed to be.
556      * @param name File name, to which path string is.
557      */
pathString(PackageElement packageElement, DocPath name)558     protected DocPath pathString(PackageElement packageElement, DocPath name) {
559         return pathToRoot.resolve(docPaths.forPackage(packageElement).resolve(name));
560     }
561 
562     /**
563      * Given a package, return the name to be used in HTML anchor tag.
564      * @param packageElement the package.
565      * @return the name to be used in HTML anchor tag.
566      */
getPackageAnchorName(PackageElement packageElement)567     public String getPackageAnchorName(PackageElement packageElement) {
568         return packageElement == null || packageElement.isUnnamed()
569                 ? SectionName.UNNAMED_PACKAGE_ANCHOR.getName()
570                 : utils.getPackageName(packageElement);
571     }
572 
573     /**
574      * Return the link to the given package.
575      *
576      * @param packageElement the package to link to.
577      * @param label the label for the link.
578      * @return a content tree for the package link.
579      */
getPackageLink(PackageElement packageElement, CharSequence label)580     public Content getPackageLink(PackageElement packageElement, CharSequence label) {
581         return getPackageLink(packageElement, new StringContent(label));
582     }
583 
getPackageLink(PackageElement packageElement)584     public Content getPackageLink(PackageElement packageElement) {
585         StringContent content =  packageElement.isUnnamed()
586                 ? new StringContent()
587                 : new StringContent(utils.getPackageName(packageElement));
588         return getPackageLink(packageElement, content);
589     }
590 
591     /**
592      * Return the link to the given package.
593      *
594      * @param packageElement the package to link to.
595      * @param label the label for the link.
596      * @return a content tree for the package link.
597      */
getPackageLink(PackageElement packageElement, Content label)598     public Content getPackageLink(PackageElement packageElement, Content label) {
599         boolean included = packageElement != null && utils.isIncluded(packageElement);
600         if (!included) {
601             for (PackageElement p : configuration.packages) {
602                 if (p.equals(packageElement)) {
603                     included = true;
604                     break;
605                 }
606             }
607         }
608         if (included || packageElement == null) {
609             return links.createLink(pathString(packageElement, DocPaths.PACKAGE_SUMMARY),
610                     label);
611         } else {
612             DocLink crossPkgLink = getCrossPackageLink(packageElement);
613             if (crossPkgLink != null) {
614                 return links.createLink(crossPkgLink, label);
615             } else {
616                 return label;
617             }
618         }
619     }
620 
621     /**
622      * Get Module link.
623      *
624      * @param mdle the module being documented
625      * @param label tag for the link
626      * @return a content for the module link
627      */
getModuleLink(ModuleElement mdle, Content label)628     public Content getModuleLink(ModuleElement mdle, Content label) {
629         boolean included = utils.isIncluded(mdle);
630         return (included)
631                 ? links.createLink(pathToRoot.resolve(docPaths.moduleSummary(mdle)), label, "", "")
632                 : label;
633     }
634 
interfaceName(TypeElement typeElement, boolean qual)635     public Content interfaceName(TypeElement typeElement, boolean qual) {
636         Content name = new StringContent((qual)
637                 ? typeElement.getQualifiedName()
638                 : utils.getSimpleName(typeElement));
639         return (utils.isInterface(typeElement)) ?  HtmlTree.SPAN(HtmlStyle.interfaceName, name) : name;
640     }
641 
642     /**
643      * Add the link to the content tree.
644      *
645      * @param element program element for which the link will be added
646      * @param label label for the link
647      * @param htmltree the content tree to which the link will be added
648      */
addSrcLink(Element element, Content label, Content htmltree)649     public void addSrcLink(Element element, Content label, Content htmltree) {
650         if (element == null) {
651             return;
652         }
653         TypeElement te = utils.getEnclosingTypeElement(element);
654         if (te == null) {
655             // must be a typeElement since in has no containing class.
656             te = (TypeElement) element;
657         }
658         if (utils.isIncluded(te)) {
659             DocPath href = pathToRoot
660                     .resolve(DocPaths.SOURCE_OUTPUT)
661                     .resolve(docPaths.forClass(te));
662             Content content = links.createLink(href
663                     .fragment(SourceToHTMLConverter.getAnchorName(utils, element)), label, "", "");
664             htmltree.addContent(content);
665         } else {
666             htmltree.addContent(label);
667         }
668     }
669 
670     /**
671      * Return the link to the given class.
672      *
673      * @param linkInfo the information about the link.
674      *
675      * @return the link for the given class.
676      */
getLink(LinkInfoImpl linkInfo)677     public Content getLink(LinkInfoImpl linkInfo) {
678         LinkFactoryImpl factory = new LinkFactoryImpl(this);
679         return factory.getLink(linkInfo);
680     }
681 
682     /**
683      * Return the type parameters for the given class.
684      *
685      * @param linkInfo the information about the link.
686      * @return the type for the given class.
687      */
getTypeParameterLinks(LinkInfoImpl linkInfo)688     public Content getTypeParameterLinks(LinkInfoImpl linkInfo) {
689         LinkFactoryImpl factory = new LinkFactoryImpl(this);
690         return factory.getTypeParameterLinks(linkInfo, false);
691     }
692 
693     /*************************************************************
694      * Return a class cross link to external class documentation.
695      * The -link option does not allow users to
696      * link to external classes in the "default" package.
697      *
698      * @param classElement the class element
699      * @param refMemName the name of the member being referenced.  This should
700      * be null or empty string if no member is being referenced.
701      * @param label the label for the external link.
702      * @param strong true if the link should be strong.
703      * @param code true if the label should be code font.
704      * @return the link
705      */
getCrossClassLink(TypeElement classElement, String refMemName, Content label, boolean strong, boolean code)706     public Content getCrossClassLink(TypeElement classElement, String refMemName,
707                                     Content label, boolean strong, boolean code) {
708         if (classElement != null) {
709             String className = utils.getSimpleName(classElement);
710             PackageElement packageElement = utils.containingPackage(classElement);
711             Content defaultLabel = new StringContent(className);
712             if (code)
713                 defaultLabel = HtmlTree.CODE(defaultLabel);
714             if (getCrossPackageLink(packageElement) != null) {
715                 /*
716                 The package exists in external documentation, so link to the external
717                 class (assuming that it exists).  This is definitely a limitation of
718                 the -link option.  There are ways to determine if an external package
719                 exists, but no way to determine if the external class exists.  We just
720                 have to assume that it does.
721                 */
722                 DocLink link = configuration.extern.getExternalLink(packageElement, pathToRoot,
723                                 className + ".html", refMemName);
724                 return links.createLink(link,
725                     (label == null) || label.isEmpty() ? defaultLabel : label,
726                     strong,
727                     resources.getText("doclet.Href_Class_Or_Interface_Title",
728                         utils.getPackageName(packageElement)), "", true);
729             }
730         }
731         return null;
732     }
733 
isClassLinkable(TypeElement typeElement)734     public boolean isClassLinkable(TypeElement typeElement) {
735         if (utils.isIncluded(typeElement)) {
736             return configuration.isGeneratedDoc(typeElement);
737         }
738         return configuration.extern.isExternal(typeElement);
739     }
740 
getCrossPackageLink(PackageElement element)741     public DocLink getCrossPackageLink(PackageElement element) {
742         return configuration.extern.getExternalLink(element, pathToRoot,
743             DocPaths.PACKAGE_SUMMARY.getPath());
744     }
745 
getCrossModuleLink(ModuleElement element)746     public DocLink getCrossModuleLink(ModuleElement element) {
747         return configuration.extern.getExternalLink(element, pathToRoot,
748             docPaths.moduleSummary(utils.getModuleName(element)).getPath());
749     }
750 
751     /**
752      * Get the class link.
753      *
754      * @param context the id of the context where the link will be added
755      * @param element to link to
756      * @return a content tree for the link
757      */
getQualifiedClassLink(LinkInfoImpl.Kind context, Element element)758     public Content getQualifiedClassLink(LinkInfoImpl.Kind context, Element element) {
759         LinkInfoImpl linkInfoImpl = new LinkInfoImpl(configuration, context, (TypeElement)element);
760         return getLink(linkInfoImpl.label(utils.getFullyQualifiedName(element)));
761     }
762 
763     /**
764      * Add the class link.
765      *
766      * @param context the id of the context where the link will be added
767      * @param typeElement to link to
768      * @param contentTree the content tree to which the link will be added
769      */
addPreQualifiedClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree)770     public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) {
771         addPreQualifiedClassLink(context, typeElement, false, contentTree);
772     }
773 
774     /**
775      * Retrieve the class link with the package portion of the label in
776      * plain text.  If the qualifier is excluded, it will not be included in the
777      * link label.
778      *
779      * @param typeElement the class to link to.
780      * @param isStrong true if the link should be strong.
781      * @return the link with the package portion of the label in plain text.
782      */
getPreQualifiedClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, boolean isStrong)783     public Content getPreQualifiedClassLink(LinkInfoImpl.Kind context,
784             TypeElement typeElement, boolean isStrong) {
785         ContentBuilder classlink = new ContentBuilder();
786         PackageElement pkg = utils.containingPackage(typeElement);
787         if (pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) {
788             classlink.addContent(getEnclosingPackageName(typeElement));
789         }
790         classlink.addContent(getLink(new LinkInfoImpl(configuration,
791                 context, typeElement).label(utils.getSimpleName(typeElement)).strong(isStrong)));
792         return classlink;
793     }
794 
795     /**
796      * Add the class link with the package portion of the label in
797      * plain text. If the qualifier is excluded, it will not be included in the
798      * link label.
799      *
800      * @param context the id of the context where the link will be added
801      * @param typeElement the class to link to
802      * @param isStrong true if the link should be strong
803      * @param contentTree the content tree to which the link with be added
804      */
addPreQualifiedClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, boolean isStrong, Content contentTree)805     public void addPreQualifiedClassLink(LinkInfoImpl.Kind context,
806             TypeElement typeElement, boolean isStrong, Content contentTree) {
807         PackageElement pkg = utils.containingPackage(typeElement);
808         if(pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) {
809             contentTree.addContent(getEnclosingPackageName(typeElement));
810         }
811         LinkInfoImpl linkinfo = new LinkInfoImpl(configuration, context, typeElement)
812                 .label(utils.getSimpleName(typeElement))
813                 .strong(isStrong);
814         Content link = getLink(linkinfo);
815         contentTree.addContent(link);
816     }
817 
818     /**
819      * Get the enclosed name of the package
820      *
821      * @param te  TypeElement
822      * @return the name
823      */
getEnclosingPackageName(TypeElement te)824     public String getEnclosingPackageName(TypeElement te) {
825 
826         PackageElement encl = configuration.utils.containingPackage(te);
827         return (encl.isUnnamed()) ? "" : (encl.getQualifiedName() + ".");
828     }
829 
830     /**
831      * Add the class link, with only class name as the strong link and prefixing
832      * plain package name.
833      *
834      * @param context the id of the context where the link will be added
835      * @param typeElement the class to link to
836      * @param contentTree the content tree to which the link with be added
837      */
addPreQualifiedStrongClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree)838     public void addPreQualifiedStrongClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) {
839         addPreQualifiedClassLink(context, typeElement, true, contentTree);
840     }
841 
842     /**
843      * Get the link for the given member.
844      *
845      * @param context the id of the context where the link will be added
846      * @param element the member being linked to
847      * @param label the label for the link
848      * @return a content tree for the element link
849      */
getDocLink(LinkInfoImpl.Kind context, Element element, CharSequence label)850     public Content getDocLink(LinkInfoImpl.Kind context, Element element, CharSequence label) {
851         return getDocLink(context, utils.getEnclosingTypeElement(element), element,
852                 new StringContent(label));
853     }
854 
855     /**
856      * Return the link for the given member.
857      *
858      * @param context the id of the context where the link will be printed.
859      * @param element the member being linked to.
860      * @param label the label for the link.
861      * @param strong true if the link should be strong.
862      * @return the link for the given member.
863      */
getDocLink(LinkInfoImpl.Kind context, Element element, CharSequence label, boolean strong)864     public Content getDocLink(LinkInfoImpl.Kind context, Element element, CharSequence label,
865             boolean strong) {
866         return getDocLink(context, utils.getEnclosingTypeElement(element), element, label, strong);
867     }
868 
869     /**
870      * Return the link for the given member.
871      *
872      * @param context the id of the context where the link will be printed.
873      * @param typeElement the typeElement that we should link to.  This is not
874                  necessarily equal to element.containingClass().  We may be
875                  inheriting comments.
876      * @param element the member being linked to.
877      * @param label the label for the link.
878      * @param strong true if the link should be strong.
879      * @return the link for the given member.
880      */
getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, CharSequence label, boolean strong)881     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
882             CharSequence label, boolean strong) {
883         return getDocLink(context, typeElement, element, label, strong, false);
884     }
885 
getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, Content label, boolean strong)886     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
887             Content label, boolean strong) {
888         return getDocLink(context, typeElement, element, label, strong, false);
889     }
890 
891    /**
892      * Return the link for the given member.
893      *
894      * @param context the id of the context where the link will be printed.
895      * @param typeElement the typeElement that we should link to.  This is not
896                  necessarily equal to element.containingClass().  We may be
897                  inheriting comments.
898      * @param element the member being linked to.
899      * @param label the label for the link.
900      * @param strong true if the link should be strong.
901      * @param isProperty true if the element parameter is a JavaFX property.
902      * @return the link for the given member.
903      */
getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, CharSequence label, boolean strong, boolean isProperty)904     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
905             CharSequence label, boolean strong, boolean isProperty) {
906         return getDocLink(context, typeElement, element, new StringContent(label), strong, isProperty);
907     }
908 
getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, Content label, boolean strong, boolean isProperty)909     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
910             Content label, boolean strong, boolean isProperty) {
911         if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) {
912             return label;
913         } else if (utils.isExecutableElement(element)) {
914             ExecutableElement ee = (ExecutableElement)element;
915             return getLink(new LinkInfoImpl(configuration, context, typeElement)
916                 .label(label)
917                 .where(links.getName(getAnchor(ee, isProperty)))
918                 .strong(strong));
919         } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) {
920             return getLink(new LinkInfoImpl(configuration, context, typeElement)
921                 .label(label)
922                 .where(links.getName(element.getSimpleName().toString()))
923                 .strong(strong));
924         } else {
925             return label;
926         }
927     }
928 
929     /**
930      * Return the link for the given member.
931      *
932      * @param context the id of the context where the link will be added
933      * @param typeElement the typeElement that we should link to.  This is not
934                  necessarily equal to element.containingClass().  We may be
935                  inheriting comments
936      * @param element the member being linked to
937      * @param label the label for the link
938      * @return the link for the given member
939      */
getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, Content label)940     public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element,
941             Content label) {
942         if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) {
943             return label;
944         } else if (utils.isExecutableElement(element)) {
945             ExecutableElement emd = (ExecutableElement) element;
946             return getLink(new LinkInfoImpl(configuration, context, typeElement)
947                 .label(label)
948                 .where(links.getName(getAnchor(emd))));
949         } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) {
950             return getLink(new LinkInfoImpl(configuration, context, typeElement)
951                 .label(label).where(links.getName(element.getSimpleName().toString())));
952         } else {
953             return label;
954         }
955     }
956 
getAnchor(ExecutableElement executableElement)957     public String getAnchor(ExecutableElement executableElement) {
958         return getAnchor(executableElement, false);
959     }
960 
getAnchor(ExecutableElement executableElement, boolean isProperty)961     public String getAnchor(ExecutableElement executableElement, boolean isProperty) {
962         if (isProperty) {
963             return executableElement.getSimpleName().toString();
964         }
965         String member = anchorName(executableElement);
966         String erasedSignature = utils.makeSignature(executableElement, true, true);
967         return member + erasedSignature;
968     }
969 
anchorName(Element member)970     public String anchorName(Element member) {
971         if (member.getKind() == ElementKind.CONSTRUCTOR
972                 && configuration.isOutputHtml5()) {
973             return "<init>";
974         } else {
975             return utils.getSimpleName(member);
976         }
977     }
978 
seeTagToContent(Element element, DocTree see)979     public Content seeTagToContent(Element element, DocTree see) {
980 
981         Kind kind = see.getKind();
982         if (!(kind == LINK || kind == SEE || kind == LINK_PLAIN)) {
983             return new ContentBuilder();
984         }
985 
986         CommentHelper ch = utils.getCommentHelper(element);
987         String tagName = ch.getTagName(see);
988         String seetext = replaceDocRootDir(utils.normalizeNewlines(ch.getText(see)).toString());
989         // Check if @see is an href or "string"
990         if (seetext.startsWith("<") || seetext.startsWith("\"")) {
991             return new RawHtml(seetext);
992         }
993         boolean isLinkPlain = kind == LINK_PLAIN;
994         Content label = plainOrCode(isLinkPlain, new RawHtml(ch.getLabel(configuration, see)));
995 
996         //The text from the @see tag.  We will output this text when a label is not specified.
997         Content text = plainOrCode(kind == LINK_PLAIN, new RawHtml(seetext));
998 
999         TypeElement refClass = ch.getReferencedClass(configuration, see);
1000         String refClassName =  ch.getReferencedClassName(configuration, see);
1001         Element refMem =       ch.getReferencedMember(configuration, see);
1002         String refMemName =    ch.getReferencedMemberName(see);
1003 
1004         if (refMemName == null && refMem != null) {
1005             refMemName = refMem.toString();
1006         }
1007         if (refClass == null) {
1008             //@see is not referencing an included class
1009             PackageElement refPackage = ch.getReferencedPackage(configuration, see);
1010             if (refPackage != null && utils.isIncluded(refPackage)) {
1011                 //@see is referencing an included package
1012                 if (label.isEmpty())
1013                     label = plainOrCode(isLinkPlain,
1014                             new StringContent(refPackage.getQualifiedName()));
1015                 return getPackageLink(refPackage, label);
1016             } else {
1017                 // @see is not referencing an included class, module or package. Check for cross links.
1018                 DocLink elementCrossLink = (configuration.extern.isModule(refClassName))
1019                         ? getCrossModuleLink(utils.elementUtils.getModuleElement(refClassName)) :
1020                         (refPackage != null) ? getCrossPackageLink(refPackage) : null;
1021                 if (elementCrossLink != null) {
1022                     // Element cross link found
1023                     return links.createLink(elementCrossLink,
1024                             (label.isEmpty() ? text : label), true);
1025                 } else {
1026                     // No cross link found so print warning
1027                     messages.warning(ch.getDocTreePath(see),
1028                             "doclet.see.class_or_package_not_found",
1029                             "@" + tagName,
1030                             seetext);
1031                     return (label.isEmpty() ? text: label);
1032                 }
1033             }
1034         } else if (refMemName == null) {
1035             // Must be a class reference since refClass is not null and refMemName is null.
1036             if (label.isEmpty()) {
1037                 /*
1038                  * it seems to me this is the right thing to do, but it causes comparator failures.
1039                  */
1040                 if (!configuration.backwardCompatibility) {
1041                     StringContent content = utils.isEnclosingPackageIncluded(refClass)
1042                             ? new StringContent(utils.getSimpleName(refClass))
1043                             : new StringContent(utils.getFullyQualifiedName(refClass));
1044                     label = plainOrCode(isLinkPlain, content);
1045                 } else {
1046                     label = plainOrCode(isLinkPlain,
1047                             new StringContent(utils.getSimpleName(refClass)));
1048                 }
1049 
1050             }
1051             return getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, refClass)
1052                     .label(label));
1053         } else if (refMem == null) {
1054             // Must be a member reference since refClass is not null and refMemName is not null.
1055             // However, refMem is null, so this referenced member does not exist.
1056             return (label.isEmpty() ? text: label);
1057         } else {
1058             // Must be a member reference since refClass is not null and refMemName is not null.
1059             // refMem is not null, so this @see tag must be referencing a valid member.
1060             TypeElement containing = utils.getEnclosingTypeElement(refMem);
1061 
1062             // Find the enclosing type where the method is actually visible
1063             // in the inheritance hierarchy.
1064             ExecutableElement overriddenMethod = null;
1065             if (refMem.getKind() == ElementKind.METHOD) {
1066                 VisibleMemberTable vmt = configuration.getVisibleMemberTable(containing);
1067                 overriddenMethod = vmt.getOverriddenMethod((ExecutableElement)refMem);
1068 
1069                 if (overriddenMethod != null)
1070                     containing = utils.getEnclosingTypeElement(overriddenMethod);
1071             }
1072             if (ch.getText(see).trim().startsWith("#") &&
1073                 ! (utils.isPublic(containing) || utils.isLinkable(containing))) {
1074                 // Since the link is relative and the holder is not even being
1075                 // documented, this must be an inherited link.  Redirect it.
1076                 // The current class either overrides the referenced member or
1077                 // inherits it automatically.
1078                 if (this instanceof ClassWriterImpl) {
1079                     containing = ((ClassWriterImpl) this).getTypeElement();
1080                 } else if (!utils.isPublic(containing)) {
1081                     messages.warning(
1082                         ch.getDocTreePath(see), "doclet.see.class_or_package_not_accessible",
1083                         tagName, utils.getFullyQualifiedName(containing));
1084                 } else {
1085                     messages.warning(
1086                         ch.getDocTreePath(see), "doclet.see.class_or_package_not_found",
1087                         tagName, seetext);
1088                 }
1089             }
1090             if (configuration.currentTypeElement != containing) {
1091                 refMemName = (utils.isConstructor(refMem))
1092                         ? refMemName
1093                         : utils.getSimpleName(containing) + "." + refMemName;
1094             }
1095             if (utils.isExecutableElement(refMem)) {
1096                 if (refMemName.indexOf('(') < 0) {
1097                     refMemName += utils.makeSignature((ExecutableElement)refMem, true);
1098                 }
1099                 if (overriddenMethod != null) {
1100                     // The method to actually link.
1101                     refMem = overriddenMethod;
1102                 }
1103             }
1104 
1105             text = plainOrCode(kind == LINK_PLAIN, new StringContent(refMemName));
1106 
1107             return getDocLink(LinkInfoImpl.Kind.SEE_TAG, containing,
1108                     refMem, (label.isEmpty() ? text: label), false);
1109         }
1110     }
1111 
plainOrCode(boolean plain, Content body)1112     private Content plainOrCode(boolean plain, Content body) {
1113         return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body);
1114     }
1115 
1116     /**
1117      * Add the inline comment.
1118      *
1119      * @param element the Element for which the inline comment will be added
1120      * @param tag the inline tag to be added
1121      * @param htmltree the content tree to which the comment will be added
1122      */
addInlineComment(Element element, DocTree tag, Content htmltree)1123     public void addInlineComment(Element element, DocTree tag, Content htmltree) {
1124         CommentHelper ch = utils.getCommentHelper(element);
1125         List<? extends DocTree> description = ch.getDescription(configuration, tag);
1126         addCommentTags(element, tag, description, false, false, htmltree);
1127     }
1128 
1129     /**
1130      * Get the deprecated phrase as content.
1131      *
1132      * @param e the Element for which the inline deprecated comment will be added
1133      * @return a content tree for the deprecated phrase.
1134      */
getDeprecatedPhrase(Element e)1135     public Content getDeprecatedPhrase(Element e) {
1136         return (utils.isDeprecatedForRemoval(e))
1137                 ? contents.deprecatedForRemovalPhrase
1138                 : contents.deprecatedPhrase;
1139     }
1140 
1141     /**
1142      * Add the inline deprecated comment.
1143      *
1144      * @param e the Element for which the inline deprecated comment will be added
1145      * @param tag the inline tag to be added
1146      * @param htmltree the content tree to which the comment will be added
1147      */
addInlineDeprecatedComment(Element e, DocTree tag, Content htmltree)1148     public void addInlineDeprecatedComment(Element e, DocTree tag, Content htmltree) {
1149         CommentHelper ch = utils.getCommentHelper(e);
1150         addCommentTags(e, ch.getBody(configuration, tag), true, false, htmltree);
1151     }
1152 
1153     /**
1154      * Adds the summary content.
1155      *
1156      * @param element the Element for which the summary will be generated
1157      * @param htmltree the documentation tree to which the summary will be added
1158      */
addSummaryComment(Element element, Content htmltree)1159     public void addSummaryComment(Element element, Content htmltree) {
1160         addSummaryComment(element, utils.getFirstSentenceTrees(element), htmltree);
1161     }
1162 
1163     /**
1164      * Adds the summary content.
1165      *
1166      * @param element the Element for which the summary will be generated
1167      * @param firstSentenceTags the first sentence tags for the doc
1168      * @param htmltree the documentation tree to which the summary will be added
1169      */
addSummaryComment(Element element, List<? extends DocTree> firstSentenceTags, Content htmltree)1170     public void addSummaryComment(Element element, List<? extends DocTree> firstSentenceTags, Content htmltree) {
1171         addCommentTags(element, firstSentenceTags, false, true, htmltree);
1172     }
1173 
addSummaryDeprecatedComment(Element element, DocTree tag, Content htmltree)1174     public void addSummaryDeprecatedComment(Element element, DocTree tag, Content htmltree) {
1175         CommentHelper ch = utils.getCommentHelper(element);
1176         List<? extends DocTree> body = ch.getBody(configuration, tag);
1177         addCommentTags(element, ch.getFirstSentenceTrees(configuration, body), true, true, htmltree);
1178     }
1179 
1180     /**
1181      * Adds the inline comment.
1182      *
1183      * @param element the Element for which the inline comments will be generated
1184      * @param htmltree the documentation tree to which the inline comments will be added
1185      */
addInlineComment(Element element, Content htmltree)1186     public void addInlineComment(Element element, Content htmltree) {
1187         addCommentTags(element, utils.getFullBody(element), false, false, htmltree);
1188     }
1189 
1190     /**
1191      * Adds the comment tags.
1192      *
1193      * @param element the Element for which the comment tags will be generated
1194      * @param tags the first sentence tags for the doc
1195      * @param depr true if it is deprecated
1196      * @param first true if the first sentence tags should be added
1197      * @param htmltree the documentation tree to which the comment tags will be added
1198      */
addCommentTags(Element element, List<? extends DocTree> tags, boolean depr, boolean first, Content htmltree)1199     private void addCommentTags(Element element, List<? extends DocTree> tags, boolean depr,
1200             boolean first, Content htmltree) {
1201         addCommentTags(element, null, tags, depr, first, htmltree);
1202     }
1203 
1204     /**
1205      * Adds the comment tags.
1206      *
1207      * @param element for which the comment tags will be generated
1208      * @param holderTag the block tag context for the inline tags
1209      * @param tags the first sentence tags for the doc
1210      * @param depr true if it is deprecated
1211      * @param first true if the first sentence tags should be added
1212      * @param htmltree the documentation tree to which the comment tags will be added
1213      */
addCommentTags(Element element, DocTree holderTag, List<? extends DocTree> tags, boolean depr, boolean first, Content htmltree)1214     private void addCommentTags(Element element, DocTree holderTag, List<? extends DocTree> tags, boolean depr,
1215             boolean first, Content htmltree) {
1216         if(configuration.nocomment){
1217             return;
1218         }
1219         Content div;
1220         Content result = commentTagsToContent(null, element, tags, first);
1221         if (depr) {
1222             div = HtmlTree.DIV(HtmlStyle.deprecationComment, result);
1223             htmltree.addContent(div);
1224         }
1225         else {
1226             div = HtmlTree.DIV(HtmlStyle.block, result);
1227             htmltree.addContent(div);
1228         }
1229         if (tags.isEmpty()) {
1230             htmltree.addContent(Contents.SPACE);
1231         }
1232     }
1233 
ignoreNonInlineTag(DocTree dtree)1234     boolean ignoreNonInlineTag(DocTree dtree) {
1235         Name name = null;
1236         if (dtree.getKind() == Kind.START_ELEMENT) {
1237             StartElementTree setree = (StartElementTree)dtree;
1238             name = setree.getName();
1239         } else if (dtree.getKind() == Kind.END_ELEMENT) {
1240             EndElementTree eetree = (EndElementTree)dtree;
1241             name = eetree.getName();
1242         }
1243 
1244         if (name != null) {
1245             com.sun.tools.doclint.HtmlTag htmlTag = com.sun.tools.doclint.HtmlTag.get(name);
1246             if (htmlTag != null &&
1247                     htmlTag.blockType != com.sun.tools.doclint.HtmlTag.BlockType.INLINE) {
1248                 return true;
1249             }
1250         }
1251         return false;
1252     }
1253 
isAllWhiteSpace(String body)1254     boolean isAllWhiteSpace(String body) {
1255         for (int i = 0 ; i < body.length(); i++) {
1256             if (!Character.isWhitespace(body.charAt(i)))
1257                 return false;
1258         }
1259         return true;
1260     }
1261 
1262     // Notify the next DocTree handler to take necessary action
1263     private boolean commentRemoved = false;
1264 
1265     /**
1266      * Converts inline tags and text to text strings, expanding the
1267      * inline tags along the way.  Called wherever text can contain
1268      * an inline tag, such as in comments or in free-form text arguments
1269      * to non-inline tags.
1270      *
1271      * @param holderTag    specific tag where comment resides
1272      * @param element    specific element where comment resides
1273      * @param tags   array of text tags and inline tags (often alternating)
1274                present in the text of interest for this element
1275      * @param isFirstSentence  true if text is first sentence
1276      * @return a Content object
1277      */
commentTagsToContent(DocTree holderTag, Element element, List<? extends DocTree> tags, boolean isFirstSentence)1278     public Content commentTagsToContent(DocTree holderTag, Element element,
1279             List<? extends DocTree> tags, boolean isFirstSentence) {
1280 
1281         final Content result = new ContentBuilder() {
1282             @Override
1283             public void addContent(CharSequence text) {
1284                 super.addContent(utils.normalizeNewlines(text));
1285             }
1286         };
1287         CommentHelper ch = utils.getCommentHelper(element);
1288         // Array of all possible inline tags for this javadoc run
1289         configuration.tagletManager.checkTags(element, tags, true);
1290         commentRemoved = false;
1291 
1292         for (ListIterator<? extends DocTree> iterator = tags.listIterator(); iterator.hasNext();) {
1293             boolean isFirstNode = !iterator.hasPrevious();
1294             DocTree tag = iterator.next();
1295             boolean isLastNode  = !iterator.hasNext();
1296 
1297             if (isFirstSentence) {
1298                 // Ignore block tags
1299                 if (ignoreNonInlineTag(tag))
1300                     continue;
1301 
1302                 // Ignore any trailing whitespace OR whitespace after removed html comment
1303                 if ((isLastNode || commentRemoved)
1304                         && tag.getKind() == TEXT
1305                         && isAllWhiteSpace(ch.getText(tag)))
1306                     continue;
1307 
1308                 // Ignore any leading html comments
1309                 if ((isFirstNode || commentRemoved) && tag.getKind() == COMMENT) {
1310                     commentRemoved = true;
1311                     continue;
1312                 }
1313             }
1314 
1315             boolean allDone = new SimpleDocTreeVisitor<Boolean, Content>() {
1316 
1317                 private boolean inAnAtag() {
1318                     if (utils.isStartElement(tag)) {
1319                         StartElementTree st = (StartElementTree)tag;
1320                         Name name = st.getName();
1321                         if (name != null) {
1322                             com.sun.tools.doclint.HtmlTag htag =
1323                                     com.sun.tools.doclint.HtmlTag.get(name);
1324                             return htag != null && htag.equals(com.sun.tools.doclint.HtmlTag.A);
1325                         }
1326                     }
1327                     return false;
1328                 }
1329 
1330                 @Override
1331                 public Boolean visitAttribute(AttributeTree node, Content c) {
1332                     StringBuilder sb = new StringBuilder(SPACER).append(node.getName());
1333                     if (node.getValueKind() == ValueKind.EMPTY) {
1334                         result.addContent(sb);
1335                         return false;
1336                     }
1337                     sb.append("=");
1338                     String quote;
1339                     switch (node.getValueKind()) {
1340                         case DOUBLE:
1341                             quote = "\"";
1342                             break;
1343                         case SINGLE:
1344                             quote = "\'";
1345                             break;
1346                         default:
1347                             quote = "";
1348                             break;
1349                     }
1350                     sb.append(quote);
1351                     result.addContent(sb);
1352                     Content docRootContent = new ContentBuilder();
1353 
1354                     boolean isHRef = inAnAtag() && node.getName().toString().equalsIgnoreCase("href");
1355                     for (DocTree dt : node.getValue()) {
1356                         if (utils.isText(dt) && isHRef) {
1357                             String text = ((TextTree) dt).getBody();
1358                             if (text.startsWith("/..") && !configuration.docrootparent.isEmpty()) {
1359                                 result.addContent(configuration.docrootparent);
1360                                 docRootContent = new ContentBuilder();
1361                                 result.addContent(textCleanup(text.substring(3), isLastNode));
1362                             } else {
1363                                 if (!docRootContent.isEmpty()) {
1364                                     docRootContent = copyDocRootContent(docRootContent);
1365                                 } else {
1366                                     text = redirectRelativeLinks(element, (TextTree) dt);
1367                                 }
1368                                 result.addContent(textCleanup(text, isLastNode));
1369                             }
1370                         } else {
1371                             docRootContent = copyDocRootContent(docRootContent);
1372                             dt.accept(this, docRootContent);
1373                         }
1374                     }
1375                     copyDocRootContent(docRootContent);
1376                     result.addContent(quote);
1377                     return false;
1378                 }
1379 
1380                 @Override
1381                 public Boolean visitComment(CommentTree node, Content c) {
1382                     result.addContent(new RawHtml(node.getBody()));
1383                     return false;
1384                 }
1385 
1386                 private Content copyDocRootContent(Content content) {
1387                     if (!content.isEmpty()) {
1388                         result.addContent(content);
1389                         return new ContentBuilder();
1390                     }
1391                     return content;
1392                 }
1393 
1394                 @Override
1395                 public Boolean visitDocRoot(DocRootTree node, Content c) {
1396                     Content docRootContent = TagletWriter.getInlineTagOutput(element,
1397                             configuration.tagletManager,
1398                             holderTag,
1399                             node,
1400                             getTagletWriterInstance(isFirstSentence));
1401                     if (c != null) {
1402                         c.addContent(docRootContent);
1403                     } else {
1404                         result.addContent(docRootContent);
1405                     }
1406                     return false;
1407                 }
1408 
1409                 @Override
1410                 public Boolean visitEndElement(EndElementTree node, Content c) {
1411                     RawHtml rawHtml = new RawHtml("</" + node.getName() + ">");
1412                     result.addContent(rawHtml);
1413                     return false;
1414                 }
1415 
1416                 @Override
1417                 public Boolean visitEntity(EntityTree node, Content c) {
1418                     result.addContent(new RawHtml(node.toString()));
1419                     return false;
1420                 }
1421 
1422                 @Override
1423                 public Boolean visitErroneous(ErroneousTree node, Content c) {
1424                     messages.warning(ch.getDocTreePath(node),
1425                             "doclet.tag.invalid_usage", node);
1426                     result.addContent(new RawHtml(node.toString()));
1427                     return false;
1428                 }
1429 
1430                 @Override
1431                 public Boolean visitInheritDoc(InheritDocTree node, Content c) {
1432                     Content output = TagletWriter.getInlineTagOutput(element,
1433                             configuration.tagletManager, holderTag,
1434                             tag, getTagletWriterInstance(isFirstSentence));
1435                     result.addContent(output);
1436                     // if we obtained the first sentence successfully, nothing more to do
1437                     return (isFirstSentence && !output.isEmpty());
1438                 }
1439 
1440                 @Override
1441                 public Boolean visitIndex(IndexTree node, Content p) {
1442                     Content output = TagletWriter.getInlineTagOutput(element,
1443                             configuration.tagletManager, holderTag, tag,
1444                             getTagletWriterInstance(isFirstSentence));
1445                     if (output != null) {
1446                         result.addContent(output);
1447                     }
1448                     return false;
1449                 }
1450 
1451                 @Override
1452                 public Boolean visitLink(LinkTree node, Content c) {
1453                     // we need to pass the DocTreeImpl here, so ignore node
1454                     result.addContent(seeTagToContent(element, tag));
1455                     return false;
1456                 }
1457 
1458                 @Override
1459                 public Boolean visitLiteral(LiteralTree node, Content c) {
1460                     String s = node.getBody().getBody();
1461                     Content content = new StringContent(utils.normalizeNewlines(s));
1462                     if (node.getKind() == CODE)
1463                         content = HtmlTree.CODE(content);
1464                     result.addContent(content);
1465                     return false;
1466                 }
1467 
1468                 @Override
1469                 public Boolean visitSee(SeeTree node, Content c) {
1470                     // we need to pass the DocTreeImpl here, so ignore node
1471                     result.addContent(seeTagToContent(element, tag));
1472                     return false;
1473                 }
1474 
1475                 @Override
1476                 public Boolean visitStartElement(StartElementTree node, Content c) {
1477                     String text = "<" + node.getName();
1478                     RawHtml rawHtml = new RawHtml(utils.normalizeNewlines(text));
1479                     result.addContent(rawHtml);
1480 
1481                     for (DocTree dt : node.getAttributes()) {
1482                         dt.accept(this, null);
1483                     }
1484                     result.addContent(new RawHtml(node.isSelfClosing() ? "/>" : ">"));
1485                     return false;
1486                 }
1487 
1488                 @Override
1489                 public Boolean visitSummary(SummaryTree node, Content c) {
1490                     Content output = TagletWriter.getInlineTagOutput(element,
1491                             configuration.tagletManager, holderTag, tag,
1492                             getTagletWriterInstance(isFirstSentence));
1493                     result.addContent(output);
1494                     return false;
1495                 }
1496 
1497                 private CharSequence textCleanup(String text, boolean isLast) {
1498                     return textCleanup(text, isLast, false);
1499                 }
1500 
1501                 private CharSequence textCleanup(String text, boolean isLast, boolean trimLeader) {
1502                     if (trimLeader) {
1503                         text = removeLeadingWhitespace(text);
1504                     }
1505                     if (isFirstSentence && isLast) {
1506                         text = removeTrailingWhitespace(text);
1507                     }
1508                     text = utils.replaceTabs(text);
1509                     return utils.normalizeNewlines(text);
1510                 }
1511 
1512                 @Override
1513                 public Boolean visitText(TextTree node, Content c) {
1514                     String text = node.getBody();
1515                     result.addContent(new RawHtml(textCleanup(text, isLastNode, commentRemoved)));
1516                     return false;
1517                 }
1518 
1519                 @Override
1520                 protected Boolean defaultAction(DocTree node, Content c) {
1521                     Content output = TagletWriter.getInlineTagOutput(element,
1522                             configuration.tagletManager, holderTag, tag,
1523                             getTagletWriterInstance(isFirstSentence));
1524                     if (output != null) {
1525                         result.addContent(output);
1526                     }
1527                     return false;
1528                 }
1529 
1530             }.visit(tag, null);
1531             commentRemoved = false;
1532             if (allDone)
1533                 break;
1534         }
1535         return result;
1536     }
1537 
removeTrailingWhitespace(String text)1538     private String removeTrailingWhitespace(String text) {
1539         char[] buf = text.toCharArray();
1540         for (int i = buf.length - 1; i > 0 ; i--) {
1541             if (!Character.isWhitespace(buf[i]))
1542                 return text.substring(0, i + 1);
1543         }
1544         return text;
1545     }
1546 
removeLeadingWhitespace(String text)1547     private String removeLeadingWhitespace(String text) {
1548         char[] buf = text.toCharArray();
1549         for (int i = 0; i < buf.length; i++) {
1550             if (!Character.isWhitespace(buf[i])) {
1551                 return text.substring(i);
1552             }
1553         }
1554         return text;
1555     }
1556 
1557     /**
1558      * Return true if relative links should not be redirected.
1559      *
1560      * @return Return true if a relative link should not be redirected.
1561      */
shouldNotRedirectRelativeLinks()1562     private boolean shouldNotRedirectRelativeLinks() {
1563         return  this instanceof AnnotationTypeWriter ||
1564                 this instanceof ClassWriter ||
1565                 this instanceof PackageSummaryWriter;
1566     }
1567 
1568     /**
1569      * Suppose a piece of documentation has a relative link.  When you copy
1570      * that documentation to another place such as the index or class-use page,
1571      * that relative link will no longer work.  We should redirect those links
1572      * so that they will work again.
1573      * <p>
1574      * Here is the algorithm used to fix the link:
1575      * <p>
1576      * {@literal <relative link> => docRoot + <relative path to file> + <relative link> }
1577      * <p>
1578      * For example, suppose DocletEnvironment has this link:
1579      * {@literal <a href="package-summary.html">The package Page</a> }
1580      * <p>
1581      * If this link appeared in the index, we would redirect
1582      * the link like this:
1583      *
1584      * {@literal <a href="./com/sun/javadoc/package-summary.html">The package Page</a>}
1585      *
1586      * @param element the Element object whose documentation is being written.
1587      * @param tt the text being written.
1588      *
1589      * @return the text, with all the relative links redirected to work.
1590      */
redirectRelativeLinks(Element element, TextTree tt)1591     private String redirectRelativeLinks(Element element, TextTree tt) {
1592         String text = tt.getBody();
1593         if (element == null || utils.isOverviewElement(element) || shouldNotRedirectRelativeLinks()) {
1594             return text;
1595         }
1596 
1597         DocPath redirectPathFromRoot = new SimpleElementVisitor9<DocPath, Void>() {
1598             @Override
1599             public DocPath visitType(TypeElement e, Void p) {
1600                 return docPaths.forPackage(utils.containingPackage(e));
1601             }
1602 
1603             @Override
1604             public DocPath visitPackage(PackageElement e, Void p) {
1605                 return docPaths.forPackage(e);
1606             }
1607 
1608             @Override
1609             public DocPath visitVariable(VariableElement e, Void p) {
1610                 return docPaths.forPackage(utils.containingPackage(e));
1611             }
1612 
1613             @Override
1614             public DocPath visitExecutable(ExecutableElement e, Void p) {
1615                 return docPaths.forPackage(utils.containingPackage(e));
1616             }
1617 
1618             @Override
1619             protected DocPath defaultAction(Element e, Void p) {
1620                 return null;
1621             }
1622         }.visit(element);
1623         if (redirectPathFromRoot == null) {
1624             return text;
1625         }
1626         String lower = Utils.toLowerCase(text);
1627         if (!(lower.startsWith("mailto:")
1628                 || lower.startsWith("http:")
1629                 || lower.startsWith("https:")
1630                 || lower.startsWith("file:"))) {
1631             text = "{@" + (new DocRootTaglet()).getName() + "}/"
1632                     + redirectPathFromRoot.resolve(text).getPath();
1633             text = replaceDocRootDir(text);
1634         }
1635         return text;
1636     }
1637 
1638     /**
1639      * According to
1640      * <cite>The Java&trade; Language Specification</cite>,
1641      * all the outer classes and static nested classes are core classes.
1642      */
isCoreClass(TypeElement typeElement)1643     public boolean isCoreClass(TypeElement typeElement) {
1644         return utils.getEnclosingTypeElement(typeElement) == null || utils.isStatic(typeElement);
1645     }
1646 
1647     /**
1648      * Adds the annotation types for the given packageElement.
1649      *
1650      * @param packageElement the package to write annotations for.
1651      * @param htmltree the documentation tree to which the annotation info will be
1652      *        added
1653      */
addAnnotationInfo(PackageElement packageElement, Content htmltree)1654     public void addAnnotationInfo(PackageElement packageElement, Content htmltree) {
1655         addAnnotationInfo(packageElement, packageElement.getAnnotationMirrors(), htmltree);
1656     }
1657 
1658     /**
1659      * Add the annotation types of the executable receiver.
1660      *
1661      * @param method the executable to write the receiver annotations for.
1662      * @param descList a list of annotation mirrors.
1663      * @param htmltree the documentation tree to which the annotation info will be
1664      *        added
1665      */
addReceiverAnnotationInfo(ExecutableElement method, List<AnnotationMirror> descList, Content htmltree)1666     public void addReceiverAnnotationInfo(ExecutableElement method, List<AnnotationMirror> descList,
1667             Content htmltree) {
1668         addAnnotationInfo(0, method, descList, false, htmltree);
1669     }
1670 
1671     /*
1672      * this is a hack to delay dealing with Annotations in the writers, the assumption
1673      * is that all necessary checks have been made to get here.
1674      */
addReceiverAnnotationInfo(ExecutableElement method, TypeMirror rcvrTypeMirror, List<? extends AnnotationMirror> annotationMirrors, Content htmltree)1675     public void addReceiverAnnotationInfo(ExecutableElement method, TypeMirror rcvrTypeMirror,
1676             List<? extends AnnotationMirror> annotationMirrors, Content htmltree) {
1677         TypeMirror rcvrType = method.getReceiverType();
1678         List<? extends AnnotationMirror> annotationMirrors1 = rcvrType.getAnnotationMirrors();
1679         addAnnotationInfo(0, method, annotationMirrors1, false, htmltree);
1680     }
1681 
1682     /**
1683      * Adds the annotation types for the given element.
1684      *
1685      * @param element the package to write annotations for
1686      * @param htmltree the content tree to which the annotation types will be added
1687      */
addAnnotationInfo(Element element, Content htmltree)1688     public void addAnnotationInfo(Element element, Content htmltree) {
1689         addAnnotationInfo(element, element.getAnnotationMirrors(), htmltree);
1690     }
1691 
1692     /**
1693      * Add the annotatation types for the given element and parameter.
1694      *
1695      * @param indent the number of spaces to indent the parameters.
1696      * @param element the element to write annotations for.
1697      * @param param the parameter to write annotations for.
1698      * @param tree the content tree to which the annotation types will be added
1699      */
addAnnotationInfo(int indent, Element element, VariableElement param, Content tree)1700     public boolean addAnnotationInfo(int indent, Element element, VariableElement param,
1701             Content tree) {
1702         return addAnnotationInfo(indent, element, param.getAnnotationMirrors(), false, tree);
1703     }
1704 
1705     /**
1706      * Adds the annotatation types for the given Element.
1707      *
1708      * @param element the element to write annotations for.
1709      * @param descList a list of annotation mirrors.
1710      * @param htmltree the documentation tree to which the annotation info will be
1711      *        added
1712      */
addAnnotationInfo(Element element, List<? extends AnnotationMirror> descList, Content htmltree)1713     private void addAnnotationInfo(Element element, List<? extends AnnotationMirror> descList,
1714             Content htmltree) {
1715         addAnnotationInfo(0, element, descList, true, htmltree);
1716     }
1717 
1718     /**
1719      * Adds the annotation types for the given element.
1720      *
1721      * @param indent the number of extra spaces to indent the annotations.
1722      * @param element the element to write annotations for.
1723      * @param descList a list of annotation mirrors.
1724      * @param htmltree the documentation tree to which the annotation info will be
1725      *        added
1726      */
addAnnotationInfo(int indent, Element element, List<? extends AnnotationMirror> descList, boolean lineBreak, Content htmltree)1727     private boolean addAnnotationInfo(int indent, Element element,
1728             List<? extends AnnotationMirror> descList, boolean lineBreak, Content htmltree) {
1729         List<Content> annotations = getAnnotations(indent, descList, lineBreak);
1730         String sep = "";
1731         if (annotations.isEmpty()) {
1732             return false;
1733         }
1734         for (Content annotation: annotations) {
1735             htmltree.addContent(sep);
1736             htmltree.addContent(annotation);
1737             if (!lineBreak) {
1738                 sep = " ";
1739             }
1740         }
1741         return true;
1742     }
1743 
1744    /**
1745      * Return the string representations of the annotation types for
1746      * the given doc.
1747      *
1748      * @param indent the number of extra spaces to indent the annotations.
1749      * @param descList a list of annotation mirrors.
1750      * @param linkBreak if true, add new line between each member value.
1751      * @return a list of strings representing the annotations being
1752      *         documented.
1753      */
getAnnotations(int indent, List<? extends AnnotationMirror> descList, boolean linkBreak)1754     private List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList, boolean linkBreak) {
1755         return getAnnotations(indent, descList, linkBreak, true);
1756     }
1757 
getAnnotations(int indent, AnnotationMirror amirror, boolean linkBreak)1758     private List<Content> getAnnotations(int indent, AnnotationMirror amirror, boolean linkBreak) {
1759         List<AnnotationMirror> descList = new ArrayList<>();
1760         descList.add(amirror);
1761         return getAnnotations(indent, descList, linkBreak, true);
1762     }
1763 
1764     /**
1765      * Return the string representations of the annotation types for
1766      * the given doc.
1767      *
1768      * A {@code null} {@code elementType} indicates that all the
1769      * annotations should be returned without any filtering.
1770      *
1771      * @param indent the number of extra spaces to indent the annotations.
1772      * @param descList a list of annotation mirrors.
1773      * @param linkBreak if true, add new line between each member value.
1774      * @param isJava5DeclarationLocation
1775      * @return a list of strings representing the annotations being
1776      *         documented.
1777      */
getAnnotations(int indent, List<? extends AnnotationMirror> descList, boolean linkBreak, boolean isJava5DeclarationLocation)1778     public List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList,
1779             boolean linkBreak, boolean isJava5DeclarationLocation) {
1780         List<Content> results = new ArrayList<>();
1781         ContentBuilder annotation;
1782         for (AnnotationMirror aDesc : descList) {
1783             TypeElement annotationElement = (TypeElement)aDesc.getAnnotationType().asElement();
1784             // If an annotation is not documented, do not add it to the list. If
1785             // the annotation is of a repeatable type, and if it is not documented
1786             // and also if its container annotation is not documented, do not add it
1787             // to the list. If an annotation of a repeatable type is not documented
1788             // but its container is documented, it will be added to the list.
1789             if (!utils.isDocumentedAnnotation(annotationElement) &&
1790                 (!isAnnotationDocumented && !isContainerDocumented)) {
1791                 continue;
1792             }
1793             /* TODO: check logic here to correctly handle declaration
1794              * and type annotations.
1795             if  (utils.isDeclarationAnnotation(annotationElement, isJava5DeclarationLocation)) {
1796                 continue;
1797             }*/
1798             annotation = new ContentBuilder();
1799             isAnnotationDocumented = false;
1800             LinkInfoImpl linkInfo = new LinkInfoImpl(configuration,
1801                                                      LinkInfoImpl.Kind.ANNOTATION, annotationElement);
1802             Map<? extends ExecutableElement, ? extends AnnotationValue> pairs = aDesc.getElementValues();
1803             // If the annotation is synthesized, do not print the container.
1804             if (utils.configuration.workArounds.isSynthesized(aDesc)) {
1805                 for (ExecutableElement ee : pairs.keySet()) {
1806                     AnnotationValue annotationValue = pairs.get(ee);
1807                     List<AnnotationValue> annotationTypeValues = new ArrayList<>();
1808 
1809                     new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() {
1810                         @Override
1811                         public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> p) {
1812                             p.addAll(vals);
1813                             return null;
1814                         }
1815 
1816                         @Override
1817                         protected Void defaultAction(Object o, List<AnnotationValue> p) {
1818                             p.add(annotationValue);
1819                             return null;
1820                         }
1821                     }.visit(annotationValue, annotationTypeValues);
1822 
1823                     String sep = "";
1824                     for (AnnotationValue av : annotationTypeValues) {
1825                         annotation.addContent(sep);
1826                         annotation.addContent(annotationValueToContent(av));
1827                         sep = " ";
1828                     }
1829                 }
1830             } else if (isAnnotationArray(pairs)) {
1831                 // If the container has 1 or more value defined and if the
1832                 // repeatable type annotation is not documented, do not print
1833                 // the container.
1834                 if (pairs.size() == 1 && isAnnotationDocumented) {
1835                     List<AnnotationValue> annotationTypeValues = new ArrayList<>();
1836                     for (AnnotationValue a :  pairs.values()) {
1837                         new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() {
1838                             @Override
1839                             public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> annotationTypeValues) {
1840                                for (AnnotationValue av : vals) {
1841                                    annotationTypeValues.add(av);
1842                                }
1843                                return null;
1844                             }
1845                         }.visit(a, annotationTypeValues);
1846                     }
1847                     String sep = "";
1848                     for (AnnotationValue av : annotationTypeValues) {
1849                         annotation.addContent(sep);
1850                         annotation.addContent(annotationValueToContent(av));
1851                         sep = " ";
1852                     }
1853                 }
1854                 // If the container has 1 or more value defined and if the
1855                 // repeatable type annotation is not documented, print the container.
1856                 else {
1857                     addAnnotations(annotationElement, linkInfo, annotation, pairs,
1858                                    indent, false);
1859                 }
1860             }
1861             else {
1862                 addAnnotations(annotationElement, linkInfo, annotation, pairs,
1863                                indent, linkBreak);
1864             }
1865             annotation.addContent(linkBreak ? DocletConstants.NL : "");
1866             results.add(annotation);
1867         }
1868         return results;
1869     }
1870 
1871     /**
1872      * Add annotation to the annotation string.
1873      *
1874      * @param annotationDoc the annotation being documented
1875      * @param linkInfo the information about the link
1876      * @param annotation the annotation string to which the annotation will be added
1877      * @param map annotation type element to annotation value pairs
1878      * @param indent the number of extra spaces to indent the annotations.
1879      * @param linkBreak if true, add new line between each member value
1880      */
addAnnotations(TypeElement annotationDoc, LinkInfoImpl linkInfo, ContentBuilder annotation, Map<? extends ExecutableElement, ? extends AnnotationValue> map, int indent, boolean linkBreak)1881     private void addAnnotations(TypeElement annotationDoc, LinkInfoImpl linkInfo,
1882                                 ContentBuilder annotation,
1883                                 Map<? extends ExecutableElement, ? extends AnnotationValue> map,
1884                                 int indent, boolean linkBreak) {
1885         linkInfo.label = new StringContent("@");
1886         linkInfo.label.addContent(annotationDoc.getSimpleName());
1887         annotation.addContent(getLink(linkInfo));
1888         if (!map.isEmpty()) {
1889             annotation.addContent("(");
1890             boolean isFirst = true;
1891             Set<? extends ExecutableElement> keys = map.keySet();
1892             boolean multipleValues = keys.size() > 1;
1893             for (ExecutableElement element : keys) {
1894                 if (isFirst) {
1895                     isFirst = false;
1896                 } else {
1897                     annotation.addContent(",");
1898                     if (linkBreak) {
1899                         annotation.addContent(DocletConstants.NL);
1900                         int spaces = annotationDoc.getSimpleName().length() + 2;
1901                         for (int k = 0; k < (spaces + indent); k++) {
1902                             annotation.addContent(" ");
1903                         }
1904                     }
1905                 }
1906                 String simpleName = element.getSimpleName().toString();
1907                 if (multipleValues || !"value".equals(simpleName)) { // Omit "value=" where unnecessary
1908                     annotation.addContent(getDocLink(LinkInfoImpl.Kind.ANNOTATION,
1909                                                      element, simpleName, false));
1910                     annotation.addContent("=");
1911                 }
1912                 AnnotationValue annotationValue = map.get(element);
1913                 List<AnnotationValue> annotationTypeValues = new ArrayList<>();
1914                 new SimpleAnnotationValueVisitor9<Void, AnnotationValue>() {
1915                     @Override
1916                     public Void visitArray(List<? extends AnnotationValue> vals, AnnotationValue p) {
1917                         annotationTypeValues.addAll(vals);
1918                         return null;
1919                     }
1920                     @Override
1921                     protected Void defaultAction(Object o, AnnotationValue p) {
1922                         annotationTypeValues.add(p);
1923                         return null;
1924                     }
1925                 }.visit(annotationValue, annotationValue);
1926                 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "{");
1927                 String sep = "";
1928                 for (AnnotationValue av : annotationTypeValues) {
1929                     annotation.addContent(sep);
1930                     annotation.addContent(annotationValueToContent(av));
1931                     sep = ",";
1932                 }
1933                 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "}");
1934                 isContainerDocumented = false;
1935             }
1936             annotation.addContent(")");
1937         }
1938     }
1939 
1940     /**
1941      * Check if the annotation contains an array of annotation as a value. This
1942      * check is to verify if a repeatable type annotation is present or not.
1943      *
1944      * @param pairs annotation type element and value pairs
1945      *
1946      * @return true if the annotation contains an array of annotation as a value.
1947      */
isAnnotationArray(Map<? extends ExecutableElement, ? extends AnnotationValue> pairs)1948     private boolean isAnnotationArray(Map<? extends ExecutableElement, ? extends AnnotationValue> pairs) {
1949         AnnotationValue annotationValue;
1950         for (ExecutableElement ee : pairs.keySet()) {
1951             annotationValue = pairs.get(ee);
1952             boolean rvalue = new SimpleAnnotationValueVisitor9<Boolean, Void>() {
1953                 @Override
1954                 public Boolean visitArray(List<? extends AnnotationValue> vals, Void p) {
1955                     if (vals.size() > 1) {
1956                         if (vals.get(0) instanceof AnnotationMirror) {
1957                             isContainerDocumented = true;
1958                             return new SimpleAnnotationValueVisitor9<Boolean, Void>() {
1959                                 @Override
1960                                 public Boolean visitAnnotation(AnnotationMirror a, Void p) {
1961                                     isContainerDocumented = true;
1962                                     Element asElement = a.getAnnotationType().asElement();
1963                                     if (utils.isDocumentedAnnotation((TypeElement)asElement)) {
1964                                         isAnnotationDocumented = true;
1965                                     }
1966                                     return true;
1967                                 }
1968                                 @Override
1969                                 protected Boolean defaultAction(Object o, Void p) {
1970                                     return false;
1971                                 }
1972                             }.visit(vals.get(0));
1973                         }
1974                     }
1975                     return false;
1976                 }
1977 
1978                 @Override
1979                 protected Boolean defaultAction(Object o, Void p) {
1980                     return false;
1981                 }
1982             }.visit(annotationValue);
1983             if (rvalue) {
1984                 return true;
1985             }
1986         }
1987         return false;
1988     }
1989 
annotationValueToContent(AnnotationValue annotationValue)1990     private Content annotationValueToContent(AnnotationValue annotationValue) {
1991         return new SimpleAnnotationValueVisitor9<Content, Void>() {
1992 
1993             @Override
1994             public Content visitType(TypeMirror t, Void p) {
1995                 return new SimpleTypeVisitor9<Content, Void>() {
1996                     @Override
1997                     public Content visitDeclared(DeclaredType t, Void p) {
1998                         LinkInfoImpl linkInfo = new LinkInfoImpl(configuration,
1999                                 LinkInfoImpl.Kind.ANNOTATION, t);
2000                         String name = utils.isIncluded(t.asElement())
2001                                 ? t.asElement().getSimpleName().toString()
2002                                 : utils.getFullyQualifiedName(t.asElement());
2003                         linkInfo.label = new StringContent(name + utils.getDimension(t) + ".class");
2004                         return getLink(linkInfo);
2005                     }
2006                     @Override
2007                     protected Content defaultAction(TypeMirror e, Void p) {
2008                         return new StringContent(t + utils.getDimension(t) + ".class");
2009                     }
2010                 }.visit(t);
2011             }
2012             @Override
2013             public Content visitAnnotation(AnnotationMirror a, Void p) {
2014                 List<Content> list = getAnnotations(0, a, false);
2015                 ContentBuilder buf = new ContentBuilder();
2016                 for (Content c : list) {
2017                     buf.addContent(c);
2018                 }
2019                 return buf;
2020             }
2021             @Override
2022             public Content visitEnumConstant(VariableElement c, Void p) {
2023                 return getDocLink(LinkInfoImpl.Kind.ANNOTATION,
2024                         c, c.getSimpleName(), false);
2025             }
2026             @Override
2027             public Content visitArray(List<? extends AnnotationValue> vals, Void p) {
2028                 ContentBuilder buf = new ContentBuilder();
2029                 String sep = "";
2030                 for (AnnotationValue av : vals) {
2031                     buf.addContent(sep);
2032                     buf.addContent(visit(av));
2033                     sep = " ";
2034                 }
2035                 return buf;
2036             }
2037             @Override
2038             protected Content defaultAction(Object o, Void p) {
2039                 return new StringContent(annotationValue.toString());
2040             }
2041         }.visit(annotationValue);
2042     }
2043 
2044     protected TableHeader getPackageTableHeader() {
2045         return new TableHeader(contents.packageLabel, contents.descriptionLabel);
2046     }
2047 
2048     /**
2049      * Returns an HtmlTree for the SCRIPT tag.
2050      *
2051      * @return an HtmlTree for the SCRIPT tag
2052      */
2053     protected Script getWinTitleScript() {
2054         Script script = new Script();
2055         if (winTitle != null && winTitle.length() > 0) {
2056             script.append("<!--\n" +
2057                     "    try {\n" +
2058                     "        if (location.href.indexOf('is-external=true') == -1) {\n" +
2059                     "            parent.document.title=")
2060                     .appendStringLiteral(winTitle)
2061                     .append(";\n" +
2062                     "        }\n" +
2063                     "    }\n" +
2064                     "    catch(err) {\n" +
2065                     "    }\n" +
2066                     "//-->\n");
2067         }
2068         return script;
2069     }
2070 
2071     /**
2072      * Returns an HtmlTree for the BODY tag.
2073      *
2074      * @param includeScript  set true if printing windowtitle script
2075      * @param title title for the window
2076      * @return an HtmlTree for the BODY tag
2077      */
2078     public HtmlTree getBody(boolean includeScript, String title) {
2079         HtmlTree body = new HtmlTree(HtmlTag.BODY);
2080         // Set window title string which is later printed
2081         this.winTitle = title;
2082         // Don't print windowtitle script for overview-frame, allclasses-frame
2083         // and package-frame
2084         if (includeScript) {
2085             this.mainBodyScript = getWinTitleScript();
2086             body.addContent(mainBodyScript.asContent());
2087             Content noScript = HtmlTree.NOSCRIPT(HtmlTree.DIV(contents.noScriptMessage));
2088             body.addContent(noScript);
2089         }
2090         return body;
2091     }
2092 
2093     Script getMainBodyScript() {
2094         return mainBodyScript;
2095     }
2096 }
2097