1 /* 2 * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.doclets.formats.html; 27 28 import java.io.*; 29 import java.text.SimpleDateFormat; 30 import java.util.*; 31 import java.util.regex.Matcher; 32 import java.util.regex.Pattern; 33 34 import com.sun.javadoc.*; 35 import com.sun.tools.doclets.formats.html.markup.*; 36 import com.sun.tools.doclets.internal.toolkit.*; 37 import com.sun.tools.doclets.internal.toolkit.taglets.*; 38 import com.sun.tools.doclets.internal.toolkit.util.*; 39 import com.sun.tools.javac.util.StringUtils; 40 41 /** 42 * Class for the Html Format Code Generation specific to JavaDoc. 43 * This Class contains methods related to the Html Code Generation which 44 * are used extensively while generating the entire documentation. 45 * 46 * <p><b>This is NOT part of any supported API. 47 * If you write code that depends on this, you do so at your own risk. 48 * This code and its internal interfaces are subject to change or 49 * deletion without notice.</b> 50 * 51 * @since 1.2 52 * @author Atul M Dambalkar 53 * @author Robert Field 54 * @author Bhavesh Patel (Modified) 55 */ 56 public class HtmlDocletWriter extends HtmlDocWriter { 57 58 /** 59 * Relative path from the file getting generated to the destination 60 * directory. For example, if the file getting generated is 193 " }" + DocletConstants.NL + 194 " else {" + DocletConstants.NL + 195 " allClassesLink.style.display = \"none\";" + DocletConstants.NL + 196 " }" + DocletConstants.NL + 197 " //-->" + DocletConstants.NL; 198 Content scriptContent = new RawHtml(scriptCode); 199 script.addContent(scriptContent); 200 Content div = HtmlTree.DIV(script); 201 Content div_noscript = HtmlTree.DIV(getResource("doclet.No_Script_Message")); 202 Content noScript = HtmlTree.NOSCRIPT(div_noscript); 203 div.addContent(noScript); 204 return div; 205 } 206 207 /** 208 * Add method information. 209 * 210 * @param method the method to be documented 211 * @param dl the content tree to which the method information will be added 212 */ 213 private void addMethodInfo(MethodDoc method, Content dl) { 214 ClassDoc[] intfacs = method.containingClass().interfaces(); 215 MethodDoc overriddenMethod = method.overriddenMethod(); 216 // Check whether there is any implementation or overridden info to be 217 // printed. If no overridden or implementation info needs to be 218 // printed, do not print this section. 219 if ((intfacs.length > 0 && 220 new ImplementedMethods(method, this.configuration).build().length > 0) || 221 overriddenMethod != null) { 222 MethodWriterImpl.addImplementsInfo(this, method, dl); 223 if (overriddenMethod != null) { 224 MethodWriterImpl.addOverridden(this, 225 method.overriddenType(), overriddenMethod, dl); 226 } 227 } 228 } 229 230 /** 231 * Adds the tags information. 232 * 233 * @param doc the doc for which the tags will be generated 234 * @param htmltree the documentation tree to which the tags will be added 235 */ 236 protected void addTagsInfo(Doc doc, Content htmltree) { 237 if (configuration.nocomment) { 238 return; 239 } 240 Content dl = new HtmlTree(HtmlTag.DL); 241 if (doc instanceof MethodDoc) { 242 addMethodInfo((MethodDoc) doc, dl); 243 } 244 Content output = new ContentBuilder(); 245 TagletWriter.genTagOuput(configuration.tagletManager, doc, 246 configuration.tagletManager.getCustomTaglets(doc), 247 getTagletWriterInstance(false), output); 248 dl.addContent(output); 249 htmltree.addContent(dl); 250 } 251 252 /** 253 * Check whether there are any tags for Serialization Overview 254 * section to be printed. 255 * 256 * @param field the FieldDoc object to check for tags. 257 * @return true if there are tags to be printed else return false. 258 */ 259 protected boolean hasSerializationOverviewTags(FieldDoc field) { 260 Content output = new ContentBuilder(); 261 TagletWriter.genTagOuput(configuration.tagletManager, field, 262 configuration.tagletManager.getCustomTaglets(field), 263 getTagletWriterInstance(false), output); 264 return !output.isEmpty(); 265 } 266 267 /** 268 * Returns a TagletWriter that knows how to write HTML. 269 * 270 * @return a TagletWriter that knows how to write HTML. 271 */ 272 public TagletWriter getTagletWriterInstance(boolean isFirstSentence) { 273 return new TagletWriterImpl(this, isFirstSentence); 274 } 275 276 /** 277 * Get Package link, with target frame. 278 * 279 * @param pd The link will be to the "package-summary.html" page for this package 280 * @param target name of the target frame 281 * @param label tag for the link 282 * @return a content for the target package link 283 */ 284 public Content getTargetPackageLink(PackageDoc pd, String target, 285 Content label) { 286 return getHyperLink(pathString(pd, DocPaths.PACKAGE_SUMMARY), label, "", target); 287 } 288 289 /** 290 * Get Profile Package link, with target frame. 291 * 292 * @param pd the packageDoc object 293 * @param target name of the target frame 294 * @param label tag for the link 295 * @param profileName the name of the profile being documented 296 * @return a content for the target profile packages link 297 */ 298 public Content getTargetProfilePackageLink(PackageDoc pd, String target, 299 Content label, String profileName) { 300 return getHyperLink(pathString(pd, DocPaths.profilePackageSummary(profileName)), 301 label, "", target); 302 } 303 304 /** 305 * Get Profile link, with target frame. 306 * 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 link 311 */ 312 public Content getTargetProfileLink(String target, Content label, 313 String profileName) { 314 return getHyperLink(pathToRoot.resolve( 315 DocPaths.profileSummary(profileName)), label, "", target); 316 } 317 318 /** 319 * Get the type name for profile search. 320 * 321 * @param cd the classDoc object for which the type name conversion is needed 322 * @return a type name string for the type 323 */ 324 public String getTypeNameForProfile(ClassDoc cd) { 325 StringBuilder typeName = 326 new StringBuilder((cd.containingPackage()).name().replace(".", "/")); 327 typeName.append("/") 328 .append(cd.name().replace(".", "$")); 329 return typeName.toString(); 330 } 331 332 /** 333 * Check if a type belongs to a profile. 334 * 335 * @param cd the classDoc object that needs to be checked 336 * @param profileValue the profile in which the type needs to be checked 337 * @return true if the type is in the profile 338 */ 339 public boolean isTypeInProfile(ClassDoc cd, int profileValue) { 340 return (configuration.profiles.getProfile(getTypeNameForProfile(cd)) <= profileValue); 341 } 342 343 public void addClassesSummary(ClassDoc[] classes, String label, 344 String tableSummary, String[] tableHeader, Content summaryContentTree, 345 int profileValue) { 346 if(classes.length > 0) { 347 Arrays.sort(classes); 348 Content caption = getTableCaption(new RawHtml(label)); 349 Content table = (configuration.isOutputHtml5()) 350 ? HtmlTree.TABLE(HtmlStyle.typeSummary, caption) 351 : HtmlTree.TABLE(HtmlStyle.typeSummary, tableSummary, caption); 352 table.addContent(getSummaryTableHeader(tableHeader, "col")); 353 Content tbody = new HtmlTree(HtmlTag.TBODY); 354 for (int i = 0; i < classes.length; i++) { 355 if (!isTypeInProfile(classes[i], profileValue)) { 356 continue; 357 } 358 if (!utils.isCoreClass(classes[i]) || 359 !configuration.isGeneratedDoc(classes[i])) { 360 continue; 361 } 362 Content classContent = getLink(new LinkInfoImpl( 363 configuration, LinkInfoImpl.Kind.PACKAGE, classes[i])); 364 Content tdClass = HtmlTree.TD(HtmlStyle.colFirst, classContent); 365 HtmlTree tr = HtmlTree.TR(tdClass); 366 if (i%2 == 0) 367 tr.addStyle(HtmlStyle.altColor); 368 else 369 tr.addStyle(HtmlStyle.rowColor); 370 HtmlTree tdClassDescription = new HtmlTree(HtmlTag.TD); 371 tdClassDescription.addStyle(HtmlStyle.colLast); 372 if (utils.isDeprecated(classes[i])) { 373 tdClassDescription.addContent(deprecatedLabel); 374 if (classes[i].tags("deprecated").length > 0) { 375 addSummaryDeprecatedComment(classes[i], 376 classes[i].tags("deprecated")[0], tdClassDescription); 377 } 378 } 379 else 380 addSummaryComment(classes[i], tdClassDescription); 381 tr.addContent(tdClassDescription); 382 tbody.addContent(tr); 383 } 384 table.addContent(tbody); 385 summaryContentTree.addContent(table); 386 } 387 } 388 389 /** 390 * Generates the HTML document tree and prints it out. 391 * 392 * @param metakeywords Array of String keywords for META tag. Each element 393 * of the array is assigned to a separate META tag. 394 * Pass in null for no array 395 * @param includeScript true if printing windowtitle script 396 * false for files that appear in the left-hand frames 397 * @param body the body htmltree to be included in the document 398 */ 399 public void printHtmlDocument(String[] metakeywords, boolean includeScript, 400 Content body) throws IOException { 401 Content htmlDocType = configuration.isOutputHtml5() 402 ? DocType.HTML5 403 : DocType.TRANSITIONAL; 404 Content htmlComment = new Comment(configuration.getText("doclet.New_Page")); 405 Content head = new HtmlTree(HtmlTag.HEAD); 406 head.addContent(getGeneratedBy(!configuration.notimestamp)); 407 head.addContent(getTitle()); 408 Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, 409 (configuration.charset.length() > 0) ? 410 configuration.charset : HtmlConstants.HTML_DEFAULT_CHARSET); 411 head.addContent(meta); 412 if (!configuration.notimestamp) { 413 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 414 meta = HtmlTree.META(configuration.isOutputHtml5() 415 ? "dc.created" 416 : "date", dateFormat.format(new Date())); 417 head.addContent(meta); 418 } 419 if (metakeywords != null) { 521 navDiv.addContent(a); 522 Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav, getHyperLink( 523 getDocLink(SectionName.SKIP_NAVBAR_BOTTOM), skipNavLinks, 524 skipNavLinks.toString(), "")); 525 navDiv.addContent(skipLinkContent); 526 } 527 if (header) { 528 navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_TOP_FIRSTROW)); 529 } else { 530 navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_BOTTOM_FIRSTROW)); 531 } 532 HtmlTree navList = new HtmlTree(HtmlTag.UL); 533 navList.addStyle(HtmlStyle.navList); 534 navList.addAttr(HtmlAttr.TITLE, 535 configuration.getText("doclet.Navigation")); 536 if (configuration.createoverview) { 537 navList.addContent(getNavLinkContents()); 538 } 539 if (configuration.packages.size() == 1) { 540 navList.addContent(getNavLinkPackage(configuration.packages.first())); 541 } else if (configuration.packages.size() > 1) { 542 navList.addContent(getNavLinkPackage()); 543 } 544 navList.addContent(getNavLinkClass()); 545 if(configuration.classuse) { 546 navList.addContent(getNavLinkClassUse()); 547 } 548 if(configuration.createtree) { 549 navList.addContent(getNavLinkTree()); 550 } 551 if(!(configuration.nodeprecated || 552 configuration.nodeprecatedlist)) { 553 navList.addContent(getNavLinkDeprecated()); 554 } 555 if(configuration.createindex) { 556 navList.addContent(getNavLinkIndex()); 557 } 558 if (!configuration.nohelp) { 559 navList.addContent(getNavLinkHelp()); 560 } 561 navDiv.addContent(navList); 562 Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, getUserHeaderFooter(header)); 563 navDiv.addContent(aboutDiv); 564 if (header) { 565 fixedNavDiv.addContent(navDiv); 566 } else { 567 tree.addContent(navDiv); 568 } 569 Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious()); 570 ulNav.addContent(getNavLinkNext()); 571 Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav); 572 Content ulFrames = HtmlTree.UL(HtmlStyle.navList, getNavShowLists()); 573 ulFrames.addContent(getNavHideLists(filename)); 574 subDiv.addContent(ulFrames); 575 HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex()); 576 ulAllClasses.addAttr(HtmlAttr.ID, allClassesId.toString()); 577 subDiv.addContent(ulAllClasses); 578 if (header && configuration.createindex) { 579 HtmlTree inputText = HtmlTree.INPUT("text", "search"); 580 HtmlTree inputReset = HtmlTree.INPUT("reset", "reset"); 581 Content searchTxt = configuration.getResource("doclet.search"); 582 searchTxt.addContent(getSpace()); 583 HtmlTree liInput = HtmlTree.LI(HtmlTree.SPAN(searchTxt)); 584 liInput.addContent(inputText); 585 liInput.addContent(inputReset); 586 HtmlTree ulSearch = HtmlTree.UL(HtmlStyle.navListSearch, liInput); 587 subDiv.addContent(ulSearch); 588 } 589 subDiv.addContent(getAllClassesLinkScript(allClassesId.toString())); 590 addSummaryDetailLinks(subDiv); 591 if (header) { 592 subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_TOP)); 593 fixedNavDiv.addContent(subDiv); 594 fixedNavDiv.addContent(HtmlConstants.END_OF_TOP_NAVBAR); 595 tree.addContent(fixedNavDiv); 596 } else { 597 subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_BOTTOM)); 598 tree.addContent(subDiv); 599 tree.addContent(HtmlConstants.END_OF_BOTTOM_NAVBAR); 600 } 601 if (configuration.allowTag(HtmlTag.NAV)) { 602 htmlTree.addContent(tree); 603 } 604 } 605 } 606 607 /** 608 * Get the word "NEXT" to indicate that no link is available. Override 609 * this method to customize next link. 631 } 632 633 /** 634 * Get link to the "overview-summary.html" page. 635 * 636 * @return a content tree for the link 637 */ 638 protected Content getNavLinkContents() { 639 Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_SUMMARY), 640 overviewLabel, "", ""); 641 Content li = HtmlTree.LI(linkContent); 642 return li; 643 } 644 645 /** 646 * Get link to the "package-summary.html" page for the package passed. 647 * 648 * @param pkg Package to which link will be generated 649 * @return a content tree for the link 650 */ 651 protected Content getNavLinkPackage(PackageDoc pkg) { 652 Content linkContent = getPackageLink(pkg, 653 packageLabel); 654 Content li = HtmlTree.LI(linkContent); 655 return li; 656 } 657 658 /** 659 * Get the word "Package" , to indicate that link is not available here. 660 * 661 * @return a content tree for the link 662 */ 663 protected Content getNavLinkPackage() { 664 Content li = HtmlTree.LI(packageLabel); 665 return li; 666 } 667 668 /** 669 * Get the word "Use", to indicate that link is not available. 670 * 671 * @return a content tree for the link 672 */ 673 protected Content getNavLinkClassUse() { 734 * Get "NO FRAMES" link, to switch to the non-frame version of the output. 735 * 736 * @param link File to be linked 737 * @return a content tree for the link 738 */ 739 protected Content getNavHideLists(DocPath link) { 740 Content noFramesContent = getHyperLink(link, noframesLabel, "", "_top"); 741 Content li = HtmlTree.LI(noFramesContent); 742 return li; 743 } 744 745 /** 746 * Get "Tree" link in the navigation bar. If there is only one package 747 * specified on the command line, then the "Tree" link will be to the 748 * only "package-tree.html" file otherwise it will be to the 749 * "overview-tree.html" file. 750 * 751 * @return a content tree for the link 752 */ 753 protected Content getNavLinkTree() { 754 Content treeLinkContent; 755 PackageDoc[] packages = configuration.root.specifiedPackages(); 756 if (packages.length == 1 && configuration.root.specifiedClasses().length == 0) { 757 treeLinkContent = getHyperLink(pathString(packages[0], 758 DocPaths.PACKAGE_TREE), treeLabel, 759 "", ""); 760 } else { 761 treeLinkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE), 762 treeLabel, "", ""); 763 } 764 Content li = HtmlTree.LI(treeLinkContent); 765 return li; 766 } 767 768 /** 769 * Get the overview tree link for the main tree. 770 * 771 * @param label the label for the link 772 * @return a content tree for the link 773 */ 774 protected Content getNavLinkMainTree(String label) { 775 Content mainTreeContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE), 776 new StringContent(label)); 777 Content li = HtmlTree.LI(mainTreeContent); 778 return li; 779 } 780 781 /** 782 * Get the word "Class", to indicate that class link is not available. 783 * 784 * @return a content tree for the link 785 */ 786 protected Content getNavLinkClass() { 842 DocPath helpfilenm; 843 if (helpfile.isEmpty()) { 844 helpfilenm = DocPaths.HELP_DOC; 845 } else { 846 DocFile file = DocFile.createFileForInput(configuration, helpfile); 847 helpfilenm = DocPath.create(file.getName()); 848 } 849 Content linkContent = getHyperLink(pathToRoot.resolve(helpfilenm), 850 helpLabel, "", ""); 851 Content li = HtmlTree.LI(linkContent); 852 return li; 853 } 854 855 /** 856 * Get summary table header. 857 * 858 * @param header the header for the table 859 * @param scope the scope of the headers 860 * @return a content tree for the header 861 */ 862 public Content getSummaryTableHeader(String[] header, String scope) { 863 Content tr = new HtmlTree(HtmlTag.TR); 864 int size = header.length; 865 Content tableHeader; 866 if (size == 1) { 867 tableHeader = new StringContent(header[0]); 868 tr.addContent(HtmlTree.TH(HtmlStyle.colOne, scope, tableHeader)); 869 return tr; 870 } 871 for (int i = 0; i < size; i++) { 872 tableHeader = new StringContent(header[i]); 873 if(i == 0) 874 tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader)); 875 else if(i == (size - 1)) 876 tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader)); 877 else 878 tr.addContent(HtmlTree.TH(scope, tableHeader)); 879 } 880 return tr; 881 } 882 883 /** 884 * Get table caption. 885 * 886 * @param rawText the caption for the table which could be raw Html 887 * @return a content tree for the caption 888 */ 889 public Content getTableCaption(Content title) { 890 Content captionSpan = HtmlTree.SPAN(title); 891 Content space = getSpace(); 892 Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space); 926 return getMarkerAnchor(sectionName.getName() + getName(anchorName), null); 927 } 928 929 /** 930 * Get the marker anchor which will be added to the documentation tree. 931 * 932 * @param anchorName the anchor name or id attribute 933 * @param anchorContent the content that should be added to the anchor 934 * @return a content tree for the marker anchor 935 */ 936 public Content getMarkerAnchor(String anchorName, Content anchorContent) { 937 if (anchorContent == null) 938 anchorContent = new Comment(" "); 939 Content markerAnchor = HtmlTree.A(configuration.htmlVersion, anchorName, anchorContent); 940 return markerAnchor; 941 } 942 943 /** 944 * Returns a packagename content. 945 * 946 * @param packageDoc the package to check 947 * @return package name content 948 */ 949 public Content getPackageName(PackageDoc packageDoc) { 950 return packageDoc == null || packageDoc.name().isEmpty() ? 951 defaultPackageLabel : 952 getPackageLabel(packageDoc.name()); 953 } 954 955 /** 956 * Returns a package name label. 957 * 958 * @param packageName the package name 959 * @return the package name content 960 */ 961 public Content getPackageLabel(String packageName) { 962 return new StringContent(packageName); 963 } 964 965 /** 966 * Add package deprecation information to the documentation tree 967 * 968 * @param deprPkgs list of deprecated packages 969 * @param headingKey the caption for the deprecated package table 970 * @param tableSummary the summary for the deprecated package table 971 * @param tableHeader table headers for the deprecated package table 972 * @param contentTree the content tree to which the deprecated package table will be added 973 */ 974 protected void addPackageDeprecatedAPI(List<Doc> deprPkgs, String headingKey, 975 String tableSummary, String[] tableHeader, Content contentTree) { 976 if (deprPkgs.size() > 0) { 977 Content caption = getTableCaption(configuration.getResource(headingKey)); 978 Content table = (configuration.isOutputHtml5()) 979 ? HtmlTree.TABLE(HtmlStyle.deprecatedSummary, caption) 980 : HtmlTree.TABLE(HtmlStyle.deprecatedSummary, tableSummary, caption); 981 table.addContent(getSummaryTableHeader(tableHeader, "col")); 982 Content tbody = new HtmlTree(HtmlTag.TBODY); 983 for (int i = 0; i < deprPkgs.size(); i++) { 984 PackageDoc pkg = (PackageDoc) deprPkgs.get(i); 985 HtmlTree td = HtmlTree.TD(HtmlStyle.colOne, 986 getPackageLink(pkg, getPackageName(pkg))); 987 if (pkg.tags("deprecated").length > 0) { 988 addInlineDeprecatedComment(pkg, pkg.tags("deprecated")[0], td); 989 } 990 HtmlTree tr = HtmlTree.TR(td); 991 if (i % 2 == 0) { 992 tr.addStyle(HtmlStyle.altColor); 993 } else { 994 tr.addStyle(HtmlStyle.rowColor); 995 } 996 tbody.addContent(tr); 997 } 998 table.addContent(tbody); 999 Content li = HtmlTree.LI(HtmlStyle.blockList, table); 1000 Content ul = HtmlTree.UL(HtmlStyle.blockList, li); 1001 contentTree.addContent(ul); 1002 } 1003 } 1004 1005 /** 1006 * Return the path to the class page for a classdoc. 1007 * 1008 * @param cd Class to which the path is requested. 1009 * @param name Name of the file(doesn't include path). 1010 */ 1011 protected DocPath pathString(ClassDoc cd, DocPath name) { 1012 return pathString(cd.containingPackage(), name); 1013 } 1014 1015 /** 1016 * Return path to the given file name in the given package. So if the name 1017 * passed is "Object.html" and the name of the package is "java.lang", and 1018 * if the relative path is "../.." then returned string will be 1019 * "../../java/lang/Object.html" 1020 * 1021 * @param pd Package in which the file name is assumed to be. 1022 * @param name File name, to which path string is. 1023 */ 1024 protected DocPath pathString(PackageDoc pd, DocPath name) { 1025 return pathToRoot.resolve(DocPath.forPackage(pd).resolve(name)); 1026 } 1027 1028 /** 1029 * Given a package, return the name to be used in HTML anchor tag. 1030 * @param packageDoc the package. 1031 * @return the name to be used in HTML anchor tag. 1032 */ 1033 public String getPackageAnchorName(PackageDoc packageDoc) { 1034 return packageDoc == null || packageDoc.name().length() == 0 ? 1035 SectionName.UNNAMED_PACKAGE_ANCHOR.getName() : packageDoc.name(); 1036 } 1037 1038 /** 1039 * Return the link to the given package. 1040 * 1041 * @param pkg the package to link to. 1042 * @param label the label for the link. 1043 * @return a content tree for the package link. 1044 */ 1045 public Content getPackageLink(PackageDoc pkg, String label) { 1046 return getPackageLink(pkg, new StringContent(label)); 1047 } 1048 1049 /** 1050 * Return the link to the given package. 1051 * 1052 * @param pkg the package to link to. 1053 * @param label the label for the link. 1054 * @return a content tree for the package link. 1055 */ 1056 public Content getPackageLink(PackageDoc pkg, Content label) { 1057 boolean included = pkg != null && pkg.isIncluded(); 1058 if (! included) { 1059 for (PackageDoc p : configuration.packages) { 1060 if (p.equals(pkg)) { 1061 included = true; 1062 break; 1063 } 1064 } 1065 } 1066 if (included || pkg == null) { 1067 return getHyperLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY), 1068 label); 1069 } else { 1070 DocLink crossPkgLink = getCrossPackageLink(utils.getPackageName(pkg)); 1071 if (crossPkgLink != null) { 1072 return getHyperLink(crossPkgLink, label); 1073 } else { 1074 return label; 1075 } 1076 } 1077 } 1078 1079 public Content italicsClassName(ClassDoc cd, boolean qual) { 1080 Content name = new StringContent((qual)? cd.qualifiedName(): cd.name()); 1081 return (cd.isInterface())? HtmlTree.SPAN(HtmlStyle.interfaceName, name): name; 1082 } 1083 1084 /** 1085 * Add the link to the content tree. 1086 * 1087 * @param doc program element doc for which the link will be added 1088 * @param label label for the link 1089 * @param htmltree the content tree to which the link will be added 1090 */ 1091 public void addSrcLink(ProgramElementDoc doc, Content label, Content htmltree) { 1092 if (doc == null) { 1093 return; 1094 } 1095 ClassDoc cd = doc.containingClass(); 1096 if (cd == null) { 1097 //d must be a class doc since in has no containing class. 1098 cd = (ClassDoc) doc; 1099 } 1100 DocPath href = pathToRoot 1101 .resolve(DocPaths.SOURCE_OUTPUT) 1102 .resolve(DocPath.forClass(cd)); 1103 Content linkContent = getHyperLink(href.fragment(SourceToHTMLConverter.getAnchorName(doc)), label, "", ""); 1104 htmltree.addContent(linkContent); 1105 } 1106 1107 /** 1108 * Return the link to the given class. 1109 * 1110 * @param linkInfo the information about the link. 1111 * 1112 * @return the link for the given class. 1113 */ 1114 public Content getLink(LinkInfoImpl linkInfo) { 1115 LinkFactoryImpl factory = new LinkFactoryImpl(this); 1116 return factory.getLink(linkInfo); 1117 } 1118 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. 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 Content getCrossClassLink(String qualifiedClassName, String refMemName, 1145 Content 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 Content defaultLabel = new StringContent(className); 1154 if (code) 1155 defaultLabel = HtmlTree.CODE(defaultLabel); 1156 packageName = packageName.substring(0, periodIndex); 1157 if (getCrossPackageLink(packageName) != null) { 1158 //The package exists in external documentation, so link to the external 1159 //class (assuming that it exists). This is definitely a limitation of 1160 //the -link option. There are ways to determine if an external package 1161 //exists, but no way to determine if the external class exists. We just 1162 //have to assume that it does. 1163 DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot, 1164 className + ".html", refMemName); 1165 return getHyperLink(link, 1166 (label == null) || label.isEmpty() ? defaultLabel : label, 1167 strong, style, 1168 configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName), 1169 ""); 1170 } 1171 } 1172 return null; 1173 } 1174 1175 public boolean isClassLinkable(ClassDoc cd) { 1176 if (cd.isIncluded()) { 1177 return configuration.isGeneratedDoc(cd); 1178 } 1179 return configuration.extern.isExternal(cd); 1180 } 1181 1182 public DocLink getCrossPackageLink(String pkgName) { 1183 return configuration.extern.getExternalLink(pkgName, pathToRoot, 1184 DocPaths.PACKAGE_SUMMARY.getPath()); 1185 } 1186 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(LinkInfoImpl.Kind context, ClassDoc cd) { 1195 return getLink(new LinkInfoImpl(configuration, context, cd) 1196 .label(configuration.getClassName(cd))); 1197 } 1198 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(LinkInfoImpl.Kind context, ClassDoc cd, Content contentTree) { 1207 addPreQualifiedClassLink(context, cd, false, contentTree); 1208 } 1209 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 Content getPreQualifiedClassLink(LinkInfoImpl.Kind context, 1220 ClassDoc cd, boolean isStrong) { 1221 ContentBuilder classlink = new ContentBuilder(); 1222 PackageDoc pd = cd.containingPackage(); 1223 if (pd != null && ! configuration.shouldExcludeQualifier(pd.name())) { 1224 classlink.addContent(getPkgName(cd)); 1225 } 1226 classlink.addContent(getLink(new LinkInfoImpl(configuration, 1227 context, cd).label(cd.name()).strong(isStrong))); 1228 return classlink; 1229 } 1230 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(LinkInfoImpl.Kind 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(getLink(new LinkInfoImpl(configuration, 1248 context, cd).label(cd.name()).strong(isStrong))); 1249 } 1250 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(LinkInfoImpl.Kind context, ClassDoc cd, Content contentTree) { 1260 addPreQualifiedClassLink(context, cd, true, contentTree); 1261 } 1262 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(LinkInfoImpl.Kind context, MemberDoc doc, String label) { 1272 return getDocLink(context, doc.containingClass(), doc, 1273 new StringContent(label)); 1274 } 1275 1276 /** 1277 * Return the link for the given member. 1278 * 1279 * @param context the id of the context where the link will be printed. 1280 * @param doc the member being linked to. 1281 * @param label the label for the link. 1282 * @param strong true if the link should be strong. 1283 * @return the link for the given member. 1284 */ 1285 public Content getDocLink(LinkInfoImpl.Kind context, MemberDoc doc, String label, 1286 boolean strong) { 1287 return getDocLink(context, doc.containingClass(), doc, label, strong); 1288 } 1289 1290 /** 1291 * Return the link for the given member. 1292 * 1293 * @param context the id of the context where the link will be printed. 1294 * @param classDoc the classDoc that we should link to. This is not 1295 * necessarily equal to doc.containingClass(). We may be 1296 * inheriting comments. 1297 * @param doc the member being linked to. 1298 * @param label the label for the link. 1299 * @param strong true if the link should be strong. 1300 * @return the link for the given member. 1301 */ 1302 public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, 1303 String label, boolean strong) { 1304 return getDocLink(context, classDoc, doc, label, strong, false); 1305 } 1306 public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, 1307 Content label, boolean strong) { 1308 return getDocLink(context, classDoc, doc, label, strong, false); 1309 } 1310 1311 /** 1312 * Return the link for the given member. 1313 * 1314 * @param context the id of the context where the link will be printed. 1315 * @param classDoc the classDoc that we should link to. This is not 1316 * necessarily equal to doc.containingClass(). We may be 1317 * inheriting comments. 1318 * @param doc the member being linked to. 1319 * @param label the label for the link. 1320 * @param strong true if the link should be strong. 1321 * @param isProperty true if the doc parameter is a JavaFX property. 1322 * @return the link for the given member. 1323 */ 1324 public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, 1325 String label, boolean strong, boolean isProperty) { 1326 return getDocLink(context, classDoc, doc, new StringContent(check(label)), strong, isProperty); 1327 } 1328 1329 String check(String s) { 1330 if (s.matches(".*[&<>].*"))throw new IllegalArgumentException(s); 1331 return s; 1332 } 1333 1334 public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, 1335 Content label, boolean strong, boolean isProperty) { 1336 if (! (doc.isIncluded() || 1337 utils.isLinkable(classDoc, configuration))) { 1338 return label; 1339 } else if (doc instanceof ExecutableMemberDoc) { 1340 ExecutableMemberDoc emd = (ExecutableMemberDoc)doc; 1341 return getLink(new LinkInfoImpl(configuration, context, classDoc) 1342 .label(label).where(getName(getAnchor(emd, isProperty))).strong(strong)); 1343 } else if (doc instanceof MemberDoc) { 1344 return getLink(new LinkInfoImpl(configuration, context, classDoc) 1345 .label(label).where(getName(doc.name())).strong(strong)); 1346 } else { 1347 return label; 1348 } 1349 } 1350 1351 /** 1352 * Return the link for the given member. 1353 * 1354 * @param context the id of the context where the link will be added 1355 * @param classDoc the classDoc that we should link to. This is not 1356 * necessarily equal to doc.containingClass(). We may be 1357 * inheriting comments 1358 * @param doc the member being linked to 1359 * @param label the label for the link 1360 * @return the link for the given member 1361 */ 1362 public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, 1363 Content label) { 1364 if (! (doc.isIncluded() || 1365 utils.isLinkable(classDoc, configuration))) { 1366 return label; 1367 } else if (doc instanceof ExecutableMemberDoc) { 1368 ExecutableMemberDoc emd = (ExecutableMemberDoc) doc; 1369 return getLink(new LinkInfoImpl(configuration, context, classDoc) 1370 .label(label).where(getName(getAnchor(emd)))); 1371 } else if (doc instanceof MemberDoc) { 1372 return getLink(new LinkInfoImpl(configuration, context, classDoc) 1373 .label(label).where(getName(doc.name()))); 1374 } else { 1375 return label; 1376 } 1377 } 1378 1379 public String getAnchor(ExecutableMemberDoc emd) { 1380 return getAnchor(emd, false); 1381 } 1382 1383 public String getAnchor(ExecutableMemberDoc emd, boolean isProperty) { 1384 if (isProperty) { 1385 return emd.name(); 1386 } 1387 StringBuilder signature = new StringBuilder(emd.signature()); 1388 StringBuilder signatureParsed = new StringBuilder(); 1389 int counter = 0; 1390 for (int i = 0; i < signature.length(); i++) { 1391 char c = signature.charAt(i); 1392 if (c == '<') { 1393 counter++; 1394 } else if (c == '>') { 1395 counter--; 1396 } else if (counter == 0) { 1397 signatureParsed.append(c); 1398 } 1399 } 1400 return emd.name() + signatureParsed.toString(); 1401 } 1402 1403 public Content seeTagToContent(SeeTag see) { 1404 String tagName = see.name(); 1405 if (! (tagName.startsWith("@link") || tagName.equals("@see"))) { 1406 return new ContentBuilder(); 1407 } 1408 1409 String seetext = replaceDocRootDir(utils.normalizeNewlines(see.text())); 1410 1411 //Check if @see is an href or "string" 1412 if (seetext.startsWith("<") || seetext.startsWith("\"")) { 1413 return new RawHtml(seetext); 1414 } 1415 1416 boolean plain = tagName.equalsIgnoreCase("@linkplain"); 1417 Content label = plainOrCode(plain, new RawHtml(see.label())); 1418 1419 //The text from the @see tag. We will output this text when a label is not specified. 1420 Content text = plainOrCode(plain, new RawHtml(seetext)); 1421 1422 ClassDoc refClass = see.referencedClass(); 1423 String refClassName = see.referencedClassName(); 1424 MemberDoc refMem = see.referencedMember(); 1425 String refMemName = see.referencedMemberName(); 1426 1427 if (refClass == null) { 1428 //@see is not referencing an included class 1429 PackageDoc refPackage = see.referencedPackage(); 1430 if (refPackage != null && refPackage.isIncluded()) { 1431 //@see is referencing an included package 1432 if (label.isEmpty()) 1433 label = plainOrCode(plain, new StringContent(refPackage.name())); 1434 return getPackageLink(refPackage, label); 1435 } else { 1436 //@see is not referencing an included class or package. Check for cross links. 1437 Content classCrossLink; 1438 DocLink packageCrossLink = getCrossPackageLink(refClassName); 1439 if (packageCrossLink != null) { 1440 //Package cross link found 1441 return getHyperLink(packageCrossLink, 1442 (label.isEmpty() ? text : label)); 1443 } else if ((classCrossLink = getCrossClassLink(refClassName, 1444 refMemName, label, false, "", !plain)) != null) { 1445 //Class cross link found (possibly to a member in the class) 1446 return classCrossLink; 1447 } else { 1448 //No cross link found so print warning 1449 configuration.getDocletSpecificMsg().warning(see.position(), "doclet.see.class_or_package_not_found", 1450 tagName, seetext); 1451 return (label.isEmpty() ? text: label); 1452 } 1453 } 1454 } else if (refMemName == null) { 1455 // Must be a class reference since refClass is not null and refMemName is null. 1456 if (label.isEmpty()) { 1457 label = plainOrCode(plain, new StringContent(refClass.name())); 1458 } 1459 return getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, refClass) 1460 .label(label)); 1461 } else if (refMem == null) { 1462 // Must be a member reference since refClass is not null and refMemName is not null. 1463 // However, refMem is null, so this referenced member does not exist. 1464 return (label.isEmpty() ? text: label); 1465 } else { 1466 // Must be a member reference since refClass is not null and refMemName is not null. 1467 // refMem is not null, so this @see tag must be referencing a valid member. 1468 ClassDoc containing = refMem.containingClass(); 1469 if (see.text().trim().startsWith("#") && 1470 ! (containing.isPublic() || 1471 utils.isLinkable(containing, configuration))) { 1472 // Since the link is relative and the holder is not even being 1473 // documented, this must be an inherited link. Redirect it. 1474 // The current class either overrides the referenced member or 1475 // inherits it automatically. 1476 if (this instanceof ClassWriterImpl) { 1477 containing = ((ClassWriterImpl) this).getClassDoc(); 1478 } else if (!containing.isPublic()){ 1479 configuration.getDocletSpecificMsg().warning( 1480 see.position(), "doclet.see.class_or_package_not_accessible", 1481 tagName, containing.qualifiedName()); 1482 } else { 1483 configuration.getDocletSpecificMsg().warning( 1484 see.position(), "doclet.see.class_or_package_not_found", 1485 tagName, seetext); 1486 } 1487 } 1488 if (configuration.currentcd != containing) { 1489 refMemName = (refMem instanceof ConstructorDoc) ? 1490 refMemName : containing.name() + "." + refMemName; 1491 } 1492 if (refMem instanceof ExecutableMemberDoc) { 1493 if (refMemName.indexOf('(') < 0) { 1494 refMemName += ((ExecutableMemberDoc)refMem).signature(); 1495 } 1496 } 1497 1498 text = plainOrCode(plain, new StringContent(refMemName)); 1499 1500 return getDocLink(LinkInfoImpl.Kind.SEE_TAG, containing, 1501 refMem, (label.isEmpty() ? text: label), false); 1502 } 1503 } 1504 1505 private Content plainOrCode(boolean plain, Content body) { 1506 return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body); 1507 } 1508 1509 /** 1510 * Add the inline comment. 1511 * 1512 * @param doc the doc for which the inline comment will be added 1513 * @param tag the inline tag to be added 1514 * @param htmltree the content tree to which the comment will be added 1515 */ 1516 public void addInlineComment(Doc doc, Tag tag, Content htmltree) { 1517 addCommentTags(doc, tag, tag.inlineTags(), false, false, htmltree); 1518 } 1519 1520 /** 1521 * Add the inline deprecated comment. 1522 * 1523 * @param doc the doc for which the inline deprecated comment will be added 1524 * @param tag the inline tag to be added 1525 * @param htmltree the content tree to which the comment will be added 1526 */ 1527 public void addInlineDeprecatedComment(Doc doc, Tag tag, Content htmltree) { 1528 addCommentTags(doc, tag.inlineTags(), true, false, htmltree); 1529 } 1530 1531 /** 1532 * Adds the summary content. 1533 * 1534 * @param doc the doc for which the summary will be generated 1535 * @param htmltree the documentation tree to which the summary will be added 1536 */ 1537 public void addSummaryComment(Doc doc, Content htmltree) { 1538 addSummaryComment(doc, doc.firstSentenceTags(), htmltree); 1539 } 1540 1541 /** 1542 * Adds the summary content. 1543 * 1544 * @param doc the doc for which the summary will be generated 1545 * @param firstSentenceTags the first sentence tags for the doc 1546 * @param htmltree the documentation tree to which the summary will be added 1547 */ 1548 public void addSummaryComment(Doc doc, Tag[] firstSentenceTags, Content htmltree) { 1549 addCommentTags(doc, firstSentenceTags, false, true, htmltree); 1550 } 1551 1552 public void addSummaryDeprecatedComment(Doc doc, Tag tag, Content htmltree) { 1553 addCommentTags(doc, tag.firstSentenceTags(), true, true, htmltree); 1554 } 1555 1556 /** 1557 * Adds the inline comment. 1558 * 1559 * @param doc the doc for which the inline comments will be generated 1560 * @param htmltree the documentation tree to which the inline comments will be added 1561 */ 1562 public void addInlineComment(Doc doc, Content htmltree) { 1563 addCommentTags(doc, doc.inlineTags(), false, false, htmltree); 1564 } 1565 1566 /** 1567 * Adds the comment tags. 1568 * 1569 * @param doc the doc for which the comment tags will be generated 1570 * @param tags the first sentence tags for the doc 1571 * @param depr true if it is deprecated 1572 * @param first true if the first sentence tags should be added 1573 * @param htmltree the documentation tree to which the comment tags will be added 1574 */ 1575 private void addCommentTags(Doc doc, Tag[] tags, boolean depr, 1576 boolean first, Content htmltree) { 1577 addCommentTags(doc, null, tags, depr, first, htmltree); 1578 } 1579 1580 /** 1581 * Adds the comment tags. 1582 * 1583 * @param doc the doc for which the comment tags will be generated 1584 * @param holderTag the block tag context for the inline tags 1585 * @param tags the first sentence tags for the doc 1586 * @param depr true if it is deprecated 1587 * @param first true if the first sentence tags should be added 1588 * @param htmltree the documentation tree to which the comment tags will be added 1589 */ 1590 private void addCommentTags(Doc doc, Tag holderTag, Tag[] tags, boolean depr, 1591 boolean first, Content htmltree) { 1592 if(configuration.nocomment){ 1593 return; 1594 } 1595 Content div; 1596 Content result = commentTagsToContent(null, doc, tags, first); 1597 if (depr) { 1598 Content italic = HtmlTree.SPAN(HtmlStyle.deprecationComment, result); 1599 div = HtmlTree.DIV(HtmlStyle.block, italic); 1600 htmltree.addContent(div); 1601 } 1602 else { 1603 div = HtmlTree.DIV(HtmlStyle.block, result); 1604 htmltree.addContent(div); 1605 } 1606 if (tags.length == 0) { 1607 htmltree.addContent(getSpace()); 1608 } 1609 } 1610 1611 /** 1612 * Converts inline tags and text to text strings, expanding the 1613 * inline tags along the way. Called wherever text can contain 1614 * an inline tag, such as in comments or in free-form text arguments 1615 * to non-inline tags. 1616 * 1617 * @param holderTag specific tag where comment resides 1618 * @param doc specific doc where comment resides 1619 * @param tags array of text tags and inline tags (often alternating) 1620 * present in the text of interest for this doc 1621 * @param isFirstSentence true if text is first sentence 1622 */ 1623 public Content commentTagsToContent(Tag holderTag, Doc doc, Tag[] tags, 1624 boolean isFirstSentence) { 1625 Content result = new ContentBuilder(); 1626 boolean textTagChange = false; 1627 // Array of all possible inline tags for this javadoc run 1628 configuration.tagletManager.checkTags(doc, tags, true); 1629 for (int i = 0; i < tags.length; i++) { 1630 Tag tagelem = tags[i]; 1631 String tagName = tagelem.name(); 1632 if (tagelem instanceof SeeTag) { 1633 result.addContent(seeTagToContent((SeeTag) tagelem)); 1634 } else if (! tagName.equals("Text")) { 1635 boolean wasEmpty = result.isEmpty(); 1636 Content output; 1637 if (configuration.docrootparent.length() > 0 1638 && tagelem.name().equals("@docRoot") 1639 && ((tags[i + 1]).text()).startsWith("/..")) { 1640 // If Xdocrootparent switch ON, set the flag to remove the /.. occurrence after 1641 // {@docRoot} tag in the very next Text tag. 1642 textTagChange = true; 1643 // Replace the occurrence of {@docRoot}/.. with the absolute link. 1644 output = new StringContent(configuration.docrootparent); 1645 } else { 1646 output = TagletWriter.getInlineTagOuput( 1647 configuration.tagletManager, holderTag, 1648 tagelem, getTagletWriterInstance(isFirstSentence)); 1649 } 1650 if (output != null) 1651 result.addContent(output); 1652 if (wasEmpty && isFirstSentence && tagelem.name().equals("@inheritDoc") && !result.isEmpty()) { 1653 break; 1654 } else { 1655 continue; 1656 } 1657 } else { 1658 String text = tagelem.text(); 1659 //If Xdocrootparent switch ON, remove the /.. occurrence after {@docRoot} tag. 1660 if (textTagChange) { 1661 text = text.replaceFirst("/..", ""); 1662 textTagChange = false; 1663 } 1664 //This is just a regular text tag. The text may contain html links (<a>) 1665 //or inline tag {@docRoot}, which will be handled as special cases. 1666 text = redirectRelativeLinks(tagelem.holder(), text); 1667 1668 // Replace @docRoot only if not represented by an instance of DocRootTaglet, 1669 // that is, only if it was not present in a source file doc comment. 1670 // This happens when inserted by the doclet (a few lines 1671 // above in this method). [It might also happen when passed in on the command 1672 // line as a text argument to an option (like -header).] 1673 text = replaceDocRootDir(text); 1674 if (isFirstSentence) { 1675 text = removeNonInlineHtmlTags(text); 1676 } 1677 text = utils.replaceTabs(configuration, text); 1678 text = utils.normalizeNewlines(text); 1679 result.addContent(new RawHtml(text)); 1680 } 1681 } 1682 return result; 1683 } 1684 1685 /** 1686 * Return true if relative links should not be redirected. 1687 * 1688 * @return Return true if a relative link should not be redirected. 1689 */ 1690 private boolean shouldNotRedirectRelativeLinks() { 1691 return this instanceof AnnotationTypeWriter || 1692 this instanceof ClassWriter || 1693 this instanceof PackageSummaryWriter; 1694 } 1695 1696 /** 1697 * Suppose a piece of documentation has a relative link. When you copy 1698 * that documentation to another place such as the index or class-use page, 1699 * that relative link will no longer work. We should redirect those links 1700 * so that they will work again. 1701 * <p> 1702 * Here is the algorithm used to fix the link: 1703 * <p> 1704 * {@literal <relative link> => docRoot + <relative path to file> + <relative link> } 1705 * <p> 1706 * For example, suppose com.sun.javadoc.RootDoc has this link: 1707 * {@literal <a href="package-summary.html">The package Page</a> } 1708 * <p> 1709 * If this link appeared in the index, we would redirect 1710 * the link like this: 1711 * 1712 * {@literal <a href="./com/sun/javadoc/package-summary.html">The package Page</a>} 1713 * 1714 * @param doc the Doc object whose documentation is being written. 1715 * @param text the text being written. 1716 * 1717 * @return the text, with all the relative links redirected to work. 1718 */ 1719 private String redirectRelativeLinks(Doc doc, String text) { 1720 if (doc == null || shouldNotRedirectRelativeLinks()) { 1721 return text; 1722 } 1723 1724 DocPath redirectPathFromRoot; 1725 if (doc instanceof ClassDoc) { 1726 redirectPathFromRoot = DocPath.forPackage(((ClassDoc) doc).containingPackage()); 1727 } else if (doc instanceof MemberDoc) { 1728 redirectPathFromRoot = DocPath.forPackage(((MemberDoc) doc).containingPackage()); 1729 } else if (doc instanceof PackageDoc) { 1730 redirectPathFromRoot = DocPath.forPackage((PackageDoc) doc); 1731 } else { 1732 return text; 1733 } 1734 1735 //Redirect all relative links. 1736 int end, begin = StringUtils.indexOfIgnoreCase(text, "<a"); 1737 if(begin >= 0){ 1738 StringBuilder textBuff = new StringBuilder(text); 1739 1740 while(begin >=0){ 1741 if (textBuff.length() > begin + 2 && ! Character.isWhitespace(textBuff.charAt(begin+2))) { 1742 begin = StringUtils.indexOfIgnoreCase(textBuff.toString(), "<a", begin + 1); 1743 continue; 1744 } 1745 1746 begin = textBuff.indexOf("=", begin) + 1; 1747 end = textBuff.indexOf(">", begin +1); 1748 if(begin == 0){ 1749 //Link has no equal symbol. 1750 configuration.root.printWarning( 1751 doc.position(), 1752 configuration.getText("doclet.malformed_html_link_tag", text)); 1753 break; 1754 } 1755 if (end == -1) { 1756 //Break without warning. This <a> tag is not necessarily malformed. The text 1757 //might be missing '>' character because the href has an inline tag. 1758 break; 1759 } 1760 1761 String quote = textBuff.substring(begin, end); 1762 quote = quote.contains("\"") ? "\"" : 1763 quote.contains("\'") ? "\'" : null; 1764 if (quote != null) { 1765 begin = textBuff.indexOf(quote, begin) + 1; 1766 end = textBuff.indexOf(quote, begin +1); 1767 if (begin == 0 || end == -1){ 1768 //Link is missing a quote. 1769 break; 1770 } 1771 } 1772 String relativeLink = textBuff.substring(begin, end); 1773 String relativeLinkLowerCase = StringUtils.toLowerCase(relativeLink); 1774 if (!(relativeLinkLowerCase.startsWith("mailto:") || 1775 relativeLinkLowerCase.startsWith("http:") || 1776 relativeLinkLowerCase.startsWith("https:") || 1777 relativeLinkLowerCase.startsWith("file:"))) { 1778 relativeLink = "{@"+(new DocRootTaglet()).getName() + "}/" 1779 + redirectPathFromRoot.resolve(relativeLink).getPath(); 1780 textBuff.replace(begin, end, relativeLink); 1781 } 1782 begin = StringUtils.indexOfIgnoreCase(textBuff.toString(), "<a", begin + 1); 1783 } 1784 return textBuff.toString(); 1785 } 1786 return text; 1787 } 1788 1789 static final Set<String> blockTags = new HashSet<>(); 1790 static { 1791 for (HtmlTag t: HtmlTag.values()) { 1792 if (t.blockType == HtmlTag.BlockType.BLOCK) 1793 blockTags.add(t.value); 1794 } 1795 } 1796 1797 public static String removeNonInlineHtmlTags(String text) { 1798 final int len = text.length(); 1799 1800 int startPos = 0; // start of text to copy 1801 int lessThanPos = text.indexOf('<'); // position of latest '<' 1802 if (lessThanPos < 0) { 1803 return text; 1804 } 1805 1806 StringBuilder result = new StringBuilder(); 1807 main: while (lessThanPos != -1) { 1808 int currPos = lessThanPos + 1; 1809 if (currPos == len) 1810 break; 1811 char ch = text.charAt(currPos); 1812 if (ch == '/') { 1813 if (++currPos == len) 1814 break; 1815 ch = text.charAt(currPos); 1816 } 1817 int tagPos = currPos; 1818 while (isHtmlTagLetterOrDigit(ch)) { 1819 if (++currPos == len) 1820 break main; 1821 ch = text.charAt(currPos); 1822 } 1823 if (ch == '>' && blockTags.contains(StringUtils.toLowerCase(text.substring(tagPos, currPos)))) { 1824 result.append(text, startPos, lessThanPos); 1825 startPos = currPos + 1; 1826 } 1827 lessThanPos = text.indexOf('<', currPos); 1828 } 1829 result.append(text.substring(startPos)); 1830 1831 return result.toString(); 1832 } 1833 1834 private static boolean isHtmlTagLetterOrDigit(char ch) { 1835 return ('a' <= ch && ch <= 'z') || 1836 ('A' <= ch && ch <= 'Z') || 1837 ('1' <= ch && ch <= '6'); 1838 } 1839 1840 /** 1841 * Add a link to the stylesheet file. 1842 * 1843 * @param head the content tree to which the files will be added 1844 */ 1845 public void addStyleSheetProperties(Content head) { 1846 String stylesheetfile = configuration.stylesheetfile; 1847 DocPath stylesheet; 1848 if (stylesheetfile.isEmpty()) { 1849 stylesheet = DocPaths.STYLESHEET; 1850 } else { 1851 DocFile file = DocFile.createFileForInput(configuration, stylesheetfile); 1852 stylesheet = DocPath.create(file.getName()); 1853 } 1854 HtmlTree link = HtmlTree.LINK("stylesheet", "text/css", 1855 pathToRoot.resolve(stylesheet).getPath(), 1856 "Style"); 1857 head.addContent(link); 1858 if (configuration.createindex) { 1859 HtmlTree jq_link = HtmlTree.LINK("stylesheet", "text/css", 1886 } 1887 } 1888 1889 /** 1890 * Add a link to the JQuery javascript file. 1891 * 1892 * @param head the content tree to which the files will be added 1893 * @param filePath the DocPath of the file that needs to be added 1894 */ 1895 private void addJQueryFile(Content head, DocPath filePath) { 1896 HtmlTree jqyeryScriptFile = HtmlTree.SCRIPT( 1897 pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(filePath)).getPath()); 1898 head.addContent(jqyeryScriptFile); 1899 } 1900 1901 /** 1902 * According to 1903 * <cite>The Java™ Language Specification</cite>, 1904 * all the outer classes and static nested classes are core classes. 1905 */ 1906 public boolean isCoreClass(ClassDoc cd) { 1907 return cd.containingClass() == null || cd.isStatic(); 1908 } 1909 1910 /** 1911 * Adds the annotatation types for the given packageDoc. 1912 * 1913 * @param packageDoc the package to write annotations for. 1914 * @param htmltree the documentation tree to which the annotation info will be 1915 * added 1916 */ 1917 public void addAnnotationInfo(PackageDoc packageDoc, Content htmltree) { 1918 addAnnotationInfo(packageDoc, packageDoc.annotations(), htmltree); 1919 } 1920 1921 /** 1922 * Add the annotation types of the executable receiver. 1923 * 1924 * @param method the executable to write the receiver annotations for. 1925 * @param descList list of annotation description. 1926 * @param htmltree the documentation tree to which the annotation info will be 1927 * added 1928 */ 1929 public void addReceiverAnnotationInfo(ExecutableMemberDoc method, AnnotationDesc[] descList, 1930 Content htmltree) { 1931 addAnnotationInfo(0, method, descList, false, htmltree); 1932 } 1933 1934 /** 1935 * Adds the annotatation types for the given doc. 1936 * 1937 * @param doc the package to write annotations for 1938 * @param htmltree the content tree to which the annotation types will be added 1939 */ 1940 public void addAnnotationInfo(ProgramElementDoc doc, Content htmltree) { 1941 addAnnotationInfo(doc, doc.annotations(), htmltree); 1942 } 1943 1944 /** 1945 * Add the annotatation types for the given doc and parameter. 1946 * 1947 * @param indent the number of spaces to indent the parameters. 1948 * @param doc the doc to write annotations for. 1949 * @param param the parameter to write annotations for. 1950 * @param tree the content tree to which the annotation types will be added 1951 */ 1952 public boolean addAnnotationInfo(int indent, Doc doc, Parameter param, 1953 Content tree) { 1954 return addAnnotationInfo(indent, doc, param.annotations(), false, tree); 1955 } 1956 1957 /** 1958 * Adds the annotatation types for the given doc. 1959 * 1960 * @param doc the doc to write annotations for. 1961 * @param descList the array of {@link AnnotationDesc}. 1962 * @param htmltree the documentation tree to which the annotation info will be 1963 * added 1964 */ 1965 private void addAnnotationInfo(Doc doc, AnnotationDesc[] descList, 1966 Content htmltree) { 1967 addAnnotationInfo(0, doc, descList, true, htmltree); 1968 } 1969 1970 /** 1971 * Adds the annotation types for the given doc. 1972 * 1973 * @param indent the number of extra spaces to indent the annotations. 1974 * @param doc the doc to write annotations for. 1975 * @param descList the array of {@link AnnotationDesc}. 1976 * @param htmltree the documentation tree to which the annotation info will be 1977 * added 1978 */ 1979 private boolean addAnnotationInfo(int indent, Doc doc, 1980 AnnotationDesc[] descList, boolean lineBreak, Content htmltree) { 1981 List<Content> annotations = getAnnotations(indent, descList, lineBreak); 1982 String sep =""; 1983 if (annotations.isEmpty()) { 1984 return false; 1985 } 1986 for (Content annotation: annotations) { 1987 htmltree.addContent(sep); 1988 htmltree.addContent(annotation); 1989 if (!lineBreak) { 1990 sep = " "; 1991 } 1992 } 1993 return true; 1994 } 1995 1996 /** 1997 * Return the string representations of the annotation types for 1998 * the given doc. 1999 * 2000 * @param indent the number of extra spaces to indent the annotations. 2001 * @param descList the array of {@link AnnotationDesc}. 2002 * @param linkBreak if true, add new line between each member value. 2003 * @return an array of strings representing the annotations being 2004 * documented. 2005 */ 2006 private List<Content> getAnnotations(int indent, AnnotationDesc[] descList, boolean linkBreak) { 2007 return getAnnotations(indent, descList, linkBreak, true); 2008 } 2009 2010 /** 2011 * Return the string representations of the annotation types for 2012 * the given doc. 2013 * 2014 * A {@code null} {@code elementType} indicates that all the 2015 * annotations should be returned without any filtering. 2016 * 2017 * @param indent the number of extra spaces to indent the annotations. 2018 * @param descList the array of {@link AnnotationDesc}. 2019 * @param linkBreak if true, add new line between each member value. 2020 * @param elementType the type of targeted element (used for filtering 2021 * type annotations from declaration annotations) 2022 * @return an array of strings representing the annotations being 2023 * documented. 2024 */ 2025 public List<Content> getAnnotations(int indent, AnnotationDesc[] descList, boolean linkBreak, 2026 boolean isJava5DeclarationLocation) { 2027 List<Content> results = new ArrayList<>(); 2028 ContentBuilder annotation; 2029 for (AnnotationDesc aDesc : descList) { 2030 AnnotationTypeDoc annotationDoc = aDesc.annotationType(); 2031 // If an annotation is not documented, do not add it to the list. If 2032 // the annotation is of a repeatable type, and if it is not documented 2033 // and also if its container annotation is not documented, do not add it 2034 // to the list. If an annotation of a repeatable type is not documented 2035 // but its container is documented, it will be added to the list. 2036 if (!utils.isDocumentedAnnotation(annotationDoc) && 2037 (!isAnnotationDocumented && !isContainerDocumented)) { 2038 continue; 2039 } 2040 /* TODO: check logic here to correctly handle declaration 2041 * and type annotations. 2042 if (util.isDeclarationAnnotation(annotationDoc, isJava5DeclarationLocation)) { 2043 continue; 2044 }*/ 2045 annotation = new ContentBuilder(); 2046 isAnnotationDocumented = false; 2047 LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, 2048 LinkInfoImpl.Kind.ANNOTATION, annotationDoc); 2049 AnnotationDesc.ElementValuePair[] pairs = aDesc.elementValues(); 2050 // If the annotation is synthesized, do not print the container. 2051 if (aDesc.isSynthesized()) { 2052 for (AnnotationDesc.ElementValuePair pair : pairs) { 2053 AnnotationValue annotationValue = pair.value(); 2054 List<AnnotationValue> annotationTypeValues = new ArrayList<>(); 2055 if (annotationValue.value() instanceof AnnotationValue[]) { 2056 AnnotationValue[] annotationArray = 2057 (AnnotationValue[]) annotationValue.value(); 2058 annotationTypeValues.addAll(Arrays.asList(annotationArray)); 2059 } else { 2060 annotationTypeValues.add(annotationValue); 2061 } 2062 String sep = ""; 2063 for (AnnotationValue av : annotationTypeValues) { 2064 annotation.addContent(sep); 2065 annotation.addContent(annotationValueToContent(av)); 2066 sep = " "; 2067 } 2068 } 2069 } 2070 else if (isAnnotationArray(pairs)) { 2071 // If the container has 1 or more value defined and if the 2072 // repeatable type annotation is not documented, do not print 2073 // the container. 2074 if (pairs.length == 1 && isAnnotationDocumented) { 2075 AnnotationValue[] annotationArray = 2076 (AnnotationValue[]) (pairs[0].value()).value(); 2077 List<AnnotationValue> annotationTypeValues = new ArrayList<>(); 2078 annotationTypeValues.addAll(Arrays.asList(annotationArray)); 2079 String sep = ""; 2080 for (AnnotationValue av : annotationTypeValues) { 2081 annotation.addContent(sep); 2082 annotation.addContent(annotationValueToContent(av)); 2083 sep = " "; 2084 } 2085 } 2086 // If the container has 1 or more value defined and if the 2087 // repeatable type annotation is not documented, print the container. 2088 else { 2089 addAnnotations(annotationDoc, linkInfo, annotation, pairs, 2090 indent, false); 2091 } 2092 } 2093 else { 2094 addAnnotations(annotationDoc, linkInfo, annotation, pairs, 2095 indent, linkBreak); 2096 } 2097 annotation.addContent(linkBreak ? DocletConstants.NL : ""); 2098 results.add(annotation); 2099 } 2100 return results; 2101 } 2102 2103 /** 2104 * Add annotation to the annotation string. 2105 * 2106 * @param annotationDoc the annotation being documented 2107 * @param linkInfo the information about the link 2108 * @param annotation the annotation string to which the annotation will be added 2109 * @param pairs annotation type element and value pairs 2110 * @param indent the number of extra spaces to indent the annotations. 2111 * @param linkBreak if true, add new line between each member value 2112 */ 2113 private void addAnnotations(AnnotationTypeDoc annotationDoc, LinkInfoImpl linkInfo, 2114 ContentBuilder annotation, AnnotationDesc.ElementValuePair[] pairs, 2115 int indent, boolean linkBreak) { 2116 linkInfo.label = new StringContent("@" + annotationDoc.name()); 2117 annotation.addContent(getLink(linkInfo)); 2118 if (pairs.length > 0) { 2119 annotation.addContent("("); 2120 for (int j = 0; j < pairs.length; j++) { 2121 if (j > 0) { 2122 annotation.addContent(","); 2123 if (linkBreak) { 2124 annotation.addContent(DocletConstants.NL); 2125 int spaces = annotationDoc.name().length() + 2; 2126 for (int k = 0; k < (spaces + indent); k++) { 2127 annotation.addContent(" "); 2128 } 2129 } 2130 } 2131 annotation.addContent(getDocLink(LinkInfoImpl.Kind.ANNOTATION, 2132 pairs[j].element(), pairs[j].element().name(), false)); 2133 annotation.addContent("="); 2134 AnnotationValue annotationValue = pairs[j].value(); 2135 List<AnnotationValue> annotationTypeValues = new ArrayList<>(); 2136 if (annotationValue.value() instanceof AnnotationValue[]) { 2137 AnnotationValue[] annotationArray = 2138 (AnnotationValue[]) annotationValue.value(); 2139 annotationTypeValues.addAll(Arrays.asList(annotationArray)); 2140 } else { 2141 annotationTypeValues.add(annotationValue); 2142 } 2143 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "{"); 2144 String sep = ""; 2145 for (AnnotationValue av : annotationTypeValues) { 2146 annotation.addContent(sep); 2147 annotation.addContent(annotationValueToContent(av)); 2148 sep = ","; 2149 } 2150 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "}"); 2151 isContainerDocumented = false; 2152 } 2153 annotation.addContent(")"); 2154 } 2155 } 2156 2157 /** 2158 * Check if the annotation contains an array of annotation as a value. This 2159 * check is to verify if a repeatable type annotation is present or not. 2160 * 2161 * @param pairs annotation type element and value pairs 2162 * 2163 * @return true if the annotation contains an array of annotation as a value. 2164 */ 2165 private boolean isAnnotationArray(AnnotationDesc.ElementValuePair[] pairs) { 2166 AnnotationValue annotationValue; 2167 for (AnnotationDesc.ElementValuePair pair : pairs) { 2168 annotationValue = pair.value(); 2169 if (annotationValue.value() instanceof AnnotationValue[]) { 2170 AnnotationValue[] annotationArray = 2171 (AnnotationValue[]) annotationValue.value(); 2172 if (annotationArray.length > 1) { 2173 if (annotationArray[0].value() instanceof AnnotationDesc) { 2174 AnnotationTypeDoc annotationDoc = 2175 ((AnnotationDesc) annotationArray[0].value()).annotationType(); 2176 isContainerDocumented = true; 2177 if (utils.isDocumentedAnnotation(annotationDoc)) { 2178 isAnnotationDocumented = true; 2179 } 2180 return true; 2181 } 2182 } 2183 } 2184 } 2185 return false; 2186 } 2187 2188 private Content annotationValueToContent(AnnotationValue annotationValue) { 2189 if (annotationValue.value() instanceof Type) { 2190 Type type = (Type) annotationValue.value(); 2191 if (type.asClassDoc() != null) { 2192 LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, 2193 LinkInfoImpl.Kind.ANNOTATION, type); 2194 linkInfo.label = new StringContent((type.asClassDoc().isIncluded() ? 2195 type.typeName() : 2196 type.qualifiedTypeName()) + type.dimension() + ".class"); 2197 return getLink(linkInfo); 2198 } else { 2199 return new StringContent(type.typeName() + type.dimension() + ".class"); 2200 } 2201 } else if (annotationValue.value() instanceof AnnotationDesc) { 2202 List<Content> list = getAnnotations(0, 2203 new AnnotationDesc[]{(AnnotationDesc) annotationValue.value()}, 2204 false); 2205 ContentBuilder buf = new ContentBuilder(); 2206 for (Content c: list) { 2207 buf.addContent(c); 2208 } 2209 return buf; 2210 } else if (annotationValue.value() instanceof MemberDoc) { 2211 return getDocLink(LinkInfoImpl.Kind.ANNOTATION, 2212 (MemberDoc) annotationValue.value(), 2213 ((MemberDoc) annotationValue.value()).name(), false); 2214 } else { 2215 return new StringContent(annotationValue.toString()); 2216 } 2217 } 2218 2219 /** 2220 * Return the configuation for this doclet. 2221 * 2222 * @return the configuration for this doclet. 2223 */ 2224 public Configuration configuration() { 2225 return configuration; 2226 } 2227 } | 1 /* 2 * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.javadoc.internal.doclets.formats.html; 27 28 import java.io.*; 29 import java.text.SimpleDateFormat; 30 import java.util.*; 31 import java.util.regex.Matcher; 32 import java.util.regex.Pattern; 33 34 import javax.lang.model.element.AnnotationMirror; 35 import javax.lang.model.element.AnnotationValue; 36 import javax.lang.model.element.Element; 37 import javax.lang.model.element.ExecutableElement; 38 import javax.lang.model.element.Name; 39 import javax.lang.model.element.PackageElement; 40 import javax.lang.model.element.TypeElement; 41 import javax.lang.model.element.VariableElement; 42 import javax.lang.model.type.DeclaredType; 43 import javax.lang.model.type.TypeMirror; 44 import javax.lang.model.util.SimpleAnnotationValueVisitor9; 45 import javax.lang.model.util.SimpleElementVisitor9; 46 import javax.lang.model.util.SimpleTypeVisitor9; 47 48 import com.sun.source.doctree.AttributeTree; 49 import com.sun.source.doctree.AttributeTree.ValueKind; 50 import com.sun.source.doctree.CommentTree; 51 import com.sun.source.doctree.DocRootTree; 52 import com.sun.source.doctree.DocTree; 53 import com.sun.source.doctree.DocTree.Kind; 54 import com.sun.source.doctree.EndElementTree; 55 import com.sun.source.doctree.EntityTree; 56 import com.sun.source.doctree.ErroneousTree; 57 import com.sun.source.doctree.InheritDocTree; 58 import com.sun.source.doctree.IndexTree; 59 import com.sun.source.doctree.LinkTree; 60 import com.sun.source.doctree.LiteralTree; 61 import com.sun.source.doctree.SeeTree; 62 import com.sun.source.doctree.StartElementTree; 63 import com.sun.source.doctree.TextTree; 64 import com.sun.source.util.SimpleDocTreeVisitor; 65 import com.sun.tools.javac.util.DefinedBy; 66 import com.sun.tools.javac.util.DefinedBy.Api; 67 68 import jdk.javadoc.internal.doclets.formats.html.markup.Comment; 69 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; 70 import jdk.javadoc.internal.doclets.formats.html.markup.DocType; 71 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; 72 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; 73 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocWriter; 74 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocument; 75 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; 76 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; 77 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; 78 import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; 79 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; 80 import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter; 81 import jdk.javadoc.internal.doclets.toolkit.ClassWriter; 82 import jdk.javadoc.internal.doclets.toolkit.Configuration; 83 import jdk.javadoc.internal.doclets.toolkit.Content; 84 import jdk.javadoc.internal.doclets.toolkit.PackageSummaryWriter; 85 import jdk.javadoc.internal.doclets.toolkit.taglets.DocRootTaglet; 86 import jdk.javadoc.internal.doclets.toolkit.taglets.TagletWriter; 87 import jdk.javadoc.internal.doclets.toolkit.util.DocFile; 88 import jdk.javadoc.internal.doclets.toolkit.util.DocLink; 89 import jdk.javadoc.internal.doclets.toolkit.util.DocPath; 90 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; 91 import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; 92 import jdk.javadoc.internal.doclets.toolkit.util.ImplementedMethods; 93 import jdk.javadoc.internal.doclets.toolkit.util.Utils; 94 import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; 95 96 import static com.sun.source.doctree.AttributeTree.ValueKind.*; 97 import static com.sun.source.doctree.DocTree.Kind.*; 98 import static jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocWriter.CONTENT_TYPE; 99 import static jdk.javadoc.internal.doclets.toolkit.util.CommentHelper.SPACER; 100 101 102 /** 103 * Class for the Html Format Code Generation specific to JavaDoc. 104 * This Class contains methods related to the Html Code Generation which 105 * are used extensively while generating the entire documentation. 106 * 107 * <p><b>This is NOT part of any supported API. 108 * If you write code that depends on this, you do so at your own risk. 109 * This code and its internal interfaces are subject to change or 110 * deletion without notice.</b> 111 * 112 * @since 1.2 113 * @author Atul M Dambalkar 114 * @author Robert Field 115 * @author Bhavesh Patel (Modified) 116 */ 117 public class HtmlDocletWriter extends HtmlDocWriter { 118 119 /** 120 * Relative path from the file getting generated to the destination 121 * directory. For example, if the file getting generated is 254 " }" + DocletConstants.NL + 255 " else {" + DocletConstants.NL + 256 " allClassesLink.style.display = \"none\";" + DocletConstants.NL + 257 " }" + DocletConstants.NL + 258 " //-->" + DocletConstants.NL; 259 Content scriptContent = new RawHtml(scriptCode); 260 script.addContent(scriptContent); 261 Content div = HtmlTree.DIV(script); 262 Content div_noscript = HtmlTree.DIV(getResource("doclet.No_Script_Message")); 263 Content noScript = HtmlTree.NOSCRIPT(div_noscript); 264 div.addContent(noScript); 265 return div; 266 } 267 268 /** 269 * Add method information. 270 * 271 * @param method the method to be documented 272 * @param dl the content tree to which the method information will be added 273 */ 274 private void addMethodInfo(ExecutableElement method, Content dl) { 275 TypeElement enclosing = utils.getEnclosingTypeElement(method); 276 List<? extends TypeMirror> intfacs = enclosing.getInterfaces(); 277 ExecutableElement overriddenMethod = utils.overriddenMethod(method); 278 // Check whether there is any implementation or overridden info to be 279 // printed. If no overridden or implementation info needs to be 280 // printed, do not print this section. 281 if ((!intfacs.isEmpty() 282 && new ImplementedMethods(method, this.configuration).build().isEmpty() == false) 283 || overriddenMethod != null) { 284 MethodWriterImpl.addImplementsInfo(this, method, dl); 285 if (overriddenMethod != null) { 286 MethodWriterImpl.addOverridden(this, 287 utils.overriddenType(method), 288 overriddenMethod, 289 dl); 290 } 291 } 292 } 293 294 /** 295 * Adds the tags information. 296 * 297 * @param e the Element for which the tags will be generated 298 * @param htmltree the documentation tree to which the tags will be added 299 */ 300 protected void addTagsInfo(Element e, Content htmltree) { 301 if (configuration.nocomment) { 302 return; 303 } 304 Content dl = new HtmlTree(HtmlTag.DL); 305 if (utils.isExecutableElement(e) && !utils.isConstructor(e)) { 306 addMethodInfo((ExecutableElement)e, dl); 307 } 308 Content output = new ContentBuilder(); 309 TagletWriter.genTagOutput(configuration.tagletManager, e, 310 configuration.tagletManager.getCustomTaglets(e), 311 getTagletWriterInstance(false), output); 312 dl.addContent(output); 313 htmltree.addContent(dl); 314 } 315 316 /** 317 * Check whether there are any tags for Serialization Overview 318 * section to be printed. 319 * 320 * @param field the VariableElement object to check for tags. 321 * @return true if there are tags to be printed else return false. 322 */ 323 protected boolean hasSerializationOverviewTags(VariableElement field) { 324 Content output = new ContentBuilder(); 325 TagletWriter.genTagOutput(configuration.tagletManager, field, 326 configuration.tagletManager.getCustomTaglets(field), 327 getTagletWriterInstance(false), output); 328 return !output.isEmpty(); 329 } 330 331 /** 332 * Returns a TagletWriter that knows how to write HTML. 333 * 334 * @return a TagletWriter that knows how to write HTML. 335 */ 336 public TagletWriter getTagletWriterInstance(boolean isFirstSentence) { 337 return new TagletWriterImpl(this, isFirstSentence); 338 } 339 340 /** 341 * Get Package link, with target frame. 342 * 343 * @param pkg The link will be to the "package-summary.html" page for this package 344 * @param target name of the target frame 345 * @param label tag for the link 346 * @return a content for the target package link 347 */ 348 public Content getTargetPackageLink(PackageElement pkg, String target, 349 Content label) { 350 return getHyperLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY), label, "", target); 351 } 352 353 354 public void addClassesSummary(SortedSet<TypeElement> classes, String label, 355 String tableSummary, List<String> tableHeader, Content summaryContentTree) { 356 if (!classes.isEmpty()) { 357 Content caption = getTableCaption(new RawHtml(label)); 358 Content table = (configuration.isOutputHtml5()) 359 ? HtmlTree.TABLE(HtmlStyle.typeSummary, caption) 360 : HtmlTree.TABLE(HtmlStyle.typeSummary, tableSummary, caption); 361 table.addContent(getSummaryTableHeader(tableHeader, "col")); 362 Content tbody = new HtmlTree(HtmlTag.TBODY); 363 boolean altColor = true; 364 for (TypeElement te : classes) { 365 if (!utils.isCoreClass(te) || 366 !configuration.isGeneratedDoc(te)) { 367 continue; 368 } 369 Content classContent = getLink(new LinkInfoImpl( 370 configuration, LinkInfoImpl.Kind.PACKAGE, te)); 371 Content tdClass = HtmlTree.TD(HtmlStyle.colFirst, classContent); 372 HtmlTree tr = HtmlTree.TR(tdClass); 373 tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); 374 altColor = !altColor; 375 HtmlTree tdClassDescription = new HtmlTree(HtmlTag.TD); 376 tdClassDescription.addStyle(HtmlStyle.colLast); 377 if (utils.isDeprecated(te)) { 378 tdClassDescription.addContent(deprecatedLabel); 379 List<? extends DocTree> tags = utils.getDeprecatedTrees(te); 380 if (!tags.isEmpty()) { 381 addSummaryDeprecatedComment(te, tags.get(0), tdClassDescription); 382 } 383 } else { 384 addSummaryComment(te, tdClassDescription); 385 } 386 tr.addContent(tdClassDescription); 387 tbody.addContent(tr); 388 } 389 table.addContent(tbody); 390 summaryContentTree.addContent(table); 391 } 392 } 393 394 /** 395 * Generates the HTML document tree and prints it out. 396 * 397 * @param metakeywords Array of String keywords for META tag. Each element 398 * of the array is assigned to a separate META tag. 399 * Pass in null for no array 400 * @param includeScript true if printing windowtitle script 401 * false for files that appear in the left-hand frames 402 * @param body the body htmltree to be included in the document 403 */ 404 public void printHtmlDocument(List<String> metakeywords, boolean includeScript, 405 Content body) throws IOException { 406 Content htmlDocType = configuration.isOutputHtml5() 407 ? DocType.HTML5 408 : DocType.TRANSITIONAL; 409 Content htmlComment = new Comment(configuration.getText("doclet.New_Page")); 410 Content head = new HtmlTree(HtmlTag.HEAD); 411 head.addContent(getGeneratedBy(!configuration.notimestamp)); 412 head.addContent(getTitle()); 413 Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, 414 (configuration.charset.length() > 0) ? 415 configuration.charset : HtmlConstants.HTML_DEFAULT_CHARSET); 416 head.addContent(meta); 417 if (!configuration.notimestamp) { 418 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 419 meta = HtmlTree.META(configuration.isOutputHtml5() 420 ? "dc.created" 421 : "date", dateFormat.format(new Date())); 422 head.addContent(meta); 423 } 424 if (metakeywords != null) { 526 navDiv.addContent(a); 527 Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav, getHyperLink( 528 getDocLink(SectionName.SKIP_NAVBAR_BOTTOM), skipNavLinks, 529 skipNavLinks.toString(), "")); 530 navDiv.addContent(skipLinkContent); 531 } 532 if (header) { 533 navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_TOP_FIRSTROW)); 534 } else { 535 navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_BOTTOM_FIRSTROW)); 536 } 537 HtmlTree navList = new HtmlTree(HtmlTag.UL); 538 navList.addStyle(HtmlStyle.navList); 539 navList.addAttr(HtmlAttr.TITLE, 540 configuration.getText("doclet.Navigation")); 541 if (configuration.createoverview) { 542 navList.addContent(getNavLinkContents()); 543 } 544 if (configuration.packages.size() == 1) { 545 navList.addContent(getNavLinkPackage(configuration.packages.first())); 546 } else if (!configuration.packages.isEmpty()) { 547 navList.addContent(getNavLinkPackage()); 548 } 549 navList.addContent(getNavLinkClass()); 550 if(configuration.classuse) { 551 navList.addContent(getNavLinkClassUse()); 552 } 553 if(configuration.createtree) { 554 navList.addContent(getNavLinkTree()); 555 } 556 if(!(configuration.nodeprecated || 557 configuration.nodeprecatedlist)) { 558 navList.addContent(getNavLinkDeprecated()); 559 } 560 if(configuration.createindex) { 561 navList.addContent(getNavLinkIndex()); 562 } 563 if (!configuration.nohelp) { 564 navList.addContent(getNavLinkHelp()); 565 } 566 navDiv.addContent(navList); 567 Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, getUserHeaderFooter(header)); 568 navDiv.addContent(aboutDiv); 569 if (header) { 570 fixedNavDiv.addContent(navDiv); 571 } else { 572 tree.addContent(navDiv); 573 } 574 Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious()); 575 ulNav.addContent(getNavLinkNext()); 576 Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav); 577 Content ulFrames = HtmlTree.UL(HtmlStyle.navList, getNavShowLists()); 578 ulFrames.addContent(getNavHideLists(filename)); 579 subDiv.addContent(ulFrames); 580 HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex()); 581 ulAllClasses.addAttr(HtmlAttr.ID, allClassesId); 582 subDiv.addContent(ulAllClasses); 583 if (header && configuration.createindex) { 584 HtmlTree inputText = HtmlTree.INPUT("text", "search"); 585 HtmlTree inputReset = HtmlTree.INPUT("reset", "reset"); 586 Content searchTxt = configuration.getResource("doclet.search"); 587 searchTxt.addContent(getSpace()); 588 HtmlTree liInput = HtmlTree.LI(HtmlTree.SPAN(searchTxt)); 589 liInput.addContent(inputText); 590 liInput.addContent(inputReset); 591 HtmlTree ulSearch = HtmlTree.UL(HtmlStyle.navListSearch, liInput); 592 subDiv.addContent(ulSearch); 593 } 594 subDiv.addContent(getAllClassesLinkScript(allClassesId)); 595 addSummaryDetailLinks(subDiv); 596 if (header) { 597 subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_TOP)); 598 fixedNavDiv.addContent(subDiv); 599 fixedNavDiv.addContent(HtmlConstants.END_OF_TOP_NAVBAR); 600 tree.addContent(fixedNavDiv); 601 } else { 602 subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_BOTTOM)); 603 tree.addContent(subDiv); 604 tree.addContent(HtmlConstants.END_OF_BOTTOM_NAVBAR); 605 } 606 if (configuration.allowTag(HtmlTag.NAV)) { 607 htmlTree.addContent(tree); 608 } 609 } 610 } 611 612 /** 613 * Get the word "NEXT" to indicate that no link is available. Override 614 * this method to customize next link. 636 } 637 638 /** 639 * Get link to the "overview-summary.html" page. 640 * 641 * @return a content tree for the link 642 */ 643 protected Content getNavLinkContents() { 644 Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_SUMMARY), 645 overviewLabel, "", ""); 646 Content li = HtmlTree.LI(linkContent); 647 return li; 648 } 649 650 /** 651 * Get link to the "package-summary.html" page for the package passed. 652 * 653 * @param pkg Package to which link will be generated 654 * @return a content tree for the link 655 */ 656 protected Content getNavLinkPackage(PackageElement pkg) { 657 Content linkContent = getPackageLink(pkg, packageLabel); 658 Content li = HtmlTree.LI(linkContent); 659 return li; 660 } 661 662 /** 663 * Get the word "Package" , to indicate that link is not available here. 664 * 665 * @return a content tree for the link 666 */ 667 protected Content getNavLinkPackage() { 668 Content li = HtmlTree.LI(packageLabel); 669 return li; 670 } 671 672 /** 673 * Get the word "Use", to indicate that link is not available. 674 * 675 * @return a content tree for the link 676 */ 677 protected Content getNavLinkClassUse() { 738 * Get "NO FRAMES" link, to switch to the non-frame version of the output. 739 * 740 * @param link File to be linked 741 * @return a content tree for the link 742 */ 743 protected Content getNavHideLists(DocPath link) { 744 Content noFramesContent = getHyperLink(link, noframesLabel, "", "_top"); 745 Content li = HtmlTree.LI(noFramesContent); 746 return li; 747 } 748 749 /** 750 * Get "Tree" link in the navigation bar. If there is only one package 751 * specified on the command line, then the "Tree" link will be to the 752 * only "package-tree.html" file otherwise it will be to the 753 * "overview-tree.html" file. 754 * 755 * @return a content tree for the link 756 */ 757 protected Content getNavLinkTree() { 758 List<PackageElement> packages = new ArrayList<>(utils.getSpecifiedPackages()); 759 DocPath docPath = packages.size() == 1 && utils.getSpecifiedClasses().isEmpty() 760 ? pathString(packages.get(0), DocPaths.PACKAGE_TREE) 761 : pathToRoot.resolve(DocPaths.OVERVIEW_TREE); 762 return HtmlTree.LI(getHyperLink(docPath, treeLabel, "", "")); 763 } 764 765 /** 766 * Get the overview tree link for the main tree. 767 * 768 * @param label the label for the link 769 * @return a content tree for the link 770 */ 771 protected Content getNavLinkMainTree(String label) { 772 Content mainTreeContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE), 773 new StringContent(label)); 774 Content li = HtmlTree.LI(mainTreeContent); 775 return li; 776 } 777 778 /** 779 * Get the word "Class", to indicate that class link is not available. 780 * 781 * @return a content tree for the link 782 */ 783 protected Content getNavLinkClass() { 839 DocPath helpfilenm; 840 if (helpfile.isEmpty()) { 841 helpfilenm = DocPaths.HELP_DOC; 842 } else { 843 DocFile file = DocFile.createFileForInput(configuration, helpfile); 844 helpfilenm = DocPath.create(file.getName()); 845 } 846 Content linkContent = getHyperLink(pathToRoot.resolve(helpfilenm), 847 helpLabel, "", ""); 848 Content li = HtmlTree.LI(linkContent); 849 return li; 850 } 851 852 /** 853 * Get summary table header. 854 * 855 * @param header the header for the table 856 * @param scope the scope of the headers 857 * @return a content tree for the header 858 */ 859 public Content getSummaryTableHeader(List<String> header, String scope) { 860 Content tr = new HtmlTree(HtmlTag.TR); 861 final int size = header.size(); 862 Content tableHeader; 863 if (size == 1) { 864 tableHeader = new StringContent(header.get(0)); 865 tr.addContent(HtmlTree.TH(HtmlStyle.colOne, scope, tableHeader)); 866 return tr; 867 } 868 for (int i = 0; i < size; i++) { 869 tableHeader = new StringContent(header.get(i)); 870 if(i == 0) 871 tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader)); 872 else if(i == (size - 1)) 873 tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader)); 874 else 875 tr.addContent(HtmlTree.TH(scope, tableHeader)); 876 } 877 return tr; 878 } 879 880 /** 881 * Get table caption. 882 * 883 * @param rawText the caption for the table which could be raw Html 884 * @return a content tree for the caption 885 */ 886 public Content getTableCaption(Content title) { 887 Content captionSpan = HtmlTree.SPAN(title); 888 Content space = getSpace(); 889 Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space); 923 return getMarkerAnchor(sectionName.getName() + getName(anchorName), null); 924 } 925 926 /** 927 * Get the marker anchor which will be added to the documentation tree. 928 * 929 * @param anchorName the anchor name or id attribute 930 * @param anchorContent the content that should be added to the anchor 931 * @return a content tree for the marker anchor 932 */ 933 public Content getMarkerAnchor(String anchorName, Content anchorContent) { 934 if (anchorContent == null) 935 anchorContent = new Comment(" "); 936 Content markerAnchor = HtmlTree.A(configuration.htmlVersion, anchorName, anchorContent); 937 return markerAnchor; 938 } 939 940 /** 941 * Returns a packagename content. 942 * 943 * @param packageElement the package to check 944 * @return package name content 945 */ 946 public Content getPackageName(PackageElement packageElement) { 947 return packageElement == null || packageElement.isUnnamed() 948 ? defaultPackageLabel 949 : getPackageLabel(packageElement.getQualifiedName().toString()); 950 } 951 952 /** 953 * Returns a package name label. 954 * 955 * @param packageName the package name 956 * @return the package name content 957 */ 958 public Content getPackageLabel(String packageName) { 959 return new StringContent(packageName); 960 } 961 962 /** 963 * Add package deprecation information to the documentation tree 964 * 965 * @param deprPkgs list of deprecated packages 966 * @param headingKey the caption for the deprecated package table 967 * @param tableSummary the summary for the deprecated package table 968 * @param tableHeader table headers for the deprecated package table 969 * @param contentTree the content tree to which the deprecated package table will be added 970 */ 971 protected void addPackageDeprecatedAPI(SortedSet<Element> deprPkgs, String headingKey, 972 String tableSummary, List<String> tableHeader, Content contentTree) { 973 if (deprPkgs.size() > 0) { 974 Content caption = getTableCaption(configuration.getResource(headingKey)); 975 Content table = (configuration.isOutputHtml5()) 976 ? HtmlTree.TABLE(HtmlStyle.deprecatedSummary, caption) 977 : HtmlTree.TABLE(HtmlStyle.deprecatedSummary, tableSummary, caption); 978 table.addContent(getSummaryTableHeader(tableHeader, "col")); 979 Content tbody = new HtmlTree(HtmlTag.TBODY); 980 boolean altColor = true; 981 for (Element e : deprPkgs) { 982 PackageElement pkg = (PackageElement) e; 983 HtmlTree td = HtmlTree.TD(HtmlStyle.colOne, 984 getPackageLink(pkg, getPackageName(pkg))); 985 List<? extends DocTree> tags = utils.getDeprecatedTrees(pkg); 986 if (!tags.isEmpty()) { 987 addInlineDeprecatedComment(pkg, tags.get(0), td); 988 } 989 HtmlTree tr = HtmlTree.TR(td); 990 tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); 991 altColor = !altColor; 992 tbody.addContent(tr); 993 } 994 table.addContent(tbody); 995 Content li = HtmlTree.LI(HtmlStyle.blockList, table); 996 Content ul = HtmlTree.UL(HtmlStyle.blockList, li); 997 contentTree.addContent(ul); 998 } 999 } 1000 1001 /** 1002 * Return the path to the class page for a typeElement. 1003 * 1004 * @param te TypeElement for which the path is requested. 1005 * @param name Name of the file(doesn't include path). 1006 */ 1007 protected DocPath pathString(TypeElement te, DocPath name) { 1008 return pathString(utils.containingPackage(te), name); 1009 } 1010 1011 /** 1012 * Return path to the given file name in the given package. So if the name 1013 * passed is "Object.html" and the name of the package is "java.lang", and 1014 * if the relative path is "../.." then returned string will be 1015 * "../../java/lang/Object.html" 1016 * 1017 * @param packageElement Package in which the file name is assumed to be. 1018 * @param name File name, to which path string is. 1019 */ 1020 protected DocPath pathString(PackageElement packageElement, DocPath name) { 1021 return pathToRoot.resolve(DocPath.forPackage(packageElement).resolve(name)); 1022 } 1023 1024 /** 1025 * Given a package, return the name to be used in HTML anchor tag. 1026 * @param packageElement the package. 1027 * @return the name to be used in HTML anchor tag. 1028 */ 1029 public String getPackageAnchorName(PackageElement packageElement) { 1030 return packageElement == null || packageElement.isUnnamed() 1031 ? SectionName.UNNAMED_PACKAGE_ANCHOR.getName() 1032 : utils.getPackageName(packageElement); 1033 } 1034 1035 /** 1036 * Return the link to the given package. 1037 * 1038 * @param packageElement the package to link to. 1039 * @param label the label for the link. 1040 * @return a content tree for the package link. 1041 */ 1042 public Content getPackageLink(PackageElement packageElement, String label) { 1043 return getPackageLink(packageElement, new StringContent(label)); 1044 } 1045 1046 public Content getPackageLink(PackageElement packageElement) { 1047 StringContent content = packageElement.isUnnamed() 1048 ? new StringContent() 1049 : new StringContent(utils.getPackageName(packageElement)); 1050 return getPackageLink(packageElement, content); 1051 } 1052 1053 /** 1054 * Return the link to the given package. 1055 * 1056 * @param packageElement the package to link to. 1057 * @param label the label for the link. 1058 * @return a content tree for the package link. 1059 */ 1060 public Content getPackageLink(PackageElement packageElement, Content label) { 1061 boolean included = packageElement != null && utils.isIncluded(packageElement); 1062 if (!included) { 1063 for (PackageElement p : configuration.packages) { 1064 if (p.equals(packageElement)) { 1065 included = true; 1066 break; 1067 } 1068 } 1069 } 1070 if (included || packageElement == null) { 1071 return getHyperLink(pathString(packageElement, DocPaths.PACKAGE_SUMMARY), 1072 label); 1073 } else { 1074 DocLink crossPkgLink = getCrossPackageLink(utils.getPackageName(packageElement)); 1075 if (crossPkgLink != null) { 1076 return getHyperLink(crossPkgLink, label); 1077 } else { 1078 return label; 1079 } 1080 } 1081 } 1082 1083 public Content interfaceName(TypeElement typeElement, boolean qual) { 1084 Content name = new StringContent((qual) 1085 ? typeElement.getQualifiedName().toString() 1086 : utils.getSimpleName(typeElement)); 1087 return (utils.isInterface(typeElement)) ? HtmlTree.SPAN(HtmlStyle.interfaceName, name) : name; 1088 } 1089 1090 /** 1091 * Add the link to the content tree. 1092 * 1093 * @param typeElement program element typeElement for which the link will be added 1094 * @param label label for the link 1095 * @param htmltree the content tree to which the link will be added 1096 */ 1097 public void addSrcLink(Element typeElement, Content label, Content htmltree) { 1098 if (typeElement == null) { 1099 return; 1100 } 1101 TypeElement te = utils.getEnclosingTypeElement(typeElement); 1102 if (te == null) { 1103 // must be a typeElement since in has no containing class. 1104 te = (TypeElement) typeElement; 1105 } 1106 DocPath href = pathToRoot 1107 .resolve(DocPaths.SOURCE_OUTPUT) 1108 .resolve(DocPath.forClass(utils, te)); 1109 Content linkContent = getHyperLink(href 1110 .fragment(SourceToHTMLConverter.getAnchorName(utils, typeElement)), label, "", ""); 1111 htmltree.addContent(linkContent); 1112 } 1113 1114 /** 1115 * Return the link to the given class. 1116 * 1117 * @param linkInfo the information about the link. 1118 * 1119 * @return the link for the given class. 1120 */ 1121 public Content getLink(LinkInfoImpl linkInfo) { 1122 LinkFactoryImpl factory = new LinkFactoryImpl(this); 1123 return factory.getLink(linkInfo); 1124 } 1125 1126 /** 1127 * Return the type parameters for the given class. 1128 * 1129 * @param linkInfo the information about the link. 1130 * @return the type for the given class. 1145 * be null or empty string if no member is being referenced. 1146 * @param label the label for the external link. 1147 * @param strong true if the link should be strong. 1148 * @param style the style of the link. 1149 * @param code true if the label should be code font. 1150 */ 1151 public Content getCrossClassLink(String qualifiedClassName, String refMemName, 1152 Content label, boolean strong, String style, 1153 boolean code) { 1154 String className = ""; 1155 String packageName = qualifiedClassName == null ? "" : qualifiedClassName; 1156 int periodIndex; 1157 while ((periodIndex = packageName.lastIndexOf('.')) != -1) { 1158 className = packageName.substring(periodIndex + 1, packageName.length()) + 1159 (className.length() > 0 ? "." + className : ""); 1160 Content defaultLabel = new StringContent(className); 1161 if (code) 1162 defaultLabel = HtmlTree.CODE(defaultLabel); 1163 packageName = packageName.substring(0, periodIndex); 1164 if (getCrossPackageLink(packageName) != null) { 1165 /* 1166 The package exists in external documentation, so link to the external 1167 class (assuming that it exists). This is definitely a limitation of 1168 the -link option. There are ways to determine if an external package 1169 exists, but no way to determine if the external class exists. We just 1170 have to assume that it does. 1171 */ 1172 DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot, 1173 className + ".html", refMemName); 1174 return getHyperLink(link, 1175 (label == null) || label.isEmpty() ? defaultLabel : label, 1176 strong, style, 1177 configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName), 1178 ""); 1179 } 1180 } 1181 return null; 1182 } 1183 1184 public boolean isClassLinkable(TypeElement typeElement) { 1185 if (utils.isIncluded(typeElement)) { 1186 return configuration.isGeneratedDoc(typeElement); 1187 } 1188 return configuration.extern.isExternal(typeElement); 1189 } 1190 1191 public DocLink getCrossPackageLink(String pkgName) { 1192 return configuration.extern.getExternalLink(pkgName, pathToRoot, 1193 DocPaths.PACKAGE_SUMMARY.getPath()); 1194 } 1195 1196 /** 1197 * Get the class link. 1198 * 1199 * @param context the id of the context where the link will be added 1200 * @param element to link to 1201 * @return a content tree for the link 1202 */ 1203 public Content getQualifiedClassLink(LinkInfoImpl.Kind context, Element element) { 1204 LinkInfoImpl linkInfoImpl = new LinkInfoImpl(configuration, context, (TypeElement)element); 1205 return getLink(linkInfoImpl.label(utils.getFullyQualifiedName(element))); 1206 } 1207 1208 /** 1209 * Add the class link. 1210 * 1211 * @param context the id of the context where the link will be added 1212 * @param typeElement to link to 1213 * @param contentTree the content tree to which the link will be added 1214 */ 1215 public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) { 1216 addPreQualifiedClassLink(context, typeElement, false, contentTree); 1217 } 1218 1219 /** 1220 * Retrieve the class link with the package portion of the label in 1221 * plain text. If the qualifier is excluded, it will not be included in the 1222 * link label. 1223 * 1224 * @param typeElement the class to link to. 1225 * @param isStrong true if the link should be strong. 1226 * @return the link with the package portion of the label in plain text. 1227 */ 1228 public Content getPreQualifiedClassLink(LinkInfoImpl.Kind context, 1229 TypeElement typeElement, boolean isStrong) { 1230 ContentBuilder classlink = new ContentBuilder(); 1231 PackageElement pkg = utils.containingPackage(typeElement); 1232 if (pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) { 1233 classlink.addContent(getEnclosingPackageName(typeElement)); 1234 } 1235 classlink.addContent(getLink(new LinkInfoImpl(configuration, 1236 context, typeElement).label(utils.getSimpleName(typeElement)).strong(isStrong))); 1237 return classlink; 1238 } 1239 1240 /** 1241 * Add the class link with the package portion of the label in 1242 * plain text. If the qualifier is excluded, it will not be included in the 1243 * link label. 1244 * 1245 * @param context the id of the context where the link will be added 1246 * @param typeElement the class to link to 1247 * @param isStrong true if the link should be strong 1248 * @param contentTree the content tree to which the link with be added 1249 */ 1250 public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, 1251 TypeElement typeElement, boolean isStrong, Content contentTree) { 1252 PackageElement pkg = utils.containingPackage(typeElement); 1253 if(pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) { 1254 contentTree.addContent(getEnclosingPackageName(typeElement)); 1255 } 1256 LinkInfoImpl linkinfo = new LinkInfoImpl(configuration, context, typeElement) 1257 .label(utils.getSimpleName(typeElement)) 1258 .strong(isStrong); 1259 Content link = getLink(linkinfo); 1260 contentTree.addContent(link); 1261 } 1262 1263 /** 1264 * Add the class link, with only class name as the strong link and prefixing 1265 * plain package name. 1266 * 1267 * @param context the id of the context where the link will be added 1268 * @param typeElement the class to link to 1269 * @param contentTree the content tree to which the link with be added 1270 */ 1271 public void addPreQualifiedStrongClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) { 1272 addPreQualifiedClassLink(context, typeElement, true, contentTree); 1273 } 1274 1275 /** 1276 * Get the link for the given member. 1277 * 1278 * @param context the id of the context where the link will be added 1279 * @param element the member being linked to 1280 * @param label the label for the link 1281 * @return a content tree for the element link 1282 */ 1283 public Content getDocLink(LinkInfoImpl.Kind context, Element element, String label) { 1284 return getDocLink(context, utils.getEnclosingTypeElement(element), element, 1285 new StringContent(label)); 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 element the member being linked to. 1293 * @param label the label for the link. 1294 * @param strong true if the link should be strong. 1295 * @return the link for the given member. 1296 */ 1297 public Content getDocLink(LinkInfoImpl.Kind context, Element element, String label, 1298 boolean strong) { 1299 return getDocLink(context, utils.getEnclosingTypeElement(element), element, label, strong); 1300 } 1301 1302 /** 1303 * Return the link for the given member. 1304 * 1305 * @param context the id of the context where the link will be printed. 1306 * @param typeElement the typeElement that we should link to. This is not 1307 necessarily equal to element.containingClass(). We may be 1308 inheriting comments. 1309 * @param element the member being linked to. 1310 * @param label the label for the link. 1311 * @param strong true if the link should be strong. 1312 * @return the link for the given member. 1313 */ 1314 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1315 String label, boolean strong) { 1316 return getDocLink(context, typeElement, element, label, strong, false); 1317 } 1318 1319 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1320 Content label, boolean strong) { 1321 return getDocLink(context, typeElement, element, label, strong, false); 1322 } 1323 1324 /** 1325 * Return the link for the given member. 1326 * 1327 * @param context the id of the context where the link will be printed. 1328 * @param typeElement the typeElement that we should link to. This is not 1329 necessarily equal to element.containingClass(). We may be 1330 inheriting comments. 1331 * @param element the member being linked to. 1332 * @param label the label for the link. 1333 * @param strong true if the link should be strong. 1334 * @param isProperty true if the element parameter is a JavaFX property. 1335 * @return the link for the given member. 1336 */ 1337 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1338 String label, boolean strong, boolean isProperty) { 1339 return getDocLink(context, typeElement, element, new StringContent(check(label)), strong, isProperty); 1340 } 1341 1342 String check(String s) { 1343 if (s.matches(".*[&<>].*")) { 1344 throw new IllegalArgumentException(s); 1345 } 1346 return s; 1347 } 1348 1349 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1350 Content label, boolean strong, boolean isProperty) { 1351 if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) { 1352 return label; 1353 } else if (utils.isExecutableElement(element)) { 1354 ExecutableElement ee = (ExecutableElement)element; 1355 return getLink(new LinkInfoImpl(configuration, context, typeElement) 1356 .label(label) 1357 .where(getName(getAnchor(ee, isProperty))) 1358 .strong(strong)); 1359 } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) { 1360 return getLink(new LinkInfoImpl(configuration, context, typeElement) 1361 .label(label) 1362 .where(getName(element.getSimpleName().toString())) 1363 .strong(strong)); 1364 } else { 1365 return label; 1366 } 1367 } 1368 1369 /** 1370 * Return the link for the given member. 1371 * 1372 * @param context the id of the context where the link will be added 1373 * @param typeElement the typeElement that we should link to. This is not 1374 necessarily equal to element.containingClass(). We may be 1375 inheriting comments 1376 * @param element the member being linked to 1377 * @param label the label for the link 1378 * @return the link for the given member 1379 */ 1380 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1381 Content label) { 1382 if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) { 1383 return label; 1384 } else if (utils.isExecutableElement(element)) { 1385 ExecutableElement emd = (ExecutableElement) element; 1386 return getLink(new LinkInfoImpl(configuration, context, typeElement) 1387 .label(label) 1388 .where(getName(getAnchor(emd)))); 1389 } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) { 1390 return getLink(new LinkInfoImpl(configuration, context, typeElement) 1391 .label(label).where(getName(element.getSimpleName().toString()))); 1392 } else { 1393 return label; 1394 } 1395 } 1396 1397 public String getAnchor(ExecutableElement executableElement) { 1398 return getAnchor(executableElement, false); 1399 } 1400 1401 public String getAnchor(ExecutableElement executableElement, boolean isProperty) { 1402 if (isProperty) { 1403 return executableElement.getSimpleName().toString(); 1404 } 1405 String signature = utils.signature(executableElement); 1406 StringBuilder signatureParsed = new StringBuilder(); 1407 int counter = 0; 1408 for (int i = 0; i < signature.length(); i++) { 1409 char c = signature.charAt(i); 1410 if (c == '<') { 1411 counter++; 1412 } else if (c == '>') { 1413 counter--; 1414 } else if (counter == 0) { 1415 signatureParsed.append(c); 1416 } 1417 } 1418 return utils.getSimpleName(executableElement) + signatureParsed.toString(); 1419 } 1420 1421 public Content seeTagToContent(Element element, DocTree see) { 1422 1423 Kind kind = see.getKind(); 1424 if (!(kind == LINK || kind == SEE || kind == LINK_PLAIN)) { 1425 return new ContentBuilder(); 1426 } 1427 1428 CommentHelper ch = utils.getCommentHelper(element); 1429 String tagName = ch.getTagName(see); 1430 String seetext = replaceDocRootDir(utils.normalizeNewlines(ch.getText(see))); 1431 // Check if @see is an href or "string" 1432 if (seetext.startsWith("<") || seetext.startsWith("\"")) { 1433 return new RawHtml(seetext); 1434 } 1435 boolean isLinkPlain = kind == LINK_PLAIN; 1436 Content label = plainOrCode(isLinkPlain, new RawHtml(ch.getLabel(configuration, see))); 1437 1438 //The text from the @see tag. We will output this text when a label is not specified. 1439 Content text = plainOrCode(kind == LINK_PLAIN, new RawHtml(seetext)); 1440 1441 TypeElement refClass = ch.getReferencedClass(configuration, see); 1442 String refClassName = ch.getReferencedClassName(configuration, see); 1443 Element refMem = ch.getReferencedMember(configuration, see); 1444 String refMemName = ch.getReferencedMemberName(see); 1445 1446 if (refMemName == null && refMem != null) { 1447 refMemName = refMem.toString(); 1448 } 1449 if (refClass == null) { 1450 //@see is not referencing an included class 1451 PackageElement refPackage = ch.getReferencedPackage(configuration, see); 1452 if (refPackage != null && utils.isIncluded(refPackage)) { 1453 //@see is referencing an included package 1454 if (label.isEmpty()) 1455 label = plainOrCode(isLinkPlain, 1456 new StringContent(refPackage.getQualifiedName().toString())); 1457 return getPackageLink(refPackage, label); 1458 } else { 1459 // @see is not referencing an included class or package. Check for cross links. 1460 Content classCrossLink; 1461 DocLink packageCrossLink = getCrossPackageLink(refClassName); 1462 if (packageCrossLink != null) { 1463 // Package cross link found 1464 return getHyperLink(packageCrossLink, 1465 (label.isEmpty() ? text : label)); 1466 } else if ((classCrossLink = getCrossClassLink(refClassName, 1467 refMemName, label, false, "", !isLinkPlain)) != null) { 1468 // Class cross link found (possibly to a member in the class) 1469 return classCrossLink; 1470 } else { 1471 // No cross link found so print warning 1472 configuration.getDocletSpecificMsg().warning(ch.getDocTreePath(see), 1473 "doclet.see.class_or_package_not_found", 1474 "@" + tagName, 1475 seetext); 1476 return (label.isEmpty() ? text: label); 1477 } 1478 } 1479 } else if (refMemName == null) { 1480 // Must be a class reference since refClass is not null and refMemName is null. 1481 if (label.isEmpty()) { 1482 /* 1483 * it seems to me this is the right thing to do, but it causes comparator failures. 1484 */ 1485 if (!configuration.backwardCompatibility) { 1486 StringContent content = utils.isEnclosingPackageIncluded(refClass) 1487 ? new StringContent(utils.getSimpleName(refClass)) 1488 : new StringContent(utils.getFullyQualifiedName(refClass)); 1489 label = plainOrCode(isLinkPlain, content); 1490 } else { 1491 label = plainOrCode(isLinkPlain, 1492 new StringContent(utils.getSimpleName(refClass))); 1493 } 1494 1495 } 1496 return getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, refClass) 1497 .label(label)); 1498 } else if (refMem == null) { 1499 // Must be a member reference since refClass is not null and refMemName is not null. 1500 // However, refMem is null, so this referenced member does not exist. 1501 return (label.isEmpty() ? text: label); 1502 } else { 1503 // Must be a member reference since refClass is not null and refMemName is not null. 1504 // refMem is not null, so this @see tag must be referencing a valid member. 1505 TypeElement containing = utils.getEnclosingTypeElement(refMem); 1506 if (ch.getText(see).trim().startsWith("#") && 1507 ! (utils.isPublic(containing) || utils.isLinkable(containing))) { 1508 // Since the link is relative and the holder is not even being 1509 // documented, this must be an inherited link. Redirect it. 1510 // The current class either overrides the referenced member or 1511 // inherits it automatically. 1512 if (this instanceof ClassWriterImpl) { 1513 containing = ((ClassWriterImpl) this).getTypeElement(); 1514 } else if (!utils.isPublic(containing)) { 1515 configuration.getDocletSpecificMsg().warning( 1516 ch.getDocTreePath(see), "doclet.see.class_or_package_not_accessible", 1517 tagName, utils.getFullyQualifiedName(containing)); 1518 } else { 1519 configuration.getDocletSpecificMsg().warning( 1520 ch.getDocTreePath(see), "doclet.see.class_or_package_not_found", 1521 tagName, seetext); 1522 } 1523 } 1524 if (configuration.currentTypeElement != containing) { 1525 refMemName = (utils.isConstructor(refMem)) 1526 ? refMemName 1527 : utils.getSimpleName(containing) + "." + refMemName; 1528 } 1529 if (utils.isExecutableElement(refMem)) { 1530 if (refMemName.indexOf('(') < 0) { 1531 refMemName += utils.makeSignature((ExecutableElement)refMem, true); 1532 } 1533 } 1534 1535 text = plainOrCode(kind == LINK_PLAIN, new StringContent(refMemName)); 1536 1537 return getDocLink(LinkInfoImpl.Kind.SEE_TAG, containing, 1538 refMem, (label.isEmpty() ? text: label), false); 1539 } 1540 } 1541 1542 private Content plainOrCode(boolean plain, Content body) { 1543 return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body); 1544 } 1545 1546 /** 1547 * Add the inline comment. 1548 * 1549 * @param element the Element for which the inline comment will be added 1550 * @param tag the inline tag to be added 1551 * @param htmltree the content tree to which the comment will be added 1552 */ 1553 public void addInlineComment(Element element, DocTree tag, Content htmltree) { 1554 CommentHelper ch = utils.getCommentHelper(element); 1555 List<? extends DocTree> description = ch.getDescription(configuration, tag); 1556 addCommentTags(element, tag, description, false, false, htmltree); 1557 } 1558 1559 /** 1560 * Add the inline deprecated comment. 1561 * 1562 * @param e the Element for which the inline deprecated comment will be added 1563 * @param tag the inline tag to be added 1564 * @param htmltree the content tree to which the comment will be added 1565 */ 1566 public void addInlineDeprecatedComment(Element e, DocTree tag, Content htmltree) { 1567 CommentHelper ch = utils.getCommentHelper(e); 1568 addCommentTags(e, ch.getBody(configuration, tag), true, false, htmltree); 1569 } 1570 1571 /** 1572 * Adds the summary content. 1573 * 1574 * @param element the Element for which the summary will be generated 1575 * @param htmltree the documentation tree to which the summary will be added 1576 */ 1577 public void addSummaryComment(Element element, Content htmltree) { 1578 addSummaryComment(element, utils.getFirstSentenceTrees(element), htmltree); 1579 } 1580 1581 /** 1582 * Adds the summary content. 1583 * 1584 * @param element the Element for which the summary will be generated 1585 * @param firstSentenceTags the first sentence tags for the doc 1586 * @param htmltree the documentation tree to which the summary will be added 1587 */ 1588 public void addSummaryComment(Element element, List<? extends DocTree> firstSentenceTags, Content htmltree) { 1589 addCommentTags(element, firstSentenceTags, false, true, htmltree); 1590 } 1591 1592 public void addSummaryDeprecatedComment(Element element, DocTree tag, Content htmltree) { 1593 CommentHelper ch = utils.getCommentHelper(element); 1594 List<? extends DocTree> body = ch.getBody(configuration, tag); 1595 addCommentTags(element, ch.getFirstSentenceTrees(configuration, body), true, true, htmltree); 1596 } 1597 1598 /** 1599 * Adds the inline comment. 1600 * 1601 * @param element the Element for which the inline comments will be generated 1602 * @param htmltree the documentation tree to which the inline comments will be added 1603 */ 1604 public void addInlineComment(Element element, Content htmltree) { 1605 addCommentTags(element, utils.getBody(element), false, false, htmltree); 1606 } 1607 1608 /** 1609 * Adds the comment tags. 1610 * 1611 * @param element the Element for which the comment tags will be generated 1612 * @param tags the first sentence tags for the doc 1613 * @param depr true if it is deprecated 1614 * @param first true if the first sentence tags should be added 1615 * @param htmltree the documentation tree to which the comment tags will be added 1616 */ 1617 private void addCommentTags(Element element, List<? extends DocTree> tags, boolean depr, 1618 boolean first, Content htmltree) { 1619 addCommentTags(element, null, tags, depr, first, htmltree); 1620 } 1621 1622 /** 1623 * Adds the comment tags. 1624 * 1625 * @param element for which the comment tags will be generated 1626 * @param holderTag the block tag context for the inline tags 1627 * @param tags the first sentence tags for the doc 1628 * @param depr true if it is deprecated 1629 * @param first true if the first sentence tags should be added 1630 * @param htmltree the documentation tree to which the comment tags will be added 1631 */ 1632 private void addCommentTags(Element element, DocTree holderTag, List<? extends DocTree> tags, boolean depr, 1633 boolean first, Content htmltree) { 1634 if(configuration.nocomment){ 1635 return; 1636 } 1637 Content div; 1638 Content result = commentTagsToContent(null, element, tags, first); 1639 if (depr) { 1640 Content italic = HtmlTree.SPAN(HtmlStyle.deprecationComment, result); 1641 div = HtmlTree.DIV(HtmlStyle.block, italic); 1642 htmltree.addContent(div); 1643 } 1644 else { 1645 div = HtmlTree.DIV(HtmlStyle.block, result); 1646 htmltree.addContent(div); 1647 } 1648 if (tags.isEmpty()) { 1649 htmltree.addContent(getSpace()); 1650 } 1651 } 1652 1653 boolean ignoreNonInlineTag(DocTree dtree) { 1654 Name name = null; 1655 if (dtree.getKind() == Kind.START_ELEMENT) { 1656 StartElementTree setree = (StartElementTree)dtree; 1657 name = setree.getName(); 1658 } else if (dtree.getKind() == Kind.END_ELEMENT) { 1659 EndElementTree eetree = (EndElementTree)dtree; 1660 name = eetree.getName(); 1661 } 1662 1663 if (name != null) { 1664 com.sun.tools.doclint.HtmlTag htmlTag = com.sun.tools.doclint.HtmlTag.get(name); 1665 if (htmlTag != null && 1666 htmlTag.blockType != com.sun.tools.doclint.HtmlTag.BlockType.INLINE) { 1667 return true; 1668 } 1669 } 1670 return false; 1671 } 1672 1673 boolean isAllWhiteSpace(String body) { 1674 for (int i = 0 ; i < body.length(); i++) { 1675 if (!Character.isWhitespace(body.charAt(i))) 1676 return false; 1677 } 1678 return true; 1679 } 1680 1681 /** 1682 * Converts inline tags and text to text strings, expanding the 1683 * inline tags along the way. Called wherever text can contain 1684 * an inline tag, such as in comments or in free-form text arguments 1685 * to non-inline tags. 1686 * 1687 * @param holderTag specific tag where comment resides 1688 * @param element specific element where comment resides 1689 * @param tags array of text tags and inline tags (often alternating) 1690 present in the text of interest for this element 1691 * @param isFirstSentence true if text is first sentence 1692 * @return a Content object 1693 */ 1694 public Content commentTagsToContent(DocTree holderTag, Element element, 1695 List<? extends DocTree> tags, boolean isFirstSentence) { 1696 1697 final Content result = new ContentBuilder() { 1698 @Override 1699 public void addContent(String text) { 1700 super.addContent(utils.normalizeNewlines(text)); 1701 } 1702 }; 1703 CommentHelper ch = utils.getCommentHelper(element); 1704 // Array of all possible inline tags for this javadoc run 1705 configuration.tagletManager.checkTags(utils, element, tags, true); 1706 for (ListIterator<? extends DocTree> iterator = tags.listIterator(); iterator.hasNext();) { 1707 DocTree tag = iterator.next(); 1708 // zap block tags 1709 if (isFirstSentence && ignoreNonInlineTag(tag)) 1710 continue; 1711 1712 if (isFirstSentence && iterator.nextIndex() == tags.size() && 1713 (tag.getKind() == TEXT && isAllWhiteSpace(ch.getText(tag)))) 1714 continue; 1715 1716 boolean allDone = new SimpleDocTreeVisitor<Boolean, Content>() { 1717 // notify the next DocTree handler to take necessary action 1718 boolean commentRemoved = false; 1719 1720 private boolean isLast(DocTree node) { 1721 return node.equals(tags.get(tags.size() - 1)); 1722 } 1723 1724 private boolean isFirst(DocTree node) { 1725 return node.equals(tags.get(0)); 1726 } 1727 1728 private boolean inAnAtag() { 1729 if (utils.isStartElement(tag)) { 1730 StartElementTree st = (StartElementTree)tag; 1731 Name name = st.getName(); 1732 if (name != null) { 1733 com.sun.tools.doclint.HtmlTag htag = 1734 com.sun.tools.doclint.HtmlTag.get(name); 1735 return htag != null && htag.equals(com.sun.tools.doclint.HtmlTag.A); 1736 } 1737 } 1738 return false; 1739 } 1740 1741 @Override @DefinedBy(Api.COMPILER_TREE) 1742 public Boolean visitAttribute(AttributeTree node, Content c) { 1743 StringBuilder sb = new StringBuilder(SPACER).append(node.getName()); 1744 if (node.getValueKind() == ValueKind.EMPTY) { 1745 result.addContent(sb.toString()); 1746 return false; 1747 } 1748 sb.append("="); 1749 String quote; 1750 switch (node.getValueKind()) { 1751 case DOUBLE: 1752 quote = "\""; 1753 break; 1754 case SINGLE: 1755 quote = "\'"; 1756 break; 1757 default: 1758 quote = ""; 1759 break; 1760 } 1761 sb.append(quote); 1762 result.addContent(sb.toString()); 1763 Content docRootContent = new ContentBuilder(); 1764 1765 for (DocTree dt : node.getValue()) { 1766 if (utils.isText(dt) && inAnAtag()) { 1767 String text = ((TextTree) dt).getBody(); 1768 if (text.startsWith("/..") && !configuration.docrootparent.isEmpty()) { 1769 result.addContent(configuration.docrootparent); 1770 docRootContent = new ContentBuilder(); 1771 text = textCleanup(text.substring(3), isLast(node)); 1772 } else { 1773 if (!docRootContent.isEmpty()) { 1774 docRootContent = copyDocRootContent(docRootContent); 1775 } else { 1776 text = redirectRelativeLinks(element, (TextTree) dt); 1777 } 1778 text = textCleanup(text, isLast(node)); 1779 } 1780 result.addContent(text); 1781 } else { 1782 docRootContent = copyDocRootContent(docRootContent); 1783 dt.accept(this, docRootContent); 1784 } 1785 } 1786 copyDocRootContent(docRootContent); 1787 result.addContent(quote); 1788 return false; 1789 } 1790 1791 @Override @DefinedBy(Api.COMPILER_TREE) 1792 public Boolean visitComment(CommentTree node, Content c) { 1793 if (isFirstSentence && isFirst(node)) { 1794 commentRemoved = true; 1795 return this.visit(iterator.next(), c); 1796 } 1797 result.addContent(new RawHtml(node.getBody())); 1798 return false; 1799 } 1800 1801 private Content copyDocRootContent(Content content) { 1802 if (!content.isEmpty()) { 1803 result.addContent(content); 1804 return new ContentBuilder(); 1805 } 1806 return content; 1807 } 1808 1809 @Override @DefinedBy(Api.COMPILER_TREE) 1810 public Boolean visitDocRoot(DocRootTree node, Content c) { 1811 Content docRootContent = TagletWriter.getInlineTagOutput(element, 1812 configuration.tagletManager, 1813 holderTag, 1814 node, 1815 getTagletWriterInstance(isFirstSentence)); 1816 if (c != null) { 1817 c.addContent(docRootContent); 1818 } else { 1819 result.addContent(docRootContent); 1820 } 1821 return false; 1822 } 1823 1824 @Override @DefinedBy(Api.COMPILER_TREE) 1825 public Boolean visitEndElement(EndElementTree node, Content c) { 1826 RawHtml rawHtml = new RawHtml("</" + node.getName() + ">"); 1827 result.addContent(rawHtml); 1828 return false; 1829 } 1830 1831 @Override @DefinedBy(Api.COMPILER_TREE) 1832 public Boolean visitEntity(EntityTree node, Content c) { 1833 result.addContent(new RawHtml(node.toString())); 1834 return false; 1835 } 1836 1837 @Override @DefinedBy(Api.COMPILER_TREE) 1838 public Boolean visitErroneous(ErroneousTree node, Content c) { 1839 configuration.getDocletSpecificMsg().warning(ch.getDocTreePath(node), 1840 "doclet.tag.invalid_usage", node); 1841 result.addContent(new RawHtml(node.toString())); 1842 return false; 1843 } 1844 1845 @Override @DefinedBy(Api.COMPILER_TREE) 1846 public Boolean visitInheritDoc(InheritDocTree node, Content c) { 1847 Content output = TagletWriter.getInlineTagOutput(element, 1848 configuration.tagletManager, holderTag, 1849 tag, getTagletWriterInstance(isFirstSentence)); 1850 result.addContent(output); 1851 // if we obtained the first sentence successfully, nothing more to do 1852 return (isFirstSentence && !output.isEmpty()); 1853 } 1854 1855 @Override @DefinedBy(Api.COMPILER_TREE) 1856 public Boolean visitIndex(IndexTree node, Content p) { 1857 Content output = TagletWriter.getInlineTagOutput(element, 1858 configuration.tagletManager, holderTag, tag, 1859 getTagletWriterInstance(isFirstSentence)); 1860 if (output != null) { 1861 result.addContent(output); 1862 } 1863 return false; 1864 } 1865 1866 @Override @DefinedBy(Api.COMPILER_TREE) 1867 public Boolean visitLink(LinkTree node, Content c) { 1868 // we need to pass the DocTreeImpl here, so ignore node 1869 result.addContent(seeTagToContent(element, tag)); 1870 return false; 1871 } 1872 1873 @Override @DefinedBy(Api.COMPILER_TREE) 1874 public Boolean visitLiteral(LiteralTree node, Content c) { 1875 String s = node.getBody().toString(); 1876 if (node.getKind() == CODE) { 1877 result.addContent(HtmlTree.CODE(new StringContent(utils.normalizeNewlines(s)))); 1878 1879 } else { 1880 result.addContent(s); 1881 } 1882 return false; 1883 } 1884 1885 @Override @DefinedBy(Api.COMPILER_TREE) 1886 public Boolean visitSee(SeeTree node, Content c) { 1887 // we need to pass the DocTreeImpl here, so ignore node 1888 result.addContent(seeTagToContent(element, tag)); 1889 return false; 1890 } 1891 1892 @Override @DefinedBy(Api.COMPILER_TREE) 1893 public Boolean visitStartElement(StartElementTree node, Content c) { 1894 String text = "<" + node.getName(); 1895 text = utils.normalizeNewlines(text); 1896 RawHtml rawHtml = new RawHtml(text); 1897 result.addContent(rawHtml); 1898 1899 for (DocTree dt : node.getAttributes()) { 1900 dt.accept(this, null); 1901 } 1902 result.addContent(new RawHtml(node.isSelfClosing() ? "/>" : ">")); 1903 return false; 1904 } 1905 1906 private String textCleanup(String text, boolean isLast) { 1907 return textCleanup(text, isLast, false); 1908 } 1909 1910 private String textCleanup(String text, boolean isLast, boolean trimLeader) { 1911 if (trimLeader) { 1912 text = removeLeadingWhitespace(text); 1913 } 1914 if (isFirstSentence && isLast) { 1915 text = removeTrailingWhitespace(text); 1916 } 1917 text = utils.replaceTabs(text); 1918 text = utils.normalizeNewlines(text); 1919 return text; 1920 } 1921 1922 @Override @DefinedBy(Api.COMPILER_TREE) 1923 public Boolean visitText(TextTree node, Content c) { 1924 String text = node.getBody(); 1925 text = textCleanup(text, isLast(node), commentRemoved); 1926 commentRemoved = false; 1927 result.addContent(new RawHtml(text)); 1928 return false; 1929 } 1930 1931 @Override @DefinedBy(Api.COMPILER_TREE) 1932 protected Boolean defaultAction(DocTree node, Content c) { 1933 Content output = TagletWriter.getInlineTagOutput(element, 1934 configuration.tagletManager, holderTag, tag, 1935 getTagletWriterInstance(isFirstSentence)); 1936 if (output != null) { 1937 result.addContent(output); 1938 } 1939 return false; 1940 } 1941 1942 }.visit(tag, null); 1943 if (allDone) 1944 break; 1945 } 1946 return result; 1947 } 1948 1949 private String removeTrailingWhitespace(String text) { 1950 char[] buf = text.toCharArray(); 1951 for (int i = buf.length - 1; i > 0 ; i--) { 1952 if (!Character.isWhitespace(buf[i])) 1953 return text.substring(0, i + 1); 1954 } 1955 return text; 1956 } 1957 1958 private String removeLeadingWhitespace(String text) { 1959 char[] buf = text.toCharArray(); 1960 for (int i = 0; i < buf.length; i++) { 1961 if (!Character.isWhitespace(buf[i])) { 1962 return text.substring(i); 1963 } 1964 } 1965 return text; 1966 } 1967 1968 /** 1969 * Return true if relative links should not be redirected. 1970 * 1971 * @return Return true if a relative link should not be redirected. 1972 */ 1973 private boolean shouldNotRedirectRelativeLinks() { 1974 return this instanceof AnnotationTypeWriter || 1975 this instanceof ClassWriter || 1976 this instanceof PackageSummaryWriter; 1977 } 1978 1979 /** 1980 * Suppose a piece of documentation has a relative link. When you copy 1981 * that documentation to another place such as the index or class-use page, 1982 * that relative link will no longer work. We should redirect those links 1983 * so that they will work again. 1984 * <p> 1985 * Here is the algorithm used to fix the link: 1986 * <p> 1987 * {@literal <relative link> => docRoot + <relative path to file> + <relative link> } 1988 * <p> 1989 * For example, suppose DocletEnvironment has this link: 1990 * {@literal <a href="package-summary.html">The package Page</a> } 1991 * <p> 1992 * If this link appeared in the index, we would redirect 1993 * the link like this: 1994 * 1995 * {@literal <a href="./com/sun/javadoc/package-summary.html">The package Page</a>} 1996 * 1997 * @param element the Element object whose documentation is being written. 1998 * @param text the text being written. 1999 * 2000 * @return the text, with all the relative links redirected to work. 2001 */ 2002 private String redirectRelativeLinks(Element element, TextTree tt) { 2003 String text = tt.getBody(); 2004 if (element == null || utils.isOverviewElement(element) || shouldNotRedirectRelativeLinks()) { 2005 return text; 2006 } 2007 2008 DocPath redirectPathFromRoot = new SimpleElementVisitor9<DocPath, Void>() { 2009 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2010 public DocPath visitType(TypeElement e, Void p) { 2011 return DocPath.forPackage(utils.containingPackage(e)); 2012 } 2013 2014 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2015 public DocPath visitPackage(PackageElement e, Void p) { 2016 return DocPath.forPackage(e); 2017 } 2018 2019 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2020 public DocPath visitVariable(VariableElement e, Void p) { 2021 return DocPath.forPackage(utils.containingPackage(e)); 2022 } 2023 2024 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2025 public DocPath visitExecutable(ExecutableElement e, Void p) { 2026 return DocPath.forPackage(utils.containingPackage(e)); 2027 } 2028 2029 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2030 protected DocPath defaultAction(Element e, Void p) { 2031 return null; 2032 } 2033 }.visit(element); 2034 if (redirectPathFromRoot == null) { 2035 return text; 2036 } 2037 String lower = Utils.toLowerCase(text); 2038 if (!(lower.startsWith("mailto:") 2039 || lower.startsWith("http:") 2040 || lower.startsWith("https:") 2041 || lower.startsWith("file:"))) { 2042 text = "{@" + (new DocRootTaglet()).getName() + "}/" 2043 + redirectPathFromRoot.resolve(text).getPath(); 2044 text = replaceDocRootDir(text); 2045 } 2046 return text; 2047 } 2048 2049 static final Set<String> blockTags = new HashSet<>(); 2050 static { 2051 for (HtmlTag t: HtmlTag.values()) { 2052 if (t.blockType == HtmlTag.BlockType.BLOCK) 2053 blockTags.add(t.value); 2054 } 2055 } 2056 2057 /** 2058 * Add a link to the stylesheet file. 2059 * 2060 * @param head the content tree to which the files will be added 2061 */ 2062 public void addStyleSheetProperties(Content head) { 2063 String stylesheetfile = configuration.stylesheetfile; 2064 DocPath stylesheet; 2065 if (stylesheetfile.isEmpty()) { 2066 stylesheet = DocPaths.STYLESHEET; 2067 } else { 2068 DocFile file = DocFile.createFileForInput(configuration, stylesheetfile); 2069 stylesheet = DocPath.create(file.getName()); 2070 } 2071 HtmlTree link = HtmlTree.LINK("stylesheet", "text/css", 2072 pathToRoot.resolve(stylesheet).getPath(), 2073 "Style"); 2074 head.addContent(link); 2075 if (configuration.createindex) { 2076 HtmlTree jq_link = HtmlTree.LINK("stylesheet", "text/css", 2103 } 2104 } 2105 2106 /** 2107 * Add a link to the JQuery javascript file. 2108 * 2109 * @param head the content tree to which the files will be added 2110 * @param filePath the DocPath of the file that needs to be added 2111 */ 2112 private void addJQueryFile(Content head, DocPath filePath) { 2113 HtmlTree jqyeryScriptFile = HtmlTree.SCRIPT( 2114 pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(filePath)).getPath()); 2115 head.addContent(jqyeryScriptFile); 2116 } 2117 2118 /** 2119 * According to 2120 * <cite>The Java™ Language Specification</cite>, 2121 * all the outer classes and static nested classes are core classes. 2122 */ 2123 public boolean isCoreClass(TypeElement typeElement) { 2124 return utils.getEnclosingTypeElement(typeElement) == null || utils.isStatic(typeElement); 2125 } 2126 2127 /** 2128 * Adds the annotation types for the given packageElement. 2129 * 2130 * @param packageElement the package to write annotations for. 2131 * @param htmltree the documentation tree to which the annotation info will be 2132 * added 2133 */ 2134 public void addAnnotationInfo(PackageElement packageElement, Content htmltree) { 2135 addAnnotationInfo(packageElement, packageElement.getAnnotationMirrors(), htmltree); 2136 } 2137 2138 /** 2139 * Add the annotation types of the executable receiver. 2140 * 2141 * @param method the executable to write the receiver annotations for. 2142 * @param descList list of annotation description. 2143 * @param htmltree the documentation tree to which the annotation info will be 2144 * added 2145 */ 2146 public void addReceiverAnnotationInfo(ExecutableElement method, List<AnnotationMirror> descList, 2147 Content htmltree) { 2148 addAnnotationInfo(0, method, descList, false, htmltree); 2149 } 2150 2151 /* 2152 * this is a hack to delay dealing with Annotations in the writers, the assumption 2153 * is that all necessary checks have been made to get here. 2154 */ 2155 public void addReceiverAnnotationInfo(ExecutableElement method, TypeMirror rcvrTypeMirror, 2156 List<? extends AnnotationMirror> annotationMirrors, Content htmltree) { 2157 TypeMirror rcvrType = method.getReceiverType(); 2158 List<? extends AnnotationMirror> annotationMirrors1 = rcvrType.getAnnotationMirrors(); 2159 addAnnotationInfo(0, method, annotationMirrors1, false, htmltree); 2160 } 2161 2162 /** 2163 * Adds the annotatation types for the given element. 2164 * 2165 * @param element the package to write annotations for 2166 * @param htmltree the content tree to which the annotation types will be added 2167 */ 2168 public void addAnnotationInfo(Element element, Content htmltree) { 2169 addAnnotationInfo(element, element.getAnnotationMirrors(), htmltree); 2170 } 2171 2172 /** 2173 * Add the annotatation types for the given element and parameter. 2174 * 2175 * @param indent the number of spaces to indent the parameters. 2176 * @param element the element to write annotations for. 2177 * @param param the parameter to write annotations for. 2178 * @param tree the content tree to which the annotation types will be added 2179 */ 2180 public boolean addAnnotationInfo(int indent, Element element, VariableElement param, 2181 Content tree) { 2182 return addAnnotationInfo(indent, element, param.getAnnotationMirrors(), false, tree); 2183 } 2184 2185 /** 2186 * Adds the annotatation types for the given Element. 2187 * 2188 * @param element the element to write annotations for. 2189 * @param descList the array of {@link AnnotationDesc}. 2190 * @param htmltree the documentation tree to which the annotation info will be 2191 * added 2192 */ 2193 private void addAnnotationInfo(Element element, List<? extends AnnotationMirror> descList, 2194 Content htmltree) { 2195 addAnnotationInfo(0, element, descList, true, htmltree); 2196 } 2197 2198 /** 2199 * Adds the annotation types for the given element. 2200 * 2201 * @param indent the number of extra spaces to indent the annotations. 2202 * @param element the element to write annotations for. 2203 * @param descList the array of {@link AnnotationDesc}. 2204 * @param htmltree the documentation tree to which the annotation info will be 2205 * added 2206 */ 2207 private boolean addAnnotationInfo(int indent, Element element, 2208 List<? extends AnnotationMirror> descList, boolean lineBreak, Content htmltree) { 2209 List<Content> annotations = getAnnotations(indent, descList, lineBreak); 2210 String sep = ""; 2211 if (annotations.isEmpty()) { 2212 return false; 2213 } 2214 for (Content annotation: annotations) { 2215 htmltree.addContent(sep); 2216 htmltree.addContent(annotation); 2217 if (!lineBreak) { 2218 sep = " "; 2219 } 2220 } 2221 return true; 2222 } 2223 2224 /** 2225 * Return the string representations of the annotation types for 2226 * the given doc. 2227 * 2228 * @param indent the number of extra spaces to indent the annotations. 2229 * @param descList the array of {@link AnnotationDesc}. 2230 * @param linkBreak if true, add new line between each member value. 2231 * @return an array of strings representing the annotations being 2232 * documented. 2233 */ 2234 private List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList, boolean linkBreak) { 2235 return getAnnotations(indent, descList, linkBreak, true); 2236 } 2237 2238 private List<Content> getAnnotations(int indent, AnnotationMirror amirror, boolean linkBreak) { 2239 List<AnnotationMirror> descList = new ArrayList<>(); 2240 descList.add(amirror); 2241 return getAnnotations(indent, descList, linkBreak, true); 2242 } 2243 2244 /** 2245 * Return the string representations of the annotation types for 2246 * the given doc. 2247 * 2248 * A {@code null} {@code elementType} indicates that all the 2249 * annotations should be returned without any filtering. 2250 * 2251 * @param indent the number of extra spaces to indent the annotations. 2252 * @param descList the array of {@link AnnotationDesc}. 2253 * @param linkBreak if true, add new line between each member value. 2254 * @param isJava5DeclarationLocation 2255 * @return an array of strings representing the annotations being 2256 * documented. 2257 */ 2258 public List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList, 2259 boolean linkBreak, boolean isJava5DeclarationLocation) { 2260 List<Content> results = new ArrayList<>(); 2261 ContentBuilder annotation; 2262 for (AnnotationMirror aDesc : descList) { 2263 TypeElement annotationElement = (TypeElement)aDesc.getAnnotationType().asElement(); 2264 // If an annotation is not documented, do not add it to the list. If 2265 // the annotation is of a repeatable type, and if it is not documented 2266 // and also if its container annotation is not documented, do not add it 2267 // to the list. If an annotation of a repeatable type is not documented 2268 // but its container is documented, it will be added to the list. 2269 if (!utils.isDocumentedAnnotation(annotationElement) && 2270 (!isAnnotationDocumented && !isContainerDocumented)) { 2271 continue; 2272 } 2273 /* TODO: check logic here to correctly handle declaration 2274 * and type annotations. 2275 if (utils.isDeclarationAnnotation(annotationElement, isJava5DeclarationLocation)) { 2276 continue; 2277 }*/ 2278 annotation = new ContentBuilder(); 2279 isAnnotationDocumented = false; 2280 LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, 2281 LinkInfoImpl.Kind.ANNOTATION, annotationElement); 2282 Map<? extends ExecutableElement, ? extends AnnotationValue> pairs = aDesc.getElementValues(); 2283 // If the annotation is synthesized, do not print the container. 2284 if (utils.configuration.workArounds.isSynthesized(aDesc)) { 2285 for (ExecutableElement ee : pairs.keySet()) { 2286 AnnotationValue annotationValue = pairs.get(ee); 2287 List<AnnotationValue> annotationTypeValues = new ArrayList<>(); 2288 2289 new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() { 2290 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2291 public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> p) { 2292 p.addAll(vals); 2293 return null; 2294 } 2295 2296 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2297 protected Void defaultAction(Object o, List<AnnotationValue> p) { 2298 p.add(annotationValue); 2299 return null; 2300 } 2301 }.visit(annotationValue, annotationTypeValues); 2302 2303 String sep = ""; 2304 for (AnnotationValue av : annotationTypeValues) { 2305 annotation.addContent(sep); 2306 annotation.addContent(annotationValueToContent(av)); 2307 sep = " "; 2308 } 2309 } 2310 } else if (isAnnotationArray(pairs)) { 2311 // If the container has 1 or more value defined and if the 2312 // repeatable type annotation is not documented, do not print 2313 // the container. 2314 if (pairs.size() == 1 && isAnnotationDocumented) { 2315 List<AnnotationValue> annotationTypeValues = new ArrayList<>(); 2316 for (AnnotationValue a : pairs.values()) { 2317 new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() { 2318 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2319 public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> annotationTypeValues) { 2320 for (AnnotationValue av : vals) { 2321 annotationTypeValues.add(av); 2322 } 2323 return null; 2324 } 2325 }.visit(a, annotationTypeValues); 2326 } 2327 String sep = ""; 2328 for (AnnotationValue av : annotationTypeValues) { 2329 annotation.addContent(sep); 2330 annotation.addContent(annotationValueToContent(av)); 2331 sep = " "; 2332 } 2333 } 2334 // If the container has 1 or more value defined and if the 2335 // repeatable type annotation is not documented, print the container. 2336 else { 2337 addAnnotations(annotationElement, linkInfo, annotation, pairs, 2338 indent, false); 2339 } 2340 } 2341 else { 2342 addAnnotations(annotationElement, linkInfo, annotation, pairs, 2343 indent, linkBreak); 2344 } 2345 annotation.addContent(linkBreak ? DocletConstants.NL : ""); 2346 results.add(annotation); 2347 } 2348 return results; 2349 } 2350 2351 /** 2352 * Add annotation to the annotation string. 2353 * 2354 * @param annotationDoc the annotation being documented 2355 * @param linkInfo the information about the link 2356 * @param annotation the annotation string to which the annotation will be added 2357 * @param pairs annotation type element and value pairs 2358 * @param indent the number of extra spaces to indent the annotations. 2359 * @param linkBreak if true, add new line between each member value 2360 */ 2361 private void addAnnotations(TypeElement annotationDoc, LinkInfoImpl linkInfo, 2362 ContentBuilder annotation, Map<? extends ExecutableElement,? extends AnnotationValue>map, 2363 int indent, boolean linkBreak) { 2364 linkInfo.label = new StringContent("@" + annotationDoc.getSimpleName().toString()); 2365 annotation.addContent(getLink(linkInfo)); 2366 if (!map.isEmpty()) { 2367 annotation.addContent("("); 2368 boolean isFirst = true; 2369 for (ExecutableElement element : map.keySet()) { 2370 if (isFirst) { 2371 isFirst = false; 2372 } else { 2373 annotation.addContent(","); 2374 if (linkBreak) { 2375 annotation.addContent(DocletConstants.NL); 2376 int spaces = annotationDoc.getSimpleName().toString().length() + 2; 2377 for (int k = 0; k < (spaces + indent); k++) { 2378 annotation.addContent(" "); 2379 } 2380 } 2381 } 2382 annotation.addContent(getDocLink(LinkInfoImpl.Kind.ANNOTATION, 2383 element, element.getSimpleName().toString(), false)); 2384 annotation.addContent("="); 2385 AnnotationValue annotationValue = map.get(element); 2386 List<AnnotationValue> annotationTypeValues = new ArrayList<>(); 2387 new SimpleAnnotationValueVisitor9<Void, AnnotationValue>() { 2388 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2389 public Void visitArray(List<? extends AnnotationValue> vals, AnnotationValue p) { 2390 annotationTypeValues.addAll(vals); 2391 return null; 2392 } 2393 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2394 protected Void defaultAction(Object o, AnnotationValue p) { 2395 annotationTypeValues.add(p); 2396 return null; 2397 } 2398 }.visit(annotationValue, annotationValue); 2399 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "{"); 2400 String sep = ""; 2401 for (AnnotationValue av : annotationTypeValues) { 2402 annotation.addContent(sep); 2403 annotation.addContent(annotationValueToContent(av)); 2404 sep = ","; 2405 } 2406 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "}"); 2407 isContainerDocumented = false; 2408 } 2409 annotation.addContent(")"); 2410 } 2411 } 2412 2413 /** 2414 * Check if the annotation contains an array of annotation as a value. This 2415 * check is to verify if a repeatable type annotation is present or not. 2416 * 2417 * @param pairs annotation type element and value pairs 2418 * 2419 * @return true if the annotation contains an array of annotation as a value. 2420 */ 2421 private boolean isAnnotationArray(Map<? extends ExecutableElement, ? extends AnnotationValue> pairs) { 2422 AnnotationValue annotationValue; 2423 for (ExecutableElement ee : pairs.keySet()) { 2424 annotationValue = pairs.get(ee); 2425 boolean rvalue = new SimpleAnnotationValueVisitor9<Boolean, Void>() { 2426 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2427 public Boolean visitArray(List<? extends AnnotationValue> vals, Void p) { 2428 if (vals.size() > 1) { 2429 if (vals.get(0) instanceof AnnotationMirror) { 2430 isContainerDocumented = true; 2431 return new SimpleAnnotationValueVisitor9<Boolean, Void>() { 2432 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2433 public Boolean visitAnnotation(AnnotationMirror a, Void p) { 2434 isContainerDocumented = true; 2435 Element asElement = a.getAnnotationType().asElement(); 2436 if (utils.isDocumentedAnnotation((TypeElement)asElement)) { 2437 isAnnotationDocumented = true; 2438 } 2439 return true; 2440 } 2441 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2442 protected Boolean defaultAction(Object o, Void p) { 2443 return false; 2444 } 2445 }.visit(vals.get(0)); 2446 } 2447 } 2448 return false; 2449 } 2450 2451 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2452 protected Boolean defaultAction(Object o, Void p) { 2453 return false; 2454 } 2455 }.visit(annotationValue); 2456 if (rvalue) { 2457 return true; 2458 } 2459 } 2460 return false; 2461 } 2462 2463 private Content annotationValueToContent(AnnotationValue annotationValue) { 2464 return new SimpleAnnotationValueVisitor9<Content, Void>() { 2465 2466 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2467 public Content visitType(TypeMirror t, Void p) { 2468 return new SimpleTypeVisitor9<Content, Void>() { 2469 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2470 public Content visitDeclared(DeclaredType t, Void p) { 2471 LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, 2472 LinkInfoImpl.Kind.ANNOTATION, t); 2473 String name = utils.isIncluded(t.asElement()) 2474 ? t.asElement().getSimpleName().toString() 2475 : utils.getFullyQualifiedName(t.asElement()); 2476 linkInfo.label = new StringContent(name + utils.getDimension(t) + ".class"); 2477 return getLink(linkInfo); 2478 } 2479 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2480 protected Content defaultAction(TypeMirror e, Void p) { 2481 return new StringContent(t + utils.getDimension(t) + ".class"); 2482 } 2483 }.visit(t); 2484 } 2485 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2486 public Content visitAnnotation(AnnotationMirror a, Void p) { 2487 List<Content> list = getAnnotations(0, a, false); 2488 ContentBuilder buf = new ContentBuilder(); 2489 for (Content c : list) { 2490 buf.addContent(c); 2491 } 2492 return buf; 2493 } 2494 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2495 public Content visitEnumConstant(VariableElement c, Void p) { 2496 return getDocLink(LinkInfoImpl.Kind.ANNOTATION, 2497 c, c.getSimpleName().toString(), false); 2498 } 2499 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2500 public Content visitArray(List<? extends AnnotationValue> vals, Void p) { 2501 ContentBuilder buf = new ContentBuilder(); 2502 String sep = ""; 2503 for (AnnotationValue av : vals) { 2504 buf.addContent(sep); 2505 buf.addContent(visit(av)); 2506 sep = " "; 2507 } 2508 return buf; 2509 } 2510 @Override @DefinedBy(Api.LANGUAGE_MODEL) 2511 protected Content defaultAction(Object o, Void p) { 2512 return new StringContent(annotationValue.toString()); 2513 } 2514 }.visit(annotationValue); 2515 } 2516 2517 /** 2518 * Return the configuration for this doclet. 2519 * 2520 * @return the configuration for this doclet. 2521 */ 2522 public Configuration configuration() { 2523 return configuration; 2524 } 2525 } |