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