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