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