001/*
002 * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package org.jdrupes.mdoclet.internal.doclets.formats.html;
027
028import java.util.List;
029import javax.lang.model.element.Element;
030
031import org.jdrupes.mdoclet.internal.doclets.formats.html.Navigation.PageMode;
032import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.BodyContents;
033import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.ContentBuilder;
034import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlId;
035import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlStyle;
036import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlTree;
037import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.TagName;
038import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.Text;
039import org.jdrupes.mdoclet.internal.doclets.toolkit.Content;
040import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocFileIOException;
041import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocLink;
042import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocPath;
043import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocPaths;
044
045/**
046 * Generate the Help File for the generated API documentation. The help file
047 * contents are helpful for browsing the generated documentation.
048 */
049public class HelpWriter extends HtmlDocletWriter {
050
051    private final String[][] SEARCH_EXAMPLES = {
052        { "\"j.l.obj\"", "\"java.lang.Object\"" },
053        { "\"InpStr\"", "\"java.io.InputStream\"" },
054        { "\"math exact long\"", "\"java.lang.Math.absExact(long)\"" }
055    };
056
057    Content overviewLink;
058    Content indexLink;
059    Content allClassesLink;
060    Content allPackagesLink;
061
062    /**
063     * Constructor to construct HelpWriter object.
064     * @param configuration the configuration
065     * @param filename File to be generated.
066     */
067    public HelpWriter(HtmlConfiguration configuration,
068            DocPath filename) {
069        super(configuration, filename);
070
071        // yes, INDEX is correct in the following line
072        overviewLink = links.createLink(DocPaths.INDEX,
073            resources.getText("doclet.Overview"));
074        allPackagesLink = links.createLink(DocPaths.ALLPACKAGES_INDEX,
075            resources.getText("doclet.All_Packages"));
076        allClassesLink = links.createLink(DocPaths.ALLCLASSES_INDEX,
077            resources.getText("doclet.All_Classes_And_Interfaces"));
078        DocPath dp = options.splitIndex()
079            ? DocPaths.INDEX_FILES.resolve(DocPaths.indexN(1))
080            : DocPaths.INDEX_ALL;
081        indexLink = links.createLink(dp, resources.getText("doclet.Index"));
082    }
083
084    /**
085     * Construct the HelpWriter object and then use it to generate the help
086     * file. The name of the generated file is "help-doc.html". The help file
087     * will get generated if and only if "-helpfile" and "-nohelp" is not used
088     * on the command line.
089     *
090     * @param configuration the configuration
091     * @throws DocFileIOException if there is a problem while generating the documentation
092     */
093    public static void generate(HtmlConfiguration configuration)
094            throws DocFileIOException {
095        DocPath filename = DocPaths.HELP_DOC;
096        HelpWriter helpgen = new HelpWriter(configuration, filename);
097        helpgen.generateHelpFile();
098    }
099
100    /**
101     * Generate the help file contents.
102     *
103     * @throws DocFileIOException if there is a problem while generating the documentation
104     */
105    protected void generateHelpFile() throws DocFileIOException {
106        String title = resources.getText("doclet.Window_Help_title");
107        HtmlTree body = getBody(getWindowTitle(title));
108        ContentBuilder helpFileContent = new ContentBuilder();
109        addHelpFileContents(helpFileContent);
110        body.add(new BodyContents()
111            .setHeader(getHeader(PageMode.HELP))
112            .addMainContent(helpFileContent)
113            .setFooter(getFooter()));
114        printHtmlDocument(null, "help", body);
115    }
116
117    /**
118     * Adds the help file contents from the resource file to the content.
119     * While adding the help file contents it also keeps track of user options.
120     *
121     * The general organization is:
122     * <ul>
123     * <li>Heading, and TOC
124     * <li>Navigation help
125     * <li>Page-specific help
126     * </ul>
127     */
128    protected void addHelpFileContents(Content content) {
129        var mainTOC = HtmlTree.UL(HtmlStyle.helpTOC);
130
131        content
132            .add(HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING, HtmlStyle.title,
133                getContent("doclet.help.main_heading")))
134            .add(mainTOC)
135            .add(new HtmlTree(TagName.HR))
136            .add(getNavigationSection(mainTOC))
137            .add(new HtmlTree(TagName.HR))
138            .add(getPageKindSection(mainTOC))
139            .add(new HtmlTree(TagName.HR))
140            .add(HtmlTree.SPAN(HtmlStyle.helpFootnote,
141                getContent("doclet.help.footnote")));
142    }
143
144    @Override
145    protected Navigation getNavBar(PageMode pageMode, Element element) {
146        return super.getNavBar(pageMode, element)
147            .setSubNavLinks(() -> List.of(
148                links.createLink(HtmlIds.HELP_NAVIGATION,
149                    contents.navHelpNavigation),
150                links.createLink(HtmlIds.HELP_PAGES, contents.navHelpPages)));
151    }
152
153    /**
154     * Creates the navigation help, adding an entry into the main table-of-contents.
155     *
156     * The general organization is:
157     * <ul>
158     * <li>General notes
159     * <li>Search
160     * </ul>
161     *
162     * @param mainTOC the main table-of-contents
163     *
164     * @return the content containing the help
165     */
166    private Content getNavigationSection(HtmlTree mainTOC) {
167        Content content = new ContentBuilder();
168
169        Content navHeading = contents.getContent("doclet.help.navigation.head");
170        var navSection = HtmlTree.DIV(HtmlStyle.subTitle)
171            .add(HtmlTree.HEADING(Headings.CONTENT_HEADING, navHeading)
172                .setId(HtmlIds.HELP_NAVIGATION))
173            .add(contents.getContent("doclet.help.navigation.intro",
174                overviewLink));
175        if (options.createIndex()) {
176            Content links = new ContentBuilder();
177            if (!configuration.packages.isEmpty()) {
178                links.add(allPackagesLink);
179                links.add(", ");
180            }
181            links.add(allClassesLink);
182            navSection.add(" ")
183                .add(contents.getContent("doclet.help.navigation.index",
184                    indexLink, links));
185        }
186        content.add(navSection);
187
188        var subTOC = HtmlTree.UL(HtmlStyle.helpSubTOC);
189
190        HtmlTree section;
191
192        // Search
193        if (options.createIndex()) {
194            section = newHelpSection(getContent("doclet.help.search.head"),
195                PageMode.SEARCH, subTOC);
196            var searchIntro
197                = HtmlTree.P(getContent("doclet.help.search.intro"));
198            var searchExamples = HtmlTree.UL(HtmlStyle.helpSectionList);
199            for (String[] example : SEARCH_EXAMPLES) {
200                searchExamples.add(HtmlTree.LI(
201                    getContent("doclet.help.search.example",
202                        HtmlTree.CODE(Text.of(example[0])), example[1])));
203            }
204            var searchSpecLink = HtmlTree.A(
205                resources.getText("doclet.help.search.spec.url",
206                    configuration.getDocletVersion().feature()),
207                getContent("doclet.help.search.spec.title"));
208            var searchRefer = HtmlTree
209                .P(getContent("doclet.help.search.refer", searchSpecLink));
210            section.add(searchIntro)
211                .add(searchExamples)
212                .add(searchRefer);
213            navSection.add(section);
214        }
215
216        mainTOC.add(HtmlTree.LI(new ContentBuilder(
217            links.createLink(DocLink.fragment(HtmlIds.HELP_NAVIGATION.name()),
218                navHeading),
219            Text.of(": "), subTOC)));
220
221        return content;
222    }
223
224    /**
225     * Creates the page-specific help, adding an entry into the main table-of-contents.
226     *
227     * The general organization is:
228     * <ul>
229     * <li>Overview
230     * <li>Declaration pages: module, package, classes
231     * <li>Derived info for declarations: use and tree
232     * <li>General summary info: deprecated, preview
233     * <li>Detailed summary info: constant values, serialized form, system properties
234     * <li>Index info: all packages, all classes, full index
235     * </ul>
236     *
237     * @param mainTOC the main table-of-contents
238     *
239     * @return the content containing the help
240     */
241    private Content getPageKindSection(HtmlTree mainTOC) {
242        Content pageKindsHeading
243            = contents.getContent("doclet.help.page_kinds.head");
244        var pageKindsSection = HtmlTree.DIV(HtmlStyle.subTitle)
245            .add(HtmlTree.HEADING(Headings.CONTENT_HEADING, pageKindsHeading)
246                .setId(HtmlIds.HELP_PAGES))
247            .add(contents.getContent("doclet.help.page_kinds.intro"));
248
249        var subTOC = HtmlTree.UL(HtmlStyle.helpSubTOC);
250
251        HtmlTree section;
252
253        // Overview
254        if (options.createOverview()) {
255            section = newHelpSection(contents.overviewLabel, PageMode.OVERVIEW,
256                subTOC);
257            String overviewKey = configuration.showModules
258                ? "doclet.help.overview.modules.body"
259                : "doclet.help.overview.packages.body";
260            section.add(HtmlTree.P(getContent(overviewKey, overviewLink)));
261            pageKindsSection.add(section);
262        }
263
264        // Module
265        if (configuration.showModules) {
266            section
267                = newHelpSection(contents.moduleLabel, PageMode.MODULE, subTOC);
268            Content moduleIntro = getContent("doclet.help.module.intro");
269            var modulePara = HtmlTree.P(moduleIntro);
270            section.add(modulePara)
271                .add(newHelpSectionList(
272                    contents.packagesLabel,
273                    contents.modulesLabel,
274                    contents.servicesLabel));
275            pageKindsSection.add(section);
276        }
277
278        // Package
279        section
280            = newHelpSection(contents.packageLabel, PageMode.PACKAGE, subTOC)
281                .add(HtmlTree.P(getContent("doclet.help.package.intro")))
282                .add(newHelpSectionList(
283                    contents.interfaces,
284                    contents.classes,
285                    contents.enums,
286                    contents.exceptionClasses,
287                    contents.annotationTypes));
288        pageKindsSection.add(section);
289
290        // Class/interface
291        Content notes = new ContentBuilder(
292            HtmlTree.SPAN(HtmlStyle.helpNote,
293                getContent("doclet.help.class_interface.note")),
294            Text.of(" "),
295            getContent("doclet.help.class_interface.anno"),
296            Text.of(" "),
297            getContent("doclet.help.class_interface.enum"),
298            Text.of(" "),
299            getContent("doclet.help.class_interface.record"),
300            Text.of(" "),
301            getContent("doclet.help.class_interface.property"));
302
303        section = newHelpSection(getContent("doclet.help.class_interface.head"),
304            PageMode.CLASS, subTOC)
305                .add(
306                    HtmlTree.P(getContent("doclet.help.class_interface.intro")))
307                .add(newHelpSectionList(
308                    getContent(
309                        "doclet.help.class_interface.inheritance_diagram"),
310                    getContent("doclet.help.class_interface.subclasses"),
311                    getContent("doclet.help.class_interface.subinterfaces"),
312                    getContent("doclet.help.class_interface.implementations"),
313                    getContent("doclet.help.class_interface.declaration"),
314                    getContent("doclet.help.class_interface.description")))
315                .add(new HtmlTree(TagName.BR))
316                .add(newHelpSectionList(
317                    contents.nestedClassSummary,
318                    contents.enumConstantSummary,
319                    contents.fieldSummaryLabel,
320                    contents.propertySummaryLabel,
321                    contents.constructorSummaryLabel,
322                    contents.methodSummary,
323                    contents.annotateTypeRequiredMemberSummaryLabel,
324                    contents.annotateTypeOptionalMemberSummaryLabel))
325                .add(new HtmlTree(TagName.BR))
326                .add(newHelpSectionList(
327                    contents.enumConstantDetailLabel,
328                    contents.fieldDetailsLabel,
329                    contents.propertyDetailsLabel,
330                    contents.constructorDetailsLabel,
331                    contents.methodDetailLabel,
332                    contents.annotationTypeMemberDetail))
333                .add(HtmlTree.P(notes))
334                .add(HtmlTree
335                    .P(getContent("doclet.help.class_interface.member_order")));
336        pageKindsSection.add(section);
337
338        section = newHelpSection(getContent("doclet.help.other_files.head"),
339            PageMode.DOC_FILE, subTOC)
340                .add(HtmlTree.P(getContent("doclet.help.other_files.body")));
341        pageKindsSection.add(section);
342
343        // Class Use
344        if (options.classUse()) {
345            section = newHelpSection(getContent("doclet.help.use.head"),
346                PageMode.USE, subTOC)
347                    .add(HtmlTree.P(getContent("doclet.help.use.body")));
348            pageKindsSection.add(section);
349        }
350
351        // Tree
352        if (options.createTree()) {
353            section = newHelpSection(getContent("doclet.help.tree.head"),
354                PageMode.TREE, subTOC);
355            Content treeIntro = getContent("doclet.help.tree.intro",
356                links.createLink(DocPaths.OVERVIEW_TREE,
357                    resources.getText("doclet.Class_Hierarchy")),
358                HtmlTree.CODE(Text.of("java.lang.Object")));
359            section.add(HtmlTree.P(treeIntro))
360                .add(newHelpSectionList(
361                    getContent("doclet.help.tree.overview"),
362                    getContent("doclet.help.tree.package")));
363            pageKindsSection.add(section);
364        }
365
366        // Preview
367        if (configuration.conditionalPages
368            .contains(HtmlConfiguration.ConditionalPage.PREVIEW)) {
369            section
370                = newHelpSection(contents.previewAPI, PageMode.PREVIEW, subTOC);
371            Content previewBody = getContent("doclet.help.preview.body",
372                links.createLink(DocPaths.PREVIEW_LIST, contents.previewAPI));
373            section.add(HtmlTree.P(previewBody));
374            pageKindsSection.add(section);
375        }
376
377        // New
378        if (configuration.conditionalPages
379            .contains(HtmlConfiguration.ConditionalPage.NEW)) {
380            section = newHelpSection(contents.newAPI, PageMode.NEW, subTOC);
381            Content newBody = getContent("doclet.help.new.body",
382                links.createLink(DocPaths.NEW_LIST, contents.newAPI));
383            section.add(HtmlTree.P(newBody));
384            pageKindsSection.add(section);
385        }
386
387        // Deprecated
388        if (configuration.conditionalPages
389            .contains(HtmlConfiguration.ConditionalPage.DEPRECATED)) {
390            section = newHelpSection(contents.deprecatedAPI,
391                PageMode.DEPRECATED, subTOC);
392            Content deprBody = getContent("doclet.help.deprecated.body",
393                links.createLink(DocPaths.DEPRECATED_LIST,
394                    resources.getText("doclet.Deprecated_API")));
395            section.add(HtmlTree.P(deprBody));
396            pageKindsSection.add(section);
397        }
398
399        // Constant Field Values
400        if (configuration.conditionalPages
401            .contains(HtmlConfiguration.ConditionalPage.CONSTANT_VALUES)) {
402            section = newHelpSection(contents.constantsSummaryTitle,
403                PageMode.CONSTANT_VALUES, subTOC);
404            Content constantsBody = getContent("doclet.help.constants.body",
405                links.createLink(DocPaths.CONSTANT_VALUES,
406                    resources.getText("doclet.Constants_Summary")));
407            section.add(HtmlTree.P(constantsBody));
408            pageKindsSection.add(section);
409        }
410
411        // Serialized Form
412        if (configuration.conditionalPages
413            .contains(HtmlConfiguration.ConditionalPage.SERIALIZED_FORM)) {
414            section = newHelpSection(contents.serializedForm,
415                PageMode.SERIALIZED_FORM, subTOC)
416                    .add(
417                        HtmlTree.P(getContent("doclet.help.serial_form.body")));
418            pageKindsSection.add(section);
419        }
420
421        // System Properties
422        if (configuration.conditionalPages
423            .contains(HtmlConfiguration.ConditionalPage.SYSTEM_PROPERTIES)) {
424            section = newHelpSection(contents.systemPropertiesLabel,
425                PageMode.SYSTEM_PROPERTIES, subTOC);
426            Content sysPropsBody
427                = getContent("doclet.help.systemProperties.body",
428                    links.createLink(DocPaths.SYSTEM_PROPERTIES,
429                        resources.getText("doclet.systemProperties")));
430            section.add(HtmlTree.P(sysPropsBody));
431            pageKindsSection.add(section);
432        }
433
434        // External Specification
435        if (configuration.conditionalPages
436            .contains(HtmlConfiguration.ConditionalPage.EXTERNAL_SPECS)) {
437            section = newHelpSection(contents.externalSpecifications,
438                PageMode.EXTERNAL_SPECS, subTOC);
439            Content extSpecsBody
440                = getContent("doclet.help.externalSpecifications.body",
441                    links.createLink(DocPaths.EXTERNAL_SPECS,
442                        resources.getText("doclet.External_Specifications")));
443            section.add(HtmlTree.P(extSpecsBody));
444            pageKindsSection.add(section);
445        }
446
447        // Index
448        if (options.createIndex()) {
449            if (!configuration.packages.isEmpty()) {
450                section = newHelpSection(
451                    getContent("doclet.help.all_packages.head"),
452                    PageMode.ALL_PACKAGES, subTOC)
453                        .add(HtmlTree.P(getContent(
454                            "doclet.help.all_packages.body", allPackagesLink)));
455                pageKindsSection.add(section);
456            }
457
458            section = newHelpSection(getContent("doclet.help.all_classes.head"),
459                PageMode.ALL_CLASSES, subTOC)
460                    .add(HtmlTree.P(getContent("doclet.help.all_classes.body",
461                        allClassesLink)));
462            pageKindsSection.add(section);
463
464            Content links = new ContentBuilder();
465            if (!configuration.packages.isEmpty()) {
466                links.add(allPackagesLink);
467                links.add(", ");
468            }
469            links.add(allClassesLink);
470            section = newHelpSection(getContent("doclet.help.index.head"),
471                PageMode.INDEX, subTOC)
472                    .add(HtmlTree.P(getContent("doclet.help.index.body",
473                        indexLink, links)));
474            pageKindsSection.add(section);
475        }
476
477        mainTOC.add(HtmlTree.LI(new ContentBuilder(
478            links.createLink(DocLink.fragment(HtmlIds.HELP_PAGES.name()),
479                pageKindsHeading),
480            Text.of(": "), subTOC)));
481
482        return pageKindsSection;
483    }
484
485    private Content getContent(String key) {
486        return contents.getContent(key);
487    }
488
489    private Content getContent(String key, Object arg) {
490        return contents.getContent(key, arg);
491    }
492
493    private Content getContent(String key, Object arg1, Object arg2) {
494        return contents.getContent(key, arg1, arg2);
495    }
496
497    private HtmlTree newHelpSection(Content headingContent, HtmlTree toc,
498            HtmlId id) {
499        Content link
500            = links.createLink(DocLink.fragment(id.name()), headingContent);
501        toc.add(HtmlTree.LI(link));
502
503        return HtmlTree.SECTION(HtmlStyle.helpSection,
504            HtmlTree.HEADING(Headings.SUB_HEADING, headingContent))
505            .setId(id);
506    }
507
508    private HtmlTree newHelpSection(Content headingContent,
509            Navigation.PageMode pm, HtmlTree toc) {
510        return newHelpSection(headingContent, toc, htmlIds.forPage(pm));
511    }
512
513    private HtmlTree newHelpSectionList(Content first, Content... rest) {
514        var list = HtmlTree.UL(HtmlStyle.helpSectionList, HtmlTree.LI(first));
515        List.of(rest).forEach(i -> list.add(HtmlTree.LI(i)));
516        return list;
517    }
518}