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