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