1 /* 2 * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.javadoc.internal.doclets.formats.html; 27 28 import jdk.javadoc.internal.doclets.formats.html.markup.Table; 29 import jdk.javadoc.internal.doclets.formats.html.markup.TableHeader; 30 31 import java.util.ArrayList; 32 import java.util.Collections; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Set; 36 import java.util.SortedSet; 37 import java.util.TreeMap; 38 import java.util.TreeSet; 39 40 import javax.lang.model.element.Element; 41 import javax.lang.model.element.ModuleElement; 42 import javax.lang.model.element.PackageElement; 43 import javax.lang.model.element.TypeElement; 44 import javax.lang.model.util.ElementFilter; 45 46 import com.sun.source.doctree.DocTree; 47 import jdk.javadoc.doclet.DocletEnvironment.ModuleMode; 48 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; 49 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; 50 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; 51 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; 52 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; 53 import jdk.javadoc.internal.doclets.formats.html.markup.Links; 54 import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; 55 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; 56 import jdk.javadoc.internal.doclets.toolkit.Content; 57 import jdk.javadoc.internal.doclets.toolkit.ModuleSummaryWriter; 58 import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; 59 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; 60 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; 61 62 /** 63 * Class to generate file for each module contents in the right-hand frame. This will list all the 64 * required modules, packages and service types for the module. A click on any of the links will update 65 * the frame with the clicked element page. 66 * 67 * <p><b>This is NOT part of any supported API. 68 * If you write code that depends on this, you do so at your own risk. 69 * This code and its internal interfaces are subject to change or 70 * deletion without notice.</b> 71 * 72 * @author Bhavesh Patel 73 */ 74 public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryWriter { 75 76 /** 77 * The prev module name in the alpha-order list. 78 */ 79 protected ModuleElement prevModule; 80 81 /** 82 * The next module name in the alpha-order list. 83 */ 84 protected ModuleElement nextModule; 85 86 /** 87 * The module being documented. 88 */ 89 protected ModuleElement mdle; 90 91 /** 92 * The module mode for this javadoc run. It can be set to "api" or "all". 93 */ 94 private final ModuleMode moduleMode; 95 96 /** 97 * Map of module elements and modifiers required by this module. 98 */ 99 private final Map<ModuleElement, Content> requires 100 = new TreeMap<>(utils.makeModuleComparator()); 101 102 /** 103 * Map of indirect modules and modifiers, transitive closure, required by this module. 104 */ 105 private final Map<ModuleElement, Content> indirectModules 106 = new TreeMap<>(utils.makeModuleComparator()); 107 108 /** 109 * Details about a package in a module. 110 * A package may be not exported, or exported to some modules, or exported to all modules. 111 * A package may be not opened, or opened to some modules, or opened to all modules. 112 * A package that is neither exported or opened to any modules is a concealed package. 113 * An open module opens all its packages to all modules. 114 */ 115 class PackageEntry { 116 /** 117 * Summary of package exports: 118 * If null, the package is not exported to any modules; 119 * if empty, the package is exported to all modules; 120 * otherwise, the package is exported to these modules. 121 */ 122 Set<ModuleElement> exportedTo; 123 124 /** 125 * Summary of package opens: 126 * If null, the package is not opened to any modules; 127 * if empty, the package is opened to all modules; 128 * otherwise, the package is opened to these modules. 129 */ 130 Set<ModuleElement> openedTo; 131 } 132 133 /** 134 * Map of packages of this module, and details of whether they are exported or opened. 135 */ 136 private final Map<PackageElement, PackageEntry> packages = new TreeMap<>(utils.makePackageComparator()); 137 138 /** 139 * Map of indirect modules (transitive closure) and their exported packages. 140 */ 141 private final Map<ModuleElement, SortedSet<PackageElement>> indirectPackages 142 = new TreeMap<>(utils.makeModuleComparator()); 143 144 /** 145 * Map of indirect modules (transitive closure) and their open packages. 146 */ 147 private final Map<ModuleElement, SortedSet<PackageElement>> indirectOpenPackages 148 = new TreeMap<>(utils.makeModuleComparator()); 149 150 /** 151 * Set of services used by the module. 152 */ 153 private final SortedSet<TypeElement> uses 154 = new TreeSet<>(utils.makeAllClassesComparator()); 155 156 /** 157 * Map of services used by the module and specified using @uses javadoc tag, and description. 158 */ 159 private final Map<TypeElement, Content> usesTrees 160 = new TreeMap<>(utils.makeAllClassesComparator()); 161 162 /** 163 * Map of services provided by this module, and set of its implementations. 164 */ 165 private final Map<TypeElement, SortedSet<TypeElement>> provides 166 = new TreeMap<>(utils.makeAllClassesComparator()); 167 168 /** 169 * Map of services provided by the module and specified using @provides javadoc tag, and 170 * description. 171 */ 172 private final Map<TypeElement, Content> providesTrees 173 = new TreeMap<>(utils.makeAllClassesComparator()); 174 175 /** 176 * The HTML tree for main tag. 177 */ 178 protected HtmlTree mainTree = HtmlTree.MAIN(); 179 180 /** 181 * The HTML tree for section tag. 182 */ 183 protected HtmlTree sectionTree = HtmlTree.SECTION(); 184 185 /** 186 * Constructor to construct ModuleWriter object and to generate "moduleName-summary.html" file. 187 * 188 * @param configuration the configuration of the doclet. 189 * @param mdle Module under consideration. 190 * @param prevModule Previous module in the sorted array. 191 * @param nextModule Next module in the sorted array. 192 */ 193 public ModuleWriterImpl(HtmlConfiguration configuration, 194 ModuleElement mdle, ModuleElement prevModule, ModuleElement nextModule) { 195 super(configuration, configuration.docPaths.moduleSummary(mdle)); 196 this.prevModule = prevModule; 197 this.nextModule = nextModule; 198 this.mdle = mdle; 199 this.moduleMode = configuration.docEnv.getModuleMode(); 200 computeModulesData(); 201 } 202 203 /** 204 * Get the module header. 205 * 206 * @param heading the heading for the section 207 */ 208 @Override 209 public Content getModuleHeader(String heading) { 210 HtmlTree bodyTree = getBody(true, getWindowTitle(mdle.getQualifiedName().toString())); 211 HtmlTree htmlTree = (configuration.allowTag(HtmlTag.HEADER)) 212 ? HtmlTree.HEADER() 213 : bodyTree; 214 addTop(htmlTree); 215 addNavLinks(true, htmlTree); 216 if (configuration.allowTag(HtmlTag.HEADER)) { 217 bodyTree.addContent(htmlTree); 218 } 219 HtmlTree div = new HtmlTree(HtmlTag.DIV); 220 div.setStyle(HtmlStyle.header); 221 Content annotationContent = new HtmlTree(HtmlTag.P); 222 addAnnotationInfo(mdle, annotationContent); 223 div.addContent(annotationContent); 224 Content label = mdle.isOpen() && (configuration.docEnv.getModuleMode() == ModuleMode.ALL) 225 ? contents.openModuleLabel : contents.moduleLabel; 226 Content tHeading = HtmlTree.HEADING(HtmlConstants.TITLE_HEADING, true, 227 HtmlStyle.title, label); 228 tHeading.addContent(Contents.SPACE); 229 Content moduleHead = new RawHtml(heading); 230 tHeading.addContent(moduleHead); 231 div.addContent(tHeading); 232 if (configuration.allowTag(HtmlTag.MAIN)) { 233 mainTree.addContent(div); 234 } else { 235 bodyTree.addContent(div); 236 } 237 return bodyTree; 238 } 239 240 /** 241 * Get the content header. 242 */ 243 @Override 244 public Content getContentHeader() { 245 HtmlTree div = new HtmlTree(HtmlTag.DIV); 246 div.setStyle(HtmlStyle.contentContainer); 247 return div; 248 } 249 250 /** 251 * Get the summary section header. 252 */ 253 @Override 254 public Content getSummaryHeader() { 255 HtmlTree li = new HtmlTree(HtmlTag.LI); 256 li.setStyle(HtmlStyle.blockList); 257 return li; 258 } 259 260 /** 261 * Get the summary tree. 262 * 263 * @param summaryContentTree the content tree to be added to the summary tree. 264 */ 265 @Override 266 public Content getSummaryTree(Content summaryContentTree) { 267 HtmlTree ul = HtmlTree.UL(HtmlStyle.blockList, summaryContentTree); 268 return ul; 269 } 270 271 /** 272 * Compute the modules data that will be displayed in various tables on the module summary page. 273 */ 274 public void computeModulesData() { 275 CommentHelper ch = utils.getCommentHelper(mdle); 276 // Get module dependencies using the module's transitive closure. 277 Map<ModuleElement, String> dependentModules = utils.getDependentModules(mdle); 278 // Add all dependent modules to indirect modules set. We will remove the modules, 279 // listed using the requires directive, from this set to come up with the table of indirect 280 // required modules. 281 dependentModules.forEach((module, mod) -> { 282 if (shouldDocument(module)) { 283 indirectModules.put(module, new StringContent(mod)); 284 } 285 }); 286 (ElementFilter.requiresIn(mdle.getDirectives())).forEach((directive) -> { 287 ModuleElement m = directive.getDependency(); 288 if (shouldDocument(m)) { 289 if (moduleMode == ModuleMode.ALL || directive.isTransitive()) { 290 requires.put(m, new StringContent(utils.getModifiers(directive))); 291 } else { 292 // For api mode, just keep the public requires in dependentModules for display of 293 // indirect packages in the "Packages" section. 294 dependentModules.remove(m); 295 } 296 indirectModules.remove(m); 297 } 298 }); 299 300 // Get all packages if module is open or if displaying concealed modules 301 for (PackageElement pkg : utils.getModulePackageMap().getOrDefault(mdle, Collections.emptySet())) { 302 if (shouldDocument(pkg) && (mdle.isOpen() || moduleMode == ModuleMode.ALL)) { 303 PackageEntry e = new PackageEntry(); 304 if (mdle.isOpen()) { 305 e.openedTo = Collections.emptySet(); 306 } 307 packages.put(pkg, e); 308 } 309 }; 310 311 // Get all exported packages for the module, using the exports directive for the module. 312 for (ModuleElement.ExportsDirective directive : ElementFilter.exportsIn(mdle.getDirectives())) { 313 PackageElement p = directive.getPackage(); 314 if (shouldDocument(p)) { 315 List<? extends ModuleElement> targetMdles = directive.getTargetModules(); 316 // Include package if in details mode, or exported to all (i.e. targetModules == null) 317 if (moduleMode == ModuleMode.ALL || targetMdles == null) { 318 PackageEntry packageEntry = packages.computeIfAbsent(p, pkg -> new PackageEntry()); 319 SortedSet<ModuleElement> mdleList = new TreeSet<>(utils.makeModuleComparator()); 320 if (targetMdles != null) { 321 mdleList.addAll(targetMdles); 322 } 323 packageEntry.exportedTo = mdleList; 324 } 325 } 326 } 327 328 // Get all opened packages for the module, using the opens directive for the module. 329 // If it is an open module, there will be no separate opens directives. 330 for (ModuleElement.OpensDirective directive : ElementFilter.opensIn(mdle.getDirectives())) { 331 PackageElement p = directive.getPackage(); 332 if (shouldDocument(p)) { 333 List<? extends ModuleElement> targetMdles = directive.getTargetModules(); 334 // Include package if in details mode, or opened to all (i.e. targetModules == null) 335 if (moduleMode == ModuleMode.ALL || targetMdles == null) { 336 PackageEntry packageEntry = packages.computeIfAbsent(p, pkg -> new PackageEntry()); 337 SortedSet<ModuleElement> mdleList = new TreeSet<>(utils.makeModuleComparator()); 338 if (targetMdles != null) { 339 mdleList.addAll(targetMdles); 340 } 341 packageEntry.openedTo = mdleList; 342 } 343 } 344 } 345 346 // Get all the exported and opened packages, for the transitive closure of the module, to be displayed in 347 // the indirect packages tables. 348 dependentModules.forEach((module, mod) -> { 349 SortedSet<PackageElement> exportPkgList = new TreeSet<>(utils.makePackageComparator()); 350 (ElementFilter.exportsIn(module.getDirectives())).forEach((directive) -> { 351 PackageElement pkg = directive.getPackage(); 352 if (shouldDocument(pkg)) { 353 // Qualified exports are not displayed in API mode 354 if (moduleMode == ModuleMode.ALL || directive.getTargetModules() == null) { 355 exportPkgList.add(pkg); 356 } 357 } 358 }); 359 // If none of the indirect modules have exported packages to be displayed, we should not be 360 // displaying the table and so it should not be added to the map. 361 if (!exportPkgList.isEmpty()) { 362 indirectPackages.put(module, exportPkgList); 363 } 364 SortedSet<PackageElement> openPkgList = new TreeSet<>(utils.makePackageComparator()); 365 if (module.isOpen()) { 366 openPkgList.addAll(utils.getModulePackageMap().getOrDefault(module, Collections.emptySet())); 367 } else { 368 (ElementFilter.opensIn(module.getDirectives())).forEach((directive) -> { 369 PackageElement pkg = directive.getPackage(); 370 if (shouldDocument(pkg)) { 371 // Qualified opens are not displayed in API mode 372 if (moduleMode == ModuleMode.ALL || directive.getTargetModules() == null) { 373 openPkgList.add(pkg); 374 } 375 } 376 }); 377 } 378 // If none of the indirect modules have opened packages to be displayed, we should not be 379 // displaying the table and so it should not be added to the map. 380 if (!openPkgList.isEmpty()) { 381 indirectOpenPackages.put(module, openPkgList); 382 } 383 }); 384 // Get all the services listed as uses directive. 385 (ElementFilter.usesIn(mdle.getDirectives())).forEach((directive) -> { 386 TypeElement u = directive.getService(); 387 if (shouldDocument(u)) { 388 uses.add(u); 389 } 390 }); 391 // Get all the services and implementations listed as provides directive. 392 (ElementFilter.providesIn(mdle.getDirectives())).forEach((directive) -> { 393 TypeElement u = directive.getService(); 394 if (shouldDocument(u)) { 395 List<? extends TypeElement> implList = directive.getImplementations(); 396 SortedSet<TypeElement> implSet = new TreeSet<>(utils.makeAllClassesComparator()); 397 implSet.addAll(implList); 398 provides.put(u, implSet); 399 } 400 }); 401 // Generate the map of all services listed using @provides, and the description. 402 (utils.getBlockTags(mdle, DocTree.Kind.PROVIDES)).forEach((tree) -> { 403 TypeElement t = ch.getServiceType(configuration, tree); 404 if (t != null) { 405 providesTrees.put(t, commentTagsToContent(tree, mdle, ch.getDescription(configuration, tree), false)); 406 } 407 }); 408 // Generate the map of all services listed using @uses, and the description. 409 (utils.getBlockTags(mdle, DocTree.Kind.USES)).forEach((tree) -> { 410 TypeElement t = ch.getServiceType(configuration, tree); 411 if (t != null) { 412 usesTrees.put(t, commentTagsToContent(tree, mdle, ch.getDescription(configuration, tree), false)); 413 } 414 }); 415 } 416 417 /** 418 * Returns true if the element should be documented on the module summary page. 419 * 420 * @param element the element to be checked 421 * @return true if the element should be documented 422 */ 423 public boolean shouldDocument(Element element) { 424 return (moduleMode == ModuleMode.ALL || utils.isIncluded(element)); 425 } 426 427 /** 428 * Returns true if there are elements to be displayed. 429 * 430 * @param section set of elements 431 * @return true if there are elements to be displayed 432 */ 433 public boolean display(Set<? extends Element> section) { 434 return section != null && !section.isEmpty(); 435 } 436 437 /** 438 * Returns true if there are elements to be displayed. 439 * 440 * @param section map of elements. 441 * @return true if there are elements to be displayed 442 */ 443 public boolean display(Map<? extends Element, ?> section) { 444 return section != null && !section.isEmpty(); 445 } 446 447 /* 448 * Returns true, in API mode, if at least one type element in 449 * the typeElements set is referenced by a javadoc tag in tagsMap. 450 */ 451 private boolean displayServices(Set<TypeElement> typeElements, 452 Map<TypeElement, Content> tagsMap) { 453 return typeElements != null && 454 typeElements.stream().anyMatch((v) -> displayServiceDirective(v, tagsMap)); 455 } 456 457 /* 458 * Returns true, in API mode, if the type element is referenced 459 * from a javadoc tag in tagsMap. 460 */ 461 private boolean displayServiceDirective(TypeElement typeElement, 462 Map<TypeElement, Content> tagsMap) { 463 return moduleMode == ModuleMode.ALL || tagsMap.containsKey(typeElement); 464 } 465 466 /** 467 * Add the summary header. 468 * 469 * @param startMarker the marker comment 470 * @param markerAnchor the marker anchor for the section 471 * @param heading the heading for the section 472 * @param htmltree the content tree to which the information is added 473 */ 474 public void addSummaryHeader(Content startMarker, SectionName markerAnchor, Content heading, 475 Content htmltree) { 476 htmltree.addContent(startMarker); 477 htmltree.addContent(links.createAnchor(markerAnchor)); 478 htmltree.addContent(HtmlTree.HEADING(HtmlTag.H3, heading)); 479 } 480 481 /** 482 * Get a table, with two columns. 483 * 484 * @param caption the table caption 485 * @param tableSummary the summary for the table 486 * @param tableStyle the table style 487 * @param tableHeader the table header 488 * @return a content object 489 */ 490 private Table getTable2(Content caption, String tableSummary, HtmlStyle tableStyle, 491 TableHeader tableHeader) { 492 return new Table(configuration.htmlVersion, tableStyle) 493 .setSummary(tableSummary) 494 .setCaption(caption) 495 .setHeader(tableHeader) 496 .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast); 497 } 498 499 /** 500 * Get a table, with three columns, with the second column being the defining column. 501 * 502 * @param caption the table caption 503 * @param tableSummary the summary for the table 504 * @param tableStyle the table style 505 * @param tableHeader the table header 506 * @return a content object 507 */ 508 private Table getTable3(Content caption, String tableSummary, HtmlStyle tableStyle, 509 TableHeader tableHeader) { 510 return new Table(configuration.htmlVersion, tableStyle) 511 .setSummary(tableSummary) 512 .setCaption(caption) 513 .setHeader(tableHeader) 514 .setRowScopeColumn(1) 515 .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast); 516 } 517 518 /** 519 * {@inheritDoc} 520 */ 521 @Override 522 public void addModulesSummary(Content summaryContentTree) { 523 if (display(requires) || display(indirectModules)) { 524 TableHeader requiresTableHeader = 525 new TableHeader(contents.modifierLabel, contents.moduleLabel, 526 contents.descriptionLabel); 527 HtmlTree li = new HtmlTree(HtmlTag.LI); 528 li.setStyle(HtmlStyle.blockList); 529 addSummaryHeader(HtmlConstants.START_OF_MODULES_SUMMARY, SectionName.MODULES, 530 contents.navModules, li); 531 if (display(requires)) { 532 String text = resources.getText("doclet.Requires_Summary"); 533 String tableSummary = resources.getText("doclet.Member_Table_Summary", 534 text, 535 resources.getText("doclet.modules")); 536 Content caption = getTableCaption(new StringContent(text)); 537 Table table = getTable3(caption, tableSummary, HtmlStyle.requiresSummary, 538 requiresTableHeader); 539 addModulesList(requires, table); 540 li.addContent(table.toContent()); 541 } 542 // Display indirect modules table in both "api" and "all" mode. 543 if (display(indirectModules)) { 544 String amrText = resources.getText("doclet.Indirect_Requires_Summary"); 545 String amrTableSummary = resources.getText("doclet.Member_Table_Summary", 546 amrText, 547 configuration.getText("doclet.modules")); 548 Content amrCaption = getTableCaption(new StringContent(amrText)); 549 Table amrTable = getTable3(amrCaption, amrTableSummary, HtmlStyle.requiresSummary, 550 requiresTableHeader); 551 addModulesList(indirectModules, amrTable); 552 li.addContent(amrTable.toContent()); 553 } 554 HtmlTree ul = HtmlTree.UL(HtmlStyle.blockList, li); 555 summaryContentTree.addContent(ul); 556 } 557 } 558 559 /** 560 * Add the list of modules. 561 * 562 * @param mdleMap map of modules and modifiers 563 * @param tbody the content tree to which the list will be added 564 */ 565 private void addModulesList(Map<ModuleElement, Content> mdleMap, Table table) { 566 for (ModuleElement m : mdleMap.keySet()) { 567 Content modifiers = mdleMap.get(m); 568 Content moduleLink = getModuleLink(m, new StringContent(m.getQualifiedName())); 569 Content moduleSummary = new ContentBuilder(); 570 addSummaryComment(m, moduleSummary); 571 table.addRow(modifiers, moduleLink, moduleSummary); 572 } 573 } 574 575 @Override 576 public void addPackagesSummary(Content summaryContentTree) { 577 if (display(packages) 578 || display(indirectPackages) || display(indirectOpenPackages)) { 579 HtmlTree li = new HtmlTree(HtmlTag.LI); 580 li.setStyle(HtmlStyle.blockList); 581 addSummaryHeader(HtmlConstants.START_OF_PACKAGES_SUMMARY, SectionName.PACKAGES, 582 contents.navPackages, li); 583 if (display(packages)) { 584 String tableSummary = resources.getText("doclet.Member_Table_Summary", 585 resources.getText("doclet.Packages_Summary"), 586 resources.getText("doclet.packages")); 587 addPackageSummary(tableSummary, li); 588 } 589 TableHeader indirectPackagesHeader = 590 new TableHeader(contents.fromLabel, contents.packagesLabel); 591 if (display(indirectPackages)) { 592 String aepText = resources.getText("doclet.Indirect_Exports_Summary"); 593 String aepTableSummary = resources.getText("doclet.Indirect_Packages_Table_Summary", 594 aepText, 595 resources.getText("doclet.modules"), 596 resources.getText("doclet.packages")); 597 Table aepTable = getTable2(new StringContent(aepText), aepTableSummary, 598 HtmlStyle.packagesSummary, indirectPackagesHeader); 599 addIndirectPackages(aepTable, indirectPackages); 600 li.addContent(aepTable.toContent()); 601 } 602 if (display(indirectOpenPackages)) { 603 String aopText = resources.getText("doclet.Indirect_Opens_Summary"); 604 String aopTableSummary = resources.getText("doclet.Indirect_Packages_Table_Summary", 605 aopText, 606 resources.getText("doclet.modules"), 607 resources.getText("doclet.packages")); 608 Table aopTable = getTable2(new StringContent(aopText), aopTableSummary, 609 HtmlStyle.packagesSummary, indirectPackagesHeader); 610 addIndirectPackages(aopTable, indirectOpenPackages); 611 li.addContent(aopTable.toContent()); 612 } 613 HtmlTree ul = HtmlTree.UL(HtmlStyle.blockList, li); 614 summaryContentTree.addContent(ul); 615 } 616 } 617 618 /** 619 * Add the package summary for the module. 620 * 621 * @param tableSummary 622 * @param li 623 */ 624 public void addPackageSummary(String tableSummary, HtmlTree li) { 625 Table table = new Table(configuration.htmlVersion, HtmlStyle.packagesSummary) 626 .setSummary(tableSummary) 627 .setDefaultTab(resources.getText("doclet.All_Packages")) 628 .addTab(resources.getText("doclet.Exported_Packages_Summary"), this::isExported) 629 .addTab(resources.getText("doclet.Opened_Packages_Summary"), this::isOpened) 630 .addTab(resources.getText("doclet.Concealed_Packages_Summary"), this::isConcealed) 631 .setTabScript(i -> String.format("showPkgs(%d);", i)) 632 .setTabScriptVariable("packages"); 633 634 // Determine whether to show the "Exported To" and "Opened To" columns, 635 // based on whether such columns would provide "useful" info. 636 int numExports = 0; 637 int numUnqualifiedExports = 0; 638 int numOpens = 0; 639 int numUnqualifiedOpens = 0; 640 641 for (PackageEntry e : packages.values()) { 642 if (e.exportedTo != null) { 643 numExports++; 644 if (e.exportedTo.isEmpty()) { 645 numUnqualifiedExports++; 646 } 647 } 648 if (e.openedTo != null) { 649 numOpens++; 650 if (e.openedTo.isEmpty()) { 651 numUnqualifiedOpens++; 652 } 653 } 654 } 655 656 boolean showExportedTo = numExports > 0 && (numOpens > 0 || numUnqualifiedExports < packages.size()); 657 boolean showOpenedTo = numOpens > 0 && (numExports > 0 || numUnqualifiedOpens < packages.size()); 658 659 // Create the table header and column styles. 660 List<Content> colHeaders = new ArrayList<>(); 661 List<HtmlStyle> colStyles = new ArrayList<>(); 662 colHeaders.add(contents.packageLabel); 663 colStyles.add(HtmlStyle.colFirst); 664 665 if (showExportedTo) { 666 colHeaders.add(contents.exportedTo); 667 colStyles.add(HtmlStyle.colSecond); 668 } 669 670 if (showOpenedTo) { 671 colHeaders.add(contents.openedTo); 672 colStyles.add(HtmlStyle.colSecond); 673 } 674 675 colHeaders.add(contents.descriptionLabel); 676 colStyles.add(HtmlStyle.colLast); 677 678 table.setHeader(new TableHeader(colHeaders).styles(colStyles)) 679 .setColumnStyles(colStyles); 680 681 // Add the table rows, based on the "packages" map. 682 for (Map.Entry<PackageElement, PackageEntry> e : packages.entrySet()) { 683 PackageElement pkg = e.getKey(); 684 PackageEntry entry = e.getValue(); 685 List<Content> row = new ArrayList<>(); 686 Content pkgLinkContent = getPackageLink(pkg, new StringContent(utils.getPackageName(pkg))); 687 row.add(pkgLinkContent); 688 689 if (showExportedTo) { 690 row.add(getPackageExportOpensTo(entry.exportedTo)); 691 } 692 if (showOpenedTo) { 693 row.add(getPackageExportOpensTo(entry.openedTo)); 694 } 695 Content summary = new ContentBuilder(); 696 addSummaryComment(pkg, summary); 697 row.add(summary); 698 699 table.addRow(pkg, row); 700 } 701 702 li.addContent(table.toContent()); 703 if (table.needsScript()) { 704 mainBodyScript.append(table.getScript()); 705 } 706 } 707 708 private boolean isExported(Element e) { 709 PackageEntry entry = packages.get((PackageElement) e); 710 return (entry != null) && (entry.exportedTo != null); 711 } 712 713 private boolean isOpened(Element e) { 714 PackageEntry entry = packages.get((PackageElement) e); 715 return (entry != null) && (entry.openedTo != null); 716 } 717 718 private boolean isConcealed(Element e) { 719 PackageEntry entry = packages.get((PackageElement) e); 720 return (entry != null) && (entry.exportedTo == null) && (entry.openedTo == null); 721 } 722 723 private Content getPackageExportOpensTo(Set<ModuleElement> modules) { 724 if (modules == null) { 725 return new StringContent(resources.getText("doclet.None")); 726 } else if (modules.isEmpty()) { 727 return new StringContent(resources.getText("doclet.All_Modules")); 728 } else { 729 Content list = new ContentBuilder(); 730 for (ModuleElement m : modules) { 731 if (!list.isEmpty()) { 732 list.addContent(new StringContent(", ")); 733 } 734 list.addContent(getModuleLink(m, new StringContent(m.getQualifiedName()))); 735 } 736 return list; 737 } 738 } 739 740 /** 741 * Add the indirect packages for the module being documented. 742 * 743 * @param table the table to which the content rows will be added 744 * @param ip indirect packages to be added 745 */ 746 public void addIndirectPackages(Table table, Map<ModuleElement, SortedSet<PackageElement>> ip) { 747 for (Map.Entry<ModuleElement, SortedSet<PackageElement>> entry : ip.entrySet()) { 748 ModuleElement m = entry.getKey(); 749 SortedSet<PackageElement> pkgList = entry.getValue(); 750 Content moduleLinkContent = getModuleLink(m, new StringContent(m.getQualifiedName())); 751 Content list = new ContentBuilder(); 752 String sep = ""; 753 for (PackageElement pkg : pkgList) { 754 list.addContent(sep); 755 list.addContent(getPackageLink(pkg, new StringContent(utils.getPackageName(pkg)))); 756 sep = " "; 757 } 758 table.addRow(moduleLinkContent, list); 759 } 760 } 761 762 /** 763 * {@inheritDoc} 764 */ 765 @Override 766 public void addServicesSummary(Content summaryContentTree) { 767 768 boolean haveUses = displayServices(uses, usesTrees); 769 boolean haveProvides = displayServices(provides.keySet(), providesTrees); 770 771 if (haveProvides || haveUses) { 772 HtmlTree li = new HtmlTree(HtmlTag.LI); 773 li.setStyle(HtmlStyle.blockList); 774 addSummaryHeader(HtmlConstants.START_OF_SERVICES_SUMMARY, SectionName.SERVICES, 775 contents.navServices, li); 776 TableHeader usesProvidesTableHeader = 777 new TableHeader(contents.typeLabel, contents.descriptionLabel); 778 if (haveProvides) { 779 String label = resources.getText("doclet.Provides_Summary"); 780 String tableSummary = resources.getText("doclet.Member_Table_Summary", 781 label, 782 resources.getText("doclet.types")); 783 Table table = getTable2(new StringContent(label), tableSummary, HtmlStyle.providesSummary, 784 usesProvidesTableHeader); 785 addProvidesList(table); 786 if (!table.isEmpty()) { 787 li.addContent(table.toContent()); 788 } 789 } 790 if (haveUses){ 791 String label = resources.getText("doclet.Uses_Summary"); 792 String tableSummary = resources.getText("doclet.Member_Table_Summary", 793 label, 794 resources.getText("doclet.types")); 795 Table table = getTable2(new StringContent(label), tableSummary, HtmlStyle.usesSummary, 796 usesProvidesTableHeader); 797 addUsesList(table); 798 if (!table.isEmpty()) { 799 li.addContent(table.toContent()); 800 } 801 } 802 HtmlTree ul = HtmlTree.UL(HtmlStyle.blockList, li); 803 summaryContentTree.addContent(ul); 804 } 805 } 806 807 /** 808 * Add the uses list for the module. 809 * 810 * @param table the table to which the services used will be added 811 */ 812 public void addUsesList(Table table) { 813 Content typeLinkContent; 814 Content description; 815 for (TypeElement t : uses) { 816 if (!displayServiceDirective(t, usesTrees)) { 817 continue; 818 } 819 typeLinkContent = getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.PACKAGE, t)); 820 Content summary = new ContentBuilder(); 821 if (display(usesTrees)) { 822 description = usesTrees.get(t); 823 if (description != null && !description.isEmpty()) { 824 summary.addContent(HtmlTree.DIV(HtmlStyle.block, description)); 825 } else { 826 addSummaryComment(t, summary); 827 } 828 } else { 829 summary.addContent(Contents.SPACE); 830 } 831 table.addRow(typeLinkContent, summary); 832 } 833 } 834 835 /** 836 * Add the provides list for the module. 837 * 838 * @param table the table to which the services provided will be added 839 */ 840 public void addProvidesList(Table table) { 841 SortedSet<TypeElement> implSet; 842 Content description; 843 for (Map.Entry<TypeElement, SortedSet<TypeElement>> entry : provides.entrySet()) { 844 TypeElement srv = entry.getKey(); 845 if (!displayServiceDirective(srv, providesTrees)) { 846 continue; 847 } 848 implSet = entry.getValue(); 849 Content srvLinkContent = getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.PACKAGE, srv)); 850 Content desc = new ContentBuilder(); 851 if (display(providesTrees)) { 852 description = providesTrees.get(srv); 853 desc.addContent((description != null && !description.isEmpty()) 854 ? HtmlTree.DIV(HtmlStyle.block, description) 855 : Contents.SPACE); 856 } else { 857 desc.addContent(Contents.SPACE); 858 } 859 // Only display the implementation details in the "all" mode. 860 if (moduleMode == ModuleMode.ALL && !implSet.isEmpty()) { 861 desc.addContent(new HtmlTree(HtmlTag.BR)); 862 desc.addContent("("); 863 HtmlTree implSpan = HtmlTree.SPAN(HtmlStyle.implementationLabel, contents.implementation); 864 desc.addContent(implSpan); 865 desc.addContent(Contents.SPACE); 866 String sep = ""; 867 for (TypeElement impl : implSet) { 868 desc.addContent(sep); 869 desc.addContent(getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.PACKAGE, impl))); 870 sep = ", "; 871 } 872 desc.addContent(")"); 873 } 874 table.addRow(srvLinkContent, desc); 875 } 876 } 877 878 /** 879 * Add the module deprecation information to the documentation tree. 880 * 881 * @param div the content tree to which the deprecation information will be added 882 */ 883 public void addDeprecationInfo(Content div) { 884 List<? extends DocTree> deprs = utils.getBlockTags(mdle, DocTree.Kind.DEPRECATED); 885 if (utils.isDeprecated(mdle)) { 886 CommentHelper ch = utils.getCommentHelper(mdle); 887 HtmlTree deprDiv = new HtmlTree(HtmlTag.DIV); 888 deprDiv.setStyle(HtmlStyle.deprecationBlock); 889 Content deprPhrase = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(mdle)); 890 deprDiv.addContent(deprPhrase); 891 if (!deprs.isEmpty()) { 892 List<? extends DocTree> commentTags = ch.getDescription(configuration, deprs.get(0)); 893 if (!commentTags.isEmpty()) { 894 addInlineDeprecatedComment(mdle, deprs.get(0), deprDiv); 895 } 896 } 897 div.addContent(deprDiv); 898 } 899 } 900 901 /** 902 * {@inheritDoc} 903 */ 904 @Override 905 public void addModuleDescription(Content moduleContentTree) { 906 if (!utils.getFullBody(mdle).isEmpty()) { 907 Content tree = configuration.allowTag(HtmlTag.SECTION) ? HtmlTree.SECTION() : moduleContentTree; 908 addDeprecationInfo(tree); 909 tree.addContent(HtmlConstants.START_OF_MODULE_DESCRIPTION); 910 tree.addContent(links.createAnchor(SectionName.MODULE_DESCRIPTION)); 911 addInlineComment(mdle, tree); 912 if (configuration.allowTag(HtmlTag.SECTION)) { 913 moduleContentTree.addContent(tree); 914 } 915 } 916 } 917 918 /** 919 * {@inheritDoc} 920 */ 921 @Override 922 public void addModuleTags(Content moduleContentTree) { 923 Content tree = (configuration.allowTag(HtmlTag.SECTION)) 924 ? HtmlTree.SECTION() 925 : moduleContentTree; 926 addTagsInfo(mdle, tree); 927 if (configuration.allowTag(HtmlTag.SECTION)) { 928 moduleContentTree.addContent(tree); 929 } 930 } 931 932 /** 933 * Add summary details to the navigation bar. 934 * 935 * @param subDiv the content tree to which the summary detail links will be added 936 */ 937 @Override 938 protected void addSummaryDetailLinks(Content subDiv) { 939 Content div = HtmlTree.DIV(getNavSummaryLinks()); 940 subDiv.addContent(div); 941 } 942 943 /** 944 * Get summary links for navigation bar. 945 * 946 * @return the content tree for the navigation summary links 947 */ 948 protected Content getNavSummaryLinks() { 949 Content li = HtmlTree.LI(contents.moduleSubNavLabel); 950 li.addContent(Contents.SPACE); 951 Content ulNav = HtmlTree.UL(HtmlStyle.subNavList, li); 952 Content liNav = new HtmlTree(HtmlTag.LI); 953 liNav.addContent(!utils.getFullBody(mdle).isEmpty() && !configuration.nocomment 954 ? links.createLink(SectionName.MODULE_DESCRIPTION, contents.navModuleDescription) 955 : contents.navModuleDescription); 956 addNavGap(liNav); 957 liNav.addContent((display(requires) || display(indirectModules)) 958 ? links.createLink(SectionName.MODULES, contents.navModules) 959 : contents.navModules); 960 addNavGap(liNav); 961 liNav.addContent((display(packages) 962 || display(indirectPackages) || display(indirectOpenPackages)) 963 ? links.createLink(SectionName.PACKAGES, contents.navPackages) 964 : contents.navPackages); 965 addNavGap(liNav); 966 liNav.addContent((displayServices(uses, usesTrees) || displayServices(provides.keySet(), providesTrees)) 967 ? links.createLink(SectionName.SERVICES, contents.navServices) 968 : contents.navServices); 969 ulNav.addContent(liNav); 970 return ulNav; 971 } 972 973 /** 974 * {@inheritDoc} 975 */ 976 @Override 977 public void addModuleContent(Content contentTree, Content moduleContentTree) { 978 if (configuration.allowTag(HtmlTag.MAIN)) { 979 mainTree.addContent(moduleContentTree); 980 contentTree.addContent(mainTree); 981 } else { 982 contentTree.addContent(moduleContentTree); 983 } 984 } 985 986 /** 987 * {@inheritDoc} 988 */ 989 @Override 990 public void addModuleFooter(Content contentTree) { 991 Content htmlTree = (configuration.allowTag(HtmlTag.FOOTER)) 992 ? HtmlTree.FOOTER() 993 : contentTree; 994 addNavLinks(false, htmlTree); 995 addBottom(htmlTree); 996 if (configuration.allowTag(HtmlTag.FOOTER)) { 997 contentTree.addContent(htmlTree); 998 } 999 } 1000 1001 /** 1002 * {@inheritDoc} 1003 * 1004 * @throws jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException 1005 */ 1006 @Override 1007 public void printDocument(Content contentTree) throws DocFileIOException { 1008 printHtmlDocument(configuration.metakeywords.getMetaKeywordsForModule(mdle), 1009 true, contentTree); 1010 } 1011 1012 /** 1013 * Add the module package deprecation information to the documentation tree. 1014 * 1015 * @param li the content tree to which the deprecation information will be added 1016 * @param pkg the PackageDoc that is added 1017 */ 1018 public void addPackageDeprecationInfo(Content li, PackageElement pkg) { 1019 List<? extends DocTree> deprs; 1020 if (utils.isDeprecated(pkg)) { 1021 deprs = utils.getDeprecatedTrees(pkg); 1022 HtmlTree deprDiv = new HtmlTree(HtmlTag.DIV); 1023 deprDiv.setStyle(HtmlStyle.deprecationBlock); 1024 Content deprPhrase = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(pkg)); 1025 deprDiv.addContent(deprPhrase); 1026 if (!deprs.isEmpty()) { 1027 CommentHelper ch = utils.getCommentHelper(pkg); 1028 List<? extends DocTree> commentTags = ch.getDescription(configuration, deprs.get(0)); 1029 if (!commentTags.isEmpty()) { 1030 addInlineDeprecatedComment(pkg, deprs.get(0), deprDiv); 1031 } 1032 } 1033 li.addContent(deprDiv); 1034 } 1035 } 1036 1037 /** 1038 * Get this module link. 1039 * 1040 * @return a content tree for the module link 1041 */ 1042 @Override 1043 protected Content getNavLinkModule() { 1044 Content li = HtmlTree.LI(HtmlStyle.navBarCell1Rev, contents.moduleLabel); 1045 return li; 1046 } 1047 1048 /** 1049 * Get "PREV MODULE" link in the navigation bar. 1050 * 1051 * @return a content tree for the previous link 1052 */ 1053 @Override 1054 public Content getNavLinkPrevious() { 1055 Content li; 1056 if (prevModule == null) { 1057 li = HtmlTree.LI(contents.prevModuleLabel); 1058 } else { 1059 li = HtmlTree.LI(links.createLink(pathToRoot.resolve(docPaths.moduleSummary( 1060 prevModule)), contents.prevModuleLabel, "", "")); 1061 } 1062 return li; 1063 } 1064 1065 /** 1066 * Get "NEXT MODULE" link in the navigation bar. 1067 * 1068 * @return a content tree for the next link 1069 */ 1070 @Override 1071 public Content getNavLinkNext() { 1072 Content li; 1073 if (nextModule == null) { 1074 li = HtmlTree.LI(contents.nextModuleLabel); 1075 } else { 1076 li = HtmlTree.LI(links.createLink(pathToRoot.resolve(docPaths.moduleSummary( 1077 nextModule)), contents.nextModuleLabel, "", "")); 1078 } 1079 return li; 1080 } 1081 }