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