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