1 /* 2 * Copyright (c) 1998, 2017, 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.text.SimpleDateFormat; 29 import java.util.*; 30 import java.util.regex.Matcher; 31 import java.util.regex.Pattern; 32 import java.util.stream.Collectors; 33 34 import javax.lang.model.element.AnnotationMirror; 35 import javax.lang.model.element.AnnotationValue; 36 import javax.lang.model.element.Element; 37 import javax.lang.model.element.ElementKind; 38 import javax.lang.model.element.ExecutableElement; 39 import javax.lang.model.element.ModuleElement; 40 import javax.lang.model.element.Name; 41 import javax.lang.model.element.PackageElement; 42 import javax.lang.model.element.TypeElement; 43 import javax.lang.model.element.TypeParameterElement; 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.Comment; 71 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; 72 import jdk.javadoc.internal.doclets.formats.html.markup.DocType; 73 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; 74 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; 75 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocWriter; 76 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocument; 77 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; 78 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; 79 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; 80 import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; 81 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; 82 import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter; 83 import jdk.javadoc.internal.doclets.toolkit.ClassWriter; 84 import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; 85 import jdk.javadoc.internal.doclets.toolkit.Content; 86 import jdk.javadoc.internal.doclets.toolkit.Messages; 87 import jdk.javadoc.internal.doclets.toolkit.PackageSummaryWriter; 88 import jdk.javadoc.internal.doclets.toolkit.Resources; 89 import jdk.javadoc.internal.doclets.toolkit.taglets.DocRootTaglet; 90 import jdk.javadoc.internal.doclets.toolkit.taglets.TagletWriter; 91 import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; 92 import jdk.javadoc.internal.doclets.toolkit.util.DocFile; 93 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; 94 import jdk.javadoc.internal.doclets.toolkit.util.DocLink; 95 import jdk.javadoc.internal.doclets.toolkit.util.DocPath; 96 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; 97 import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; 98 import jdk.javadoc.internal.doclets.toolkit.util.ImplementedMethods; 99 import jdk.javadoc.internal.doclets.toolkit.util.Utils; 100 import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap; 101 102 import static com.sun.source.doctree.DocTree.Kind.*; 103 import static jdk.javadoc.internal.doclets.toolkit.util.CommentHelper.SPACER; 104 105 106 /** 107 * Class for the Html Format Code Generation specific to JavaDoc. 108 * This Class contains methods related to the Html Code Generation which 109 * are used extensively while generating the entire documentation. 110 * 111 * <p><b>This is NOT part of any supported API. 112 * If you write code that depends on this, you do so at your own risk. 113 * This code and its internal interfaces are subject to change or 114 * deletion without notice.</b> 115 * 116 * @author Atul M Dambalkar 117 * @author Robert Field 118 * @author Bhavesh Patel (Modified) 119 */ 120 public class HtmlDocletWriter extends HtmlDocWriter { 121 122 /** 123 * Relative path from the file getting generated to the destination 124 * directory. For example, if the file getting generated is 125 * "java/lang/Object.html", then the path to the root is "../..". 126 * This string can be empty if the file getting generated is in 127 * the destination directory. 128 */ 129 public final DocPath pathToRoot; 130 131 /** 132 * Platform-independent path from the current or the 133 * destination directory to the file getting generated. 134 * Used when creating the file. 135 */ 136 public final DocPath path; 137 138 /** 139 * Name of the file getting generated. If the file getting generated is 140 * "java/lang/Object.html", then the filename is "Object.html". 141 */ 142 public final DocPath filename; 143 144 /** 145 * The global configuration information for this run. 146 */ 147 public final HtmlConfiguration configuration; 148 149 protected final Utils utils; 150 151 protected final Contents contents; 152 153 protected final Messages messages; 154 155 protected final Resources resources; 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 final static Pattern IMPROPER_HTML_CHARS = Pattern.compile(".*[&<>].*"); 180 181 /** 182 * Constructor to construct the HtmlStandardWriter object. 183 * 184 * @param path File to be generated. 185 */ 186 public HtmlDocletWriter(HtmlConfiguration configuration, DocPath path) { 187 super(configuration, path); 188 this.configuration = configuration; 189 this.contents = configuration.contents; 190 this.messages = configuration.messages; 191 this.resources = configuration.resources; 192 this.utils = configuration.utils; 193 this.path = path; 194 this.pathToRoot = path.parent().invert(); 195 this.filename = path.basename(); 196 } 197 198 /** 199 * Replace {@docRoot} tag used in options that accept HTML text, such 200 * as -header, -footer, -top and -bottom, and when converting a relative 201 * HREF where commentTagsToString inserts a {@docRoot} where one was 202 * missing. (Also see DocRootTaglet for {@docRoot} tags in doc 203 * comments.) 204 * <p> 205 * Replace {@docRoot} tag in htmlstr with the relative path to the 206 * destination directory from the directory where the file is being 207 * written, looping to handle all such tags in htmlstr. 208 * <p> 209 * For example, for "-d docs" and -header containing {@docRoot}, when 210 * the HTML page for source file p/C1.java is being generated, the 211 * {@docRoot} tag would be inserted into the header as "../", 212 * the relative path from docs/p/ to docs/ (the document root). 213 * <p> 214 * Note: This doc comment was written with '&#064;' representing '@' 215 * to prevent the inline tag from being interpreted. 216 */ 217 public String replaceDocRootDir(String htmlstr) { 218 // Return if no inline tags exist 219 int index = htmlstr.indexOf("{@"); 220 if (index < 0) { 221 return htmlstr; 222 } 223 Matcher docrootMatcher = docrootPattern.matcher(htmlstr); 224 if (!docrootMatcher.find()) { 225 return htmlstr; 226 } 227 StringBuilder buf = new StringBuilder(); 228 int prevEnd = 0; 229 do { 230 int match = docrootMatcher.start(); 231 // append htmlstr up to start of next {@docroot} 232 buf.append(htmlstr.substring(prevEnd, match)); 233 prevEnd = docrootMatcher.end(); 234 if (configuration.docrootparent.length() > 0 && htmlstr.startsWith("/..", prevEnd)) { 235 // Insert the absolute link if {@docRoot} is followed by "/..". 236 buf.append(configuration.docrootparent); 237 prevEnd += 3; 238 } else { 239 // Insert relative path where {@docRoot} was located 240 buf.append(pathToRoot.isEmpty() ? "." : pathToRoot.getPath()); 241 } 242 // Append slash if next character is not a slash 243 if (prevEnd < htmlstr.length() && htmlstr.charAt(prevEnd) != '/') { 244 buf.append('/'); 245 } 246 } while (docrootMatcher.find()); 247 buf.append(htmlstr.substring(prevEnd)); 248 return buf.toString(); 249 } 250 //where: 251 // Note: {@docRoot} is not case sensitive when passed in w/command line option: 252 private static final Pattern docrootPattern = 253 Pattern.compile(Pattern.quote("{@docroot}"), Pattern.CASE_INSENSITIVE); 254 255 /** 256 * Get the script to show or hide the All classes link. 257 * 258 * @param id id of the element to show or hide 259 * @return a content tree for the script 260 */ 261 public Content getAllClassesLinkScript(String id) { 262 HtmlTree script = HtmlTree.SCRIPT(); 263 String scriptCode = "<!--\n" + 264 " allClassesLink = document.getElementById(\"" + id + "\");\n" + 265 " if(window==top) {\n" + 266 " allClassesLink.style.display = \"block\";\n" + 267 " }\n" + 268 " else {\n" + 269 " allClassesLink.style.display = \"none\";\n" + 270 " }\n" + 271 " //-->\n"; 272 Content scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL)); 273 script.addContent(scriptContent); 274 Content div = HtmlTree.DIV(script); 275 Content div_noscript = HtmlTree.DIV(contents.noScriptMessage); 276 Content noScript = HtmlTree.NOSCRIPT(div_noscript); 277 div.addContent(noScript); 278 return div; 279 } 280 281 /** 282 * Add method information. 283 * 284 * @param method the method to be documented 285 * @param dl the content tree to which the method information will be added 286 */ 287 private void addMethodInfo(ExecutableElement method, Content dl) { 288 TypeElement enclosing = utils.getEnclosingTypeElement(method); 289 List<? extends TypeMirror> intfacs = enclosing.getInterfaces(); 290 ExecutableElement overriddenMethod = utils.overriddenMethod(method); 291 // Check whether there is any implementation or overridden info to be 292 // printed. If no overridden or implementation info needs to be 293 // printed, do not print this section. 294 if ((!intfacs.isEmpty() 295 && new ImplementedMethods(method, this.configuration).build().isEmpty() == false) 296 || overriddenMethod != null) { 297 MethodWriterImpl.addImplementsInfo(this, method, dl); 298 if (overriddenMethod != null) { 299 MethodWriterImpl.addOverridden(this, 300 utils.overriddenType(method), 301 overriddenMethod, 302 dl); 303 } 304 } 305 } 306 307 /** 308 * Adds the tags information. 309 * 310 * @param e the Element for which the tags will be generated 311 * @param htmltree the documentation tree to which the tags will be added 312 */ 313 protected void addTagsInfo(Element e, Content htmltree) { 314 if (configuration.nocomment) { 315 return; 316 } 317 Content dl = new HtmlTree(HtmlTag.DL); 318 if (utils.isExecutableElement(e) && !utils.isConstructor(e)) { 319 addMethodInfo((ExecutableElement)e, dl); 320 } 321 Content output = new ContentBuilder(); 322 TagletWriter.genTagOutput(configuration.tagletManager, e, 323 configuration.tagletManager.getCustomTaglets(e), 324 getTagletWriterInstance(false), output); 325 dl.addContent(output); 326 htmltree.addContent(dl); 327 } 328 329 /** 330 * Check whether there are any tags for Serialization Overview 331 * section to be printed. 332 * 333 * @param field the VariableElement object to check for tags. 334 * @return true if there are tags to be printed else return false. 335 */ 336 protected boolean hasSerializationOverviewTags(VariableElement field) { 337 Content output = new ContentBuilder(); 338 TagletWriter.genTagOutput(configuration.tagletManager, field, 339 configuration.tagletManager.getCustomTaglets(field), 340 getTagletWriterInstance(false), output); 341 return !output.isEmpty(); 342 } 343 344 /** 345 * Returns a TagletWriter that knows how to write HTML. 346 * 347 * @return a TagletWriter that knows how to write HTML. 348 */ 349 public TagletWriter getTagletWriterInstance(boolean isFirstSentence) { 350 return new TagletWriterImpl(this, isFirstSentence); 351 } 352 353 /** 354 * Get Package link, with target frame. 355 * 356 * @param pkg The link will be to the "package-summary.html" page for this package 357 * @param target name of the target frame 358 * @param label tag for the link 359 * @return a content for the target package link 360 */ 361 public Content getTargetPackageLink(PackageElement pkg, String target, 362 Content label) { 363 return getHyperLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY), label, "", target); 364 } 365 366 /** 367 * Get Module Package link, with target frame. 368 * 369 * @param pkg the PackageElement 370 * @param target name of the target frame 371 * @param label tag for the link 372 * @param mdle the module being documented 373 * @return a content for the target module packages link 374 */ 375 public Content getTargetModulePackageLink(PackageElement pkg, String target, 376 Content label, ModuleElement mdle) { 377 return getHyperLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY), 378 label, "", target); 379 } 380 381 /** 382 * Get Module link, with target frame. 383 * 384 * @param target name of the target frame 385 * @param label tag for the link 386 * @param mdle the module being documented 387 * @return a content for the target module link 388 */ 389 public Content getTargetModuleLink(String target, Content label, ModuleElement mdle) { 390 return getHyperLink(pathToRoot.resolve( 391 DocPaths.moduleSummary(mdle)), label, "", target); 392 } 393 394 public void addClassesSummary(SortedSet<TypeElement> classes, String label, 395 String tableSummary, List<String> tableHeader, Content summaryContentTree) { 396 if (!classes.isEmpty()) { 397 Content caption = getTableCaption(new RawHtml(label)); 398 Content table = (configuration.isOutputHtml5()) 399 ? HtmlTree.TABLE(HtmlStyle.typeSummary, caption) 400 : HtmlTree.TABLE(HtmlStyle.typeSummary, tableSummary, caption); 401 table.addContent(getSummaryTableHeader(tableHeader, "col")); 402 Content tbody = new HtmlTree(HtmlTag.TBODY); 403 boolean altColor = true; 404 for (TypeElement te : classes) { 405 if (!utils.isCoreClass(te) || 406 !configuration.isGeneratedDoc(te)) { 407 continue; 408 } 409 Content classContent = getLink(new LinkInfoImpl( 410 configuration, LinkInfoImpl.Kind.PACKAGE, te)); 411 Content tdClass = HtmlTree.TH_ROW_SCOPE(HtmlStyle.colFirst, classContent); 412 HtmlTree tr = HtmlTree.TR(tdClass); 413 tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); 414 altColor = !altColor; 415 HtmlTree tdClassDescription = new HtmlTree(HtmlTag.TD); 416 tdClassDescription.addStyle(HtmlStyle.colLast); 417 if (utils.isDeprecated(te)) { 418 tdClassDescription.addContent(getDeprecatedPhrase(te)); 419 List<? extends DocTree> tags = utils.getDeprecatedTrees(te); 420 if (!tags.isEmpty()) { 421 addSummaryDeprecatedComment(te, tags.get(0), tdClassDescription); 422 } 423 } else { 424 addSummaryComment(te, tdClassDescription); 425 } 426 tr.addContent(tdClassDescription); 427 tbody.addContent(tr); 428 } 429 table.addContent(tbody); 430 summaryContentTree.addContent(table); 431 } 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 includeScript true if printing windowtitle script 441 * false for files that appear in the left-hand frames 442 * @param body the body htmltree to be included in the document 443 * @throws DocFileIOException if there is a problem writing the file 444 */ 445 public void printHtmlDocument(List<String> metakeywords, boolean includeScript, 446 Content body) throws DocFileIOException { 447 Content htmlDocType = configuration.isOutputHtml5() 448 ? DocType.HTML5 449 : DocType.TRANSITIONAL; 450 Content htmlComment = new Comment(configuration.getText("doclet.New_Page")); 451 Content head = new HtmlTree(HtmlTag.HEAD); 452 head.addContent(getGeneratedBy(!configuration.notimestamp)); 453 head.addContent(getTitle()); 454 Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, configuration.charset); 455 head.addContent(meta); 456 if (!configuration.notimestamp) { 457 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 458 meta = HtmlTree.META(configuration.isOutputHtml5() 459 ? "dc.created" 460 : "date", dateFormat.format(new Date())); 461 head.addContent(meta); 462 } 463 if (metakeywords != null) { 464 for (String metakeyword : metakeywords) { 465 meta = HtmlTree.META("keywords", metakeyword); 466 head.addContent(meta); 467 } 468 } 469 addStyleSheetProperties(head); 470 addScriptProperties(head); 471 Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(), 472 head, body); 473 Content htmlDocument = new HtmlDocument(htmlDocType, 474 htmlComment, htmlTree); 475 write(htmlDocument); 476 } 477 478 /** 479 * Get the window title. 480 * 481 * @param title the title string to construct the complete window title 482 * @return the window title string 483 */ 484 public String getWindowTitle(String title) { 485 if (configuration.windowtitle.length() > 0) { 486 title += " (" + configuration.windowtitle + ")"; 487 } 488 return title; 489 } 490 491 /** 492 * Get user specified header and the footer. 493 * 494 * @param header if true print the user provided header else print the 495 * user provided footer. 496 */ 497 public Content getUserHeaderFooter(boolean header) { 498 String content; 499 if (header) { 500 content = replaceDocRootDir(configuration.header); 501 } else { 502 if (configuration.footer.length() != 0) { 503 content = replaceDocRootDir(configuration.footer); 504 } else { 505 content = replaceDocRootDir(configuration.header); 506 } 507 } 508 Content rawContent = new RawHtml(content); 509 return rawContent; 510 } 511 512 /** 513 * Adds the user specified top. 514 * 515 * @param htmlTree the content tree to which user specified top will be added 516 */ 517 public void addTop(Content htmlTree) { 518 Content top = new RawHtml(replaceDocRootDir(configuration.top)); 519 fixedNavDiv.addContent(top); 520 } 521 522 /** 523 * Adds the user specified bottom. 524 * 525 * @param htmlTree the content tree to which user specified bottom will be added 526 */ 527 public void addBottom(Content htmlTree) { 528 Content bottom = new RawHtml(replaceDocRootDir(configuration.bottom)); 529 Content small = HtmlTree.SMALL(bottom); 530 Content p = HtmlTree.P(HtmlStyle.legalCopy, small); 531 htmlTree.addContent(p); 532 } 533 534 /** 535 * Adds the navigation bar for the Html page at the top and and the bottom. 536 * 537 * @param header If true print navigation bar at the top of the page else 538 * @param htmlTree the HtmlTree to which the nav links will be added 539 */ 540 protected void addNavLinks(boolean header, Content htmlTree) { 541 if (!configuration.nonavbar) { 542 Content tree = (configuration.allowTag(HtmlTag.NAV)) 543 ? HtmlTree.NAV() 544 : htmlTree; 545 String allClassesId = "allclasses_"; 546 HtmlTree navDiv = new HtmlTree(HtmlTag.DIV); 547 fixedNavDiv.addStyle(HtmlStyle.fixedNav); 548 Content skipNavLinks = configuration.getContent("doclet.Skip_navigation_links"); 549 if (header) { 550 fixedNavDiv.addContent(HtmlConstants.START_OF_TOP_NAVBAR); 551 navDiv.addStyle(HtmlStyle.topNav); 552 allClassesId += "navbar_top"; 553 Content a = getMarkerAnchor(SectionName.NAVBAR_TOP); 554 //WCAG - Hyperlinks should contain text or an image with alt text - for AT tools 555 navDiv.addContent(a); 556 Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav, getHyperLink( 557 getDocLink(SectionName.SKIP_NAVBAR_TOP), skipNavLinks, 558 skipNavLinks.toString(), "")); 559 navDiv.addContent(skipLinkContent); 560 } else { 561 tree.addContent(HtmlConstants.START_OF_BOTTOM_NAVBAR); 562 navDiv.addStyle(HtmlStyle.bottomNav); 563 allClassesId += "navbar_bottom"; 564 Content a = getMarkerAnchor(SectionName.NAVBAR_BOTTOM); 565 navDiv.addContent(a); 566 Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav, getHyperLink( 567 getDocLink(SectionName.SKIP_NAVBAR_BOTTOM), skipNavLinks, 568 skipNavLinks.toString(), "")); 569 navDiv.addContent(skipLinkContent); 570 } 571 if (header) { 572 navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_TOP_FIRSTROW)); 573 } else { 574 navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_BOTTOM_FIRSTROW)); 575 } 576 HtmlTree navList = new HtmlTree(HtmlTag.UL); 577 navList.addStyle(HtmlStyle.navList); 578 navList.addAttr(HtmlAttr.TITLE, 579 configuration.getText("doclet.Navigation")); 580 if (configuration.createoverview) { 581 navList.addContent(getNavLinkContents()); 582 } 583 if (configuration.showModules) { 584 if (configuration.modules.size() == 1) { 585 navList.addContent(getNavLinkModule(configuration.modules.first())); 586 } else if (!configuration.modules.isEmpty()) { 587 navList.addContent(getNavLinkModule()); 588 } 589 } 590 if (configuration.packages.size() == 1) { 591 navList.addContent(getNavLinkPackage(configuration.packages.first())); 592 } else if (!configuration.packages.isEmpty()) { 593 navList.addContent(getNavLinkPackage()); 594 } 595 navList.addContent(getNavLinkClass()); 596 if(configuration.classuse) { 597 navList.addContent(getNavLinkClassUse()); 598 } 599 if(configuration.createtree) { 600 navList.addContent(getNavLinkTree()); 601 } 602 if(!(configuration.nodeprecated || 603 configuration.nodeprecatedlist)) { 604 navList.addContent(getNavLinkDeprecated()); 605 } 606 if(configuration.createindex) { 607 navList.addContent(getNavLinkIndex()); 608 } 609 if (!configuration.nohelp) { 610 navList.addContent(getNavLinkHelp()); 611 } 612 navDiv.addContent(navList); 613 Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, getUserHeaderFooter(header)); 614 navDiv.addContent(aboutDiv); 615 if (header) { 616 fixedNavDiv.addContent(navDiv); 617 } else { 618 tree.addContent(navDiv); 619 } 620 Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious(), getNavLinkNext()); 621 Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav); 622 if (configuration.frames) { 623 Content ulFrames = HtmlTree.UL(HtmlStyle.navList, 624 getNavShowLists(), getNavHideLists(filename)); 625 subDiv.addContent(ulFrames); 626 } 627 HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex()); 628 ulAllClasses.addAttr(HtmlAttr.ID, allClassesId); 629 subDiv.addContent(ulAllClasses); 630 if (header && configuration.createindex) { 631 String searchValueId = "search"; 632 String reset = "reset"; 633 HtmlTree inputText = HtmlTree.INPUT("text", searchValueId, searchValueId); 634 HtmlTree inputReset = HtmlTree.INPUT(reset, reset, reset); 635 Content searchTxt = configuration.getContent("doclet.search"); 636 HtmlTree liInput = HtmlTree.LI(HtmlTree.LABEL(searchValueId, searchTxt)); 637 liInput.addContent(inputText); 638 liInput.addContent(inputReset); 639 HtmlTree ulSearch = HtmlTree.UL(HtmlStyle.navListSearch, liInput); 640 subDiv.addContent(ulSearch); 641 } 642 subDiv.addContent(getAllClassesLinkScript(allClassesId)); 643 addSummaryDetailLinks(subDiv); 644 if (header) { 645 subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_TOP)); 646 fixedNavDiv.addContent(subDiv); 647 fixedNavDiv.addContent(HtmlConstants.END_OF_TOP_NAVBAR); 648 tree.addContent(fixedNavDiv); 649 HtmlTree paddingDiv = HtmlTree.DIV(HtmlStyle.navPadding, Contents.SPACE); 650 tree.addContent(paddingDiv); 651 HtmlTree scriptTree = HtmlTree.SCRIPT(); 652 String scriptCode = "<!--\n" 653 + "$('.navPadding').css('padding-top', $('.fixedNav').css(\"height\"));\n" 654 + "//-->\n"; 655 RawHtml scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL)); 656 scriptTree.addContent(scriptContent); 657 tree.addContent(scriptTree); 658 } else { 659 subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_BOTTOM)); 660 tree.addContent(subDiv); 661 tree.addContent(HtmlConstants.END_OF_BOTTOM_NAVBAR); 662 } 663 if (configuration.allowTag(HtmlTag.NAV)) { 664 htmlTree.addContent(tree); 665 } 666 } 667 } 668 669 /** 670 * Get the word "NEXT" to indicate that no link is available. Override 671 * this method to customize next link. 672 * 673 * @return a content tree for the link 674 */ 675 protected Content getNavLinkNext() { 676 return getNavLinkNext(null); 677 } 678 679 /** 680 * Get the word "PREV" to indicate that no link is available. Override 681 * this method to customize prev link. 682 * 683 * @return a content tree for the link 684 */ 685 protected Content getNavLinkPrevious() { 686 return getNavLinkPrevious(null); 687 } 688 689 /** 690 * Do nothing. This is the default method. 691 */ 692 protected void addSummaryDetailLinks(Content navDiv) { 693 } 694 695 /** 696 * Get link to the "overview-summary.html" page. 697 * 698 * @return a content tree for the link 699 */ 700 protected Content getNavLinkContents() { 701 Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.overviewSummary(configuration.frames)), 702 contents.overviewLabel, "", ""); 703 Content li = HtmlTree.LI(linkContent); 704 return li; 705 } 706 707 /** 708 * Get link to the module summary page for the module passed. 709 * 710 * @param mdle Module to which link will be generated 711 * @return a content tree for the link 712 */ 713 protected Content getNavLinkModule(ModuleElement mdle) { 714 Content linkContent = getModuleLink(mdle, contents.moduleLabel); 715 Content li = HtmlTree.LI(linkContent); 716 return li; 717 } 718 719 /** 720 * Get the word "Module", to indicate that link is not available here. 721 * 722 * @return a content tree for the link 723 */ 724 protected Content getNavLinkModule() { 725 Content li = HtmlTree.LI(contents.moduleLabel); 726 return li; 727 } 728 729 /** 730 * Get link to the "package-summary.html" page for the package passed. 731 * 732 * @param pkg Package to which link will be generated 733 * @return a content tree for the link 734 */ 735 protected Content getNavLinkPackage(PackageElement pkg) { 736 Content linkContent = getPackageLink(pkg, contents.packageLabel); 737 Content li = HtmlTree.LI(linkContent); 738 return li; 739 } 740 741 /** 742 * Get the word "Package" , to indicate that link is not available here. 743 * 744 * @return a content tree for the link 745 */ 746 protected Content getNavLinkPackage() { 747 Content li = HtmlTree.LI(contents.packageLabel); 748 return li; 749 } 750 751 /** 752 * Get the word "Use", to indicate that link is not available. 753 * 754 * @return a content tree for the link 755 */ 756 protected Content getNavLinkClassUse() { 757 Content li = HtmlTree.LI(contents.useLabel); 758 return li; 759 } 760 761 /** 762 * Get link for previous file. 763 * 764 * @param prev File name for the prev link 765 * @return a content tree for the link 766 */ 767 public Content getNavLinkPrevious(DocPath prev) { 768 Content li; 769 if (prev != null) { 770 li = HtmlTree.LI(getHyperLink(prev, contents.prevLabel, "", "")); 771 } 772 else 773 li = HtmlTree.LI(contents.prevLabel); 774 return li; 775 } 776 777 /** 778 * Get link for next file. If next is null, just print the label 779 * without linking it anywhere. 780 * 781 * @param next File name for the next link 782 * @return a content tree for the link 783 */ 784 public Content getNavLinkNext(DocPath next) { 785 Content li; 786 if (next != null) { 787 li = HtmlTree.LI(getHyperLink(next, contents.nextLabel, "", "")); 788 } 789 else 790 li = HtmlTree.LI(contents.nextLabel); 791 return li; 792 } 793 794 /** 795 * Get "FRAMES" link, to switch to the frame version of the output. 796 * 797 * @param link File to be linked, "index.html" 798 * @return a content tree for the link 799 */ 800 protected Content getNavShowLists(DocPath link) { 801 DocLink dl = new DocLink(link, path.getPath(), null); 802 Content framesContent = getHyperLink(dl, contents.framesLabel, "", "_top"); 803 Content li = HtmlTree.LI(framesContent); 804 return li; 805 } 806 807 /** 808 * Get "FRAMES" link, to switch to the frame version of the output. 809 * 810 * @return a content tree for the link 811 */ 812 protected Content getNavShowLists() { 813 return getNavShowLists(pathToRoot.resolve(DocPaths.INDEX)); 814 } 815 816 /** 817 * Get "NO FRAMES" link, to switch to the non-frame version of the output. 818 * 819 * @param link File to be linked 820 * @return a content tree for the link 821 */ 822 protected Content getNavHideLists(DocPath link) { 823 Content noFramesContent = getHyperLink(link, contents.noFramesLabel, "", "_top"); 824 Content li = HtmlTree.LI(noFramesContent); 825 return li; 826 } 827 828 /** 829 * Get "Tree" link in the navigation bar. If there is only one package 830 * specified on the command line, then the "Tree" link will be to the 831 * only "package-tree.html" file otherwise it will be to the 832 * "overview-tree.html" file. 833 * 834 * @return a content tree for the link 835 */ 836 protected Content getNavLinkTree() { 837 List<PackageElement> packages = new ArrayList<>(configuration.getSpecifiedPackageElements()); 838 DocPath docPath = packages.size() == 1 && configuration.getSpecifiedTypeElements().isEmpty() 839 ? pathString(packages.get(0), DocPaths.PACKAGE_TREE) 840 : pathToRoot.resolve(DocPaths.OVERVIEW_TREE); 841 return HtmlTree.LI(getHyperLink(docPath, contents.treeLabel, "", "")); 842 } 843 844 /** 845 * Get the overview tree link for the main tree. 846 * 847 * @param label the label for the link 848 * @return a content tree for the link 849 */ 850 protected Content getNavLinkMainTree(String label) { 851 Content mainTreeContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE), 852 new StringContent(label)); 853 Content li = HtmlTree.LI(mainTreeContent); 854 return li; 855 } 856 857 /** 858 * Get the word "Class", to indicate that class link is not available. 859 * 860 * @return a content tree for the link 861 */ 862 protected Content getNavLinkClass() { 863 Content li = HtmlTree.LI(contents.classLabel); 864 return li; 865 } 866 867 /** 868 * Get "Deprecated" API link in the navigation bar. 869 * 870 * @return a content tree for the link 871 */ 872 protected Content getNavLinkDeprecated() { 873 Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.DEPRECATED_LIST), 874 contents.deprecatedLabel, "", ""); 875 Content li = HtmlTree.LI(linkContent); 876 return li; 877 } 878 879 /** 880 * Get link for generated index. If the user has used "-splitindex" 881 * command line option, then link to file "index-files/index-1.html" is 882 * generated otherwise link to file "index-all.html" is generated. 883 * 884 * @return a content tree for the link 885 */ 886 protected Content getNavLinkClassIndex() { 887 Content allClassesContent = getHyperLink(pathToRoot.resolve( 888 DocPaths.AllClasses(configuration.frames)), 889 contents.allClassesLabel, "", ""); 890 Content li = HtmlTree.LI(allClassesContent); 891 return li; 892 } 893 894 /** 895 * Get link for generated class index. 896 * 897 * @return a content tree for the link 898 */ 899 protected Content getNavLinkIndex() { 900 Content linkContent = getHyperLink(pathToRoot.resolve( 901 (configuration.splitindex 902 ? DocPaths.INDEX_FILES.resolve(DocPaths.indexN(1)) 903 : DocPaths.INDEX_ALL)), 904 contents.indexLabel, "", ""); 905 Content li = HtmlTree.LI(linkContent); 906 return li; 907 } 908 909 /** 910 * Get help file link. If user has provided a help file, then generate a 911 * link to the user given file, which is already copied to current or 912 * destination directory. 913 * 914 * @return a content tree for the link 915 */ 916 protected Content getNavLinkHelp() { 917 String helpfile = configuration.helpfile; 918 DocPath helpfilenm; 919 if (helpfile.isEmpty()) { 920 helpfilenm = DocPaths.HELP_DOC; 921 } else { 922 DocFile file = DocFile.createFileForInput(configuration, helpfile); 923 helpfilenm = DocPath.create(file.getName()); 924 } 925 Content linkContent = getHyperLink(pathToRoot.resolve(helpfilenm), 926 contents.helpLabel, "", ""); 927 Content li = HtmlTree.LI(linkContent); 928 return li; 929 } 930 931 /** 932 * Add gap between navigation bar elements. 933 * 934 * @param liNav the content tree to which the gap will be added 935 */ 936 protected void addNavGap(Content liNav) { 937 liNav.addContent(Contents.SPACE); 938 liNav.addContent("|"); 939 liNav.addContent(Contents.SPACE); 940 } 941 942 /** 943 * Get summary table header. 944 * 945 * @param header the header for the table 946 * @param scope the scope of the headers 947 * @return a content tree for the header 948 */ 949 public Content getSummaryTableHeader(List<String> header, String scope) { 950 Content tr = new HtmlTree(HtmlTag.TR); 951 final int size = header.size(); 952 Content tableHeader; 953 if (size == 2) { 954 tableHeader = new StringContent(header.get(0)); 955 tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader)); 956 tableHeader = new StringContent(header.get(1)); 957 tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader)); 958 return tr; 959 } 960 for (int i = 0; i < size; i++) { 961 tableHeader = new StringContent(header.get(i)); 962 if (i == 0) 963 tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader)); 964 else if (i == 1) 965 tr.addContent(HtmlTree.TH(HtmlStyle.colSecond, scope, tableHeader)); 966 else if (i == (size - 1)) 967 tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader)); 968 else 969 tr.addContent(HtmlTree.TH(scope, tableHeader)); 970 } 971 return tr; 972 } 973 974 /** 975 * Get table caption. 976 * 977 * @param rawText the caption for the table which could be raw Html 978 * @return a content tree for the caption 979 */ 980 public Content getTableCaption(Content title) { 981 Content captionSpan = HtmlTree.SPAN(title); 982 Content space = Contents.SPACE; 983 Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space); 984 Content caption = HtmlTree.CAPTION(captionSpan); 985 caption.addContent(tabSpan); 986 return caption; 987 } 988 989 /** 990 * Get the marker anchor which will be added to the documentation tree. 991 * 992 * @param anchorName the anchor name attribute 993 * @return a content tree for the marker anchor 994 */ 995 public Content getMarkerAnchor(String anchorName) { 996 return getMarkerAnchor(getName(anchorName), null); 997 } 998 999 /** 1000 * Get the marker anchor which will be added to the documentation tree. 1001 * 1002 * @param sectionName the section name anchor attribute for page 1003 * @return a content tree for the marker anchor 1004 */ 1005 public Content getMarkerAnchor(SectionName sectionName) { 1006 return getMarkerAnchor(sectionName.getName(), null); 1007 } 1008 1009 /** 1010 * Get the marker anchor which will be added to the documentation tree. 1011 * 1012 * @param sectionName the section name anchor attribute for page 1013 * @param anchorName the anchor name combined with section name attribute for the page 1014 * @return a content tree for the marker anchor 1015 */ 1016 public Content getMarkerAnchor(SectionName sectionName, String anchorName) { 1017 return getMarkerAnchor(sectionName.getName() + getName(anchorName), null); 1018 } 1019 1020 /** 1021 * Get the marker anchor which will be added to the documentation tree. 1022 * 1023 * @param anchorName the anchor name or id attribute 1024 * @param anchorContent the content that should be added to the anchor 1025 * @return a content tree for the marker anchor 1026 */ 1027 public Content getMarkerAnchor(String anchorName, Content anchorContent) { 1028 if (anchorContent == null) 1029 anchorContent = new Comment(" "); 1030 Content markerAnchor = HtmlTree.A(configuration.htmlVersion, anchorName, anchorContent); 1031 return markerAnchor; 1032 } 1033 1034 /** 1035 * Returns a packagename content. 1036 * 1037 * @param packageElement the package to check 1038 * @return package name content 1039 */ 1040 public Content getPackageName(PackageElement packageElement) { 1041 return packageElement == null || packageElement.isUnnamed() 1042 ? contents.defaultPackageLabel 1043 : getPackageLabel(packageElement.getQualifiedName()); 1044 } 1045 1046 /** 1047 * Returns a package name label. 1048 * 1049 * @param packageName the package name 1050 * @return the package name content 1051 */ 1052 public Content getPackageLabel(CharSequence packageName) { 1053 return new StringContent(packageName); 1054 } 1055 1056 /** 1057 * Return the path to the class page for a typeElement. 1058 * 1059 * @param te TypeElement for which the path is requested. 1060 * @param name Name of the file(doesn't include path). 1061 */ 1062 protected DocPath pathString(TypeElement te, DocPath name) { 1063 return pathString(utils.containingPackage(te), name); 1064 } 1065 1066 /** 1067 * Return path to the given file name in the given package. So if the name 1068 * passed is "Object.html" and the name of the package is "java.lang", and 1069 * if the relative path is "../.." then returned string will be 1070 * "../../java/lang/Object.html" 1071 * 1072 * @param packageElement Package in which the file name is assumed to be. 1073 * @param name File name, to which path string is. 1074 */ 1075 protected DocPath pathString(PackageElement packageElement, DocPath name) { 1076 return pathToRoot.resolve(DocPath.forPackage(packageElement).resolve(name)); 1077 } 1078 1079 /** 1080 * Given a package, return the name to be used in HTML anchor tag. 1081 * @param packageElement the package. 1082 * @return the name to be used in HTML anchor tag. 1083 */ 1084 public String getPackageAnchorName(PackageElement packageElement) { 1085 return packageElement == null || packageElement.isUnnamed() 1086 ? SectionName.UNNAMED_PACKAGE_ANCHOR.getName() 1087 : utils.getPackageName(packageElement); 1088 } 1089 1090 /** 1091 * Return the link to the given package. 1092 * 1093 * @param packageElement the package to link to. 1094 * @param label the label for the link. 1095 * @return a content tree for the package link. 1096 */ 1097 public Content getPackageLink(PackageElement packageElement, CharSequence label) { 1098 return getPackageLink(packageElement, new StringContent(label)); 1099 } 1100 1101 public Content getPackageLink(PackageElement packageElement) { 1102 StringContent content = packageElement.isUnnamed() 1103 ? new StringContent() 1104 : new StringContent(utils.getPackageName(packageElement)); 1105 return getPackageLink(packageElement, content); 1106 } 1107 1108 /** 1109 * Return the link to the given package. 1110 * 1111 * @param packageElement the package to link to. 1112 * @param label the label for the link. 1113 * @return a content tree for the package link. 1114 */ 1115 public Content getPackageLink(PackageElement packageElement, Content label) { 1116 boolean included = packageElement != null && utils.isIncluded(packageElement); 1117 if (!included) { 1118 for (PackageElement p : configuration.packages) { 1119 if (p.equals(packageElement)) { 1120 included = true; 1121 break; 1122 } 1123 } 1124 } 1125 if (included || packageElement == null) { 1126 return getHyperLink(pathString(packageElement, DocPaths.PACKAGE_SUMMARY), 1127 label); 1128 } else { 1129 DocLink crossPkgLink = getCrossPackageLink(utils.getPackageName(packageElement)); 1130 if (crossPkgLink != null) { 1131 return getHyperLink(crossPkgLink, label); 1132 } else { 1133 return label; 1134 } 1135 } 1136 } 1137 1138 /** 1139 * Get Module link. 1140 * 1141 * @param mdle the module being documented 1142 * @param label tag for the link 1143 * @return a content for the module link 1144 */ 1145 public Content getModuleLink(ModuleElement mdle, Content label) { 1146 boolean included = utils.isIncluded(mdle); 1147 return (included) 1148 ? getHyperLink(pathToRoot.resolve(DocPaths.moduleSummary(mdle)), label, "", "") 1149 : label; 1150 } 1151 1152 public Content interfaceName(TypeElement typeElement, boolean qual) { 1153 Content name = new StringContent((qual) 1154 ? typeElement.getQualifiedName() 1155 : utils.getSimpleName(typeElement)); 1156 return (utils.isInterface(typeElement)) ? HtmlTree.SPAN(HtmlStyle.interfaceName, name) : name; 1157 } 1158 1159 /** 1160 * Add the link to the content tree. 1161 * 1162 * @param typeElement program element typeElement for which the link will be added 1163 * @param label label for the link 1164 * @param htmltree the content tree to which the link will be added 1165 */ 1166 public void addSrcLink(Element typeElement, Content label, Content htmltree) { 1167 if (typeElement == null) { 1168 return; 1169 } 1170 TypeElement te = utils.getEnclosingTypeElement(typeElement); 1171 if (te == null) { 1172 // must be a typeElement since in has no containing class. 1173 te = (TypeElement) typeElement; 1174 } 1175 DocPath href = pathToRoot 1176 .resolve(DocPaths.SOURCE_OUTPUT) 1177 .resolve(DocPath.forClass(utils, te)); 1178 Content linkContent = getHyperLink(href 1179 .fragment(SourceToHTMLConverter.getAnchorName(utils, typeElement)), label, "", ""); 1180 htmltree.addContent(linkContent); 1181 } 1182 1183 /** 1184 * Return the link to the given class. 1185 * 1186 * @param linkInfo the information about the link. 1187 * 1188 * @return the link for the given class. 1189 */ 1190 public Content getLink(LinkInfoImpl linkInfo) { 1191 LinkFactoryImpl factory = new LinkFactoryImpl(this); 1192 return factory.getLink(linkInfo); 1193 } 1194 1195 /** 1196 * Return the type parameters for the given class. 1197 * 1198 * @param linkInfo the information about the link. 1199 * @return the type for the given class. 1200 */ 1201 public Content getTypeParameterLinks(LinkInfoImpl linkInfo) { 1202 LinkFactoryImpl factory = new LinkFactoryImpl(this); 1203 return factory.getTypeParameterLinks(linkInfo, false); 1204 } 1205 1206 /************************************************************* 1207 * Return a class cross link to external class documentation. 1208 * The name must be fully qualified to determine which package 1209 * the class is in. The -link option does not allow users to 1210 * link to external classes in the "default" package. 1211 * 1212 * @param qualifiedClassName the qualified name of the external class. 1213 * @param refMemName the name of the member being referenced. This should 1214 * be null or empty string if no member is being referenced. 1215 * @param label the label for the external link. 1216 * @param strong true if the link should be strong. 1217 * @param style the style of the link. 1218 * @param code true if the label should be code font. 1219 */ 1220 public Content getCrossClassLink(String qualifiedClassName, String refMemName, 1221 Content label, boolean strong, String style, 1222 boolean code) { 1223 String className = ""; 1224 String packageName = qualifiedClassName == null ? "" : qualifiedClassName; 1225 int periodIndex; 1226 while ((periodIndex = packageName.lastIndexOf('.')) != -1) { 1227 className = packageName.substring(periodIndex + 1, packageName.length()) + 1228 (className.length() > 0 ? "." + className : ""); 1229 Content defaultLabel = new StringContent(className); 1230 if (code) 1231 defaultLabel = HtmlTree.CODE(defaultLabel); 1232 packageName = packageName.substring(0, periodIndex); 1233 if (getCrossPackageLink(packageName) != null) { 1234 /* 1235 The package exists in external documentation, so link to the external 1236 class (assuming that it exists). This is definitely a limitation of 1237 the -link option. There are ways to determine if an external package 1238 exists, but no way to determine if the external class exists. We just 1239 have to assume that it does. 1240 */ 1241 DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot, 1242 className + ".html", refMemName); 1243 return getHyperLink(link, 1244 (label == null) || label.isEmpty() ? defaultLabel : label, 1245 strong, style, 1246 configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName), 1247 ""); 1248 } 1249 } 1250 return null; 1251 } 1252 1253 public boolean isClassLinkable(TypeElement typeElement) { 1254 if (utils.isIncluded(typeElement)) { 1255 return configuration.isGeneratedDoc(typeElement); 1256 } 1257 return configuration.extern.isExternal(typeElement); 1258 } 1259 1260 public DocLink getCrossPackageLink(String pkgName) { 1261 return configuration.extern.getExternalLink(pkgName, pathToRoot, 1262 DocPaths.PACKAGE_SUMMARY.getPath()); 1263 } 1264 1265 /** 1266 * Get the class link. 1267 * 1268 * @param context the id of the context where the link will be added 1269 * @param element to link to 1270 * @return a content tree for the link 1271 */ 1272 public Content getQualifiedClassLink(LinkInfoImpl.Kind context, Element element) { 1273 LinkInfoImpl linkInfoImpl = new LinkInfoImpl(configuration, context, (TypeElement)element); 1274 return getLink(linkInfoImpl.label(utils.getFullyQualifiedName(element))); 1275 } 1276 1277 /** 1278 * Add the class link. 1279 * 1280 * @param context the id of the context where the link will be added 1281 * @param typeElement to link to 1282 * @param contentTree the content tree to which the link will be added 1283 */ 1284 public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) { 1285 addPreQualifiedClassLink(context, typeElement, false, contentTree); 1286 } 1287 1288 /** 1289 * Retrieve the class link with the package portion of the label in 1290 * plain text. If the qualifier is excluded, it will not be included in the 1291 * link label. 1292 * 1293 * @param typeElement the class to link to. 1294 * @param isStrong true if the link should be strong. 1295 * @return the link with the package portion of the label in plain text. 1296 */ 1297 public Content getPreQualifiedClassLink(LinkInfoImpl.Kind context, 1298 TypeElement typeElement, boolean isStrong) { 1299 ContentBuilder classlink = new ContentBuilder(); 1300 PackageElement pkg = utils.containingPackage(typeElement); 1301 if (pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) { 1302 classlink.addContent(getEnclosingPackageName(typeElement)); 1303 } 1304 classlink.addContent(getLink(new LinkInfoImpl(configuration, 1305 context, typeElement).label(utils.getSimpleName(typeElement)).strong(isStrong))); 1306 return classlink; 1307 } 1308 1309 /** 1310 * Add the class link with the package portion of the label in 1311 * plain text. If the qualifier is excluded, it will not be included in the 1312 * link label. 1313 * 1314 * @param context the id of the context where the link will be added 1315 * @param typeElement the class to link to 1316 * @param isStrong true if the link should be strong 1317 * @param contentTree the content tree to which the link with be added 1318 */ 1319 public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, 1320 TypeElement typeElement, boolean isStrong, Content contentTree) { 1321 PackageElement pkg = utils.containingPackage(typeElement); 1322 if(pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) { 1323 contentTree.addContent(getEnclosingPackageName(typeElement)); 1324 } 1325 LinkInfoImpl linkinfo = new LinkInfoImpl(configuration, context, typeElement) 1326 .label(utils.getSimpleName(typeElement)) 1327 .strong(isStrong); 1328 Content link = getLink(linkinfo); 1329 contentTree.addContent(link); 1330 } 1331 1332 /** 1333 * Add the class link, with only class name as the strong link and prefixing 1334 * plain package name. 1335 * 1336 * @param context the id of the context where the link will be added 1337 * @param typeElement the class to link to 1338 * @param contentTree the content tree to which the link with be added 1339 */ 1340 public void addPreQualifiedStrongClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) { 1341 addPreQualifiedClassLink(context, typeElement, true, contentTree); 1342 } 1343 1344 /** 1345 * Get the link for the given member. 1346 * 1347 * @param context the id of the context where the link will be added 1348 * @param element the member being linked to 1349 * @param label the label for the link 1350 * @return a content tree for the element link 1351 */ 1352 public Content getDocLink(LinkInfoImpl.Kind context, Element element, CharSequence label) { 1353 return getDocLink(context, utils.getEnclosingTypeElement(element), element, 1354 new StringContent(label)); 1355 } 1356 1357 /** 1358 * Return the link for the given member. 1359 * 1360 * @param context the id of the context where the link will be printed. 1361 * @param element the member being linked to. 1362 * @param label the label for the link. 1363 * @param strong true if the link should be strong. 1364 * @return the link for the given member. 1365 */ 1366 public Content getDocLink(LinkInfoImpl.Kind context, Element element, CharSequence label, 1367 boolean strong) { 1368 return getDocLink(context, utils.getEnclosingTypeElement(element), element, label, strong); 1369 } 1370 1371 /** 1372 * Return the link for the given member. 1373 * 1374 * @param context the id of the context where the link will be printed. 1375 * @param typeElement the typeElement that we should link to. This is not 1376 necessarily equal to element.containingClass(). We may be 1377 inheriting comments. 1378 * @param element the member being linked to. 1379 * @param label the label for the link. 1380 * @param strong true if the link should be strong. 1381 * @return the link for the given member. 1382 */ 1383 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1384 CharSequence label, boolean strong) { 1385 return getDocLink(context, typeElement, element, label, strong, false); 1386 } 1387 1388 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1389 Content label, boolean strong) { 1390 return getDocLink(context, typeElement, element, label, strong, false); 1391 } 1392 1393 /** 1394 * Return the link for the given member. 1395 * 1396 * @param context the id of the context where the link will be printed. 1397 * @param typeElement the typeElement that we should link to. This is not 1398 necessarily equal to element.containingClass(). We may be 1399 inheriting comments. 1400 * @param element the member being linked to. 1401 * @param label the label for the link. 1402 * @param strong true if the link should be strong. 1403 * @param isProperty true if the element parameter is a JavaFX property. 1404 * @return the link for the given member. 1405 */ 1406 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1407 CharSequence label, boolean strong, boolean isProperty) { 1408 return getDocLink(context, typeElement, element, new StringContent(check(label)), strong, isProperty); 1409 } 1410 1411 CharSequence check(CharSequence s) { 1412 Matcher m = IMPROPER_HTML_CHARS.matcher(s); 1413 if (m.matches()) { 1414 throw new IllegalArgumentException(s.toString()); 1415 } 1416 return s; 1417 } 1418 1419 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1420 Content label, boolean strong, boolean isProperty) { 1421 if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) { 1422 return label; 1423 } else if (utils.isExecutableElement(element)) { 1424 ExecutableElement ee = (ExecutableElement)element; 1425 return getLink(new LinkInfoImpl(configuration, context, typeElement) 1426 .label(label) 1427 .where(getName(getAnchor(ee, isProperty))) 1428 .strong(strong)); 1429 } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) { 1430 return getLink(new LinkInfoImpl(configuration, context, typeElement) 1431 .label(label) 1432 .where(getName(element.getSimpleName().toString())) 1433 .strong(strong)); 1434 } else { 1435 return label; 1436 } 1437 } 1438 1439 /** 1440 * Return the link for the given member. 1441 * 1442 * @param context the id of the context where the link will be added 1443 * @param typeElement the typeElement that we should link to. This is not 1444 necessarily equal to element.containingClass(). We may be 1445 inheriting comments 1446 * @param element the member being linked to 1447 * @param label the label for the link 1448 * @return the link for the given member 1449 */ 1450 public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, 1451 Content label) { 1452 if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) { 1453 return label; 1454 } else if (utils.isExecutableElement(element)) { 1455 ExecutableElement emd = (ExecutableElement) element; 1456 return getLink(new LinkInfoImpl(configuration, context, typeElement) 1457 .label(label) 1458 .where(getName(getAnchor(emd)))); 1459 } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) { 1460 return getLink(new LinkInfoImpl(configuration, context, typeElement) 1461 .label(label).where(getName(element.getSimpleName().toString()))); 1462 } else { 1463 return label; 1464 } 1465 } 1466 1467 public String getAnchor(ExecutableElement executableElement) { 1468 return getAnchor(executableElement, false); 1469 } 1470 1471 public String getAnchor(ExecutableElement executableElement, boolean isProperty) { 1472 if (isProperty) { 1473 return executableElement.getSimpleName().toString(); 1474 } 1475 String signature = utils.signature(executableElement); 1476 StringBuilder signatureParsed = new StringBuilder(); 1477 int counter = 0; 1478 for (int i = 0; i < signature.length(); i++) { 1479 char c = signature.charAt(i); 1480 if (c == '<') { 1481 counter++; 1482 } else if (c == '>') { 1483 counter--; 1484 } else if (counter == 0) { 1485 signatureParsed.append(c); 1486 } 1487 } 1488 return utils.getSimpleName(executableElement) + signatureParsed.toString(); 1489 } 1490 1491 public Content seeTagToContent(Element element, DocTree see) { 1492 1493 Kind kind = see.getKind(); 1494 if (!(kind == LINK || kind == SEE || kind == LINK_PLAIN)) { 1495 return new ContentBuilder(); 1496 } 1497 1498 CommentHelper ch = utils.getCommentHelper(element); 1499 String tagName = ch.getTagName(see); 1500 String seetext = replaceDocRootDir(utils.normalizeNewlines(ch.getText(see)).toString()); 1501 // Check if @see is an href or "string" 1502 if (seetext.startsWith("<") || seetext.startsWith("\"")) { 1503 return new RawHtml(seetext); 1504 } 1505 boolean isLinkPlain = kind == LINK_PLAIN; 1506 Content label = plainOrCode(isLinkPlain, new RawHtml(ch.getLabel(configuration, see))); 1507 1508 //The text from the @see tag. We will output this text when a label is not specified. 1509 Content text = plainOrCode(kind == LINK_PLAIN, new RawHtml(seetext)); 1510 1511 TypeElement refClass = ch.getReferencedClass(configuration, see); 1512 String refClassName = ch.getReferencedClassName(configuration, see); 1513 Element refMem = ch.getReferencedMember(configuration, see); 1514 String refMemName = ch.getReferencedMemberName(see); 1515 1516 if (refMemName == null && refMem != null) { 1517 refMemName = refMem.toString(); 1518 } 1519 if (refClass == null) { 1520 //@see is not referencing an included class 1521 PackageElement refPackage = ch.getReferencedPackage(configuration, see); 1522 if (refPackage != null && utils.isIncluded(refPackage)) { 1523 //@see is referencing an included package 1524 if (label.isEmpty()) 1525 label = plainOrCode(isLinkPlain, 1526 new StringContent(refPackage.getQualifiedName())); 1527 return getPackageLink(refPackage, label); 1528 } else { 1529 // @see is not referencing an included class or package. Check for cross links. 1530 Content classCrossLink; 1531 DocLink packageCrossLink = getCrossPackageLink(refClassName); 1532 if (packageCrossLink != null) { 1533 // Package cross link found 1534 return getHyperLink(packageCrossLink, 1535 (label.isEmpty() ? text : label)); 1536 } else if ((classCrossLink = getCrossClassLink(refClassName, 1537 refMemName, label, false, "", !isLinkPlain)) != null) { 1538 // Class cross link found (possibly to a member in the class) 1539 return classCrossLink; 1540 } else { 1541 // No cross link found so print warning 1542 messages.warning(ch.getDocTreePath(see), 1543 "doclet.see.class_or_package_not_found", 1544 "@" + tagName, 1545 seetext); 1546 return (label.isEmpty() ? text: label); 1547 } 1548 } 1549 } else if (refMemName == null) { 1550 // Must be a class reference since refClass is not null and refMemName is null. 1551 if (label.isEmpty()) { 1552 /* 1553 * it seems to me this is the right thing to do, but it causes comparator failures. 1554 */ 1555 if (!configuration.backwardCompatibility) { 1556 StringContent content = utils.isEnclosingPackageIncluded(refClass) 1557 ? new StringContent(utils.getSimpleName(refClass)) 1558 : new StringContent(utils.getFullyQualifiedName(refClass)); 1559 label = plainOrCode(isLinkPlain, content); 1560 } else { 1561 label = plainOrCode(isLinkPlain, 1562 new StringContent(utils.getSimpleName(refClass))); 1563 } 1564 1565 } 1566 return getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, refClass) 1567 .label(label)); 1568 } else if (refMem == null) { 1569 // Must be a member reference since refClass is not null and refMemName is not null. 1570 // However, refMem is null, so this referenced member does not exist. 1571 return (label.isEmpty() ? text: label); 1572 } else { 1573 // Must be a member reference since refClass is not null and refMemName is not null. 1574 // refMem is not null, so this @see tag must be referencing a valid member. 1575 TypeElement containing = utils.getEnclosingTypeElement(refMem); 1576 1577 // Find the enclosing type where the method is actually visible, 1578 // in the inheritance hierarchy. 1579 if (refMem.getKind() == ElementKind.METHOD) { 1580 VisibleMemberMap vmm = configuration. 1581 getVisibleMemberMap(containing, VisibleMemberMap.Kind.METHODS); 1582 ExecutableElement overriddenMethod = vmm.getVisibleMethod((ExecutableElement)refMem); 1583 if (overriddenMethod != null) 1584 containing = utils.getEnclosingTypeElement(overriddenMethod); 1585 } 1586 if (ch.getText(see).trim().startsWith("#") && 1587 ! (utils.isPublic(containing) || utils.isLinkable(containing))) { 1588 // Since the link is relative and the holder is not even being 1589 // documented, this must be an inherited link. Redirect it. 1590 // The current class either overrides the referenced member or 1591 // inherits it automatically. 1592 if (this instanceof ClassWriterImpl) { 1593 containing = ((ClassWriterImpl) this).getTypeElement(); 1594 } else if (!utils.isPublic(containing)) { 1595 messages.warning( 1596 ch.getDocTreePath(see), "doclet.see.class_or_package_not_accessible", 1597 tagName, utils.getFullyQualifiedName(containing)); 1598 } else { 1599 messages.warning( 1600 ch.getDocTreePath(see), "doclet.see.class_or_package_not_found", 1601 tagName, seetext); 1602 } 1603 } 1604 if (configuration.currentTypeElement != containing) { 1605 refMemName = (utils.isConstructor(refMem)) 1606 ? refMemName 1607 : utils.getSimpleName(containing) + "." + refMemName; 1608 } 1609 if (utils.isExecutableElement(refMem)) { 1610 if (refMemName.indexOf('(') < 0) { 1611 refMemName += utils.makeSignature((ExecutableElement)refMem, true); 1612 } 1613 } 1614 1615 text = plainOrCode(kind == LINK_PLAIN, new StringContent(refMemName)); 1616 1617 return getDocLink(LinkInfoImpl.Kind.SEE_TAG, containing, 1618 refMem, (label.isEmpty() ? text: label), false); 1619 } 1620 } 1621 1622 private Content plainOrCode(boolean plain, Content body) { 1623 return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body); 1624 } 1625 1626 /** 1627 * Add the inline comment. 1628 * 1629 * @param element the Element for which the inline comment will be added 1630 * @param tag the inline tag to be added 1631 * @param htmltree the content tree to which the comment will be added 1632 */ 1633 public void addInlineComment(Element element, DocTree tag, Content htmltree) { 1634 CommentHelper ch = utils.getCommentHelper(element); 1635 List<? extends DocTree> description = ch.getDescription(configuration, tag); 1636 addCommentTags(element, tag, description, false, false, htmltree); 1637 } 1638 1639 /** 1640 * Get the deprecated phrase as content. 1641 * 1642 * @param e the Element for which the inline deprecated comment will be added 1643 * @return a content tree for the deprecated phrase. 1644 */ 1645 public Content getDeprecatedPhrase(Element e) { 1646 return (utils.isDeprecatedForRemoval(e)) 1647 ? contents.deprecatedForRemovalPhrase 1648 : contents.deprecatedPhrase; 1649 } 1650 1651 /** 1652 * Add the inline deprecated comment. 1653 * 1654 * @param e the Element for which the inline deprecated comment will be added 1655 * @param tag the inline tag to be added 1656 * @param htmltree the content tree to which the comment will be added 1657 */ 1658 public void addInlineDeprecatedComment(Element e, DocTree tag, Content htmltree) { 1659 CommentHelper ch = utils.getCommentHelper(e); 1660 addCommentTags(e, ch.getBody(configuration, tag), true, false, htmltree); 1661 } 1662 1663 /** 1664 * Adds the summary content. 1665 * 1666 * @param element the Element for which the summary will be generated 1667 * @param htmltree the documentation tree to which the summary will be added 1668 */ 1669 public void addSummaryComment(Element element, Content htmltree) { 1670 addSummaryComment(element, utils.getFirstSentenceTrees(element), htmltree); 1671 } 1672 1673 /** 1674 * Adds the summary content. 1675 * 1676 * @param element the Element for which the summary will be generated 1677 * @param firstSentenceTags the first sentence tags for the doc 1678 * @param htmltree the documentation tree to which the summary will be added 1679 */ 1680 public void addSummaryComment(Element element, List<? extends DocTree> firstSentenceTags, Content htmltree) { 1681 addCommentTags(element, firstSentenceTags, false, true, htmltree); 1682 } 1683 1684 public void addSummaryDeprecatedComment(Element element, DocTree tag, Content htmltree) { 1685 CommentHelper ch = utils.getCommentHelper(element); 1686 List<? extends DocTree> body = ch.getBody(configuration, tag); 1687 addCommentTags(element, ch.getFirstSentenceTrees(configuration, body), true, true, htmltree); 1688 } 1689 1690 /** 1691 * Adds the inline comment. 1692 * 1693 * @param element the Element for which the inline comments will be generated 1694 * @param htmltree the documentation tree to which the inline comments will be added 1695 */ 1696 public void addInlineComment(Element element, Content htmltree) { 1697 addCommentTags(element, utils.getFullBody(element), false, false, htmltree); 1698 } 1699 1700 /** 1701 * Adds the comment tags. 1702 * 1703 * @param element the Element for which the comment tags will be generated 1704 * @param tags the first sentence tags for the doc 1705 * @param depr true if it is deprecated 1706 * @param first true if the first sentence tags should be added 1707 * @param htmltree the documentation tree to which the comment tags will be added 1708 */ 1709 private void addCommentTags(Element element, List<? extends DocTree> tags, boolean depr, 1710 boolean first, Content htmltree) { 1711 addCommentTags(element, null, tags, depr, first, htmltree); 1712 } 1713 1714 /** 1715 * Adds the comment tags. 1716 * 1717 * @param element for which the comment tags will be generated 1718 * @param holderTag the block tag context for the inline tags 1719 * @param tags the first sentence tags for the doc 1720 * @param depr true if it is deprecated 1721 * @param first true if the first sentence tags should be added 1722 * @param htmltree the documentation tree to which the comment tags will be added 1723 */ 1724 private void addCommentTags(Element element, DocTree holderTag, List<? extends DocTree> tags, boolean depr, 1725 boolean first, Content htmltree) { 1726 if(configuration.nocomment){ 1727 return; 1728 } 1729 Content div; 1730 Content result = commentTagsToContent(null, element, tags, first); 1731 if (depr) { 1732 div = HtmlTree.DIV(HtmlStyle.deprecationComment, result); 1733 htmltree.addContent(div); 1734 } 1735 else { 1736 div = HtmlTree.DIV(HtmlStyle.block, result); 1737 htmltree.addContent(div); 1738 } 1739 if (tags.isEmpty()) { 1740 htmltree.addContent(Contents.SPACE); 1741 } 1742 } 1743 1744 boolean ignoreNonInlineTag(DocTree dtree) { 1745 Name name = null; 1746 if (dtree.getKind() == Kind.START_ELEMENT) { 1747 StartElementTree setree = (StartElementTree)dtree; 1748 name = setree.getName(); 1749 } else if (dtree.getKind() == Kind.END_ELEMENT) { 1750 EndElementTree eetree = (EndElementTree)dtree; 1751 name = eetree.getName(); 1752 } 1753 1754 if (name != null) { 1755 com.sun.tools.doclint.HtmlTag htmlTag = com.sun.tools.doclint.HtmlTag.get(name); 1756 if (htmlTag != null && 1757 htmlTag.blockType != com.sun.tools.doclint.HtmlTag.BlockType.INLINE) { 1758 return true; 1759 } 1760 } 1761 return false; 1762 } 1763 1764 boolean isAllWhiteSpace(String body) { 1765 for (int i = 0 ; i < body.length(); i++) { 1766 if (!Character.isWhitespace(body.charAt(i))) 1767 return false; 1768 } 1769 return true; 1770 } 1771 1772 // Notify the next DocTree handler to take necessary action 1773 private boolean commentRemoved = false; 1774 1775 /** 1776 * Converts inline tags and text to text strings, expanding the 1777 * inline tags along the way. Called wherever text can contain 1778 * an inline tag, such as in comments or in free-form text arguments 1779 * to non-inline tags. 1780 * 1781 * @param holderTag specific tag where comment resides 1782 * @param element specific element where comment resides 1783 * @param tags array of text tags and inline tags (often alternating) 1784 present in the text of interest for this element 1785 * @param isFirstSentence true if text is first sentence 1786 * @return a Content object 1787 */ 1788 public Content commentTagsToContent(DocTree holderTag, Element element, 1789 List<? extends DocTree> tags, boolean isFirstSentence) { 1790 1791 final Content result = new ContentBuilder() { 1792 @Override 1793 public void addContent(CharSequence text) { 1794 super.addContent(utils.normalizeNewlines(text)); 1795 } 1796 }; 1797 CommentHelper ch = utils.getCommentHelper(element); 1798 // Array of all possible inline tags for this javadoc run 1799 configuration.tagletManager.checkTags(utils, element, tags, true); 1800 commentRemoved = false; 1801 1802 for (ListIterator<? extends DocTree> iterator = tags.listIterator(); iterator.hasNext();) { 1803 boolean isFirstNode = !iterator.hasPrevious(); 1804 DocTree tag = iterator.next(); 1805 boolean isLastNode = !iterator.hasNext(); 1806 1807 if (isFirstSentence) { 1808 // Ignore block tags 1809 if (ignoreNonInlineTag(tag)) 1810 continue; 1811 1812 // Ignore any trailing whitespace OR whitespace after removed html comment 1813 if ((isLastNode || commentRemoved) 1814 && tag.getKind() == TEXT 1815 && isAllWhiteSpace(ch.getText(tag))) 1816 continue; 1817 1818 // Ignore any leading html comments 1819 if ((isFirstNode || commentRemoved) && tag.getKind() == COMMENT) { 1820 commentRemoved = true; 1821 continue; 1822 } 1823 } 1824 1825 boolean allDone = new SimpleDocTreeVisitor<Boolean, Content>() { 1826 1827 private boolean inAnAtag() { 1828 if (utils.isStartElement(tag)) { 1829 StartElementTree st = (StartElementTree)tag; 1830 Name name = st.getName(); 1831 if (name != null) { 1832 com.sun.tools.doclint.HtmlTag htag = 1833 com.sun.tools.doclint.HtmlTag.get(name); 1834 return htag != null && htag.equals(com.sun.tools.doclint.HtmlTag.A); 1835 } 1836 } 1837 return false; 1838 } 1839 1840 @Override 1841 public Boolean visitAttribute(AttributeTree node, Content c) { 1842 StringBuilder sb = new StringBuilder(SPACER).append(node.getName()); 1843 if (node.getValueKind() == ValueKind.EMPTY) { 1844 result.addContent(sb); 1845 return false; 1846 } 1847 sb.append("="); 1848 String quote; 1849 switch (node.getValueKind()) { 1850 case DOUBLE: 1851 quote = "\""; 1852 break; 1853 case SINGLE: 1854 quote = "\'"; 1855 break; 1856 default: 1857 quote = ""; 1858 break; 1859 } 1860 sb.append(quote); 1861 result.addContent(sb); 1862 Content docRootContent = new ContentBuilder(); 1863 1864 for (DocTree dt : node.getValue()) { 1865 if (utils.isText(dt) && inAnAtag()) { 1866 String text = ((TextTree) dt).getBody(); 1867 if (text.startsWith("/..") && !configuration.docrootparent.isEmpty()) { 1868 result.addContent(configuration.docrootparent); 1869 docRootContent = new ContentBuilder(); 1870 result.addContent(textCleanup(text.substring(3), isLastNode)); 1871 } else { 1872 if (!docRootContent.isEmpty()) { 1873 docRootContent = copyDocRootContent(docRootContent); 1874 } else { 1875 text = redirectRelativeLinks(element, (TextTree) dt); 1876 } 1877 result.addContent(textCleanup(text, isLastNode)); 1878 } 1879 } else { 1880 docRootContent = copyDocRootContent(docRootContent); 1881 dt.accept(this, docRootContent); 1882 } 1883 } 1884 copyDocRootContent(docRootContent); 1885 result.addContent(quote); 1886 return false; 1887 } 1888 1889 @Override 1890 public Boolean visitComment(CommentTree node, Content c) { 1891 result.addContent(new RawHtml(node.getBody())); 1892 return false; 1893 } 1894 1895 private Content copyDocRootContent(Content content) { 1896 if (!content.isEmpty()) { 1897 result.addContent(content); 1898 return new ContentBuilder(); 1899 } 1900 return content; 1901 } 1902 1903 @Override 1904 public Boolean visitDocRoot(DocRootTree node, Content c) { 1905 Content docRootContent = TagletWriter.getInlineTagOutput(element, 1906 configuration.tagletManager, 1907 holderTag, 1908 node, 1909 getTagletWriterInstance(isFirstSentence)); 1910 if (c != null) { 1911 c.addContent(docRootContent); 1912 } else { 1913 result.addContent(docRootContent); 1914 } 1915 return false; 1916 } 1917 1918 @Override 1919 public Boolean visitEndElement(EndElementTree node, Content c) { 1920 RawHtml rawHtml = new RawHtml("</" + node.getName() + ">"); 1921 result.addContent(rawHtml); 1922 return false; 1923 } 1924 1925 @Override 1926 public Boolean visitEntity(EntityTree node, Content c) { 1927 result.addContent(new RawHtml(node.toString())); 1928 return false; 1929 } 1930 1931 @Override 1932 public Boolean visitErroneous(ErroneousTree node, Content c) { 1933 messages.warning(ch.getDocTreePath(node), 1934 "doclet.tag.invalid_usage", node); 1935 result.addContent(new RawHtml(node.toString())); 1936 return false; 1937 } 1938 1939 @Override 1940 public Boolean visitInheritDoc(InheritDocTree node, Content c) { 1941 Content output = TagletWriter.getInlineTagOutput(element, 1942 configuration.tagletManager, holderTag, 1943 tag, getTagletWriterInstance(isFirstSentence)); 1944 result.addContent(output); 1945 // if we obtained the first sentence successfully, nothing more to do 1946 return (isFirstSentence && !output.isEmpty()); 1947 } 1948 1949 @Override 1950 public Boolean visitIndex(IndexTree node, Content p) { 1951 Content output = TagletWriter.getInlineTagOutput(element, 1952 configuration.tagletManager, holderTag, tag, 1953 getTagletWriterInstance(isFirstSentence)); 1954 if (output != null) { 1955 result.addContent(output); 1956 } 1957 return false; 1958 } 1959 1960 @Override 1961 public Boolean visitLink(LinkTree node, Content c) { 1962 // we need to pass the DocTreeImpl here, so ignore node 1963 result.addContent(seeTagToContent(element, tag)); 1964 return false; 1965 } 1966 1967 @Override 1968 public Boolean visitLiteral(LiteralTree node, Content c) { 1969 String s = node.getBody().toString(); 1970 Content content = new StringContent(utils.normalizeNewlines(s)); 1971 if (node.getKind() == CODE) 1972 content = HtmlTree.CODE(content); 1973 result.addContent(content); 1974 return false; 1975 } 1976 1977 @Override 1978 public Boolean visitSee(SeeTree node, Content c) { 1979 // we need to pass the DocTreeImpl here, so ignore node 1980 result.addContent(seeTagToContent(element, tag)); 1981 return false; 1982 } 1983 1984 @Override 1985 public Boolean visitStartElement(StartElementTree node, Content c) { 1986 String text = "<" + node.getName(); 1987 RawHtml rawHtml = new RawHtml(utils.normalizeNewlines(text)); 1988 result.addContent(rawHtml); 1989 1990 for (DocTree dt : node.getAttributes()) { 1991 dt.accept(this, null); 1992 } 1993 result.addContent(new RawHtml(node.isSelfClosing() ? "/>" : ">")); 1994 return false; 1995 } 1996 1997 @Override 1998 public Boolean visitSummary(SummaryTree node, Content c) { 1999 Content output = TagletWriter.getInlineTagOutput(element, 2000 configuration.tagletManager, holderTag, tag, 2001 getTagletWriterInstance(isFirstSentence)); 2002 result.addContent(output); 2003 return false; 2004 } 2005 2006 private CharSequence textCleanup(String text, boolean isLast) { 2007 return textCleanup(text, isLast, false); 2008 } 2009 2010 private CharSequence textCleanup(String text, boolean isLast, boolean trimLeader) { 2011 if (trimLeader) { 2012 text = removeLeadingWhitespace(text); 2013 } 2014 if (isFirstSentence && isLast) { 2015 text = removeTrailingWhitespace(text); 2016 } 2017 text = utils.replaceTabs(text); 2018 return utils.normalizeNewlines(text); 2019 } 2020 2021 @Override 2022 public Boolean visitText(TextTree node, Content c) { 2023 String text = node.getBody(); 2024 result.addContent(new RawHtml(textCleanup(text, isLastNode, commentRemoved))); 2025 return false; 2026 } 2027 2028 @Override 2029 protected Boolean defaultAction(DocTree node, Content c) { 2030 Content output = TagletWriter.getInlineTagOutput(element, 2031 configuration.tagletManager, holderTag, tag, 2032 getTagletWriterInstance(isFirstSentence)); 2033 if (output != null) { 2034 result.addContent(output); 2035 } 2036 return false; 2037 } 2038 2039 }.visit(tag, null); 2040 commentRemoved = false; 2041 if (allDone) 2042 break; 2043 } 2044 return result; 2045 } 2046 2047 private String removeTrailingWhitespace(String text) { 2048 char[] buf = text.toCharArray(); 2049 for (int i = buf.length - 1; i > 0 ; i--) { 2050 if (!Character.isWhitespace(buf[i])) 2051 return text.substring(0, i + 1); 2052 } 2053 return text; 2054 } 2055 2056 private String removeLeadingWhitespace(String text) { 2057 char[] buf = text.toCharArray(); 2058 for (int i = 0; i < buf.length; i++) { 2059 if (!Character.isWhitespace(buf[i])) { 2060 return text.substring(i); 2061 } 2062 } 2063 return text; 2064 } 2065 2066 /** 2067 * Return true if relative links should not be redirected. 2068 * 2069 * @return Return true if a relative link should not be redirected. 2070 */ 2071 private boolean shouldNotRedirectRelativeLinks() { 2072 return this instanceof AnnotationTypeWriter || 2073 this instanceof ClassWriter || 2074 this instanceof PackageSummaryWriter; 2075 } 2076 2077 /** 2078 * Suppose a piece of documentation has a relative link. When you copy 2079 * that documentation to another place such as the index or class-use page, 2080 * that relative link will no longer work. We should redirect those links 2081 * so that they will work again. 2082 * <p> 2083 * Here is the algorithm used to fix the link: 2084 * <p> 2085 * {@literal <relative link> => docRoot + <relative path to file> + <relative link> } 2086 * <p> 2087 * For example, suppose DocletEnvironment has this link: 2088 * {@literal <a href="package-summary.html">The package Page</a> } 2089 * <p> 2090 * If this link appeared in the index, we would redirect 2091 * the link like this: 2092 * 2093 * {@literal <a href="./com/sun/javadoc/package-summary.html">The package Page</a>} 2094 * 2095 * @param element the Element object whose documentation is being written. 2096 * @param text the text being written. 2097 * 2098 * @return the text, with all the relative links redirected to work. 2099 */ 2100 private String redirectRelativeLinks(Element element, TextTree tt) { 2101 String text = tt.getBody(); 2102 if (element == null || utils.isOverviewElement(element) || shouldNotRedirectRelativeLinks()) { 2103 return text; 2104 } 2105 2106 DocPath redirectPathFromRoot = new SimpleElementVisitor9<DocPath, Void>() { 2107 @Override 2108 public DocPath visitType(TypeElement e, Void p) { 2109 return DocPath.forPackage(utils.containingPackage(e)); 2110 } 2111 2112 @Override 2113 public DocPath visitPackage(PackageElement e, Void p) { 2114 return DocPath.forPackage(e); 2115 } 2116 2117 @Override 2118 public DocPath visitVariable(VariableElement e, Void p) { 2119 return DocPath.forPackage(utils.containingPackage(e)); 2120 } 2121 2122 @Override 2123 public DocPath visitExecutable(ExecutableElement e, Void p) { 2124 return DocPath.forPackage(utils.containingPackage(e)); 2125 } 2126 2127 @Override 2128 protected DocPath defaultAction(Element e, Void p) { 2129 return null; 2130 } 2131 }.visit(element); 2132 if (redirectPathFromRoot == null) { 2133 return text; 2134 } 2135 String lower = Utils.toLowerCase(text); 2136 if (!(lower.startsWith("mailto:") 2137 || lower.startsWith("http:") 2138 || lower.startsWith("https:") 2139 || lower.startsWith("file:"))) { 2140 text = "{@" + (new DocRootTaglet()).getName() + "}/" 2141 + redirectPathFromRoot.resolve(text).getPath(); 2142 text = replaceDocRootDir(text); 2143 } 2144 return text; 2145 } 2146 2147 static final Set<String> blockTags = new HashSet<>(); 2148 static { 2149 for (HtmlTag t: HtmlTag.values()) { 2150 if (t.blockType == HtmlTag.BlockType.BLOCK) 2151 blockTags.add(t.value); 2152 } 2153 } 2154 2155 /** 2156 * Add a link to the stylesheet file. 2157 * 2158 * @param head the content tree to which the files will be added 2159 */ 2160 public void addStyleSheetProperties(Content head) { 2161 String stylesheetfile = configuration.stylesheetfile; 2162 DocPath stylesheet; 2163 if (stylesheetfile.isEmpty()) { 2164 stylesheet = DocPaths.STYLESHEET; 2165 } else { 2166 DocFile file = DocFile.createFileForInput(configuration, stylesheetfile); 2167 stylesheet = DocPath.create(file.getName()); 2168 } 2169 HtmlTree link = HtmlTree.LINK("stylesheet", "text/css", 2170 pathToRoot.resolve(stylesheet).getPath(), 2171 "Style"); 2172 head.addContent(link); 2173 if (configuration.createindex) { 2174 HtmlTree jq_link = HtmlTree.LINK("stylesheet", "text/css", 2175 pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(DocPaths.JQUERY_STYLESHEET_FILE)).getPath(), 2176 "Style"); 2177 head.addContent(jq_link); 2178 } 2179 } 2180 2181 /** 2182 * Add a link to the JavaScript file. 2183 * 2184 * @param head the content tree to which the files will be added 2185 */ 2186 public void addScriptProperties(Content head) { 2187 HtmlTree javascript = HtmlTree.SCRIPT(pathToRoot.resolve(DocPaths.JAVASCRIPT).getPath()); 2188 head.addContent(javascript); 2189 if (configuration.createindex) { 2190 if (pathToRoot != null && script != null) { 2191 String ptrPath = pathToRoot.isEmpty() ? "." : pathToRoot.getPath(); 2192 script.addContent(new RawHtml("var pathtoroot = \"" + ptrPath + "/\";loadScripts(document, \'script\');")); 2193 } 2194 addJQueryFile(head, DocPaths.JSZIP_MIN); 2195 addJQueryFile(head, DocPaths.JSZIPUTILS_MIN); 2196 head.addContent(new RawHtml("<!--[if IE]>")); 2197 addJQueryFile(head, DocPaths.JSZIPUTILS_IE_MIN); 2198 head.addContent(new RawHtml("<![endif]-->")); 2199 addJQueryFile(head, DocPaths.JQUERY_JS_1_10); 2200 addJQueryFile(head, DocPaths.JQUERY_JS); 2201 } 2202 } 2203 2204 /** 2205 * Add a link to the JQuery javascript file. 2206 * 2207 * @param head the content tree to which the files will be added 2208 * @param filePath the DocPath of the file that needs to be added 2209 */ 2210 private void addJQueryFile(Content head, DocPath filePath) { 2211 HtmlTree jqyeryScriptFile = HtmlTree.SCRIPT( 2212 pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(filePath)).getPath()); 2213 head.addContent(jqyeryScriptFile); 2214 } 2215 2216 /** 2217 * According to 2218 * <cite>The Java™ Language Specification</cite>, 2219 * all the outer classes and static nested classes are core classes. 2220 */ 2221 public boolean isCoreClass(TypeElement typeElement) { 2222 return utils.getEnclosingTypeElement(typeElement) == null || utils.isStatic(typeElement); 2223 } 2224 2225 /** 2226 * Adds the annotation types for the given packageElement. 2227 * 2228 * @param packageElement the package to write annotations for. 2229 * @param htmltree the documentation tree to which the annotation info will be 2230 * added 2231 */ 2232 public void addAnnotationInfo(PackageElement packageElement, Content htmltree) { 2233 addAnnotationInfo(packageElement, packageElement.getAnnotationMirrors(), htmltree); 2234 } 2235 2236 /** 2237 * Add the annotation types of the executable receiver. 2238 * 2239 * @param method the executable to write the receiver annotations for. 2240 * @param descList list of annotation description. 2241 * @param htmltree the documentation tree to which the annotation info will be 2242 * added 2243 */ 2244 public void addReceiverAnnotationInfo(ExecutableElement method, List<AnnotationMirror> descList, 2245 Content htmltree) { 2246 addAnnotationInfo(0, method, descList, false, htmltree); 2247 } 2248 2249 /* 2250 * this is a hack to delay dealing with Annotations in the writers, the assumption 2251 * is that all necessary checks have been made to get here. 2252 */ 2253 public void addReceiverAnnotationInfo(ExecutableElement method, TypeMirror rcvrTypeMirror, 2254 List<? extends AnnotationMirror> annotationMirrors, Content htmltree) { 2255 TypeMirror rcvrType = method.getReceiverType(); 2256 List<? extends AnnotationMirror> annotationMirrors1 = rcvrType.getAnnotationMirrors(); 2257 addAnnotationInfo(0, method, annotationMirrors1, false, htmltree); 2258 } 2259 2260 /** 2261 * Adds the annotatation types for the given element. 2262 * 2263 * @param element the package to write annotations for 2264 * @param htmltree the content tree to which the annotation types will be added 2265 */ 2266 public void addAnnotationInfo(Element element, Content htmltree) { 2267 addAnnotationInfo(element, element.getAnnotationMirrors(), htmltree); 2268 } 2269 2270 /** 2271 * Add the annotatation types for the given element and parameter. 2272 * 2273 * @param indent the number of spaces to indent the parameters. 2274 * @param element the element to write annotations for. 2275 * @param param the parameter to write annotations for. 2276 * @param tree the content tree to which the annotation types will be added 2277 */ 2278 public boolean addAnnotationInfo(int indent, Element element, VariableElement param, 2279 Content tree) { 2280 return addAnnotationInfo(indent, element, param.getAnnotationMirrors(), false, tree); 2281 } 2282 2283 /** 2284 * Adds the annotatation types for the given Element. 2285 * 2286 * @param element the element to write annotations for. 2287 * @param descList the array of {@link AnnotationDesc}. 2288 * @param htmltree the documentation tree to which the annotation info will be 2289 * added 2290 */ 2291 private void addAnnotationInfo(Element element, List<? extends AnnotationMirror> descList, 2292 Content htmltree) { 2293 addAnnotationInfo(0, element, descList, true, htmltree); 2294 } 2295 2296 /** 2297 * Adds the annotation types for the given element. 2298 * 2299 * @param indent the number of extra spaces to indent the annotations. 2300 * @param element the element to write annotations for. 2301 * @param descList the array of {@link AnnotationDesc}. 2302 * @param htmltree the documentation tree to which the annotation info will be 2303 * added 2304 */ 2305 private boolean addAnnotationInfo(int indent, Element element, 2306 List<? extends AnnotationMirror> descList, boolean lineBreak, Content htmltree) { 2307 List<Content> annotations = getAnnotations(indent, descList, lineBreak); 2308 String sep = ""; 2309 if (annotations.isEmpty()) { 2310 return false; 2311 } 2312 for (Content annotation: annotations) { 2313 htmltree.addContent(sep); 2314 htmltree.addContent(annotation); 2315 if (!lineBreak) { 2316 sep = " "; 2317 } 2318 } 2319 return true; 2320 } 2321 2322 /** 2323 * Return the string representations of the annotation types for 2324 * the given doc. 2325 * 2326 * @param indent the number of extra spaces to indent the annotations. 2327 * @param descList the array of {@link AnnotationDesc}. 2328 * @param linkBreak if true, add new line between each member value. 2329 * @return an array of strings representing the annotations being 2330 * documented. 2331 */ 2332 private List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList, boolean linkBreak) { 2333 return getAnnotations(indent, descList, linkBreak, true); 2334 } 2335 2336 private List<Content> getAnnotations(int indent, AnnotationMirror amirror, boolean linkBreak) { 2337 List<AnnotationMirror> descList = new ArrayList<>(); 2338 descList.add(amirror); 2339 return getAnnotations(indent, descList, linkBreak, true); 2340 } 2341 2342 /** 2343 * Return the string representations of the annotation types for 2344 * the given doc. 2345 * 2346 * A {@code null} {@code elementType} indicates that all the 2347 * annotations should be returned without any filtering. 2348 * 2349 * @param indent the number of extra spaces to indent the annotations. 2350 * @param descList the array of {@link AnnotationDesc}. 2351 * @param linkBreak if true, add new line between each member value. 2352 * @param isJava5DeclarationLocation 2353 * @return an array of strings representing the annotations being 2354 * documented. 2355 */ 2356 public List<Content> getAnnotations(int indent, List<? extends AnnotationMirror> descList, 2357 boolean linkBreak, boolean isJava5DeclarationLocation) { 2358 List<Content> results = new ArrayList<>(); 2359 ContentBuilder annotation; 2360 for (AnnotationMirror aDesc : descList) { 2361 TypeElement annotationElement = (TypeElement)aDesc.getAnnotationType().asElement(); 2362 // If an annotation is not documented, do not add it to the list. If 2363 // the annotation is of a repeatable type, and if it is not documented 2364 // and also if its container annotation is not documented, do not add it 2365 // to the list. If an annotation of a repeatable type is not documented 2366 // but its container is documented, it will be added to the list. 2367 if (!utils.isDocumentedAnnotation(annotationElement) && 2368 (!isAnnotationDocumented && !isContainerDocumented)) { 2369 continue; 2370 } 2371 /* TODO: check logic here to correctly handle declaration 2372 * and type annotations. 2373 if (utils.isDeclarationAnnotation(annotationElement, isJava5DeclarationLocation)) { 2374 continue; 2375 }*/ 2376 annotation = new ContentBuilder(); 2377 isAnnotationDocumented = false; 2378 LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, 2379 LinkInfoImpl.Kind.ANNOTATION, annotationElement); 2380 Map<? extends ExecutableElement, ? extends AnnotationValue> pairs = aDesc.getElementValues(); 2381 // If the annotation is synthesized, do not print the container. 2382 if (utils.configuration.workArounds.isSynthesized(aDesc)) { 2383 for (ExecutableElement ee : pairs.keySet()) { 2384 AnnotationValue annotationValue = pairs.get(ee); 2385 List<AnnotationValue> annotationTypeValues = new ArrayList<>(); 2386 2387 new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() { 2388 @Override 2389 public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> p) { 2390 p.addAll(vals); 2391 return null; 2392 } 2393 2394 @Override 2395 protected Void defaultAction(Object o, List<AnnotationValue> p) { 2396 p.add(annotationValue); 2397 return null; 2398 } 2399 }.visit(annotationValue, annotationTypeValues); 2400 2401 String sep = ""; 2402 for (AnnotationValue av : annotationTypeValues) { 2403 annotation.addContent(sep); 2404 annotation.addContent(annotationValueToContent(av)); 2405 sep = " "; 2406 } 2407 } 2408 } else if (isAnnotationArray(pairs)) { 2409 // If the container has 1 or more value defined and if the 2410 // repeatable type annotation is not documented, do not print 2411 // the container. 2412 if (pairs.size() == 1 && isAnnotationDocumented) { 2413 List<AnnotationValue> annotationTypeValues = new ArrayList<>(); 2414 for (AnnotationValue a : pairs.values()) { 2415 new SimpleAnnotationValueVisitor9<Void, List<AnnotationValue>>() { 2416 @Override 2417 public Void visitArray(List<? extends AnnotationValue> vals, List<AnnotationValue> annotationTypeValues) { 2418 for (AnnotationValue av : vals) { 2419 annotationTypeValues.add(av); 2420 } 2421 return null; 2422 } 2423 }.visit(a, annotationTypeValues); 2424 } 2425 String sep = ""; 2426 for (AnnotationValue av : annotationTypeValues) { 2427 annotation.addContent(sep); 2428 annotation.addContent(annotationValueToContent(av)); 2429 sep = " "; 2430 } 2431 } 2432 // If the container has 1 or more value defined and if the 2433 // repeatable type annotation is not documented, print the container. 2434 else { 2435 addAnnotations(annotationElement, linkInfo, annotation, pairs, 2436 indent, false); 2437 } 2438 } 2439 else { 2440 addAnnotations(annotationElement, linkInfo, annotation, pairs, 2441 indent, linkBreak); 2442 } 2443 annotation.addContent(linkBreak ? DocletConstants.NL : ""); 2444 results.add(annotation); 2445 } 2446 return results; 2447 } 2448 2449 /** 2450 * Add annotation to the annotation string. 2451 * 2452 * @param annotationDoc the annotation being documented 2453 * @param linkInfo the information about the link 2454 * @param annotation the annotation string to which the annotation will be added 2455 * @param pairs annotation type element and value pairs 2456 * @param indent the number of extra spaces to indent the annotations. 2457 * @param linkBreak if true, add new line between each member value 2458 */ 2459 private void addAnnotations(TypeElement annotationDoc, LinkInfoImpl linkInfo, 2460 ContentBuilder annotation, Map<? extends ExecutableElement,? extends AnnotationValue>map, 2461 int indent, boolean linkBreak) { 2462 linkInfo.label = new StringContent("@"); 2463 linkInfo.label.addContent(annotationDoc.getSimpleName()); 2464 annotation.addContent(getLink(linkInfo)); 2465 if (!map.isEmpty()) { 2466 annotation.addContent("("); 2467 boolean isFirst = true; 2468 Set<? extends ExecutableElement> keys = map.keySet(); 2469 boolean multipleValues = keys.size() > 1; 2470 for (ExecutableElement element : keys) { 2471 if (isFirst) { 2472 isFirst = false; 2473 } else { 2474 annotation.addContent(","); 2475 if (linkBreak) { 2476 annotation.addContent(DocletConstants.NL); 2477 int spaces = annotationDoc.getSimpleName().length() + 2; 2478 for (int k = 0; k < (spaces + indent); k++) { 2479 annotation.addContent(" "); 2480 } 2481 } 2482 } 2483 String simpleName = element.getSimpleName().toString(); 2484 if (multipleValues || !"value".equals(simpleName)) { // Omit "value=" where unnecessary 2485 annotation.addContent(getDocLink(LinkInfoImpl.Kind.ANNOTATION, 2486 element, simpleName, false)); 2487 annotation.addContent("="); 2488 } 2489 AnnotationValue annotationValue = map.get(element); 2490 List<AnnotationValue> annotationTypeValues = new ArrayList<>(); 2491 new SimpleAnnotationValueVisitor9<Void, AnnotationValue>() { 2492 @Override 2493 public Void visitArray(List<? extends AnnotationValue> vals, AnnotationValue p) { 2494 annotationTypeValues.addAll(vals); 2495 return null; 2496 } 2497 @Override 2498 protected Void defaultAction(Object o, AnnotationValue p) { 2499 annotationTypeValues.add(p); 2500 return null; 2501 } 2502 }.visit(annotationValue, annotationValue); 2503 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "{"); 2504 String sep = ""; 2505 for (AnnotationValue av : annotationTypeValues) { 2506 annotation.addContent(sep); 2507 annotation.addContent(annotationValueToContent(av)); 2508 sep = ","; 2509 } 2510 annotation.addContent(annotationTypeValues.size() == 1 ? "" : "}"); 2511 isContainerDocumented = false; 2512 } 2513 annotation.addContent(")"); 2514 } 2515 } 2516 2517 /** 2518 * Check if the annotation contains an array of annotation as a value. This 2519 * check is to verify if a repeatable type annotation is present or not. 2520 * 2521 * @param pairs annotation type element and value pairs 2522 * 2523 * @return true if the annotation contains an array of annotation as a value. 2524 */ 2525 private boolean isAnnotationArray(Map<? extends ExecutableElement, ? extends AnnotationValue> pairs) { 2526 AnnotationValue annotationValue; 2527 for (ExecutableElement ee : pairs.keySet()) { 2528 annotationValue = pairs.get(ee); 2529 boolean rvalue = new SimpleAnnotationValueVisitor9<Boolean, Void>() { 2530 @Override 2531 public Boolean visitArray(List<? extends AnnotationValue> vals, Void p) { 2532 if (vals.size() > 1) { 2533 if (vals.get(0) instanceof AnnotationMirror) { 2534 isContainerDocumented = true; 2535 return new SimpleAnnotationValueVisitor9<Boolean, Void>() { 2536 @Override 2537 public Boolean visitAnnotation(AnnotationMirror a, Void p) { 2538 isContainerDocumented = true; 2539 Element asElement = a.getAnnotationType().asElement(); 2540 if (utils.isDocumentedAnnotation((TypeElement)asElement)) { 2541 isAnnotationDocumented = true; 2542 } 2543 return true; 2544 } 2545 @Override 2546 protected Boolean defaultAction(Object o, Void p) { 2547 return false; 2548 } 2549 }.visit(vals.get(0)); 2550 } 2551 } 2552 return false; 2553 } 2554 2555 @Override 2556 protected Boolean defaultAction(Object o, Void p) { 2557 return false; 2558 } 2559 }.visit(annotationValue); 2560 if (rvalue) { 2561 return true; 2562 } 2563 } 2564 return false; 2565 } 2566 2567 private Content annotationValueToContent(AnnotationValue annotationValue) { 2568 return new SimpleAnnotationValueVisitor9<Content, Void>() { 2569 2570 @Override 2571 public Content visitType(TypeMirror t, Void p) { 2572 return new SimpleTypeVisitor9<Content, Void>() { 2573 @Override 2574 public Content visitDeclared(DeclaredType t, Void p) { 2575 LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, 2576 LinkInfoImpl.Kind.ANNOTATION, t); 2577 String name = utils.isIncluded(t.asElement()) 2578 ? t.asElement().getSimpleName().toString() 2579 : utils.getFullyQualifiedName(t.asElement()); 2580 linkInfo.label = new StringContent(name + utils.getDimension(t) + ".class"); 2581 return getLink(linkInfo); 2582 } 2583 @Override 2584 protected Content defaultAction(TypeMirror e, Void p) { 2585 return new StringContent(t + utils.getDimension(t) + ".class"); 2586 } 2587 }.visit(t); 2588 } 2589 @Override 2590 public Content visitAnnotation(AnnotationMirror a, Void p) { 2591 List<Content> list = getAnnotations(0, a, false); 2592 ContentBuilder buf = new ContentBuilder(); 2593 for (Content c : list) { 2594 buf.addContent(c); 2595 } 2596 return buf; 2597 } 2598 @Override 2599 public Content visitEnumConstant(VariableElement c, Void p) { 2600 return getDocLink(LinkInfoImpl.Kind.ANNOTATION, 2601 c, c.getSimpleName(), false); 2602 } 2603 @Override 2604 public Content visitArray(List<? extends AnnotationValue> vals, Void p) { 2605 ContentBuilder buf = new ContentBuilder(); 2606 String sep = ""; 2607 for (AnnotationValue av : vals) { 2608 buf.addContent(sep); 2609 buf.addContent(visit(av)); 2610 sep = " "; 2611 } 2612 return buf; 2613 } 2614 @Override 2615 protected Content defaultAction(Object o, Void p) { 2616 return new StringContent(annotationValue.toString()); 2617 } 2618 }.visit(annotationValue); 2619 } 2620 2621 /** 2622 * Return the configuration for this doclet. 2623 * 2624 * @return the configuration for this doclet. 2625 */ 2626 @Override 2627 public BaseConfiguration configuration() { 2628 return configuration; 2629 } 2630 }