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