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