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 code true if the label should be code font. 1093 * @return the link 1094 */ 1095 public Content getCrossClassLink(String qualifiedClassName, String refMemName, 1096 Content label, boolean strong, boolean code) { 1097 String className = ""; 1098 String packageName = qualifiedClassName == null ? "" : qualifiedClassName; 1099 int periodIndex; 1100 while ((periodIndex = packageName.lastIndexOf('.')) != -1) { 1101 className = packageName.substring(periodIndex + 1, packageName.length()) + 1102 (className.length() > 0 ? "." + className : ""); 1103 Content defaultLabel = new StringContent(className); 1104 if (code) 1105 defaultLabel = HtmlTree.CODE(defaultLabel); 1106 packageName = packageName.substring(0, periodIndex); 1107 if (getCrossPackageLink(packageName) != null) { 1108 /* 1109 The package exists in external documentation, so link to the external 1110 class (assuming that it exists). This is definitely a limitation of 1111 the -link option. There are ways to determine if an external package 1112 exists, but no way to determine if the external class exists. We just 1113 have to assume that it does. 1114 */ 1115 DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot, 1116 className + ".html", refMemName); 1117 return Links.createLink(link, 1118 (label == null) || label.isEmpty() ? defaultLabel : label, 1119 strong, 1120 resources.getText("doclet.Href_Class_Or_Interface_Title", packageName), 1121 ""); 1122 } 1123 } 1124 return null; 1125 } 1126 1127 public boolean isClassLinkable(TypeElement typeElement) { 1128 if (utils.isIncluded(typeElement)) { 1129 return configuration.isGeneratedDoc(typeElement); 1130 } 1131 return configuration.extern.isExternal(typeElement); 1132 } 1133 1134 public DocLink getCrossPackageLink(String pkgName) { 1135 return configuration.extern.getExternalLink(pkgName, pathToRoot, 1136 DocPaths.PACKAGE_SUMMARY.getPath()); 1137 } 1138 1139 /** 1140 * Get the class link. 1141 * 1142 * @param context the id of the context where the link will be added 1143 * @param element to link to 1144 * @return a content tree for the link 1145 */ 1146 public Content getQualifiedClassLink(LinkInfoImpl.Kind context, Element element) { 1147 LinkInfoImpl linkInfoImpl = new LinkInfoImpl(configuration, context, (TypeElement)element); 1148 return getLink(linkInfoImpl.label(utils.getFullyQualifiedName(element))); 1149 } 1150 1151 /** 1152 * Add the class link. 1153 * 1154 * @param context the id of the context where the link will be added 1155 * @param typeElement to link to 1156 * @param contentTree the content tree to which the link will be added 1157 */ 1158 public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) { 1159 addPreQualifiedClassLink(context, typeElement, false, contentTree); 1160 } 1161 1162 /** 1163 * Retrieve the class link with the package portion of the label in 1164 * plain text. If the qualifier is excluded, it will not be included in the 1165 * link label. 1166 * 1167 * @param typeElement the class to link to. 1168 * @param isStrong true if the link should be strong. 1169 * @return the link with the package portion of the label in plain text. 1170 */ 1171 public Content getPreQualifiedClassLink(LinkInfoImpl.Kind context, 1172 TypeElement typeElement, boolean isStrong) { 1173 ContentBuilder classlink = new ContentBuilder(); 1174 PackageElement pkg = utils.containingPackage(typeElement); 1175 if (pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) { 1176 classlink.addContent(getEnclosingPackageName(typeElement)); 1177 } 1178 classlink.addContent(getLink(new LinkInfoImpl(configuration, 1179 context, typeElement).label(utils.getSimpleName(typeElement)).strong(isStrong))); 1180 return classlink; 1181 } 1182 1183 /** 1184 * Add the class link with the package portion of the label in 1185 * plain text. If the qualifier is excluded, it will not be included in the 1186 * link label. 1187 * 1188 * @param context the id of the context where the link will be added 1189 * @param typeElement the class to link to 1190 * @param isStrong true if the link should be strong 1191 * @param contentTree the content tree to which the link with be added 1192 */ 1193 public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, 1194 TypeElement typeElement, boolean isStrong, Content contentTree) { 1195 PackageElement pkg = utils.containingPackage(typeElement); 1196 if(pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) { 1197 contentTree.addContent(getEnclosingPackageName(typeElement)); 1198 } 1199 LinkInfoImpl linkinfo = new LinkInfoImpl(configuration, context, typeElement) 1200 .label(utils.getSimpleName(typeElement)) 1201 .strong(isStrong); 1202 Content link = getLink(linkinfo); 1203 contentTree.addContent(link); 1204 } 1205 1206 /** 1207 * Add the class link, with only class name as the strong link and prefixing 1208 * plain package name. 1209 * 1210 * @param context the id of the context where the link will be added 1211 * @param typeElement the class to link to 1212 * @param contentTree the content tree to which the link with be added 1213 */ 1214 public void addPreQualifiedStrongClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) { 1215 addPreQualifiedClassLink(context, typeElement, true, contentTree); 1216 } 1217 1218 /** 1219 * Get the link for the given member. 1220 * 1221 * @param context the id of the context where the link will be added 1222 * @param element the member being linked to 1223 * @param label the label for the link 1224 * @return a content tree for the element link 1225 */ 1226 public Content getDocLink(LinkInfoImpl.Kind context, Element element, CharSequence label) { 1227 return getDocLink(context, utils.getEnclosingTypeElement(element), element, 1228 new StringContent(label)); 1229 } 1230 1231 /** 1232 * Return the link for the given member. 1233 * 1234 * @param context the id of the context where the link will be printed. 1235 * @param element the member being linked to. 1236 * @param label the label for the link. 1237 * @param strong true if the link should be strong. 1238 * @return the link for the given member. 1239 */ 1240 public Content getDocLink(LinkInfoImpl.Kind context, Element element, CharSequence label, 1241 boolean strong) { 1242 return getDocLink(context, utils.getEnclosingTypeElement(element), element, label, strong); 1243 } 1244 1245 /** 1246 * Return the link for the given member. 1247 * 1248 * @param context the id of the context where the link will be printed. 1249 * @param typeElement the typeElement that we should link to. This is not 1250 necessarily equal to element.containingClass(). We may be 1251 inheriting comments. 1252 * @param element the member being linked to. 1253 * @param label the label for the link. 1254 * @param strong true if the link should be strong. 1255 * @return the link for the given member. 1256 */ 1257 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1258 CharSequence label, boolean strong) { 1259 return getDocLink(context, typeElement, element, label, strong, false); 1260 } 1261 1262 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1263 Content label, boolean strong) { 1264 return getDocLink(context, typeElement, element, label, strong, false); 1265 } 1266 1267 /** 1268 * Return the link for the given member. 1269 * 1270 * @param context the id of the context where the link will be printed. 1271 * @param typeElement the typeElement that we should link to. This is not 1272 necessarily equal to element.containingClass(). We may be 1273 inheriting comments. 1274 * @param element the member being linked to. 1275 * @param label the label for the link. 1276 * @param strong true if the link should be strong. 1277 * @param isProperty true if the element parameter is a JavaFX property. 1278 * @return the link for the given member. 1279 */ 1280 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1281 CharSequence label, boolean strong, boolean isProperty) { 1282 return getDocLink(context, typeElement, element, new StringContent(check(label)), strong, isProperty); 1283 } 1284 1285 CharSequence check(CharSequence s) { 1286 Matcher m = IMPROPER_HTML_CHARS.matcher(s); 1287 if (m.matches()) { 1288 throw new IllegalArgumentException(s.toString()); 1289 } 1290 return s; 1291 } 1292 1293 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1294 Content label, boolean strong, boolean isProperty) { 1295 if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) { 1296 return label; 1297 } else if (utils.isExecutableElement(element)) { 1298 ExecutableElement ee = (ExecutableElement)element; 1299 return getLink(new LinkInfoImpl(configuration, context, typeElement) 1300 .label(label) 1301 .where(links.getName(getAnchor(ee, isProperty))) 1302 .strong(strong)); 1303 } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) { 1304 return getLink(new LinkInfoImpl(configuration, context, typeElement) 1305 .label(label) 1306 .where(links.getName(element.getSimpleName().toString())) 1307 .strong(strong)); 1308 } else { 1309 return label; 1310 } 1311 } 1312 1313 /** 1314 * Return the link for the given member. 1315 * 1316 * @param context the id of the context where the link will be added 1317 * @param typeElement the typeElement that we should link to. This is not 1318 necessarily equal to element.containingClass(). We may be 1319 inheriting comments 1320 * @param element the member being linked to 1321 * @param label the label for the link 1322 * @return the link for the given member 1323 */ 1324 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1325 Content label) { 1326 if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) { 1327 return label; 1328 } else if (utils.isExecutableElement(element)) { 1329 ExecutableElement emd = (ExecutableElement) element; 1330 return getLink(new LinkInfoImpl(configuration, context, typeElement) 1331 .label(label) 1332 .where(links.getName(getAnchor(emd)))); 1333 } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) { 1334 return getLink(new LinkInfoImpl(configuration, context, typeElement) 1335 .label(label).where(links.getName(element.getSimpleName().toString()))); 1336 } else { 1337 return label; 1338 } 1339 } 1340 1341 public String getAnchor(ExecutableElement executableElement) { 1342 return getAnchor(executableElement, false); 1343 } 1344 1345 public String getAnchor(ExecutableElement executableElement, boolean isProperty) { 1346 if (isProperty) { 1347 return executableElement.getSimpleName().toString(); 1348 } 1349 String member = anchorName(executableElement); 1350 String erasedSignature = utils.makeSignature(executableElement, true, true); 1351 return member + erasedSignature; 1352 } 1353 1354 public String anchorName(Element member) { 1355 if (member.getKind() == ElementKind.CONSTRUCTOR 1356 && configuration.isOutputHtml5()) { 1357 return "<init>"; 1358 } else { 1359 return utils.getSimpleName(member); 1360 } 1361 } 1362 1363 public Content seeTagToContent(Element element, DocTree see) { 1364 1365 Kind kind = see.getKind(); 1366 if (!(kind == LINK || kind == SEE || kind == LINK_PLAIN)) { 1367 return new ContentBuilder(); 1368 } 1369 1370 CommentHelper ch = utils.getCommentHelper(element); 1371 String tagName = ch.getTagName(see); 1372 String seetext = replaceDocRootDir(utils.normalizeNewlines(ch.getText(see)).toString()); 1373 // Check if @see is an href or "string" 1374 if (seetext.startsWith("<") || seetext.startsWith("\"")) { 1375 return new RawHtml(seetext); 1376 } 1377 boolean isLinkPlain = kind == LINK_PLAIN; 1378 Content label = plainOrCode(isLinkPlain, new RawHtml(ch.getLabel(configuration, see))); 1379 1380 //The text from the @see tag. We will output this text when a label is not specified. 1381 Content text = plainOrCode(kind == LINK_PLAIN, new RawHtml(seetext)); 1382 1383 TypeElement refClass = ch.getReferencedClass(configuration, see); 1384 String refClassName = ch.getReferencedClassName(configuration, see); 1385 Element refMem = ch.getReferencedMember(configuration, see); 1386 String refMemName = ch.getReferencedMemberName(see); 1387 1388 if (refMemName == null && refMem != null) { 1389 refMemName = refMem.toString(); 1390 } 1391 if (refClass == null) { 1392 //@see is not referencing an included class 1393 PackageElement refPackage = ch.getReferencedPackage(configuration, see); 1394 if (refPackage != null && utils.isIncluded(refPackage)) { 1395 //@see is referencing an included package 1396 if (label.isEmpty()) 1397 label = plainOrCode(isLinkPlain, 1398 new StringContent(refPackage.getQualifiedName())); 1399 return getPackageLink(refPackage, label); 1400 } else { 1401 // @see is not referencing an included class or package. Check for cross links. 1402 Content classCrossLink; 1403 DocLink packageCrossLink = getCrossPackageLink(refClassName); 1404 if (packageCrossLink != null) { 1405 // Package cross link found 1406 return Links.createLink(packageCrossLink, 1407 (label.isEmpty() ? text : label)); 1408 } else if ((classCrossLink = getCrossClassLink(refClassName, 1409 refMemName, label, false, !isLinkPlain)) != null) { 1410 // Class cross link found (possibly to a member in the class) 1411 return classCrossLink; 1412 } else { 1413 // No cross link found so print warning 1414 messages.warning(ch.getDocTreePath(see), 1415 "doclet.see.class_or_package_not_found", 1416 "@" + tagName, 1417 seetext); 1418 return (label.isEmpty() ? text: label); 1419 } 1420 } 1421 } else if (refMemName == null) { 1422 // Must be a class reference since refClass is not null and refMemName is null. 1423 if (label.isEmpty()) { 1424 /* 1425 * it seems to me this is the right thing to do, but it causes comparator failures. 1426 */ 1427 if (!configuration.backwardCompatibility) { 1428 StringContent content = utils.isEnclosingPackageIncluded(refClass) 1429 ? new StringContent(utils.getSimpleName(refClass)) 1430 : new StringContent(utils.getFullyQualifiedName(refClass)); 1431 label = plainOrCode(isLinkPlain, content); 1432 } else { 1433 label = plainOrCode(isLinkPlain, 1434 new StringContent(utils.getSimpleName(refClass))); 1435 } 1436 1437 } 1438 return getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, refClass) 1439 .label(label)); 1440 } else if (refMem == null) { 1441 // Must be a member reference since refClass is not null and refMemName is not null. 1442 // However, refMem is null, so this referenced member does not exist. 1443 return (label.isEmpty() ? text: label); 1444 } else { 1445 // Must be a member reference since refClass is not null and refMemName is not null. 1446 // refMem is not null, so this @see tag must be referencing a valid member. 1447 TypeElement containing = utils.getEnclosingTypeElement(refMem); 1448 1449 // Find the enclosing type where the method is actually visible 1450 // in the inheritance hierarchy. 1451 if (refMem.getKind() == ElementKind.METHOD) { 1452 VisibleMemberMap vmm = configuration.getVisibleMemberMap(containing, 1453 VisibleMemberMap.Kind.METHODS); 1454 ExecutableElement overriddenMethod = vmm.getVisibleMethod((ExecutableElement)refMem); 1455 if (overriddenMethod != null) 1456 containing = utils.getEnclosingTypeElement(overriddenMethod); 1457 } 1458 if (ch.getText(see).trim().startsWith("#") && 1459 ! (utils.isPublic(containing) || utils.isLinkable(containing))) { 1460 // Since the link is relative and the holder is not even being 1461 // documented, this must be an inherited link. Redirect it. 1462 // The current class either overrides the referenced member or 1463 // inherits it automatically. 1464 if (this instanceof ClassWriterImpl) { 1465 containing = ((ClassWriterImpl) this).getTypeElement(); 1466 } else if (!utils.isPublic(containing)) { 1467 messages.warning( 1468 ch.getDocTreePath(see), "doclet.see.class_or_package_not_accessible", 1469 tagName, utils.getFullyQualifiedName(containing)); 1470 } else { 1471 messages.warning( 1472 ch.getDocTreePath(see), "doclet.see.class_or_package_not_found", 1473 tagName, seetext); 1474 } 1475 } 1476 if (configuration.currentTypeElement != containing) { 1477 refMemName = (utils.isConstructor(refMem)) 1478 ? refMemName 1479 : utils.getSimpleName(containing) + "." + refMemName; 1480 } 1481 if (utils.isExecutableElement(refMem)) { 1482 if (refMemName.indexOf('(') < 0) { 1483 refMemName += utils.makeSignature((ExecutableElement)refMem, true); 1484 } 1485 } 1486 1487 text = plainOrCode(kind == LINK_PLAIN, new StringContent(refMemName)); 1488 1489 return getDocLink(LinkInfoImpl.Kind.SEE_TAG, containing, 1490 refMem, (label.isEmpty() ? text: label), false); 1491 } 1492 } 1493 1494 private Content plainOrCode(boolean plain, Content body) { 1495 return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body); 1496 } 1497 1498 /** 1499 * Add the inline comment. 1500 * 1501 * @param element the Element for which the inline comment will be added 1502 * @param tag the inline tag to be added 1503 * @param htmltree the content tree to which the comment will be added 1504 */ 1505 public void addInlineComment(Element element, DocTree tag, Content htmltree) { 1506 CommentHelper ch = utils.getCommentHelper(element); 1507 List<? extends DocTree> description = ch.getDescription(configuration, tag); 1508 addCommentTags(element, tag, description, false, false, htmltree); 1509 } 1510 1511 /** 1512 * Get the deprecated phrase as content. 1513 * 1514 * @param e the Element for which the inline deprecated comment will be added 1515 * @return a content tree for the deprecated phrase. 1516 */ 1517 public Content getDeprecatedPhrase(Element e) { 1518 return (utils.isDeprecatedForRemoval(e)) 1519 ? contents.deprecatedForRemovalPhrase 1520 : contents.deprecatedPhrase; 1521 } 1522 1523 /** 1524 * Add the inline deprecated comment. 1525 * 1526 * @param e the Element for which the inline deprecated comment will be added 1527 * @param tag the inline tag to be added 1528 * @param htmltree the content tree to which the comment will be added 1529 */ 1530 public void addInlineDeprecatedComment(Element e, DocTree tag, Content htmltree) { 1531 CommentHelper ch = utils.getCommentHelper(e); 1532 addCommentTags(e, ch.getBody(configuration, tag), true, false, htmltree); 1533 } 1534 1535 /** 1536 * Adds the summary content. 1537 * 1538 * @param element the Element for which the summary will be generated 1539 * @param htmltree the documentation tree to which the summary will be added 1540 */ 1541 public void addSummaryComment(Element element, Content htmltree) { 1542 addSummaryComment(element, utils.getFirstSentenceTrees(element), htmltree); 1543 } 1544 1545 /** 1546 * Adds the summary content. 1547 * 1548 * @param element the Element for which the summary will be generated 1549 * @param firstSentenceTags the first sentence tags for the doc 1550 * @param htmltree the documentation tree to which the summary will be added 1551 */ 1552 public void addSummaryComment(Element element, List<? extends DocTree> firstSentenceTags, Content htmltree) { 1553 addCommentTags(element, firstSentenceTags, false, true, htmltree); 1554 } 1555 1556 public void addSummaryDeprecatedComment(Element element, DocTree tag, Content htmltree) { 1557 CommentHelper ch = utils.getCommentHelper(element); 1558 List<? extends DocTree> body = ch.getBody(configuration, tag); 1559 addCommentTags(element, ch.getFirstSentenceTrees(configuration, body), true, true, htmltree); 1560 } 1561 1562 /** 1563 * Adds the inline comment. 1564 * 1565 * @param element the Element for which the inline comments will be generated 1566 * @param htmltree the documentation tree to which the inline comments will be added 1567 */ 1568 public void addInlineComment(Element element, Content htmltree) { 1569 addCommentTags(element, utils.getFullBody(element), false, false, htmltree); 1570 } 1571 1572 /** 1573 * Adds the comment tags. 1574 * 1575 * @param element the Element for which the comment tags will be generated 1576 * @param tags the first sentence tags for the doc 1577 * @param depr true if it is deprecated 1578 * @param first true if the first sentence tags should be added 1579 * @param htmltree the documentation tree to which the comment tags will be added 1580 */ 1581 private void addCommentTags(Element element, List<? extends DocTree> tags, boolean depr, 1582 boolean first, Content htmltree) { 1583 addCommentTags(element, null, tags, depr, first, htmltree); 1584 } 1585 1586 /** 1587 * Adds the comment tags. 1588 * 1589 * @param element for which the comment tags will be generated 1590 * @param holderTag the block tag context for the inline tags 1591 * @param tags the first sentence tags for the doc 1592 * @param depr true if it is deprecated 1593 * @param first true if the first sentence tags should be added 1594 * @param htmltree the documentation tree to which the comment tags will be added 1595 */ 1596 private void addCommentTags(Element element, DocTree holderTag, List<? extends DocTree> tags, boolean depr, 1597 boolean first, Content htmltree) { 1598 if(configuration.nocomment){ 1599 return; 1600 } 1601 Content div; 1602 Content result = commentTagsToContent(null, element, tags, first); 1603 if (depr) { 1604 div = HtmlTree.DIV(HtmlStyle.deprecationComment, result); 1605 htmltree.addContent(div); 1606 } 1607 else { 1608 div = HtmlTree.DIV(HtmlStyle.block, result); 1609 htmltree.addContent(div); 1610 } 1611 if (tags.isEmpty()) { 1612 htmltree.addContent(Contents.SPACE); 1613 } 1614 } 1615 1616 boolean ignoreNonInlineTag(DocTree dtree) { 1617 Name name = null; 1618 if (dtree.getKind() == Kind.START_ELEMENT) { 1619 StartElementTree setree = (StartElementTree)dtree; 1620 name = setree.getName(); 1621 } else if (dtree.getKind() == Kind.END_ELEMENT) { 1622 EndElementTree eetree = (EndElementTree)dtree; 1623 name = eetree.getName(); 1624 } 1625 1626 if (name != null) { 1627 com.sun.tools.doclint.HtmlTag htmlTag = com.sun.tools.doclint.HtmlTag.get(name); 1628 if (htmlTag != null && 1629 htmlTag.blockType != com.sun.tools.doclint.HtmlTag.BlockType.INLINE) { 1630 return true; 1631 } 1632 } 1633 return false; 1634 } 1635 1636 boolean isAllWhiteSpace(String body) { 1637 for (int i = 0 ; i < body.length(); i++) { 1638 if (!Character.isWhitespace(body.charAt(i))) 1639 return false; 1640 } 1641 return true; 1642 } 1643 1644 // Notify the next DocTree handler to take necessary action 1645 private boolean commentRemoved = false; 1646 1647 /** 1648 * Converts inline tags and text to text strings, expanding the 1649 * inline tags along the way. Called wherever text can contain 1650 * an inline tag, such as in comments or in free-form text arguments 1651 * to non-inline tags. 1652 * 1653 * @param holderTag specific tag where comment resides 1654 * @param element specific element where comment resides 1655 * @param tags array of text tags and inline tags (often alternating) 1656 present in the text of interest for this element 1657 * @param isFirstSentence true if text is first sentence 1658 * @return a Content object 1659 */ 1660 public Content commentTagsToContent(DocTree holderTag, Element element, 1661 List<? extends DocTree> tags, boolean isFirstSentence) { 1662 1663 final Content result = new ContentBuilder() { 1664 @Override 1665 public void addContent(CharSequence text) { 1666 super.addContent(utils.normalizeNewlines(text)); 1667 } 1668 }; 1669 CommentHelper ch = utils.getCommentHelper(element); 1670 // Array of all possible inline tags for this javadoc run 1671 configuration.tagletManager.checkTags(utils, element, tags, true); 1672 commentRemoved = false; 1673 1674 for (ListIterator<? extends DocTree> iterator = tags.listIterator(); iterator.hasNext();) { 1675 boolean isFirstNode = !iterator.hasPrevious(); 1676 DocTree tag = iterator.next(); 1677 boolean isLastNode = !iterator.hasNext(); 1678 1679 if (isFirstSentence) { 1680 // Ignore block tags 1681 if (ignoreNonInlineTag(tag)) 1682 continue; 1683 1684 // Ignore any trailing whitespace OR whitespace after removed html comment 1685 if ((isLastNode || commentRemoved) 1686 && tag.getKind() == TEXT 1687 && isAllWhiteSpace(ch.getText(tag))) 1688 continue; 1689 1690 // Ignore any leading html comments 1691 if ((isFirstNode || commentRemoved) && tag.getKind() == COMMENT) { 1692 commentRemoved = true; 1693 continue; 1694 } 1695 } 1696 1697 boolean allDone = new SimpleDocTreeVisitor<Boolean, Content>() { 1698 1699 private boolean inAnAtag() { 1700 if (utils.isStartElement(tag)) { 1701 StartElementTree st = (StartElementTree)tag; 1702 Name name = st.getName(); 1703 if (name != null) { 1704 com.sun.tools.doclint.HtmlTag htag = 1705 com.sun.tools.doclint.HtmlTag.get(name); 1706 return htag != null && htag.equals(com.sun.tools.doclint.HtmlTag.A); 1707 } 1708 } 1709 return false; 1710 } 1711 1712 @Override 1713 public Boolean visitAttribute(AttributeTree node, Content c) { 1714 StringBuilder sb = new StringBuilder(SPACER).append(node.getName()); 1715 if (node.getValueKind() == ValueKind.EMPTY) { 1716 result.addContent(sb); 1717 return false; 1718 } 1719 sb.append("="); 1720 String quote; 1721 switch (node.getValueKind()) { 1722 case DOUBLE: 1723 quote = "\""; 1724 break; 1725 case SINGLE: 1726 quote = "\'"; 1727 break; 1728 default: 1729 quote = ""; 1730 break; 1731 } 1732 sb.append(quote); 1733 result.addContent(sb); 1734 Content docRootContent = new ContentBuilder(); 1735 1736 for (DocTree dt : node.getValue()) { 1737 if (utils.isText(dt) && inAnAtag()) { 1738 String text = ((TextTree) dt).getBody(); 1739 if (text.startsWith("/..") && !configuration.docrootparent.isEmpty()) { 1740 result.addContent(configuration.docrootparent); 1741 docRootContent = new ContentBuilder(); 1742 result.addContent(textCleanup(text.substring(3), isLastNode)); 1743 } else { 1744 if (!docRootContent.isEmpty()) { 1745 docRootContent = copyDocRootContent(docRootContent); 1746 } else { 1747 text = redirectRelativeLinks(element, (TextTree) dt); 1748 } 1749 result.addContent(textCleanup(text, isLastNode)); 1750 } 1751 } else { 1752 docRootContent = copyDocRootContent(docRootContent); 1753 dt.accept(this, docRootContent); 1754 } 1755 } 1756 copyDocRootContent(docRootContent); 1757 result.addContent(quote); 1758 return false; 1759 } 1760 1761 @Override 1762 public Boolean visitComment(CommentTree node, Content c) { 1763 result.addContent(new RawHtml(node.getBody())); 1764 return false; 1765 } 1766 1767 private Content copyDocRootContent(Content content) { 1768 if (!content.isEmpty()) { 1769 result.addContent(content); 1770 return new ContentBuilder(); 1771 } 1772 return content; 1773 } 1774 1775 @Override 1776 public Boolean visitDocRoot(DocRootTree node, Content c) { 1777 Content docRootContent = TagletWriter.getInlineTagOutput(element, 1778 configuration.tagletManager, 1779 holderTag, 1780 node, 1781 getTagletWriterInstance(isFirstSentence)); 1782 if (c != null) { 1783 c.addContent(docRootContent); 1784 } else { 1785 result.addContent(docRootContent); 1786 } 1787 return false; 1788 } 1789 1790 @Override 1791 public Boolean visitEndElement(EndElementTree node, Content c) { 1792 RawHtml rawHtml = new RawHtml("</" + node.getName() + ">"); 1793 result.addContent(rawHtml); 1794 return false; 1795 } 1796 1797 @Override 1798 public Boolean visitEntity(EntityTree node, Content c) { 1799 result.addContent(new RawHtml(node.toString())); 1800 return false; 1801 } 1802 1803 @Override 1804 public Boolean visitErroneous(ErroneousTree node, Content c) { 1805 messages.warning(ch.getDocTreePath(node), 1806 "doclet.tag.invalid_usage", node); 1807 result.addContent(new RawHtml(node.toString())); 1808 return false; 1809 } 1810 1811 @Override 1812 public Boolean visitInheritDoc(InheritDocTree node, Content c) { 1813 Content output = TagletWriter.getInlineTagOutput(element, 1814 configuration.tagletManager, holderTag, 1815 tag, getTagletWriterInstance(isFirstSentence)); 1816 result.addContent(output); 1817 // if we obtained the first sentence successfully, nothing more to do 1818 return (isFirstSentence && !output.isEmpty()); 1819 } 1820 1821 @Override 1822 public Boolean visitIndex(IndexTree node, Content p) { 1823 Content output = TagletWriter.getInlineTagOutput(element, 1824 configuration.tagletManager, holderTag, tag, 1825 getTagletWriterInstance(isFirstSentence)); 1826 if (output != null) { 1827 result.addContent(output); 1828 } 1829 return false; 1830 } 1831 1832 @Override 1833 public Boolean visitLink(LinkTree node, Content c) { 1834 // we need to pass the DocTreeImpl here, so ignore node 1835 result.addContent(seeTagToContent(element, tag)); 1836 return false; 1837 } 1838 1839 @Override 1840 public Boolean visitLiteral(LiteralTree node, Content c) { 1841 String s = node.getBody().toString(); 1842 Content content = new StringContent(utils.normalizeNewlines(s)); 1843 if (node.getKind() == CODE) 1844 content = HtmlTree.CODE(content); 1845 result.addContent(content); 1846 return false; 1847 } 1848 1849 @Override 1850 public Boolean visitSee(SeeTree node, Content c) { 1851 // we need to pass the DocTreeImpl here, so ignore node 1852 result.addContent(seeTagToContent(element, tag)); 1853 return false; 1854 } 1855 1856 @Override 1857 public Boolean visitStartElement(StartElementTree node, Content c) { 1858 String text = "<" + node.getName(); 1859 RawHtml rawHtml = new RawHtml(utils.normalizeNewlines(text)); 1860 result.addContent(rawHtml); 1861 1862 for (DocTree dt : node.getAttributes()) { 1863 dt.accept(this, null); 1864 } 1865 result.addContent(new RawHtml(node.isSelfClosing() ? "/>" : ">")); 1866 return false; 1867 } 1868 1869 @Override 1870 public Boolean visitSummary(SummaryTree node, Content c) { 1871 Content output = TagletWriter.getInlineTagOutput(element, 1872 configuration.tagletManager, holderTag, tag, 1873 getTagletWriterInstance(isFirstSentence)); 1874 result.addContent(output); 1875 return false; 1876 } 1877 1878 private CharSequence textCleanup(String text, boolean isLast) { 1879 return textCleanup(text, isLast, false); 1880 } 1881 1882 private CharSequence textCleanup(String text, boolean isLast, boolean trimLeader) { 1883 if (trimLeader) { 1884 text = removeLeadingWhitespace(text); 1885 } 1886 if (isFirstSentence && isLast) { 1887 text = removeTrailingWhitespace(text); 1888 } 1889 text = utils.replaceTabs(text); 1890 return utils.normalizeNewlines(text); 1891 } 1892 1893 @Override 1894 public Boolean visitText(TextTree node, Content c) { 1895 String text = node.getBody(); 1896 result.addContent(new RawHtml(textCleanup(text, isLastNode, commentRemoved))); 1897 return false; 1898 } 1899 1900 @Override 1901 protected Boolean defaultAction(DocTree node, Content c) { 1902 Content output = TagletWriter.getInlineTagOutput(element, 1903 configuration.tagletManager, holderTag, tag, 1904 getTagletWriterInstance(isFirstSentence)); 1905 if (output != null) { 1906 result.addContent(output); 1907 } 1908 return false; 1909 } 1910 1911 }.visit(tag, null); 1912 commentRemoved = false; 1913 if (allDone) 1914 break; 1915 } 1916 return result; 1917 } 1918 1919 private String removeTrailingWhitespace(String text) { 1920 char[] buf = text.toCharArray(); 1921 for (int i = buf.length - 1; i > 0 ; i--) { 1922 if (!Character.isWhitespace(buf[i])) 1923 return text.substring(0, i + 1); 1924 } 1925 return text; 1926 } 1927 1928 private String removeLeadingWhitespace(String text) { 1929 char[] buf = text.toCharArray(); 1930 for (int i = 0; i < buf.length; i++) { 1931 if (!Character.isWhitespace(buf[i])) { 1932 return text.substring(i); 1933 } 1934 } 1935 return text; 1936 } 1937 1938 /** 1939 * Return true if relative links should not be redirected. 1940 * 1941 * @return Return true if a relative link should not be redirected. 1942 */ 1943 private boolean shouldNotRedirectRelativeLinks() { 1944 return this instanceof AnnotationTypeWriter || 1945 this instanceof ClassWriter || 1946 this instanceof PackageSummaryWriter; 1947 } 1948 1949 /** 1950 * Suppose a piece of documentation has a relative link. When you copy 1951 * that documentation to another place such as the index or class-use page, 1952 * that relative link will no longer work. We should redirect those links 1953 * so that they will work again. 1954 * <p> 1955 * Here is the algorithm used to fix the link: 1956 * <p> 1957 * {@literal <relative link> => docRoot + <relative path to file> + <relative link> } 1958 * <p> 1959 * For example, suppose DocletEnvironment has this link: 1960 * {@literal <a href="package-summary.html">The package Page</a> } 1961 * <p> 1962 * If this link appeared in the index, we would redirect 1963 * the link like this: 1964 * 1965 * {@literal <a href="./com/sun/javadoc/package-summary.html">The package Page</a>} 1966 * 1967 * @param element the Element object whose documentation is being written. 1968 * @param text the text being written. 1969 * 1970 * @return the text, with all the relative links redirected to work. 1971 */ 1972 private String redirectRelativeLinks(Element element, TextTree tt) { 1973 String text = tt.getBody(); 1974 if (element == null || utils.isOverviewElement(element) || shouldNotRedirectRelativeLinks()) { 1975 return text; 1976 } 1977 1978 DocPath redirectPathFromRoot = new SimpleElementVisitor9<DocPath, Void>() { 1979 @Override 1980 public DocPath visitType(TypeElement e, Void p) { 1981 return DocPath.forPackage(utils.containingPackage(e)); 1982 } 1983 1984 @Override 1985 public DocPath visitPackage(PackageElement e, Void p) { 1986 return DocPath.forPackage(e); 1987 } 1988 1989 @Override 1990 public DocPath visitVariable(VariableElement e, Void p) { 1991 return DocPath.forPackage(utils.containingPackage(e)); 1992 } 1993 1994 @Override 1995 public DocPath visitExecutable(ExecutableElement e, Void p) { 1996 return DocPath.forPackage(utils.containingPackage(e)); 1997 } 1998 1999 @Override 2000 protected DocPath defaultAction(Element e, Void p) { 2001 return null; 2002 } 2003 }.visit(element); 2004 if (redirectPathFromRoot == null) { 2005 return text; 2006 } 2007 String lower = Utils.toLowerCase(text); 2008 if (!(lower.startsWith("mailto:") 2009 || lower.startsWith("http:") 2010 || lower.startsWith("https:") 2011 || lower.startsWith("file:"))) { 2012 text = "{@" + (new DocRootTaglet()).getName() + "}/" 2013 + redirectPathFromRoot.resolve(text).getPath(); 2014 text = replaceDocRootDir(text); 2015 } 2016 return text; 2017 } 2018 2019 /** 2020 * According to 2021 * <cite>The Java™ Language Specification</cite>, 2022 * all the outer classes and static nested classes are core classes. 2023 */ 2024 public boolean isCoreClass(TypeElement typeElement) { 2025 return utils.getEnclosingTypeElement(typeElement) == null || utils.isStatic(typeElement); 2026 } 2027 2028 /** 2029 * Adds the annotation types for the given packageElement. 2030 * 2031 * @param packageElement the package to write annotations for. 2032 * @param htmltree the documentation tree to which the annotation info will be 2033 * added 2034 */ 2035 public void addAnnotationInfo(PackageElement packageElement, Content htmltree) { 2036 addAnnotationInfo(packageElement, packageElement.getAnnotationMirrors(), htmltree); 2037 } 2038 2039 /** 2040 * Add the annotation types of the executable receiver. 2041 * 2042 * @param method the executable to write the receiver annotations for. 2043 * @param descList list of annotation description. 2044 * @param htmltree the documentation tree to which the annotation info will be 2045 * added 2046 */ 2047 public void addReceiverAnnotationInfo(ExecutableElement method, List<AnnotationMirror> descList, 2048 Content htmltree) { 2049 addAnnotationInfo(0, method, descList, false, htmltree); 2050 } 2051 2052 /* 2053 * this is a hack to delay dealing with Annotations in the writers, the assumption 2054 * is that all necessary checks have been made to get here. 2055 */ 2056 public void addReceiverAnnotationInfo(ExecutableElement method, TypeMirror rcvrTypeMirror, 2057 List<? extends AnnotationMirror> annotationMirrors, Content htmltree) { 2058 TypeMirror rcvrType = method.getReceiverType(); 2059 List<? extends AnnotationMirror> annotationMirrors1 = rcvrType.getAnnotationMirrors(); 2060 addAnnotationInfo(0, method, annotationMirrors1, false, htmltree); 2061 } 2062 2063 /** 2064 * Adds the annotation types for the given element. 2065 * 2066 * @param element the package to write annotations for 2067 * @param htmltree the content tree to which the annotation types will be added 2068 */ 2069 public void addAnnotationInfo(Element element, Content htmltree) { 2070 addAnnotationInfo(element, element.getAnnotationMirrors(), htmltree); 2071 } 2072 2073 /** 2074 * Add the annotatation types for the given element and parameter. 2075 * 2076 * @param indent the number of spaces to indent the parameters. 2077 * @param element the element to write annotations for. 2078 * @param param the parameter to write annotations for. 2079 * @param tree the content tree to which the annotation types will be added 2080 */ 2081 public boolean addAnnotationInfo(int indent, Element element, VariableElement param, 2082 Content tree) { 2083 return addAnnotationInfo(indent, element, param.getAnnotationMirrors(), false, tree); 2084 } 2085 2086 /** 2087 * Adds the annotatation types for the given Element. 2088 * 2089 * @param element the element to write annotations for. 2090 * @param descList the array of {@link AnnotationDesc}. 2091 * @param htmltree the documentation tree to which the annotation info will be 2092 * added 2093 */ 2094 private void addAnnotationInfo(Element element, List<? extends AnnotationMirror> descList, 2095 Content htmltree) { 2096 addAnnotationInfo(0, element, descList, true, htmltree); 2097 } 2098 2099 /** 2100 * Adds the annotation types for the given element. 2101 * 2102 * @param indent the number of extra spaces to indent the annotations. 2103 * @param element the element to write annotations for. 2104 * @param descList the array of {@link AnnotationDesc}. 2105 * @param htmltree the documentation tree to which the annotation info will be 2106 * added 2107 */ 2108 private boolean addAnnotationInfo(int indent, Element element, 2109 List<? extends AnnotationMirror> descList, boolean lineBreak, Content htmltree) { 2110 List<Content> annotations = getAnnotations(indent, descList, lineBreak); 2111 String sep = ""; 2112 if (annotations.isEmpty()) { 2113 return false; 2114 } 2115 for (Content annotation: annotations) { 2116 htmltree.addContent(sep); 2117 htmltree.addContent(annotation); 2118 if (!lineBreak) { 2119 sep = " "; 2120 } 2121 } 2122 return true; 2123 } 2124 2125 /** 2126 * Return the string representations of the annotation types for 2127 * the given doc. 2128 * 2129 * @param indent the number of extra spaces to indent the annotations. 2130 * @param descList the array of {@link AnnotationDesc}. 2131 * @param linkBreak if true, add new line between each member value. 2132 * @return an array of strings representing the annotations being 2133 * documented. 2134 */ 2135 private List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList, boolean linkBreak) { 2136 return getAnnotations(indent, descList, linkBreak, true); 2137 } 2138 2139 private List<Content> getAnnotations(int indent, AnnotationMirror amirror, boolean linkBreak) { 2140 List<AnnotationMirror> descList = new ArrayList<>(); 2141 descList.add(amirror); 2142 return getAnnotations(indent, descList, linkBreak, true); 2143 } 2144 2145 /** 2146 * Return the string representations of the annotation types for 2147 * the given doc. 2148 * 2149 * A {@code null} {@code elementType} indicates that all the 2150 * annotations should be returned without any filtering. 2151 * 2152 * @param indent the number of extra spaces to indent the annotations. 2153 * @param descList the array of {@link AnnotationDesc}. 2154 * @param linkBreak if true, add new line between each member value. 2155 * @param isJava5DeclarationLocation 2156 * @return an array of strings representing the annotations being 2157 * documented. 2158 */ 2159 public List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList, 2160 boolean linkBreak, boolean isJava5DeclarationLocation) { 2161 List<Content> results = new ArrayList<>(); 2162 ContentBuilder annotation; 2163 for (AnnotationMirror aDesc : descList) { 2164 TypeElement annotationElement = (TypeElement)aDesc.getAnnotationType().asElement(); 2165 // If an annotation is not documented, do not add it to the list. If 2166 // the annotation is of a repeatable type, and if it is not documented 2167 // and also if its container annotation is not documented, do not add it 2168 // to the list. If an annotation of a repeatable type is not documented 2169 // but its container is documented, it will be added to the list. 2170 if (!utils.isDocumentedAnnotation(annotationElement) && 2171 (!isAnnotationDocumented && !isContainerDocumented)) { 2172 continue; 2173 } 2174 /* TODO: check logic here to correctly handle declaration 2175 * and type annotations. 2176 if (utils.isDeclarationAnnotation(annotationElement, isJava5DeclarationLocation)) { 2177 continue; 2178 }*/ 2179 annotation = new ContentBuilder(); 2180 isAnnotationDocumented = false; 2181 LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, 2182 LinkInfoImpl.Kind.ANNOTATION, annotationElement); 2183 Map<? extends ExecutableElement, ? extends AnnotationValue> pairs = aDesc.getElementValues(); 2184 // If the annotation is synthesized, do not print the container. 2185 if (utils.configuration.workArounds.isSynthesized(aDesc)) { 2186 for (ExecutableElement ee : pairs.keySet()) { 2187 AnnotationValue annotationValue = pairs.get(ee); 2188 List<AnnotationValue> annotationTypeValues = new ArrayList<>(); 2189 2190 new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() { 2191 @Override 2192 public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> p) { 2193 p.addAll(vals); 2194 return null; 2195 } 2196 2197 @Override 2198 protected Void defaultAction(Object o, List<AnnotationValue> p) { 2199 p.add(annotationValue); 2200 return null; 2201 } 2202 }.visit(annotationValue, annotationTypeValues); 2203 2204 String sep = ""; 2205 for (AnnotationValue av : annotationTypeValues) { 2206 annotation.addContent(sep); 2207 annotation.addContent(annotationValueToContent(av)); 2208 sep = " "; 2209 } 2210 } 2211 } else if (isAnnotationArray(pairs)) { 2212 // If the container has 1 or more value defined and if the 2213 // repeatable type annotation is not documented, do not print 2214 // the container. 2215 if (pairs.size() == 1 && isAnnotationDocumented) { 2216 List<AnnotationValue> annotationTypeValues = new ArrayList<>(); 2217 for (AnnotationValue a : pairs.values()) { 2218 new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() { 2219 @Override 2220 public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> annotationTypeValues) { 2221 for (AnnotationValue av : vals) { 2222 annotationTypeValues.add(av); 2223 } 2224 return null; 2225 } 2226 }.visit(a, annotationTypeValues); 2227 } 2228 String sep = ""; 2229 for (AnnotationValue av : annotationTypeValues) { 2230 annotation.addContent(sep); 2231 annotation.addContent(annotationValueToContent(av)); 2232 sep = " "; 2233 } 2234 } 2235 // If the container has 1 or more value defined and if the 2236 // repeatable type annotation is not documented, print the container. 2237 else { 2238 addAnnotations(annotationElement, linkInfo, annotation, pairs, 2239 indent, false); 2240 } 2241 } 2242 else { 2243 addAnnotations(annotationElement, linkInfo, annotation, pairs, 2244 indent, linkBreak); 2245 } 2246 annotation.addContent(linkBreak ? DocletConstants.NL : ""); 2247 results.add(annotation); 2248 } 2249 return results; 2250 } 2251 2252 /** 2253 * Add annotation to the annotation string. 2254 * 2255 * @param annotationDoc the annotation being documented 2256 * @param linkInfo the information about the link 2257 * @param annotation the annotation string to which the annotation will be added 2258 * @param pairs annotation type element and value pairs 2259 * @param indent the number of extra spaces to indent the annotations. 2260 * @param linkBreak if true, add new line between each member value 2261 */ 2262 private void addAnnotations(TypeElement annotationDoc, LinkInfoImpl linkInfo, 2263 ContentBuilder annotation, Map<? extends ExecutableElement,? extends AnnotationValue>map, 2264 int indent, boolean linkBreak) { 2265 linkInfo.label = new StringContent("@"); 2266 linkInfo.label.addContent(annotationDoc.getSimpleName()); 2267 annotation.addContent(getLink(linkInfo)); 2268 if (!map.isEmpty()) { 2269 annotation.addContent("("); 2270 boolean isFirst = true; 2271 Set<? extends ExecutableElement> keys = map.keySet(); 2272 boolean multipleValues = keys.size() > 1; 2273 for (ExecutableElement element : keys) { 2274 if (isFirst) { 2275 isFirst = false; 2276 } else { 2277 annotation.addContent(","); 2278 if (linkBreak) { 2279 annotation.addContent(DocletConstants.NL); 2280 int spaces = annotationDoc.getSimpleName().length() + 2; 2281 for (int k = 0; k < (spaces + indent); k++) { 2282 annotation.addContent(" "); 2283 } 2284 } 2285 } 2286 String simpleName = element.getSimpleName().toString(); 2287 if (multipleValues || !"value".equals(simpleName)) { // Omit "value=" where unnecessary 2288 annotation.addContent(getDocLink(LinkInfoImpl.Kind.ANNOTATION, 2289 element, simpleName, false)); 2290 annotation.addContent("="); 2291 } 2292 AnnotationValue annotationValue = map.get(element); 2293 List<AnnotationValue> annotationTypeValues = new ArrayList<>(); 2294 new SimpleAnnotationValueVisitor9<Void, AnnotationValue>() { 2295 @Override 2296 public Void visitArray(List<? extends AnnotationValue> vals, AnnotationValue p) { 2297 annotationTypeValues.addAll(vals); 2298 return null; 2299 } 2300 @Override 2301 protected Void defaultAction(Object o, AnnotationValue p) { 2302 annotationTypeValues.add(p); 2303 return null; 2304 } 2305 }.visit(annotationValue, annotationValue); 2306 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "{"); 2307 String sep = ""; 2308 for (AnnotationValue av : annotationTypeValues) { 2309 annotation.addContent(sep); 2310 annotation.addContent(annotationValueToContent(av)); 2311 sep = ","; 2312 } 2313 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "}"); 2314 isContainerDocumented = false; 2315 } 2316 annotation.addContent(")"); 2317 } 2318 } 2319 2320 /** 2321 * Check if the annotation contains an array of annotation as a value. This 2322 * check is to verify if a repeatable type annotation is present or not. 2323 * 2324 * @param pairs annotation type element and value pairs 2325 * 2326 * @return true if the annotation contains an array of annotation as a value. 2327 */ 2328 private boolean isAnnotationArray(Map<? extends ExecutableElement, ? extends AnnotationValue> pairs) { 2329 AnnotationValue annotationValue; 2330 for (ExecutableElement ee : pairs.keySet()) { 2331 annotationValue = pairs.get(ee); 2332 boolean rvalue = new SimpleAnnotationValueVisitor9<Boolean, Void>() { 2333 @Override 2334 public Boolean visitArray(List<? extends AnnotationValue> vals, Void p) { 2335 if (vals.size() > 1) { 2336 if (vals.get(0) instanceof AnnotationMirror) { 2337 isContainerDocumented = true; 2338 return new SimpleAnnotationValueVisitor9<Boolean, Void>() { 2339 @Override 2340 public Boolean visitAnnotation(AnnotationMirror a, Void p) { 2341 isContainerDocumented = true; 2342 Element asElement = a.getAnnotationType().asElement(); 2343 if (utils.isDocumentedAnnotation((TypeElement)asElement)) { 2344 isAnnotationDocumented = true; 2345 } 2346 return true; 2347 } 2348 @Override 2349 protected Boolean defaultAction(Object o, Void p) { 2350 return false; 2351 } 2352 }.visit(vals.get(0)); 2353 } 2354 } 2355 return false; 2356 } 2357 2358 @Override 2359 protected Boolean defaultAction(Object o, Void p) { 2360 return false; 2361 } 2362 }.visit(annotationValue); 2363 if (rvalue) { 2364 return true; 2365 } 2366 } 2367 return false; 2368 } 2369 2370 private Content annotationValueToContent(AnnotationValue annotationValue) { 2371 return new SimpleAnnotationValueVisitor9<Content, Void>() { 2372 2373 @Override 2374 public Content visitType(TypeMirror t, Void p) { 2375 return new SimpleTypeVisitor9<Content, Void>() { 2376 @Override 2377 public Content visitDeclared(DeclaredType t, Void p) { 2378 LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, 2379 LinkInfoImpl.Kind.ANNOTATION, t); 2380 String name = utils.isIncluded(t.asElement()) 2381 ? t.asElement().getSimpleName().toString() 2382 : utils.getFullyQualifiedName(t.asElement()); 2383 linkInfo.label = new StringContent(name + utils.getDimension(t) + ".class"); 2384 return getLink(linkInfo); 2385 } 2386 @Override 2387 protected Content defaultAction(TypeMirror e, Void p) { 2388 return new StringContent(t + utils.getDimension(t) + ".class"); 2389 } 2390 }.visit(t); 2391 } 2392 @Override 2393 public Content visitAnnotation(AnnotationMirror a, Void p) { 2394 List<Content> list = getAnnotations(0, a, false); 2395 ContentBuilder buf = new ContentBuilder(); 2396 for (Content c : list) { 2397 buf.addContent(c); 2398 } 2399 return buf; 2400 } 2401 @Override 2402 public Content visitEnumConstant(VariableElement c, Void p) { 2403 return getDocLink(LinkInfoImpl.Kind.ANNOTATION, 2404 c, c.getSimpleName(), false); 2405 } 2406 @Override 2407 public Content visitArray(List<? extends AnnotationValue> vals, Void p) { 2408 ContentBuilder buf = new ContentBuilder(); 2409 String sep = ""; 2410 for (AnnotationValue av : vals) { 2411 buf.addContent(sep); 2412 buf.addContent(visit(av)); 2413 sep = " "; 2414 } 2415 return buf; 2416 } 2417 @Override 2418 protected Content defaultAction(Object o, Void p) { 2419 return new StringContent(annotationValue.toString()); 2420 } 2421 }.visit(annotationValue); 2422 } 2423 2424 protected TableHeader getPackageTableHeader() { 2425 return new TableHeader(contents.packageLabel, contents.descriptionLabel); 2426 } 2427 2428 /** 2429 * Returns an HtmlTree for the SCRIPT tag. 2430 * 2431 * @return an HtmlTree for the SCRIPT tag 2432 */ 2433 protected Script getWinTitleScript() { 2434 Script script = new Script(); 2435 if (winTitle != null && winTitle.length() > 0) { 2436 script.append("<!--\n" + 2437 " try {\n" + 2438 " if (location.href.indexOf('is-external=true') == -1) {\n" + 2439 " parent.document.title=") 2440 .appendStringLiteral(winTitle) 2441 .append(";\n" + 2442 " }\n" + 2443 " }\n" + 2444 " catch(err) {\n" + 2445 " }\n" + 2446 "//-->\n"); 2447 } 2448 return script; 2449 } 2450 2451 /** 2452 * Returns an HtmlTree for the BODY tag. 2453 * 2454 * @param includeScript set true if printing windowtitle script 2455 * @param title title for the window 2456 * @return an HtmlTree for the BODY tag 2457 */ 2458 public HtmlTree getBody(boolean includeScript, String title) { 2459 HtmlTree body = new HtmlTree(HtmlTag.BODY); 2460 // Set window title string which is later printed 2461 this.winTitle = title; 2462 // Don't print windowtitle script for overview-frame, allclasses-frame 2463 // and package-frame 2464 if (includeScript) { 2465 this.mainBodyScript = getWinTitleScript(); 2466 body.addContent(mainBodyScript.asContent()); 2467 Content noScript = HtmlTree.NOSCRIPT(HtmlTree.DIV(contents.noScriptMessage)); 2468 body.addContent(noScript); 2469 } 2470 return body; 2471 } 2472 2473 Script getMainBodyScript() { 2474 return mainBodyScript; 2475 } 2476 }