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