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