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