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