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
26package jdk.javadoc.internal.doclets.formats.html;
27
28import java.util.Collections;
29import java.util.EnumSet;
30import java.util.LinkedHashMap;
31import java.util.List;
32import java.util.Map;
33import java.util.Set;
34import java.util.SortedSet;
35import java.util.TreeMap;
36import java.util.TreeSet;
37
38import javax.lang.model.element.Element;
39import javax.lang.model.element.ModuleElement;
40import javax.lang.model.element.PackageElement;
41import javax.lang.model.element.TypeElement;
42import javax.lang.model.util.ElementFilter;
43
44import com.sun.source.doctree.DocTree;
45import jdk.javadoc.doclet.DocletEnvironment.ModuleMode;
46import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr;
47import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants;
48import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
49import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
50import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
51import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml;
52import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
53import jdk.javadoc.internal.doclets.toolkit.Content;
54import jdk.javadoc.internal.doclets.toolkit.ModuleSummaryWriter;
55import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
56import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
57import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
58import 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 */
72public 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(ConfigurationImpl 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, Content htmltree) {
461        htmltree.addContent(startMarker);
462        htmltree.addContent(getMarkerAnchor(markerAnchor));
463        htmltree.addContent(HtmlTree.HEADING(HtmlTag.H3, heading));
464    }
465
466    /**
467     * Get table header.
468     *
469     * @param text the table caption
470     * @param tableSummary the summary for the table
471     * @param tableStyle the table style
472     * @param tableHeader the table header
473     * @return a content object
474     */
475    public Content getTableHeader(String text, String tableSummary, HtmlStyle tableStyle,
476            List<String> tableHeader) {
477        return getTableHeader(getTableCaption(new RawHtml(text)), tableSummary, tableStyle, tableHeader);
478    }
479
480    /**
481     * Get table header.
482     *
483     * @param caption the table caption
484     * @param tableSummary the summary for the table
485     * @param tableStyle the table style
486     * @param tableHeader the table header
487     * @return a content object
488     */
489    public Content getTableHeader(Content caption, String tableSummary, HtmlStyle tableStyle,
490            List<String> tableHeader) {
491        Content table = (configuration.isOutputHtml5())
492                ? HtmlTree.TABLE(tableStyle, caption)
493                : HtmlTree.TABLE(tableStyle, tableSummary, caption);
494        table.addContent(getSummaryTableHeader(tableHeader, "col"));
495        return table;
496    }
497
498    /**
499     * {@inheritDoc}
500     */
501    public void addModulesSummary(Content summaryContentTree) {
502        if (display(requires) || display(indirectModules)) {
503            HtmlTree li = new HtmlTree(HtmlTag.LI);
504            li.addStyle(HtmlStyle.blockList);
505            addSummaryHeader(HtmlConstants.START_OF_MODULES_SUMMARY, SectionName.MODULES,
506                    contents.navModules, li);
507            if (display(requires)) {
508                String text = configuration.getText("doclet.Requires_Summary");
509                String tableSummary = configuration.getText("doclet.Member_Table_Summary",
510                        configuration.getText("doclet.Requires_Summary"),
511                        configuration.getText("doclet.modules"));
512                Content table = getTableHeader(text, tableSummary, HtmlStyle.requiresSummary, requiresTableHeader);
513                Content tbody = new HtmlTree(HtmlTag.TBODY);
514                addModulesList(requires, tbody);
515                table.addContent(tbody);
516                li.addContent(table);
517            }
518            // Display indirect modules table in both "api" and "all" mode.
519            if (display(indirectModules)) {
520                String amrText = configuration.getText("doclet.Indirect_Requires_Summary");
521                String amrTableSummary = configuration.getText("doclet.Member_Table_Summary",
522                        configuration.getText("doclet.Indirect_Requires_Summary"),
523                        configuration.getText("doclet.modules"));
524                Content amrTable = getTableHeader(amrText, amrTableSummary, HtmlStyle.requiresSummary, requiresTableHeader);
525                Content amrTbody = new HtmlTree(HtmlTag.TBODY);
526                addModulesList(indirectModules, amrTbody);
527                amrTable.addContent(amrTbody);
528                li.addContent(amrTable);
529            }
530            HtmlTree ul = HtmlTree.UL(HtmlStyle.blockList, li);
531            summaryContentTree.addContent(ul);
532        }
533    }
534
535    /**
536     * Add the list of modules.
537     *
538     * @param mdleMap map of modules and modifiers
539     * @param tbody the content tree to which the list will be added
540     */
541    public void addModulesList(Map<ModuleElement, Content> mdleMap, Content tbody) {
542        boolean altColor = true;
543        for (ModuleElement m : mdleMap.keySet()) {
544            Content tdModifiers = HtmlTree.TD(HtmlStyle.colFirst, mdleMap.get(m));
545            Content moduleLinkContent = getModuleLink(m, new StringContent(m.getQualifiedName()));
546            Content thModule = HtmlTree.TH_ROW_SCOPE(HtmlStyle.colSecond, moduleLinkContent);
547            HtmlTree tdSummary = new HtmlTree(HtmlTag.TD);
548            tdSummary.addStyle(HtmlStyle.colLast);
549            addSummaryComment(m, tdSummary);
550            HtmlTree tr = HtmlTree.TR(tdModifiers);
551            tr.addContent(thModule);
552            tr.addContent(tdSummary);
553            tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
554            tbody.addContent(tr);
555            altColor = !altColor;
556        }
557    }
558
559    public void addPackagesSummary(Content summaryContentTree) {
560        if (display(exportedPackages) || display(openedPackages) || display(concealedPackages)
561                || display(indirectPackages) || display(indirectOpenPackages)) {
562            HtmlTree li = new HtmlTree(HtmlTag.LI);
563            li.addStyle(HtmlStyle.blockList);
564            addSummaryHeader(HtmlConstants.START_OF_PACKAGES_SUMMARY, SectionName.PACKAGES,
565                    contents.navPackages, li);
566            String tableSummary = configuration.getText("doclet.Member_Table_Summary",
567                    configuration.getText("doclet.Packages_Summary"),
568                    configuration.getText("doclet.packages"));
569            if (display(exportedPackages) || display(openedPackages) || display(concealedPackages)) {
570                addPackageSummary(tableSummary, li);
571            }
572            if (display(indirectPackages)) {
573                String aepText = configuration.getText("doclet.Indirect_Exports_Summary");
574                String aepTableSummary = configuration.getText("doclet.Indirect_Packages_Table_Summary",
575                        configuration.getText("doclet.Indirect_Exports_Summary"),
576                        configuration.getText("doclet.modules"),
577                        configuration.getText("doclet.packages"));
578                Content aepTable = getTableHeader(aepText, aepTableSummary, HtmlStyle.packagesSummary,
579                        indirectPackagesTableHeader);
580                Content aepTbody = new HtmlTree(HtmlTag.TBODY);
581                addIndirectPackages(aepTbody, indirectPackages);
582                aepTable.addContent(aepTbody);
583                li.addContent(aepTable);
584            }
585            if (display(indirectOpenPackages)) {
586                String aopText = configuration.getText("doclet.Indirect_Opens_Summary");
587                String aopTableSummary = configuration.getText("doclet.Indirect_Packages_Table_Summary",
588                        configuration.getText("doclet.Indirect_Opens_Summary"),
589                        configuration.getText("doclet.modules"),
590                        configuration.getText("doclet.packages"));
591                Content aopTable = getTableHeader(aopText, aopTableSummary, HtmlStyle.packagesSummary,
592                        indirectPackagesTableHeader);
593                Content aopTbody = new HtmlTree(HtmlTag.TBODY);
594                addIndirectPackages(aopTbody, indirectOpenPackages);
595                aopTable.addContent(aopTbody);
596                li.addContent(aopTable);
597            }
598            HtmlTree ul = HtmlTree.UL(HtmlStyle.blockList, li);
599            summaryContentTree.addContent(ul);
600        }
601    }
602
603    /**
604     * Add the package summary for the module.
605     *
606     * @param tableSummary
607     * @param li
608     */
609    public void addPackageSummary(String tableSummary, HtmlTree li) {
610        Content caption;
611        Content tbody = getPackageTableRows();
612        if (showTabs()) {
613            caption = getTableCaption();
614            generateTableTabTypesScript(typeMap, modulePackageTypes, "packages");
615        } else {
616            ModulePackageTypes type = modulePackageTypes.iterator().next();
617            caption = getTableCaption(configuration.getContent(type.tableTabs().resourceKey()));
618        }
619        Content table = getTableHeader(caption, tableSummary, HtmlStyle.packagesSummary, exportedPackagesTableHeader);
620        table.addContent(tbody);
621        li.addContent(table);
622    }
623
624    /**
625     * Returns true if the table tabs needs to be displayed.
626     *
627     * @return true if the tabs should be displayed
628     */
629    public boolean showTabs() {
630        int value;
631        for (ModulePackageTypes type : EnumSet.allOf(ModulePackageTypes.class)) {
632            value = type.tableTabs().value();
633            if ((value & packageTypesOr) == value) {
634                modulePackageTypes.add(type);
635            }
636        }
637        boolean showTabs = modulePackageTypes.size() > 1;
638        if (showTabs) {
639            modulePackageTypes.add(ModulePackageTypes.ALL);
640        }
641        return showTabs;
642    }
643
644    /**
645     * Get the summary table caption.
646     *
647     * @return the caption for the summary table
648     */
649    public Content getTableCaption() {
650        Content tabbedCaption = new HtmlTree(HtmlTag.CAPTION);
651        for (ModulePackageTypes type : modulePackageTypes) {
652            Content captionSpan;
653            Content span;
654            if (type.tableTabs().isDefaultTab()) {
655                captionSpan = HtmlTree.SPAN(configuration.getContent(type.tableTabs().resourceKey()));
656                span = HtmlTree.SPAN(type.tableTabs().tabId(),
657                        HtmlStyle.activeTableTab, captionSpan);
658            } else {
659                captionSpan = HtmlTree.SPAN(getPackageTypeLinks(type));
660                span = HtmlTree.SPAN(type.tableTabs().tabId(),
661                        HtmlStyle.tableTab, captionSpan);
662            }
663            Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, Contents.SPACE);
664            span.addContent(tabSpan);
665            tabbedCaption.addContent(span);
666        }
667        return tabbedCaption;
668    }
669
670    /**
671     * Get the package type links for the table caption.
672     *
673     * @param packageType the package type to be displayed as link
674     * @return the content tree for the package type link
675     */
676    public Content getPackageTypeLinks(ModulePackageTypes packageType) {
677        String jsShow = "javascript:showPkgs(" + packageType.tableTabs().value() + ");";
678        HtmlTree link = HtmlTree.A(jsShow, configuration.getContent(packageType.tableTabs().resourceKey()));
679        return link;
680    }
681
682    /**
683     * Get the package table rows.
684     *
685     * @return a content object
686     */
687    public Content getPackageTableRows() {
688        Content tbody = new HtmlTree(HtmlTag.TBODY);
689        boolean altColor = true;
690        int counter = 0;
691        counter = addPackageTableRows(tbody, counter, ModulePackageTypes.EXPORTED, exportedPackages);
692        counter = addPackageTableRows(tbody, counter, ModulePackageTypes.OPENED, openedPackages);
693        // Show concealed packages only in "all" mode.
694        if (moduleMode == ModuleMode.ALL) {
695            for (PackageElement pkg : concealedPackages) {
696                Content pkgLinkContent = getPackageLink(pkg, new StringContent(utils.getPackageName(pkg)));
697                Content thPackage = HtmlTree.TH_ROW_SCOPE(HtmlStyle.colFirst, pkgLinkContent);
698                HtmlTree tdModules = new HtmlTree(HtmlTag.TD);
699                tdModules.addStyle(HtmlStyle.colSecond);
700                tdModules.addContent(configuration.getText("doclet.None"));
701        HtmlTree tdSummary = new HtmlTree(HtmlTag.TD);
702        tdSummary.addStyle(HtmlStyle.colLast);
703                addSummaryComment(pkg, tdSummary);
704        HtmlTree tr = HtmlTree.TR(thPackage);
705                tr.addContent(tdModules);
706        tr.addContent(tdSummary);
707        tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
708                int pkgType = ModulePackageTypes.CONCEALED.tableTabs().value();
709                packageTypesOr = packageTypesOr | pkgType;
710                String tableId = "i" + counter;
711                counter++;
712                typeMap.put(tableId, pkgType);
713                tr.addAttr(HtmlAttr.ID, tableId);
714        tbody.addContent(tr);
715                altColor = !altColor;
716            }
717        }
718        return tbody;
719    }
720
721    public int addPackageTableRows(Content tbody, int counter, ModulePackageTypes pType,
722            Map<PackageElement,SortedSet<ModuleElement>> ap) {
723        boolean altColor = true;
724        for (Map.Entry<PackageElement, SortedSet<ModuleElement>> entry : ap.entrySet()) {
725            PackageElement pkg = entry.getKey();
726            SortedSet<ModuleElement> mdleList = entry.getValue();
727            Content pkgLinkContent = getPackageLink(pkg, new StringContent(utils.getPackageName(pkg)));
728            Content thPackage = HtmlTree.TH_ROW_SCOPE(HtmlStyle.colFirst, pkgLinkContent);
729            HtmlTree tr = HtmlTree.TR(thPackage);
730            if (moduleMode == ModuleMode.ALL) {
731                HtmlTree tdModules = new HtmlTree(HtmlTag.TD);
732                tdModules.addStyle(HtmlStyle.colSecond);
733                if (!mdleList.isEmpty()) {
734                    int sep = 0;
735                    for (ModuleElement m : mdleList) {
736                        if (sep > 0) {
737                            tdModules.addContent(new HtmlTree(HtmlTag.BR));
738                        }
739                        tdModules.addContent(getModuleLink(m, new StringContent(m.getQualifiedName())));
740                        sep++;
741                    }
742                } else {
743                    tdModules.addContent(configuration.getText("doclet.All_Modules"));
744                }
745                tr.addContent(tdModules);
746            }
747            HtmlTree tdSummary = new HtmlTree(HtmlTag.TD);
748            tdSummary.addStyle(HtmlStyle.colLast);
749            addSummaryComment(pkg, tdSummary);
750            tr.addContent(tdSummary);
751            tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
752            int pkgType = pType.tableTabs().value();
753            packageTypesOr = packageTypesOr | pkgType;
754            String tableId = "i" + counter;
755            counter++;
756            typeMap.put(tableId, pkgType);
757            tr.addAttr(HtmlAttr.ID, tableId);
758            tbody.addContent(tr);
759            altColor = !altColor;
760        }
761        return counter;
762    }
763
764    /**
765     * Add the indirect packages for the module being documented.
766     *
767     * @param tbody the content tree to which the table will be added
768     * @param ip indirect packages to be added
769     */
770    public void addIndirectPackages(Content tbody, Map<ModuleElement, SortedSet<PackageElement>> ip) {
771        boolean altColor = true;
772        for (Map.Entry<ModuleElement, SortedSet<PackageElement>> entry : ip.entrySet()) {
773            ModuleElement m = entry.getKey();
774            SortedSet<PackageElement> pkgList = entry.getValue();
775            Content moduleLinkContent = getModuleLink(m, new StringContent(m.getQualifiedName()));
776            Content thModule = HtmlTree.TH_ROW_SCOPE(HtmlStyle.colFirst, moduleLinkContent);
777            HtmlTree tdPackages = new HtmlTree(HtmlTag.TD);
778            tdPackages.addStyle(HtmlStyle.colLast);
779            String sep = "";
780            for (PackageElement pkg : pkgList) {
781                tdPackages.addContent(sep);
782                tdPackages.addContent(getPackageLink(pkg, new StringContent(utils.getPackageName(pkg))));
783                sep = " ";
784            }
785            HtmlTree tr = HtmlTree.TR(thModule);
786            tr.addContent(tdPackages);
787            tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
788            tbody.addContent(tr);
789            altColor = !altColor;
790        }
791    }
792
793    /**
794     * {@inheritDoc}
795     */
796    public void addServicesSummary(Content summaryContentTree) {
797
798        boolean haveUses = displayServices(uses, usesTrees);
799        boolean haveProvides = displayServices(provides.keySet(), providesTrees);
800
801        if (haveProvides || haveUses) {
802            HtmlTree li = new HtmlTree(HtmlTag.LI);
803            li.addStyle(HtmlStyle.blockList);
804            addSummaryHeader(HtmlConstants.START_OF_SERVICES_SUMMARY, SectionName.SERVICES,
805                    contents.navServices, li);
806            String text;
807            String tableSummary;
808            if (haveProvides) {
809                text = configuration.getText("doclet.Provides_Summary");
810                tableSummary = configuration.getText("doclet.Member_Table_Summary",
811                        configuration.getText("doclet.Provides_Summary"),
812                        configuration.getText("doclet.types"));
813                Content table = getTableHeader(text, tableSummary, HtmlStyle.providesSummary, providesTableHeader);
814                Content tbody = new HtmlTree(HtmlTag.TBODY);
815                addProvidesList(tbody);
816                if (!tbody.isEmpty()) {
817                    table.addContent(tbody);
818                    li.addContent(table);
819                }
820            }
821            if (haveUses){
822                text = configuration.getText("doclet.Uses_Summary");
823                tableSummary = configuration.getText("doclet.Member_Table_Summary",
824                        configuration.getText("doclet.Uses_Summary"),
825                        configuration.getText("doclet.types"));
826                Content table = getTableHeader(text, tableSummary, HtmlStyle.usesSummary, usesTableHeader);
827                Content tbody = new HtmlTree(HtmlTag.TBODY);
828                addUsesList(tbody);
829                if (!tbody.isEmpty()) {
830                    table.addContent(tbody);
831                    li.addContent(table);
832                }
833            }
834            HtmlTree ul = HtmlTree.UL(HtmlStyle.blockList, li);
835            summaryContentTree.addContent(ul);
836        }
837    }
838
839    /**
840     * Add the uses list for the module.
841     *
842     * @param tbody the content tree to which the directive will be added
843     */
844    public void addUsesList(Content tbody) {
845        boolean altColor = true;
846        Content typeLinkContent;
847        Content thType;
848        HtmlTree tdSummary;
849        Content description;
850        for (TypeElement t : uses) {
851            if (!displayServiceDirective(t, usesTrees)) {
852                continue;
853            }
854            typeLinkContent = getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.PACKAGE, t));
855            thType = HtmlTree.TH_ROW_SCOPE(HtmlStyle.colFirst, typeLinkContent);
856            tdSummary = new HtmlTree(HtmlTag.TD);
857            tdSummary.addStyle(HtmlStyle.colLast);
858            if (display(usesTrees)) {
859                description = usesTrees.get(t);
860                if (description != null) {
861                    tdSummary.addContent(description);
862                }
863            }
864            addSummaryComment(t, tdSummary);
865            HtmlTree tr = HtmlTree.TR(thType);
866            tr.addContent(tdSummary);
867            tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
868            tbody.addContent(tr);
869            altColor = !altColor;
870        }
871    }
872
873    /**
874     * Add the provides list for the module.
875     *
876     * @param tbody the content tree to which the directive will be added
877     */
878    public void addProvidesList(Content tbody) {
879        boolean altColor = true;
880        SortedSet<TypeElement> implSet;
881        Content description;
882        for (Map.Entry<TypeElement, SortedSet<TypeElement>> entry : provides.entrySet()) {
883            TypeElement srv = entry.getKey();
884            if (!displayServiceDirective(srv, providesTrees)) {
885                continue;
886            }
887            implSet = entry.getValue();
888            Content srvLinkContent = getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.PACKAGE, srv));
889            HtmlTree thType = HtmlTree.TH_ROW_SCOPE(HtmlStyle.colFirst, srvLinkContent);
890            HtmlTree tdDesc = new HtmlTree(HtmlTag.TD);
891            tdDesc.addStyle(HtmlStyle.colLast);
892            if (display(providesTrees)) {
893                description = providesTrees.get(srv);
894                if (description != null) {
895                    tdDesc.addContent(description);
896                }
897            }
898            addSummaryComment(srv, tdDesc);
899            // Only display the implementation details in the "all" mode.
900            if (moduleMode == ModuleMode.ALL && !implSet.isEmpty()) {
901                tdDesc.addContent(new HtmlTree(HtmlTag.BR));
902                tdDesc.addContent("(");
903                HtmlTree implSpan = HtmlTree.SPAN(HtmlStyle.implementationLabel, contents.implementation);
904                tdDesc.addContent(implSpan);
905                tdDesc.addContent(Contents.SPACE);
906                String sep = "";
907                for (TypeElement impl : implSet) {
908                    tdDesc.addContent(sep);
909                    tdDesc.addContent(getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.PACKAGE, impl)));
910                    sep = ", ";
911                }
912                tdDesc.addContent(")");
913            }
914            HtmlTree tr = HtmlTree.TR(thType);
915            tr.addContent(tdDesc);
916            tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor);
917            tbody.addContent(tr);
918            altColor = !altColor;
919        }
920    }
921
922    /**
923     * Add the module deprecation information to the documentation tree.
924     *
925     * @param div the content tree to which the deprecation information will be added
926     */
927    public void addDeprecationInfo(Content div) {
928        List<? extends DocTree> deprs = utils.getBlockTags(mdle, DocTree.Kind.DEPRECATED);
929        if (utils.isDeprecated(mdle)) {
930            CommentHelper ch = utils.getCommentHelper(mdle);
931            HtmlTree deprDiv = new HtmlTree(HtmlTag.DIV);
932            deprDiv.addStyle(HtmlStyle.deprecatedContent);
933            Content deprPhrase = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(mdle));
934            deprDiv.addContent(deprPhrase);
935            if (!deprs.isEmpty()) {
936                List<? extends DocTree> commentTags = ch.getDescription(configuration, deprs.get(0));
937                if (!commentTags.isEmpty()) {
938                    addInlineDeprecatedComment(mdle, deprs.get(0), deprDiv);
939                }
940            }
941            div.addContent(deprDiv);
942        }
943    }
944
945    /**
946     * {@inheritDoc}
947     */
948    @Override
949    public void addModuleDescription(Content moduleContentTree) {
950        if (!utils.getFullBody(mdle).isEmpty()) {
951            Content tree = configuration.allowTag(HtmlTag.SECTION) ? HtmlTree.SECTION() : moduleContentTree;
952            addDeprecationInfo(tree);
953            tree.addContent(HtmlConstants.START_OF_MODULE_DESCRIPTION);
954            tree.addContent(getMarkerAnchor(SectionName.MODULE_DESCRIPTION));
955            addInlineComment(mdle, tree);
956            if (configuration.allowTag(HtmlTag.SECTION)) {
957                moduleContentTree.addContent(tree);
958            }
959        }
960    }
961
962    /**
963     * {@inheritDoc}
964     */
965    @Override
966    public void addModuleTags(Content moduleContentTree) {
967        Content tree = (configuration.allowTag(HtmlTag.SECTION))
968                ? HtmlTree.SECTION()
969                : moduleContentTree;
970        addTagsInfo(mdle, tree);
971        if (configuration.allowTag(HtmlTag.SECTION)) {
972            moduleContentTree.addContent(tree);
973        }
974    }
975
976    /**
977     * Add summary details to the navigation bar.
978     *
979     * @param subDiv the content tree to which the summary detail links will be added
980     */
981    @Override
982    protected void addSummaryDetailLinks(Content subDiv) {
983        Content div = HtmlTree.DIV(getNavSummaryLinks());
984        subDiv.addContent(div);
985    }
986
987    /**
988     * Get summary links for navigation bar.
989     *
990     * @return the content tree for the navigation summary links
991     */
992    protected Content getNavSummaryLinks() {
993        Content li = HtmlTree.LI(contents.moduleSubNavLabel);
994        li.addContent(Contents.SPACE);
995        Content ulNav = HtmlTree.UL(HtmlStyle.subNavList, li);
996        Content liNav = new HtmlTree(HtmlTag.LI);
997        liNav.addContent(!utils.getFullBody(mdle).isEmpty() && !configuration.nocomment
998                ? getHyperLink(SectionName.MODULE_DESCRIPTION, contents.navModuleDescription)
999                : contents.navModuleDescription);
1000        addNavGap(liNav);
1001        liNav.addContent((display(requires) || display(indirectModules))
1002                ? getHyperLink(SectionName.MODULES, contents.navModules)
1003                : contents.navModules);
1004        addNavGap(liNav);
1005        liNav.addContent((display(exportedPackages) || display(openedPackages) || display(concealedPackages)
1006                || display(indirectPackages) || display(indirectOpenPackages))
1007                ? getHyperLink(SectionName.PACKAGES, contents.navPackages)
1008                : contents.navPackages);
1009        addNavGap(liNav);
1010        liNav.addContent((display(uses) || (moduleMode == ModuleMode.API && display(usesTrees))
1011                || display(provides) || (moduleMode == ModuleMode.API && display(providesTrees)))
1012                ? getHyperLink(SectionName.SERVICES, contents.navServices)
1013                : contents.navServices);
1014        ulNav.addContent(liNav);
1015        return ulNav;
1016    }
1017
1018    /**
1019     * {@inheritDoc}
1020     */
1021    @Override
1022    public void addModuleContent(Content contentTree, Content moduleContentTree) {
1023        if (configuration.allowTag(HtmlTag.MAIN)) {
1024            mainTree.addContent(moduleContentTree);
1025            contentTree.addContent(mainTree);
1026        } else {
1027            contentTree.addContent(moduleContentTree);
1028        }
1029    }
1030
1031    /**
1032     * {@inheritDoc}
1033     */
1034    @Override
1035    public void addModuleFooter(Content contentTree) {
1036        Content htmlTree = (configuration.allowTag(HtmlTag.FOOTER))
1037                ? HtmlTree.FOOTER()
1038                : contentTree;
1039        addNavLinks(false, htmlTree);
1040        addBottom(htmlTree);
1041        if (configuration.allowTag(HtmlTag.FOOTER)) {
1042            contentTree.addContent(htmlTree);
1043        }
1044    }
1045
1046    /**
1047     * {@inheritDoc}
1048     *
1049     * @throws jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException
1050     */
1051    @Override
1052    public void printDocument(Content contentTree) throws DocFileIOException {
1053        printHtmlDocument(configuration.metakeywords.getMetaKeywordsForModule(mdle),
1054                true, contentTree);
1055    }
1056
1057    /**
1058     * Add the module package deprecation information to the documentation tree.
1059     *
1060     * @param li the content tree to which the deprecation information will be added
1061     * @param pkg the PackageDoc that is added
1062     */
1063    public void addPackageDeprecationInfo(Content li, PackageElement pkg) {
1064        List<? extends DocTree> deprs;
1065        if (utils.isDeprecated(pkg)) {
1066            deprs = utils.getDeprecatedTrees(pkg);
1067            HtmlTree deprDiv = new HtmlTree(HtmlTag.DIV);
1068            deprDiv.addStyle(HtmlStyle.deprecatedContent);
1069            Content deprPhrase = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(pkg));
1070            deprDiv.addContent(deprPhrase);
1071            if (!deprs.isEmpty()) {
1072                CommentHelper ch = utils.getCommentHelper(pkg);
1073                List<? extends DocTree> commentTags = ch.getDescription(configuration, deprs.get(0));
1074                if (!commentTags.isEmpty()) {
1075                    addInlineDeprecatedComment(pkg, deprs.get(0), deprDiv);
1076                }
1077            }
1078            li.addContent(deprDiv);
1079        }
1080    }
1081
1082    /**
1083     * Get this module link.
1084     *
1085     * @return a content tree for the module link
1086     */
1087    @Override
1088    protected Content getNavLinkModule() {
1089        Content li = HtmlTree.LI(HtmlStyle.navBarCell1Rev, contents.moduleLabel);
1090        return li;
1091    }
1092
1093    /**
1094     * Get "PREV MODULE" link in the navigation bar.
1095     *
1096     * @return a content tree for the previous link
1097     */
1098    @Override
1099    public Content getNavLinkPrevious() {
1100        Content li;
1101        if (prevModule == null) {
1102            li = HtmlTree.LI(contents.prevModuleLabel);
1103        } else {
1104            li = HtmlTree.LI(getHyperLink(pathToRoot.resolve(DocPaths.moduleSummary(
1105                    prevModule)), contents.prevModuleLabel, "", ""));
1106        }
1107        return li;
1108    }
1109
1110    /**
1111     * Get "NEXT MODULE" link in the navigation bar.
1112     *
1113     * @return a content tree for the next link
1114     */
1115    @Override
1116    public Content getNavLinkNext() {
1117        Content li;
1118        if (nextModule == null) {
1119            li = HtmlTree.LI(contents.nextModuleLabel);
1120        } else {
1121            li = HtmlTree.LI(getHyperLink(pathToRoot.resolve(DocPaths.moduleSummary(
1122                    nextModule)), contents.nextModuleLabel, "", ""));
1123        }
1124        return li;
1125    }
1126}
1127