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