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