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 */ 25 26 package com.sun.tools.doclets.formats.html; 27 28 import java.io.*; 29 import java.text.SimpleDateFormat; 30 import java.util.*; 31 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.*; 37 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 { 54 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; 63 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; 70 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; 76 77 /** 78 * The display length used for indentation while generating the class page. 79 */ 80 public int displayLength = 0; 81 82 /** 83 * The global configuration information for this run. 84 */ 85 public final ConfigurationImpl configuration; 86 87 /** 88 * To check whether annotation heading is printed or not. 89 */ 90 protected boolean printedAnnotationHeading = false; 91 92 /** 93 * To check whether the repeated annotations is documented or not. 94 */ 95 private boolean isAnnotationDocumented = false; 96 97 /** 98 * To check whether the container annotations is documented or not. 99 */ 100 private boolean isContainerDocumented = false; 101 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 } 115 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 164 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 } 192 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 } 216 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 } 239 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 } 265 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 } 280 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 } 289 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 } 302 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 } 317 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 } 331 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 } 345 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 } 356 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 } 403 404 /** 405 * Generates the HTML document tree and prints it out. 406 * 407 * @param metakeywords Array of String keywords for META tag. Each element 408 * of the array is assigned to a separate META tag. 409 * Pass in null for no array 410 * @param includeScript true if printing windowtitle script 411 * false for files that appear in the left-hand frames 412 * @param body the body htmltree to be included in the document 413 */ 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 } 448 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 } 461 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 } 483 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 } 493 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 } 505 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, "Navigation"); 547 if (configuration.createoverview) { 548 navList.addContent(getNavLinkContents()); 549 } 550 if (configuration.packages.length == 1) { 551 navList.addContent(getNavLinkPackage(configuration.packages[0])); 552 } else if (configuration.packages.length > 1) { 553 navList.addContent(getNavLinkPackage()); 554 } 555 navList.addContent(getNavLinkClass()); 556 if(configuration.classuse) { 557 navList.addContent(getNavLinkClassUse()); 558 } 559 if(configuration.createtree) { 560 navList.addContent(getNavLinkTree()); 561 } 562 if(!(configuration.nodeprecated || 563 configuration.nodeprecatedlist)) { 564 navList.addContent(getNavLinkDeprecated()); 565 } 566 if(configuration.createindex) { 567 navList.addContent(getNavLinkIndex()); 568 } 569 if (!configuration.nohelp) { 570 navList.addContent(getNavLinkHelp()); 571 } 572 navDiv.addContent(navList); 573 Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, getUserHeaderFooter(header)); 574 navDiv.addContent(aboutDiv); 575 body.addContent(navDiv); 576 Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious()); 577 ulNav.addContent(getNavLinkNext()); 578 Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav); 579 Content ulFrames = HtmlTree.UL(HtmlStyle.navList, getNavShowLists()); 580 ulFrames.addContent(getNavHideLists(filename)); 581 subDiv.addContent(ulFrames); 582 HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex()); 583 ulAllClasses.addAttr(HtmlAttr.ID, allClassesId.toString()); 584 subDiv.addContent(ulAllClasses); 585 subDiv.addContent(getAllClassesLinkScript(allClassesId.toString())); 586 addSummaryDetailLinks(subDiv); 587 if (header) { 588 subDiv.addContent(getMarkerAnchor("skip-navbar_top")); 589 body.addContent(subDiv); 590 body.addContent(HtmlConstants.END_OF_TOP_NAVBAR); 591 } else { 592 subDiv.addContent(getMarkerAnchor("skip-navbar_bottom")); 593 body.addContent(subDiv); 594 body.addContent(HtmlConstants.END_OF_BOTTOM_NAVBAR); 595 } 596 } 597 } 598 599 /** 600 * Get the word "NEXT" to indicate that no link is available. Override 601 * this method to customize next link. 602 * 603 * @return a content tree for the link 604 */ 605 protected Content getNavLinkNext() { 606 return getNavLinkNext(null); 607 } 608 609 /** 610 * Get the word "PREV" to indicate that no link is available. Override 611 * this method to customize prev link. 612 * 613 * @return a content tree for the link 614 */ 615 protected Content getNavLinkPrevious() { 616 return getNavLinkPrevious(null); 617 } 618 619 /** 620 * Do nothing. This is the default method. 621 */ 622 protected void addSummaryDetailLinks(Content navDiv) { 623 } 624 625 /** 626 * Get link to the "overview-summary.html" page. 627 * 628 * @return a content tree for the link 629 */ 630 protected Content getNavLinkContents() { 631 Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_SUMMARY), 632 overviewLabel, "", ""); 633 Content li = HtmlTree.LI(linkContent); 634 return li; 635 } 636 637 /** 638 * Get link to the "package-summary.html" page for the package passed. 639 * 640 * @param pkg Package to which link will be generated 641 * @return a content tree for the link 642 */ 643 protected Content getNavLinkPackage(PackageDoc pkg) { 644 Content linkContent = getPackageLink(pkg, 645 packageLabel); 646 Content li = HtmlTree.LI(linkContent); 647 return li; 648 } 649 650 /** 651 * Get the word "Package" , to indicate that link is not available here. 652 * 653 * @return a content tree for the link 654 */ 655 protected Content getNavLinkPackage() { 656 Content li = HtmlTree.LI(packageLabel); 657 return li; 658 } 659 660 /** 661 * Get the word "Use", to indicate that link is not available. 662 * 663 * @return a content tree for the link 664 */ 665 protected Content getNavLinkClassUse() { 666 Content li = HtmlTree.LI(useLabel); 667 return li; 668 } 669 670 /** 671 * Get link for previous file. 672 * 673 * @param prev File name for the prev link 674 * @return a content tree for the link 675 */ 676 public Content getNavLinkPrevious(DocPath prev) { 677 Content li; 678 if (prev != null) { 679 li = HtmlTree.LI(getHyperLink(prev, prevLabel, "", "")); 680 } 681 else 682 li = HtmlTree.LI(prevLabel); 683 return li; 684 } 685 686 /** 687 * Get link for next file. If next is null, just print the label 688 * without linking it anywhere. 689 * 690 * @param next File name for the next link 691 * @return a content tree for the link 692 */ 693 public Content getNavLinkNext(DocPath next) { 694 Content li; 695 if (next != null) { 696 li = HtmlTree.LI(getHyperLink(next, nextLabel, "", "")); 697 } 698 else 699 li = HtmlTree.LI(nextLabel); 700 return li; 701 } 702 703 /** 704 * Get "FRAMES" link, to switch to the frame version of the output. 705 * 706 * @param link File to be linked, "index.html" 707 * @return a content tree for the link 708 */ 709 protected Content getNavShowLists(DocPath link) { 710 DocLink dl = new DocLink(link, path.getPath(), null); 711 Content framesContent = getHyperLink(dl, framesLabel, "", "_top"); 712 Content li = HtmlTree.LI(framesContent); 713 return li; 714 } 715 716 /** 717 * Get "FRAMES" link, to switch to the frame version of the output. 718 * 719 * @return a content tree for the link 720 */ 721 protected Content getNavShowLists() { 722 return getNavShowLists(pathToRoot.resolve(DocPaths.INDEX)); 723 } 724 725 /** 726 * Get "NO FRAMES" link, to switch to the non-frame version of the output. 727 * 728 * @param link File to be linked 729 * @return a content tree for the link 730 */ 731 protected Content getNavHideLists(DocPath link) { 732 Content noFramesContent = getHyperLink(link, noframesLabel, "", "_top"); 733 Content li = HtmlTree.LI(noFramesContent); 734 return li; 735 } 736 737 /** 738 * Get "Tree" link in the navigation bar. If there is only one package 739 * specified on the command line, then the "Tree" link will be to the 740 * only "package-tree.html" file otherwise it will be to the 741 * "overview-tree.html" file. 742 * 743 * @return a content tree for the link 744 */ 745 protected Content getNavLinkTree() { 746 Content treeLinkContent; 747 PackageDoc[] packages = configuration.root.specifiedPackages(); 748 if (packages.length == 1 && configuration.root.specifiedClasses().length == 0) { 749 treeLinkContent = getHyperLink(pathString(packages[0], 750 DocPaths.PACKAGE_TREE), treeLabel, 751 "", ""); 752 } else { 753 treeLinkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE), 754 treeLabel, "", ""); 755 } 756 Content li = HtmlTree.LI(treeLinkContent); 757 return li; 758 } 759 760 /** 761 * Get the overview tree link for the main tree. 762 * 763 * @param label the label for the link 764 * @return a content tree for the link 765 */ 766 protected Content getNavLinkMainTree(String label) { 767 Content mainTreeContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE), 768 new StringContent(label)); 769 Content li = HtmlTree.LI(mainTreeContent); 770 return li; 771 } 772 773 /** 774 * Get the word "Class", to indicate that class link is not available. 775 * 776 * @return a content tree for the link 777 */ 778 protected Content getNavLinkClass() { 779 Content li = HtmlTree.LI(classLabel); 780 return li; 781 } 782 783 /** 784 * Get "Deprecated" API link in the navigation bar. 785 * 786 * @return a content tree for the link 787 */ 788 protected Content getNavLinkDeprecated() { 789 Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.DEPRECATED_LIST), 790 deprecatedLabel, "", ""); 791 Content li = HtmlTree.LI(linkContent); 792 return li; 793 } 794 795 /** 796 * Get link for generated index. If the user has used "-splitindex" 797 * command line option, then link to file "index-files/index-1.html" is 798 * generated otherwise link to file "index-all.html" is generated. 799 * 800 * @return a content tree for the link 801 */ 802 protected Content getNavLinkClassIndex() { 803 Content allClassesContent = getHyperLink(pathToRoot.resolve( 804 DocPaths.ALLCLASSES_NOFRAME), 805 allclassesLabel, "", ""); 806 Content li = HtmlTree.LI(allClassesContent); 807 return li; 808 } 809 810 /** 811 * Get link for generated class index. 812 * 813 * @return a content tree for the link 814 */ 815 protected Content getNavLinkIndex() { 816 Content linkContent = getHyperLink(pathToRoot.resolve( 817 (configuration.splitindex 818 ? DocPaths.INDEX_FILES.resolve(DocPaths.indexN(1)) 819 : DocPaths.INDEX_ALL)), 820 indexLabel, "", ""); 821 Content li = HtmlTree.LI(linkContent); 822 return li; 823 } 824 825 /** 826 * Get help file link. If user has provided a help file, then generate a 827 * link to the user given file, which is already copied to current or 828 * destination directory. 829 * 830 * @return a content tree for the link 831 */ 832 protected Content getNavLinkHelp() { 833 String helpfile = configuration.helpfile; 834 DocPath helpfilenm; 835 if (helpfile.isEmpty()) { 836 helpfilenm = DocPaths.HELP_DOC; 837 } else { 838 DocFile file = DocFile.createFileForInput(configuration, helpfile); 839 helpfilenm = DocPath.create(file.getName()); 840 } 841 Content linkContent = getHyperLink(pathToRoot.resolve(helpfilenm), 842 helpLabel, "", ""); 843 Content li = HtmlTree.LI(linkContent); 844 return li; 845 } 846 847 /** 848 * Get summary table header. 849 * 850 * @param header the header for the table 851 * @param scope the scope of the headers 852 * @return a content tree for the header 853 */ 854 public Content getSummaryTableHeader(String[] header, String scope) { 855 Content tr = new HtmlTree(HtmlTag.TR); 856 int size = header.length; 857 Content tableHeader; 858 if (size == 1) { 859 tableHeader = new StringContent(header[0]); 860 tr.addContent(HtmlTree.TH(HtmlStyle.colOne, scope, tableHeader)); 861 return tr; 862 } 863 for (int i = 0; i < size; i++) { 864 tableHeader = new StringContent(header[i]); 865 if(i == 0) 866 tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader)); 867 else if(i == (size - 1)) 868 tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader)); 869 else 870 tr.addContent(HtmlTree.TH(scope, tableHeader)); 871 } 872 return tr; 873 } 874 875 /** 876 * Get table caption. 877 * 878 * @param rawText the caption for the table which could be raw Html 879 * @return a content tree for the caption 880 */ 881 public Content getTableCaption(String rawText) { 882 Content title = new RawHtml(rawText); 883 Content captionSpan = HtmlTree.SPAN(title); 884 Content space = getSpace(); 885 Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space); 886 Content caption = HtmlTree.CAPTION(captionSpan); 887 caption.addContent(tabSpan); 888 return caption; 889 } 890 891 /** 892 * Get the marker anchor which will be added to the documentation tree. 893 * 894 * @param anchorName the anchor name attribute 895 * @return a content tree for the marker anchor 896 */ 897 public Content getMarkerAnchor(String anchorName) { 898 return getMarkerAnchor(anchorName, null); 899 } 900 901 /** 902 * Get the marker anchor which will be added to the documentation tree. 903 * 904 * @param anchorName the anchor name attribute 905 * @param anchorContent the content that should be added to the anchor 906 * @return a content tree for the marker anchor 907 */ 908 public Content getMarkerAnchor(String anchorName, Content anchorContent) { 909 if (anchorContent == null) 910 anchorContent = new Comment(" "); 911 Content markerAnchor = HtmlTree.A_NAME(anchorName, anchorContent); 912 return markerAnchor; 913 } 914 915 /** 916 * Returns a packagename content. 917 * 918 * @param packageDoc the package to check 919 * @return package name content 920 */ 921 public Content getPackageName(PackageDoc packageDoc) { 922 return packageDoc == null || packageDoc.name().length() == 0 ? 923 defaultPackageLabel : 924 getPackageLabel(packageDoc.name()); 925 } 926 927 /** 928 * Returns a package name label. 929 * 930 * @param packageName the package name 931 * @return the package name content 932 */ 933 public Content getPackageLabel(String packageName) { 934 return new StringContent(packageName); 935 } 936 937 /** 938 * Add package deprecation information to the documentation tree 939 * 940 * @param deprPkgs list of deprecated packages 941 * @param headingKey the caption for the deprecated package table 942 * @param tableSummary the summary for the deprecated package table 943 * @param tableHeader table headers for the deprecated package table 944 * @param contentTree the content tree to which the deprecated package table will be added 945 */ 946 protected void addPackageDeprecatedAPI(List<Doc> deprPkgs, String headingKey, 947 String tableSummary, String[] tableHeader, Content contentTree) { 948 if (deprPkgs.size() > 0) { 949 Content table = HtmlTree.TABLE(0, 3, 0, tableSummary, 950 getTableCaption(configuration.getText(headingKey))); 951 table.addContent(getSummaryTableHeader(tableHeader, "col")); 952 Content tbody = new HtmlTree(HtmlTag.TBODY); 953 for (int i = 0; i < deprPkgs.size(); i++) { 954 PackageDoc pkg = (PackageDoc) deprPkgs.get(i); 955 HtmlTree td = HtmlTree.TD(HtmlStyle.colOne, 956 getPackageLink(pkg, getPackageName(pkg))); 957 if (pkg.tags("deprecated").length > 0) { 958 addInlineDeprecatedComment(pkg, pkg.tags("deprecated")[0], td); 959 } 960 HtmlTree tr = HtmlTree.TR(td); 961 if (i % 2 == 0) { 962 tr.addStyle(HtmlStyle.altColor); 963 } else { 964 tr.addStyle(HtmlStyle.rowColor); 965 } 966 tbody.addContent(tr); 967 } 968 table.addContent(tbody); 969 Content li = HtmlTree.LI(HtmlStyle.blockList, table); 970 Content ul = HtmlTree.UL(HtmlStyle.blockList, li); 971 contentTree.addContent(ul); 972 } 973 } 974 975 /** 976 * Return the path to the class page for a classdoc. 977 * 978 * @param cd Class to which the path is requested. 979 * @param name Name of the file(doesn't include path). 980 */ 981 protected DocPath pathString(ClassDoc cd, DocPath name) { 982 return pathString(cd.containingPackage(), name); 983 } 984 985 /** 986 * Return path to the given file name in the given package. So if the name 987 * passed is "Object.html" and the name of the package is "java.lang", and 988 * if the relative path is "../.." then returned string will be 989 * "../../java/lang/Object.html" 990 * 991 * @param pd Package in which the file name is assumed to be. 992 * @param name File name, to which path string is. 993 */ 994 protected DocPath pathString(PackageDoc pd, DocPath name) { 995 return pathToRoot.resolve(DocPath.forPackage(pd).resolve(name)); 996 } 997 998 /** 999 * Return the link to the given package. 1000 * 1001 * @param pkg the package to link to. 1002 * @param label the label for the link. 1003 * @param isStrong true if the label should be strong. 1004 * @return the link to the given package. 1005 */ 1006 public String getPackageLinkString(PackageDoc pkg, String label, 1007 boolean isStrong) { 1008 return getPackageLinkString(pkg, label, isStrong, ""); 1009 } 1010 1011 /** 1012 * Return the link to the given package. 1013 * 1014 * @param pkg the package to link to. 1015 * @param label the label for the link. 1016 * @param isStrong true if the label should be strong. 1017 * @param style the font of the package link label. 1018 * @return the link to the given package. 1019 */ 1020 public String getPackageLinkString(PackageDoc pkg, String label, boolean isStrong, 1021 String style) { 1022 boolean included = pkg != null && pkg.isIncluded(); 1023 if (! included) { 1024 PackageDoc[] packages = configuration.packages; 1025 for (int i = 0; i < packages.length; i++) { 1026 if (packages[i].equals(pkg)) { 1027 included = true; 1028 break; 1029 } 1030 } 1031 } 1032 if (included || pkg == null) { 1033 return getHyperLinkString(pathString(pkg, DocPaths.PACKAGE_SUMMARY), 1034 label, isStrong, style); 1035 } else { 1036 DocLink crossPkgLink = getCrossPackageLink(Util.getPackageName(pkg)); 1037 if (crossPkgLink != null) { 1038 return getHyperLinkString(crossPkgLink, label, isStrong, style); 1039 } else { 1040 return label; 1041 } 1042 } 1043 } 1044 1045 /** 1046 * Return the link to the given package. 1047 * 1048 * @param pkg the package to link to. 1049 * @param label the label for the link. 1050 * @return a content tree for the package link. 1051 */ 1052 public Content getPackageLink(PackageDoc pkg, Content label) { 1053 boolean included = pkg != null && pkg.isIncluded(); 1054 if (! included) { 1055 PackageDoc[] packages = configuration.packages; 1056 for (int i = 0; i < packages.length; i++) { 1057 if (packages[i].equals(pkg)) { 1058 included = true; 1059 break; 1060 } 1061 } 1062 } 1063 if (included || pkg == null) { 1064 return getHyperLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY), 1065 label); 1066 } else { 1067 DocLink crossPkgLink = getCrossPackageLink(Util.getPackageName(pkg)); 1068 if (crossPkgLink != null) { 1069 return getHyperLink(crossPkgLink, label); 1070 } else { 1071 return label; 1072 } 1073 } 1074 } 1075 1076 public String italicsClassName(ClassDoc cd, boolean qual) { 1077 String name = (qual)? cd.qualifiedName(): cd.name(); 1078 return (cd.isInterface())? italicsText(name): name; 1079 } 1080 1081 /** 1082 * Add the link to the content tree. 1083 * 1084 * @param doc program element doc for which the link will be added 1085 * @param label label for the link 1086 * @param htmltree the content tree to which the link will be added 1087 */ 1088 public void addSrcLink(ProgramElementDoc doc, Content label, Content htmltree) { 1089 if (doc == null) { 1090 return; 1091 } 1092 ClassDoc cd = doc.containingClass(); 1093 if (cd == null) { 1094 //d must be a class doc since in has no containing class. 1095 cd = (ClassDoc) doc; 1096 } 1097 DocPath href = pathToRoot 1098 .resolve(DocPaths.SOURCE_OUTPUT) 1099 .resolve(DocPath.forClass(cd)); 1100 Content linkContent = getHyperLink(href.fragment(SourceToHTMLConverter.getAnchorName(doc)), label, "", ""); 1101 htmltree.addContent(linkContent); 1102 } 1103 1104 /** 1105 * Return the link to the given class. 1106 * 1107 * @param linkInfo the information about the link. 1108 * 1109 * @return the link for the given class. 1110 */ 1111 public String getLink(LinkInfoImpl linkInfo) { 1112 LinkFactoryImpl factory = new LinkFactoryImpl(this); 1113 String link = factory.getLinkOutput(linkInfo).toString(); 1114 displayLength += linkInfo.displayLength; 1115 return link; 1116 } 1117 1118 /** 1119 * Return the type parameters for the given class. 1120 * 1121 * @param linkInfo the information about the link. 1122 * @return the type for the given class. 1123 */ 1124 public String getTypeParameterLinks(LinkInfoImpl linkInfo) { 1125 LinkFactoryImpl factory = new LinkFactoryImpl(this); 1126 return factory.getTypeParameterLinks(linkInfo, false).toString(); 1127 } 1128 1129 /************************************************************* 1130 * Return a class cross link to external class documentation. 1131 * The name must be fully qualified to determine which package 1132 * the class is in. The -link option does not allow users to 1133 * link to external classes in the "default" package. 1134 * 1135 * @param qualifiedClassName the qualified name of the external class. 1136 * @param refMemName the name of the member being referenced. This should 1137 * be null or empty string if no member is being referenced. 1138 * @param label the label for the external link. 1139 * @param strong true if the link should be strong. 1140 * @param style the style of the link. 1141 * @param code true if the label should be code font. 1142 */ 1143 public String getCrossClassLink(String qualifiedClassName, String refMemName, 1144 String label, boolean strong, String style, 1145 boolean code) { 1146 String className = ""; 1147 String packageName = qualifiedClassName == null ? "" : qualifiedClassName; 1148 int periodIndex; 1149 while ((periodIndex = packageName.lastIndexOf('.')) != -1) { 1150 className = packageName.substring(periodIndex + 1, packageName.length()) + 1151 (className.length() > 0 ? "." + className : ""); 1152 String defaultLabel = code ? codeText(className) : className; 1153 packageName = packageName.substring(0, periodIndex); 1154 if (getCrossPackageLink(packageName) != null) { 1155 //The package exists in external documentation, so link to the external 1156 //class (assuming that it exists). This is definitely a limitation of 1157 //the -link option. There are ways to determine if an external package 1158 //exists, but no way to determine if the external class exists. We just 1159 //have to assume that it does. 1160 DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot, 1161 className + ".html", refMemName); 1162 return getHyperLinkString(link, 1163 (label == null) || label.length() == 0 ? defaultLabel : label, 1164 1165 1166 strong, style, 1167 configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName), 1168 ""); 1169 } 1170 } 1171 return null; 1172 } 1173 1174 public boolean isClassLinkable(ClassDoc cd) { 1175 if (cd.isIncluded()) { 1176 return configuration.isGeneratedDoc(cd); 1177 } 1178 return configuration.extern.isExternal(cd); 1179 } 1180 1181 public DocLink getCrossPackageLink(String pkgName) { 1182 return configuration.extern.getExternalLink(pkgName, pathToRoot, 1183 DocPaths.PACKAGE_SUMMARY.getPath()); 1184 } 1185 1186 /** 1187 * Get the class link. 1188 * 1189 * @param context the id of the context where the link will be added 1190 * @param cd the class doc to link to 1191 * @return a content tree for the link 1192 */ 1193 public Content getQualifiedClassLink(int context, ClassDoc cd) { 1194 return new RawHtml(getLink(new LinkInfoImpl(configuration, context, cd, 1195 configuration.getClassName(cd), ""))); 1196 } 1197 1198 /** 1199 * Add the class link. 1200 * 1201 * @param context the id of the context where the link will be added 1202 * @param cd the class doc to link to 1203 * @param contentTree the content tree to which the link will be added 1204 */ 1205 public void addPreQualifiedClassLink(int context, ClassDoc cd, Content contentTree) { 1206 addPreQualifiedClassLink(context, cd, false, contentTree); 1207 } 1208 1209 /** 1210 * Retrieve the class link with the package portion of the label in 1211 * plain text. If the qualifier is excluded, it will not be included in the 1212 * link label. 1213 * 1214 * @param cd the class to link to. 1215 * @param isStrong true if the link should be strong. 1216 * @return the link with the package portion of the label in plain text. 1217 */ 1218 public String getPreQualifiedClassLink(int context, 1219 ClassDoc cd, boolean isStrong) { 1220 String classlink = ""; 1221 PackageDoc pd = cd.containingPackage(); 1222 if(pd != null && ! configuration.shouldExcludeQualifier(pd.name())) { 1223 classlink = getPkgName(cd); 1224 } 1225 classlink += getLink(new LinkInfoImpl(configuration, 1226 context, cd, cd.name(), isStrong)); 1227 return classlink; 1228 } 1229 1230 /** 1231 * Add the class link with the package portion of the label in 1232 * plain text. If the qualifier is excluded, it will not be included in the 1233 * link label. 1234 * 1235 * @param context the id of the context where the link will be added 1236 * @param cd the class to link to 1237 * @param isStrong true if the link should be strong 1238 * @param contentTree the content tree to which the link with be added 1239 */ 1240 public void addPreQualifiedClassLink(int context, 1241 ClassDoc cd, boolean isStrong, Content contentTree) { 1242 PackageDoc pd = cd.containingPackage(); 1243 if(pd != null && ! configuration.shouldExcludeQualifier(pd.name())) { 1244 contentTree.addContent(getPkgName(cd)); 1245 } 1246 contentTree.addContent(new RawHtml(getLink(new LinkInfoImpl(configuration, 1247 context, cd, cd.name(), isStrong)))); 1248 } 1249 1250 /** 1251 * Add the class link, with only class name as the strong link and prefixing 1252 * plain package name. 1253 * 1254 * @param context the id of the context where the link will be added 1255 * @param cd the class to link to 1256 * @param contentTree the content tree to which the link with be added 1257 */ 1258 public void addPreQualifiedStrongClassLink(int context, ClassDoc cd, Content contentTree) { 1259 addPreQualifiedClassLink(context, cd, true, contentTree); 1260 } 1261 1262 /** 1263 * Get the link for the given member. 1264 * 1265 * @param context the id of the context where the link will be added 1266 * @param doc the member being linked to 1267 * @param label the label for the link 1268 * @return a content tree for the doc link 1269 */ 1270 public Content getDocLink(int context, MemberDoc doc, String label) { 1271 return getDocLink(context, doc.containingClass(), doc, label); 1272 } 1273 1274 /** 1275 * Return the link for the given member. 1276 * 1277 * @param context the id of the context where the link will be printed. 1278 * @param doc the member being linked to. 1279 * @param label the label for the link. 1280 * @param strong true if the link should be strong. 1281 * @return the link for the given member. 1282 */ 1283 public String getDocLink(int context, MemberDoc doc, String label, 1284 boolean strong) { 1285 return getDocLink(context, doc.containingClass(), doc, label, strong); 1286 } 1287 1288 /** 1289 * Return the link for the given member. 1290 * 1291 * @param context the id of the context where the link will be printed. 1292 * @param classDoc the classDoc that we should link to. This is not 1293 * necessarily equal to doc.containingClass(). We may be 1294 * inheriting comments. 1295 * @param doc the member being linked to. 1296 * @param label the label for the link. 1297 * @param strong true if the link should be strong. 1298 * @return the link for the given member. 1299 */ 1300 public String getDocLink(int context, ClassDoc classDoc, MemberDoc doc, 1301 String label, boolean strong) { 1302 if (! (doc.isIncluded() || 1303 Util.isLinkable(classDoc, configuration))) { 1304 return label; 1305 } else if (doc instanceof ExecutableMemberDoc) { 1306 ExecutableMemberDoc emd = (ExecutableMemberDoc)doc; 1307 return getLink(new LinkInfoImpl(configuration, context, classDoc, 1308 getAnchor(emd), label, strong)); 1309 } else if (doc instanceof MemberDoc) { 1310 return getLink(new LinkInfoImpl(configuration, context, classDoc, 1311 doc.name(), label, strong)); 1312 } else { 1313 return label; 1314 } 1315 } 1316 1317 /** 1318 * Return the link for the given member. 1319 * 1320 * @param context the id of the context where the link will be added 1321 * @param classDoc the classDoc that we should link to. This is not 1322 * necessarily equal to doc.containingClass(). We may be 1323 * inheriting comments 1324 * @param doc the member being linked to 1325 * @param label the label for the link 1326 * @return the link for the given member 1327 */ 1328 public Content getDocLink(int context, ClassDoc classDoc, MemberDoc doc, 1329 String label) { 1330 if (! (doc.isIncluded() || 1331 Util.isLinkable(classDoc, configuration))) { 1332 return new StringContent(label); 1333 } else if (doc instanceof ExecutableMemberDoc) { 1334 ExecutableMemberDoc emd = (ExecutableMemberDoc)doc; 1335 return new RawHtml(getLink(new LinkInfoImpl(configuration, context, classDoc, 1336 getAnchor(emd), label, false))); 1337 } else if (doc instanceof MemberDoc) { 1338 return new RawHtml(getLink(new LinkInfoImpl(configuration, context, classDoc, 1339 doc.name(), label, false))); 1340 } else { 1341 return new StringContent(label); 1342 } 1343 } 1344 1345 public String getAnchor(ExecutableMemberDoc emd) { 1346 StringBuilder signature = new StringBuilder(emd.signature()); 1347 StringBuilder signatureParsed = new StringBuilder(); 1348 int counter = 0; 1349 for (int i = 0; i < signature.length(); i++) { 1350 char c = signature.charAt(i); 1351 if (c == '<') { 1352 counter++; 1353 } else if (c == '>') { 1354 counter--; 1355 } else if (counter == 0) { 1356 signatureParsed.append(c); 1357 } 1358 } 1359 return emd.name() + signatureParsed.toString(); 1360 } 1361 1362 public String seeTagToString(SeeTag see) { 1363 String tagName = see.name(); 1364 if (! (tagName.startsWith("@link") || tagName.equals("@see"))) { 1365 return ""; 1366 } 1367 1368 String seetext = replaceDocRootDir(see.text()); 1369 1370 //Check if @see is an href or "string" 1371 if (seetext.startsWith("<") || seetext.startsWith("\"")) { 1372 return seetext; 1373 } 1374 1375 boolean plain = tagName.equalsIgnoreCase("@linkplain"); 1376 String label = plainOrCodeText(plain, see.label()); 1377 1378 //The text from the @see tag. We will output this text when a label is not specified. 1379 String text = plainOrCodeText(plain, seetext); 1380 1381 ClassDoc refClass = see.referencedClass(); 1382 String refClassName = see.referencedClassName(); 1383 MemberDoc refMem = see.referencedMember(); 1384 String refMemName = see.referencedMemberName(); 1385 1386 if (refClass == null) { 1387 //@see is not referencing an included class 1388 PackageDoc refPackage = see.referencedPackage(); 1389 if (refPackage != null && refPackage.isIncluded()) { 1390 //@see is referencing an included package 1391 if (label.isEmpty()) 1392 label = plainOrCodeText(plain, refPackage.name()); 1393 return getPackageLinkString(refPackage, label, false); 1394 } else { 1395 //@see is not referencing an included class or package. Check for cross links. 1396 String classCrossLink; 1397 DocLink packageCrossLink = getCrossPackageLink(refClassName); 1398 if (packageCrossLink != null) { 1399 //Package cross link found 1400 return getHyperLinkString(packageCrossLink, 1401 (label.isEmpty() ? text : label), false); 1402 } else if ((classCrossLink = getCrossClassLink(refClassName, 1403 refMemName, label, false, "", !plain)) != null) { 1404 //Class cross link found (possibly to a member in the class) 1405 return classCrossLink; 1406 } else { 1407 //No cross link found so print warning 1408 configuration.getDocletSpecificMsg().warning(see.position(), "doclet.see.class_or_package_not_found", 1409 tagName, seetext); 1410 return (label.isEmpty() ? text: label); 1411 } 1412 } 1413 } else if (refMemName == null) { 1414 // Must be a class reference since refClass is not null and refMemName is null. 1415 if (label.isEmpty()) { 1416 label = plainOrCodeText(plain, refClass.name()); 1417 } 1418 return getLink(new LinkInfoImpl(configuration, refClass, label)); 1419 } else if (refMem == null) { 1420 // Must be a member reference since refClass is not null and refMemName is not null. 1421 // However, refMem is null, so this referenced member does not exist. 1422 return (label.isEmpty() ? text: label); 1423 } else { 1424 // Must be a member reference since refClass is not null and refMemName is not null. 1425 // refMem is not null, so this @see tag must be referencing a valid member. 1426 ClassDoc containing = refMem.containingClass(); 1427 if (see.text().trim().startsWith("#") && 1428 ! (containing.isPublic() || 1429 Util.isLinkable(containing, configuration))) { 1430 // Since the link is relative and the holder is not even being 1431 // documented, this must be an inherited link. Redirect it. 1432 // The current class either overrides the referenced member or 1433 // inherits it automatically. 1434 if (this instanceof ClassWriterImpl) { 1435 containing = ((ClassWriterImpl) this).getClassDoc(); 1436 } else if (!containing.isPublic()){ 1437 configuration.getDocletSpecificMsg().warning( 1438 see.position(), "doclet.see.class_or_package_not_accessible", 1439 tagName, containing.qualifiedName()); 1440 } else { 1441 configuration.getDocletSpecificMsg().warning( 1442 see.position(), "doclet.see.class_or_package_not_found", 1443 tagName, seetext); 1444 } 1445 } 1446 if (configuration.currentcd != containing) { 1447 refMemName = containing.name() + "." + refMemName; 1448 } 1449 if (refMem instanceof ExecutableMemberDoc) { 1450 if (refMemName.indexOf('(') < 0) { 1451 refMemName += ((ExecutableMemberDoc)refMem).signature(); 1452 } 1453 } 1454 1455 text = plainOrCodeText(plain, Util.escapeHtmlChars(refMemName)); 1456 1457 return getDocLink(LinkInfoImpl.CONTEXT_SEE_TAG, containing, 1458 refMem, (label.isEmpty() ? text: label), false); 1459 } 1460 } 1461 1462 private String plainOrCodeText(boolean plain, String text) { 1463 return (plain || text.isEmpty()) ? text : codeText(text); 1464 } 1465 1466 /** 1467 * Add the inline comment. 1468 * 1469 * @param doc the doc for which the inline comment will be added 1470 * @param tag the inline tag to be added 1471 * @param htmltree the content tree to which the comment will be added 1472 */ 1473 public void addInlineComment(Doc doc, Tag tag, Content htmltree) { 1474 addCommentTags(doc, tag.inlineTags(), false, false, htmltree); 1475 } 1476 1477 /** 1478 * Add the inline deprecated comment. 1479 * 1480 * @param doc the doc for which the inline deprecated comment will be added 1481 * @param tag the inline tag to be added 1482 * @param htmltree the content tree to which the comment will be added 1483 */ 1484 public void addInlineDeprecatedComment(Doc doc, Tag tag, Content htmltree) { 1485 addCommentTags(doc, tag.inlineTags(), true, false, htmltree); 1486 } 1487 1488 /** 1489 * Adds the summary content. 1490 * 1491 * @param doc the doc for which the summary will be generated 1492 * @param htmltree the documentation tree to which the summary will be added 1493 */ 1494 public void addSummaryComment(Doc doc, Content htmltree) { 1495 addSummaryComment(doc, doc.firstSentenceTags(), htmltree); 1496 } 1497 1498 /** 1499 * Adds the summary content. 1500 * 1501 * @param doc the doc for which the summary will be generated 1502 * @param firstSentenceTags the first sentence tags for the doc 1503 * @param htmltree the documentation tree to which the summary will be added 1504 */ 1505 public void addSummaryComment(Doc doc, Tag[] firstSentenceTags, Content htmltree) { 1506 addCommentTags(doc, firstSentenceTags, false, true, htmltree); 1507 } 1508 1509 public void addSummaryDeprecatedComment(Doc doc, Tag tag, Content htmltree) { 1510 addCommentTags(doc, tag.firstSentenceTags(), true, true, htmltree); 1511 } 1512 1513 /** 1514 * Adds the inline comment. 1515 * 1516 * @param doc the doc for which the inline comments will be generated 1517 * @param htmltree the documentation tree to which the inline comments will be added 1518 */ 1519 public void addInlineComment(Doc doc, Content htmltree) { 1520 addCommentTags(doc, doc.inlineTags(), false, false, htmltree); 1521 } 1522 1523 /** 1524 * Adds the comment tags. 1525 * 1526 * @param doc the doc for which the comment tags will be generated 1527 * @param tags the first sentence tags for the doc 1528 * @param depr true if it is deprecated 1529 * @param first true if the first sentence tags should be added 1530 * @param htmltree the documentation tree to which the comment tags will be added 1531 */ 1532 private void addCommentTags(Doc doc, Tag[] tags, boolean depr, 1533 boolean first, Content htmltree) { 1534 if(configuration.nocomment){ 1535 return; 1536 } 1537 Content div; 1538 Content result = new RawHtml(commentTagsToString(null, doc, tags, first)); 1539 if (depr) { 1540 Content italic = HtmlTree.I(result); 1541 div = HtmlTree.DIV(HtmlStyle.block, italic); 1542 htmltree.addContent(div); 1543 } 1544 else { 1545 div = HtmlTree.DIV(HtmlStyle.block, result); 1546 htmltree.addContent(div); 1547 } 1548 if (tags.length == 0) { 1549 htmltree.addContent(getSpace()); 1550 } 1551 } 1552 1553 /** 1554 * Converts inline tags and text to text strings, expanding the 1555 * inline tags along the way. Called wherever text can contain 1556 * an inline tag, such as in comments or in free-form text arguments 1557 * to non-inline tags. 1558 * 1559 * @param holderTag specific tag where comment resides 1560 * @param doc specific doc where comment resides 1561 * @param tags array of text tags and inline tags (often alternating) 1562 * present in the text of interest for this doc 1563 * @param isFirstSentence true if text is first sentence 1564 */ 1565 public String commentTagsToString(Tag holderTag, Doc doc, Tag[] tags, 1566 boolean isFirstSentence) { 1567 StringBuilder result = new StringBuilder(); 1568 boolean textTagChange = false; 1569 // Array of all possible inline tags for this javadoc run 1570 configuration.tagletManager.checkTags(doc, tags, true); 1571 for (int i = 0; i < tags.length; i++) { 1572 Tag tagelem = tags[i]; 1573 String tagName = tagelem.name(); 1574 if (tagelem instanceof SeeTag) { 1575 result.append(seeTagToString((SeeTag)tagelem)); 1576 } else if (! tagName.equals("Text")) { 1577 int originalLength = result.length(); 1578 TagletOutput output = TagletWriter.getInlineTagOuput( 1579 configuration.tagletManager, holderTag, 1580 tagelem, getTagletWriterInstance(isFirstSentence)); 1581 result.append(output == null ? "" : output.toString()); 1582 if (originalLength == 0 && isFirstSentence && tagelem.name().equals("@inheritDoc") && result.length() > 0) { 1583 break; 1584 } else if (configuration.docrootparent.length() > 0 && 1585 tagelem.name().equals("@docRoot") && 1586 ((tags[i + 1]).text()).startsWith("/..")) { 1587 //If Xdocrootparent switch ON, set the flag to remove the /.. occurance after 1588 //{@docRoot} tag in the very next Text tag. 1589 textTagChange = true; 1590 continue; 1591 } else { 1592 continue; 1593 } 1594 } else { 1595 String text = tagelem.text(); 1596 //If Xdocrootparent switch ON, remove the /.. occurance after {@docRoot} tag. 1597 if (textTagChange) { 1598 text = text.replaceFirst("/..", ""); 1599 textTagChange = false; 1600 } 1601 //This is just a regular text tag. The text may contain html links (<a>) 1602 //or inline tag {@docRoot}, which will be handled as special cases. 1603 text = redirectRelativeLinks(tagelem.holder(), text); 1604 1605 // Replace @docRoot only if not represented by an instance of DocRootTaglet, 1606 // that is, only if it was not present in a source file doc comment. 1607 // This happens when inserted by the doclet (a few lines 1608 // above in this method). [It might also happen when passed in on the command 1609 // line as a text argument to an option (like -header).] 1610 text = replaceDocRootDir(text); 1611 if (isFirstSentence) { 1612 text = removeNonInlineHtmlTags(text); 1613 } 1614 StringTokenizer lines = new StringTokenizer(text, "\r\n", true); 1615 StringBuilder textBuff = new StringBuilder(); 1616 while (lines.hasMoreTokens()) { 1617 StringBuilder line = new StringBuilder(lines.nextToken()); 1618 Util.replaceTabs(configuration, line); 1619 textBuff.append(line.toString()); 1620 } 1621 result.append(textBuff); 1622 } 1623 } 1624 return result.toString(); 1625 } 1626 1627 /** 1628 * Return true if relative links should not be redirected. 1629 * 1630 * @return Return true if a relative link should not be redirected. 1631 */ 1632 private boolean shouldNotRedirectRelativeLinks() { 1633 return this instanceof AnnotationTypeWriter || 1634 this instanceof ClassWriter || 1635 this instanceof PackageSummaryWriter; 1636 } 1637 1638 /** 1639 * Suppose a piece of documentation has a relative link. When you copy 1640 * that documentation to another place such as the index or class-use page, 1641 * that relative link will no longer work. We should redirect those links 1642 * so that they will work again. 1643 * <p> 1644 * Here is the algorithm used to fix the link: 1645 * <p> 1646 * {@literal <relative link> => docRoot + <relative path to file> + <relative link> } 1647 * <p> 1648 * For example, suppose com.sun.javadoc.RootDoc has this link: 1649 * {@literal <a href="package-summary.html">The package Page</a> } 1650 * <p> 1651 * If this link appeared in the index, we would redirect 1652 * the link like this: 1653 * 1654 * {@literal <a href="./com/sun/javadoc/package-summary.html">The package Page</a>} 1655 * 1656 * @param doc the Doc object whose documentation is being written. 1657 * @param text the text being written. 1658 * 1659 * @return the text, with all the relative links redirected to work. 1660 */ 1661 private String redirectRelativeLinks(Doc doc, String text) { 1662 if (doc == null || shouldNotRedirectRelativeLinks()) { 1663 return text; 1664 } 1665 1666 DocPath redirectPathFromRoot; 1667 if (doc instanceof ClassDoc) { 1668 redirectPathFromRoot = DocPath.forPackage(((ClassDoc) doc).containingPackage()); 1669 } else if (doc instanceof MemberDoc) { 1670 redirectPathFromRoot = DocPath.forPackage(((MemberDoc) doc).containingPackage()); 1671 } else if (doc instanceof PackageDoc) { 1672 redirectPathFromRoot = DocPath.forPackage((PackageDoc) doc); 1673 } else { 1674 return text; 1675 } 1676 1677 //Redirect all relative links. 1678 int end, begin = text.toLowerCase().indexOf("<a"); 1679 if(begin >= 0){ 1680 StringBuilder textBuff = new StringBuilder(text); 1681 1682 while(begin >=0){ 1683 if (textBuff.length() > begin + 2 && ! Character.isWhitespace(textBuff.charAt(begin+2))) { 1684 begin = textBuff.toString().toLowerCase().indexOf("<a", begin + 1); 1685 continue; 1686 } 1687 1688 begin = textBuff.indexOf("=", begin) + 1; 1689 end = textBuff.indexOf(">", begin +1); 1690 if(begin == 0){ 1691 //Link has no equal symbol. 1692 configuration.root.printWarning( 1693 doc.position(), 1694 configuration.getText("doclet.malformed_html_link_tag", text)); 1695 break; 1696 } 1697 if (end == -1) { 1698 //Break without warning. This <a> tag is not necessarily malformed. The text 1699 //might be missing '>' character because the href has an inline tag. 1700 break; 1701 } 1702 if (textBuff.substring(begin, end).indexOf("\"") != -1){ 1703 begin = textBuff.indexOf("\"", begin) + 1; 1704 end = textBuff.indexOf("\"", begin +1); 1705 if (begin == 0 || end == -1){ 1706 //Link is missing a quote. 1707 break; 1708 } 1709 } 1710 String relativeLink = textBuff.substring(begin, end); 1711 if (!(relativeLink.toLowerCase().startsWith("mailto:") || 1712 relativeLink.toLowerCase().startsWith("http:") || 1713 relativeLink.toLowerCase().startsWith("https:") || 1714 relativeLink.toLowerCase().startsWith("file:"))) { 1715 relativeLink = "{@"+(new DocRootTaglet()).getName() + "}/" 1716 + redirectPathFromRoot.resolve(relativeLink).getPath(); 1717 textBuff.replace(begin, end, relativeLink); 1718 } 1719 begin = textBuff.toString().toLowerCase().indexOf("<a", begin + 1); 1720 } 1721 return textBuff.toString(); 1722 } 1723 return text; 1724 } 1725 1726 public String removeNonInlineHtmlTags(String text) { 1727 if (text.indexOf('<') < 0) { 1728 return text; 1729 } 1730 String noninlinetags[] = { "<ul>", "</ul>", "<ol>", "</ol>", 1731 "<dl>", "</dl>", "<table>", "</table>", 1732 "<tr>", "</tr>", "<td>", "</td>", 1733 "<th>", "</th>", "<p>", "</p>", 1734 "<li>", "</li>", "<dd>", "</dd>", 1735 "<dir>", "</dir>", "<dt>", "</dt>", 1736 "<h1>", "</h1>", "<h2>", "</h2>", 1737 "<h3>", "</h3>", "<h4>", "</h4>", 1738 "<h5>", "</h5>", "<h6>", "</h6>", 1739 "<pre>", "</pre>", "<menu>", "</menu>", 1740 "<listing>", "</listing>", "<hr>", 1741 "<blockquote>", "</blockquote>", 1742 "<center>", "</center>", 1743 "<UL>", "</UL>", "<OL>", "</OL>", 1744 "<DL>", "</DL>", "<TABLE>", "</TABLE>", 1745 "<TR>", "</TR>", "<TD>", "</TD>", 1746 "<TH>", "</TH>", "<P>", "</P>", 1747 "<LI>", "</LI>", "<DD>", "</DD>", 1748 "<DIR>", "</DIR>", "<DT>", "</DT>", 1749 "<H1>", "</H1>", "<H2>", "</H2>", 1750 "<H3>", "</H3>", "<H4>", "</H4>", 1751 "<H5>", "</H5>", "<H6>", "</H6>", 1752 "<PRE>", "</PRE>", "<MENU>", "</MENU>", 1753 "<LISTING>", "</LISTING>", "<HR>", 1754 "<BLOCKQUOTE>", "</BLOCKQUOTE>", 1755 "<CENTER>", "</CENTER>" 1756 }; 1757 for (int i = 0; i < noninlinetags.length; i++) { 1758 text = replace(text, noninlinetags[i], ""); 1759 } 1760 return text; 1761 } 1762 1763 public String replace(String text, String tobe, String by) { 1764 while (true) { 1765 int startindex = text.indexOf(tobe); 1766 if (startindex < 0) { 1767 return text; 1768 } 1769 int endindex = startindex + tobe.length(); 1770 StringBuilder replaced = new StringBuilder(); 1771 if (startindex > 0) { 1772 replaced.append(text.substring(0, startindex)); 1773 } 1774 replaced.append(by); 1775 if (text.length() > endindex) { 1776 replaced.append(text.substring(endindex)); 1777 } 1778 text = replaced.toString(); 1779 } 1780 } 1781 1782 /** 1783 * Returns a link to the stylesheet file. 1784 * 1785 * @return an HtmlTree for the lINK tag which provides the stylesheet location 1786 */ 1787 public HtmlTree getStyleSheetProperties() { 1788 String stylesheetfile = configuration.stylesheetfile; 1789 DocPath stylesheet; 1790 if (stylesheetfile.isEmpty()) { 1791 stylesheet = DocPaths.STYLESHEET; 1792 } else { 1793 DocFile file = DocFile.createFileForInput(configuration, stylesheetfile); 1794 stylesheet = DocPath.create(file.getName()); 1795 } 1796 HtmlTree link = HtmlTree.LINK("stylesheet", "text/css", 1797 pathToRoot.resolve(stylesheet).getPath(), 1798 "Style"); 1799 return link; 1800 } 1801 1802 /** 1803 * Returns a link to the JavaScript file. 1804 * 1805 * @return an HtmlTree for the Script tag which provides the JavaScript location 1806 */ 1807 public HtmlTree getScriptProperties() { 1808 HtmlTree script = HtmlTree.SCRIPT("text/javascript", 1809 pathToRoot.resolve(DocPaths.JAVASCRIPT).getPath()); 1810 return script; 1811 } 1812 1813 /** 1814 * According to 1815 * <cite>The Java™ Language Specification</cite>, 1816 * all the outer classes and static nested classes are core classes. 1817 */ 1818 public boolean isCoreClass(ClassDoc cd) { 1819 return cd.containingClass() == null || cd.isStatic(); 1820 } 1821 1822 /** 1823 * Adds the annotatation types for the given packageDoc. 1824 * 1825 * @param packageDoc the package to write annotations for. 1826 * @param htmltree the documentation tree to which the annotation info will be 1827 * added 1828 */ 1829 public void addAnnotationInfo(PackageDoc packageDoc, Content htmltree) { 1830 addAnnotationInfo(packageDoc, packageDoc.annotations(), htmltree); 1831 } 1832 1833 /** 1834 * Adds the annotatation types for the given doc. 1835 * 1836 * @param doc the package to write annotations for 1837 * @param htmltree the content tree to which the annotation types will be added 1838 */ 1839 public void addAnnotationInfo(ProgramElementDoc doc, Content htmltree) { 1840 addAnnotationInfo(doc, doc.annotations(), htmltree); 1841 } 1842 1843 /** 1844 * Add the annotatation types for the given doc and parameter. 1845 * 1846 * @param indent the number of spaces to indent the parameters. 1847 * @param doc the doc to write annotations for. 1848 * @param param the parameter to write annotations for. 1849 * @param tree the content tree to which the annotation types will be added 1850 */ 1851 public boolean addAnnotationInfo(int indent, Doc doc, Parameter param, 1852 Content tree) { 1853 return addAnnotationInfo(indent, doc, param.annotations(), false, tree); 1854 } 1855 1856 /** 1857 * Adds the annotatation types for the given doc. 1858 * 1859 * @param doc the doc to write annotations for. 1860 * @param descList the array of {@link AnnotationDesc}. 1861 * @param htmltree the documentation tree to which the annotation info will be 1862 * added 1863 */ 1864 private void addAnnotationInfo(Doc doc, AnnotationDesc[] descList, 1865 Content htmltree) { 1866 addAnnotationInfo(0, doc, descList, true, htmltree); 1867 } 1868 1869 /** 1870 * Adds the annotatation types for the given doc. 1871 * 1872 * @param indent the number of extra spaces to indent the annotations. 1873 * @param doc the doc to write annotations for. 1874 * @param descList the array of {@link AnnotationDesc}. 1875 * @param htmltree the documentation tree to which the annotation info will be 1876 * added 1877 */ 1878 private boolean addAnnotationInfo(int indent, Doc doc, 1879 AnnotationDesc[] descList, boolean lineBreak, Content htmltree) { 1880 List<String> annotations = getAnnotations(indent, descList, lineBreak); 1881 if (annotations.size() == 0) { 1882 return false; 1883 } 1884 Content annotationContent; 1885 for (Iterator<String> iter = annotations.iterator(); iter.hasNext();) { 1886 annotationContent = new RawHtml(iter.next()); 1887 htmltree.addContent(annotationContent); 1888 } 1889 return true; 1890 } 1891 1892 /** 1893 * Return the string representations of the annotation types for 1894 * the given doc. 1895 * 1896 * @param indent the number of extra spaces to indent the annotations. 1897 * @param descList the array of {@link AnnotationDesc}. 1898 * @param linkBreak if true, add new line between each member value. 1899 * @return an array of strings representing the annotations being 1900 * documented. 1901 */ 1902 private List<String> getAnnotations(int indent, AnnotationDesc[] descList, boolean linkBreak) { 1903 List<String> results = new ArrayList<String>(); 1904 StringBuilder annotation; 1905 for (int i = 0; i < descList.length; i++) { 1906 AnnotationTypeDoc annotationDoc = descList[i].annotationType(); 1907 // If an annotation is not documented, do not add it to the list. If 1908 // the annotation is of a repeatable type, and if it is not documented 1909 // and also if its container annotation is not documented, do not add it 1910 // to the list. If an annotation of a repeatable type is not documented 1911 // but its container is documented, it will be added to the list. 1912 if (! Util.isDocumentedAnnotation(annotationDoc) && 1913 (!isAnnotationDocumented && !isContainerDocumented)) { 1914 continue; 1915 } 1916 annotation = new StringBuilder(); 1917 isAnnotationDocumented = false; 1918 LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, 1919 LinkInfoImpl.CONTEXT_ANNOTATION, annotationDoc); 1920 AnnotationDesc.ElementValuePair[] pairs = descList[i].elementValues(); 1921 // If the annotation is synthesized, do not print the container. 1922 if (descList[i].isSynthesized()) { 1923 for (int j = 0; j < pairs.length; j++) { 1924 AnnotationValue annotationValue = pairs[j].value(); 1925 List<AnnotationValue> annotationTypeValues = new ArrayList<AnnotationValue>(); 1926 if (annotationValue.value() instanceof AnnotationValue[]) { 1927 AnnotationValue[] annotationArray = 1928 (AnnotationValue[]) annotationValue.value(); 1929 annotationTypeValues.addAll(Arrays.asList(annotationArray)); 1930 } else { 1931 annotationTypeValues.add(annotationValue); 1932 } 1933 String sep = ""; 1934 for (AnnotationValue av : annotationTypeValues) { 1935 annotation.append(sep); 1936 annotation.append(annotationValueToString(av)); 1937 sep = " "; 1938 } 1939 } 1940 } 1941 else if (isAnnotationArray(pairs)) { 1942 // If the container has 1 or more value defined and if the 1943 // repeatable type annotation is not documented, do not print 1944 // the container. 1945 if (pairs.length == 1 && isAnnotationDocumented) { 1946 AnnotationValue[] annotationArray = 1947 (AnnotationValue[]) (pairs[0].value()).value(); 1948 List<AnnotationValue> annotationTypeValues = new ArrayList<AnnotationValue>(); 1949 annotationTypeValues.addAll(Arrays.asList(annotationArray)); 1950 String sep = ""; 1951 for (AnnotationValue av : annotationTypeValues) { 1952 annotation.append(sep); 1953 annotation.append(annotationValueToString(av)); 1954 sep = " "; 1955 } 1956 } 1957 // If the container has 1 or more value defined and if the 1958 // repeatable type annotation is not documented, print the container. 1959 else { 1960 addAnnotations(annotationDoc, linkInfo, annotation, pairs, 1961 indent, false); 1962 } 1963 } 1964 else { 1965 addAnnotations(annotationDoc, linkInfo, annotation, pairs, 1966 indent, linkBreak); 1967 } 1968 annotation.append(linkBreak ? DocletConstants.NL : ""); 1969 results.add(annotation.toString()); 1970 } 1971 return results; 1972 } 1973 1974 /** 1975 * Add annotation to the annotation string. 1976 * 1977 * @param annotationDoc the annotation being documented 1978 * @param linkInfo the information about the link 1979 * @param annotation the annotation string to which the annotation will be added 1980 * @param pairs annotation type element and value pairs 1981 * @param indent the number of extra spaces to indent the annotations. 1982 * @param linkBreak if true, add new line between each member value 1983 */ 1984 private void addAnnotations(AnnotationTypeDoc annotationDoc, LinkInfoImpl linkInfo, 1985 StringBuilder annotation, AnnotationDesc.ElementValuePair[] pairs, 1986 int indent, boolean linkBreak) { 1987 linkInfo.label = "@" + annotationDoc.name(); 1988 annotation.append(getLink(linkInfo)); 1989 if (pairs.length > 0) { 1990 annotation.append('('); 1991 for (int j = 0; j < pairs.length; j++) { 1992 if (j > 0) { 1993 annotation.append(","); 1994 if (linkBreak) { 1995 annotation.append(DocletConstants.NL); 1996 int spaces = annotationDoc.name().length() + 2; 1997 for (int k = 0; k < (spaces + indent); k++) { 1998 annotation.append(' '); 1999 } 2000 } 2001 } 2002 annotation.append(getDocLink(LinkInfoImpl.CONTEXT_ANNOTATION, 2003 pairs[j].element(), pairs[j].element().name(), false)); 2004 annotation.append('='); 2005 AnnotationValue annotationValue = pairs[j].value(); 2006 List<AnnotationValue> annotationTypeValues = new ArrayList<AnnotationValue>(); 2007 if (annotationValue.value() instanceof AnnotationValue[]) { 2008 AnnotationValue[] annotationArray = 2009 (AnnotationValue[]) annotationValue.value(); 2010 annotationTypeValues.addAll(Arrays.asList(annotationArray)); 2011 } else { 2012 annotationTypeValues.add(annotationValue); 2013 } 2014 annotation.append(annotationTypeValues.size() == 1 ? "" : "{"); 2015 String sep = ""; 2016 for (AnnotationValue av : annotationTypeValues) { 2017 annotation.append(sep); 2018 annotation.append(annotationValueToString(av)); 2019 sep = ","; 2020 } 2021 annotation.append(annotationTypeValues.size() == 1 ? "" : "}"); 2022 isContainerDocumented = false; 2023 } 2024 annotation.append(")"); 2025 } 2026 } 2027 2028 /** 2029 * Check if the annotation contains an array of annotation as a value. This 2030 * check is to verify if a repeatable type annotation is present or not. 2031 * 2032 * @param pairs annotation type element and value pairs 2033 * 2034 * @return true if the annotation contains an array of annotation as a value. 2035 */ 2036 private boolean isAnnotationArray(AnnotationDesc.ElementValuePair[] pairs) { 2037 AnnotationValue annotationValue; 2038 for (int j = 0; j < pairs.length; j++) { 2039 annotationValue = pairs[j].value(); 2040 if (annotationValue.value() instanceof AnnotationValue[]) { 2041 AnnotationValue[] annotationArray = 2042 (AnnotationValue[]) annotationValue.value(); 2043 if (annotationArray.length > 1) { 2044 if (annotationArray[0].value() instanceof AnnotationDesc) { 2045 AnnotationTypeDoc annotationDoc = 2046 ((AnnotationDesc) annotationArray[0].value()).annotationType(); 2047 isContainerDocumented = true; 2048 if (Util.isDocumentedAnnotation(annotationDoc)) { 2049 isAnnotationDocumented = true; 2050 } 2051 return true; 2052 } 2053 } 2054 } 2055 } 2056 return false; 2057 } 2058 2059 private String annotationValueToString(AnnotationValue annotationValue) { 2060 if (annotationValue.value() instanceof Type) { 2061 Type type = (Type) annotationValue.value(); 2062 if (type.asClassDoc() != null) { 2063 LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, 2064 LinkInfoImpl.CONTEXT_ANNOTATION, type); 2065 linkInfo.label = (type.asClassDoc().isIncluded() ? 2066 type.typeName() : 2067 type.qualifiedTypeName()) + type.dimension() + ".class"; 2068 return getLink(linkInfo); 2069 } else { 2070 return type.typeName() + type.dimension() + ".class"; 2071 } 2072 } else if (annotationValue.value() instanceof AnnotationDesc) { 2073 List<String> list = getAnnotations(0, 2074 new AnnotationDesc[]{(AnnotationDesc) annotationValue.value()}, 2075 false); 2076 StringBuilder buf = new StringBuilder(); 2077 for (String s: list) { 2078 buf.append(s); 2079 } 2080 return buf.toString(); 2081 } else if (annotationValue.value() instanceof MemberDoc) { 2082 return getDocLink(LinkInfoImpl.CONTEXT_ANNOTATION, 2083 (MemberDoc) annotationValue.value(), 2084 ((MemberDoc) annotationValue.value()).name(), false); 2085 } else { 2086 return annotationValue.toString(); 2087 } 2088 } 2089 2090 /** 2091 * Return the configuation for this doclet. 2092 * 2093 * @return the configuration for this doclet. 2094 */ 2095 public Configuration configuration() { 2096 return configuration; 2097 } 2098 }