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