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