001/*
002 * Copyright (c) 2018, 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 static org.jdrupes.mdoclet.internal.doclets.toolkit.util.VisibleMemberTable.Kind.*;
029
030import java.util.ArrayList;
031import java.util.List;
032import java.util.Set;
033
034import javax.lang.model.element.Element;
035import javax.lang.model.element.ModuleElement;
036import javax.lang.model.element.PackageElement;
037import javax.lang.model.element.TypeElement;
038
039import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.ContentBuilder;
040import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.Entity;
041import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlAttr;
042import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlStyle;
043import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlTree;
044import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.Links;
045import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.TagName;
046import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.Text;
047import org.jdrupes.mdoclet.internal.doclets.toolkit.Content;
048import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocFile;
049import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocLink;
050import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocPath;
051import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocPaths;
052import org.jdrupes.mdoclet.internal.doclets.toolkit.util.VisibleMemberTable;
053
054/**
055 * Factory for navigation bar.
056 *
057 * <p>
058 * <b>This is NOT part of any supported API. If you write code that depends on this, you do so at
059 * your own risk. This code and its internal interfaces are subject to change or deletion without
060 * notice.</b>
061 */
062public class Navigation {
063
064    private final HtmlConfiguration configuration;
065    private final HtmlOptions options;
066    private final Element element;
067    private final Contents contents;
068    private final HtmlIds htmlIds;
069    private final DocPath path;
070    private final DocPath pathToRoot;
071    private final Links links;
072    private final PageMode documentedPage;
073    private Content navLinkModule;
074    private Content navLinkPackage;
075    private Content navLinkClass;
076    private Content userHeader;
077    private final String rowListTitle;
078    private final Content searchLabel;
079    private final String searchPlaceholder;
080    private SubNavLinks subNavLinks;
081
082    public enum PageMode {
083        ALL_CLASSES,
084        ALL_PACKAGES,
085        CLASS,
086        CONSTANT_VALUES,
087        DEPRECATED,
088        DOC_FILE,
089        EXTERNAL_SPECS,
090        HELP,
091        INDEX,
092        MODULE,
093        NEW,
094        OVERVIEW,
095        PACKAGE,
096        PREVIEW,
097        SERIALIZED_FORM,
098        SEARCH,
099        SYSTEM_PROPERTIES,
100        TREE,
101        USE;
102    }
103
104    /**
105     * An interface to provide links for the subnavigation area.
106     */
107    public interface SubNavLinks {
108        /**
109         * {@return a list of links to display in the subnavigation area}
110         * Links should be wrapped in {@code HtmlTree.LI} elements as they are
111         * displayed within an unordered list.
112         */
113        List<Content> getSubNavLinks();
114    }
115
116    /**
117     * Creates a {@code Navigation} object for a specific file, to be written in a specific HTML
118     * version.
119     *
120     * @param element element being documented. null if its not an element documentation page
121     * @param configuration the configuration object
122     * @param page the kind of page being documented
123     * @param path the DocPath object
124     */
125    public Navigation(Element element, HtmlConfiguration configuration,
126            PageMode page, DocPath path) {
127        this.configuration = configuration;
128        this.options = configuration.getOptions();
129        this.element = element;
130        this.contents = configuration.getContents();
131        this.htmlIds = configuration.htmlIds;
132        this.documentedPage = page;
133        this.path = path;
134        this.pathToRoot = path.parent().invert();
135        this.links = new Links(path);
136        this.rowListTitle
137            = configuration.getDocResources().getText("doclet.Navigation");
138        this.searchLabel = contents.getContent("doclet.search");
139        this.searchPlaceholder = configuration.getDocResources()
140            .getText("doclet.search_placeholder");
141    }
142
143    public Navigation setNavLinkModule(Content navLinkModule) {
144        this.navLinkModule = navLinkModule;
145        return this;
146    }
147
148    public Navigation setNavLinkPackage(Content navLinkPackage) {
149        this.navLinkPackage = navLinkPackage;
150        return this;
151    }
152
153    public Navigation setNavLinkClass(Content navLinkClass) {
154        this.navLinkClass = navLinkClass;
155        return this;
156    }
157
158    public Navigation setUserHeader(Content userHeader) {
159        this.userHeader = userHeader;
160        return this;
161    }
162
163    public Navigation setSubNavLinks(SubNavLinks subNavLinks) {
164        this.subNavLinks = subNavLinks;
165        return this;
166    }
167
168    /**
169     * Adds the links for the main navigation.
170     *
171     * @param target the content to which the main navigation will added
172     */
173    private void addMainNavLinks(Content target) {
174        switch (documentedPage) {
175        case OVERVIEW:
176            addActivePageLink(target, contents.overviewLabel,
177                options.createOverview());
178            addModuleLink(target);
179            addPackageLink(target);
180            addPageLabel(target, contents.classLabel, true);
181            addPageLabel(target, contents.useLabel, options.classUse());
182            addTreeLink(target);
183            addPreviewLink(target);
184            addNewLink(target);
185            addDeprecatedLink(target);
186            addIndexLink(target);
187            addHelpLink(target);
188            break;
189        case MODULE:
190            addOverviewLink(target);
191            addActivePageLink(target, contents.moduleLabel,
192                configuration.showModules);
193            addPackageLink(target);
194            addPageLabel(target, contents.classLabel, true);
195            addPageLabel(target, contents.useLabel, options.classUse());
196            addTreeLink(target);
197            addPreviewLink(target);
198            addNewLink(target);
199            addDeprecatedLink(target);
200            addIndexLink(target);
201            addHelpLink(target);
202            break;
203        case PACKAGE:
204            addOverviewLink(target);
205            addModuleOfElementLink(target);
206            addActivePageLink(target, contents.packageLabel, true);
207            addPageLabel(target, contents.classLabel, true);
208            if (options.classUse()) {
209                addItemToList(target, links.createLink(DocPaths.PACKAGE_USE,
210                    contents.useLabel, ""));
211            }
212            if (options.createTree()) {
213                addItemToList(target, links.createLink(DocPaths.PACKAGE_TREE,
214                    contents.treeLabel, ""));
215            }
216            addPreviewLink(target);
217            addNewLink(target);
218            addDeprecatedLink(target);
219            addIndexLink(target);
220            addHelpLink(target);
221            break;
222        case CLASS:
223            addOverviewLink(target);
224            addModuleOfElementLink(target);
225            addPackageSummaryLink(target);
226            addActivePageLink(target, contents.classLabel, true);
227            if (options.classUse()) {
228                addItemToList(target,
229                    links.createLink(
230                        DocPaths.CLASS_USE.resolve(path.basename()),
231                        contents.useLabel));
232            }
233            if (options.createTree()) {
234                addItemToList(target, links.createLink(DocPaths.PACKAGE_TREE,
235                    contents.treeLabel, ""));
236            }
237            addPreviewLink(target);
238            addNewLink(target);
239            addDeprecatedLink(target);
240            addIndexLink(target);
241            addHelpLink(target);
242            break;
243        case USE:
244            addOverviewLink(target);
245            addModuleOfElementLink(target);
246            if (element instanceof PackageElement) {
247                addPackageSummaryLink(target);
248                addPageLabel(target, contents.classLabel, true);
249            } else {
250                addPackageOfElementLink(target);
251                addItemToList(target, navLinkClass);
252            }
253            addActivePageLink(target, contents.useLabel, options.classUse());
254            if (element instanceof PackageElement) {
255                addItemToList(target, links.createLink(DocPaths.PACKAGE_TREE,
256                    contents.treeLabel));
257            } else {
258                addItemToList(target,
259                    configuration.utils
260                        .isEnclosingPackageIncluded((TypeElement) element)
261                            ? links.createLink(
262                                DocPath.parent.resolve(DocPaths.PACKAGE_TREE),
263                                contents.treeLabel)
264                            : links.createLink(
265                                pathToRoot.resolve(DocPaths.OVERVIEW_TREE),
266                                contents.treeLabel));
267            }
268            addPreviewLink(target);
269            addNewLink(target);
270            addDeprecatedLink(target);
271            addIndexLink(target);
272            addHelpLink(target);
273            break;
274        case TREE:
275            addOverviewLink(target);
276            if (element == null) {
277                addPageLabel(target, contents.moduleLabel,
278                    configuration.showModules);
279                addPageLabel(target, contents.packageLabel, true);
280            } else {
281                addModuleOfElementLink(target);
282                addPackageSummaryLink(target);
283            }
284            addPageLabel(target, contents.classLabel, true);
285            addPageLabel(target, contents.useLabel, options.classUse());
286            addActivePageLink(target, contents.treeLabel, options.createTree());
287            addPreviewLink(target);
288            addNewLink(target);
289            addDeprecatedLink(target);
290            addIndexLink(target);
291            addHelpLink(target);
292            break;
293        case DEPRECATED:
294        case INDEX:
295        case HELP:
296        case PREVIEW:
297        case NEW:
298            addOverviewLink(target);
299            addModuleLink(target);
300            addPackageLink(target);
301            addPageLabel(target, contents.classLabel, true);
302            addPageLabel(target, contents.useLabel, options.classUse());
303            addTreeLink(target);
304            if (documentedPage == PageMode.PREVIEW) {
305                addActivePageLink(target, contents.previewLabel,
306                    configuration.conditionalPages
307                        .contains(HtmlConfiguration.ConditionalPage.PREVIEW));
308            } else {
309                addPreviewLink(target);
310            }
311            if (documentedPage == PageMode.NEW) {
312                addActivePageLink(target, contents.newLabel,
313                    configuration.conditionalPages
314                        .contains(HtmlConfiguration.ConditionalPage.NEW));
315            } else {
316                addNewLink(target);
317            }
318            if (documentedPage == PageMode.DEPRECATED) {
319                addActivePageLink(target, contents.deprecatedLabel,
320                    configuration.conditionalPages.contains(
321                        HtmlConfiguration.ConditionalPage.DEPRECATED));
322            } else {
323                addDeprecatedLink(target);
324            }
325            if (documentedPage == PageMode.INDEX) {
326                addActivePageLink(target, contents.indexLabel,
327                    options.createIndex());
328            } else {
329                addIndexLink(target);
330            }
331            if (documentedPage == PageMode.HELP) {
332                addActivePageLink(target, contents.helpLabel,
333                    !options.noHelp());
334            } else {
335                addHelpLink(target);
336            }
337            break;
338        case ALL_CLASSES:
339        case ALL_PACKAGES:
340        case CONSTANT_VALUES:
341        case EXTERNAL_SPECS:
342        case SERIALIZED_FORM:
343        case SEARCH:
344        case SYSTEM_PROPERTIES:
345            addOverviewLink(target);
346            addModuleLink(target);
347            addPackageLink(target);
348            addPageLabel(target, contents.classLabel, true);
349            addPageLabel(target, contents.useLabel, options.classUse());
350            addTreeLink(target);
351            addPreviewLink(target);
352            addNewLink(target);
353            addDeprecatedLink(target);
354            addIndexLink(target);
355            addHelpLink(target);
356            break;
357        case DOC_FILE:
358            addOverviewLink(target);
359            addModuleOfElementLink(target);
360            addItemToList(target, navLinkPackage);
361            addPageLabel(target, contents.classLabel, true);
362            addPageLabel(target, contents.useLabel, options.classUse());
363            addTreeLink(target);
364            addPreviewLink(target);
365            addNewLink(target);
366            addDeprecatedLink(target);
367            addIndexLink(target);
368            addHelpLink(target);
369            break;
370        default:
371            break;
372        }
373    }
374
375    /**
376     * Adds the summary links to the subnavigation.
377     *
378     * @param target the content to which the subnavigation will be added
379     * @param nested whether to create a flat or nested list
380     */
381    private void addSummaryLinks(Content target, boolean nested) {
382        switch (documentedPage) {
383        case MODULE, PACKAGE, CLASS, HELP -> {
384            List<? extends Content> listContents = subNavLinks.getSubNavLinks()
385                .stream().map(HtmlTree::LI).toList();
386            if (!listContents.isEmpty()) {
387                Content label = switch (documentedPage) {
388                case MODULE -> contents.moduleSubNavLabel;
389                case PACKAGE -> contents.packageSubNavLabel;
390                case CLASS -> contents.summaryLabel;
391                case HELP -> contents.helpSubNavLabel;
392                default -> Text.EMPTY;
393                };
394                if (nested) {
395                    target.add(HtmlTree.LI(HtmlTree.P(label))
396                        .add(new HtmlTree(TagName.UL).add(listContents)));
397                } else {
398                    target.add(HtmlTree.LI(label).add(Entity.NO_BREAK_SPACE));
399                    addListToNav(listContents, target);
400                }
401            }
402        }
403        }
404    }
405
406    /**
407     * Adds the detail links to subnavigation.
408     *
409     * @param target the content to which the links will be added
410     * @param nested whether to create a flat or nested list
411     */
412    private void addDetailLinks(Content target, boolean nested) {
413        if (documentedPage == PageMode.CLASS) {
414            List<Content> listContents = new ArrayList<>();
415            VisibleMemberTable vmt
416                = configuration.getVisibleMemberTable((TypeElement) element);
417            Set<VisibleMemberTable.Kind> detailSet
418                = VisibleMemberTable.Kind.forDetailsOf(element.getKind());
419            for (VisibleMemberTable.Kind kind : detailSet) {
420                addTypeDetailLink(kind, !vmt.getVisibleMembers(kind).isEmpty(),
421                    listContents);
422            }
423            if (!listContents.isEmpty()) {
424                if (nested) {
425                    var li = HtmlTree.LI(HtmlTree.P(contents.detailLabel));
426                    li.add(new HtmlTree(TagName.UL).add(listContents));
427                    target.add(li);
428                } else {
429                    var li = HtmlTree.LI(contents.detailLabel);
430                    li.add(Entity.NO_BREAK_SPACE);
431                    target.add(li);
432                    addListToNav(listContents, target);
433                }
434            }
435        }
436    }
437
438    /**
439     * Adds the navigation Type detail link.
440     *
441     * @param kind the kind of member being documented
442     * @param link true if the members are listed and need to be linked
443     * @param listContents the list of contents to which the detail will be added.
444     */
445    protected void addTypeDetailLink(VisibleMemberTable.Kind kind, boolean link,
446            List<Content> listContents) {
447        addContentToList(listContents, switch (kind) {
448        case CONSTRUCTORS -> links.createLink(HtmlIds.CONSTRUCTOR_DETAIL,
449            contents.navConstructor, link);
450        case ENUM_CONSTANTS -> links.createLink(HtmlIds.ENUM_CONSTANT_DETAIL,
451            contents.navEnum, link);
452        case FIELDS -> links.createLink(HtmlIds.FIELD_DETAIL, contents.navField,
453            link);
454        case METHODS -> links.createLink(HtmlIds.METHOD_DETAIL,
455            contents.navMethod, link);
456        case PROPERTIES -> links.createLink(HtmlIds.PROPERTY_DETAIL,
457            contents.navProperty, link);
458        case ANNOTATION_TYPE_MEMBER -> links.createLink(
459            HtmlIds.ANNOTATION_TYPE_ELEMENT_DETAIL,
460            contents.navAnnotationTypeMember, link);
461        default -> Text.EMPTY;
462        });
463    }
464
465    private void addContentToList(List<Content> listContents, Content source) {
466        listContents.add(HtmlTree.LI(source));
467    }
468
469    private void addItemToList(Content list, Content item) {
470        list.add(HtmlTree.LI(item));
471    }
472
473    private void addListToNav(List<? extends Content> listContents,
474            Content target) {
475        int count = 0;
476        for (Content liContent : listContents) {
477            if (count < listContents.size() - 1) {
478                liContent.add(Entity.NO_BREAK_SPACE);
479                liContent.add("|");
480                liContent.add(Entity.NO_BREAK_SPACE);
481            }
482            target.add(liContent);
483            count++;
484        }
485    }
486
487    private void addActivePageLink(Content target, Content label,
488            boolean display) {
489        if (display) {
490            target.add(HtmlTree.LI(HtmlStyle.navBarCell1Rev, label));
491        }
492    }
493
494    private void addPageLabel(Content target, Content label, boolean display) {
495        if (display) {
496            target.add(HtmlTree.LI(label));
497        }
498    }
499
500    private void addOverviewLink(Content target) {
501        if (options.createOverview()) {
502            target.add(
503                HtmlTree.LI(links.createLink(pathToRoot.resolve(DocPaths.INDEX),
504                    contents.overviewLabel, "")));
505        }
506    }
507
508    private void addModuleLink(Content target) {
509        if (configuration.showModules) {
510            if (configuration.modules.size() == 1) {
511                ModuleElement mdle = configuration.modules.first();
512                boolean included = configuration.utils.isIncluded(mdle);
513                target.add(HtmlTree.LI((included)
514                    ? links.createLink(
515                        pathToRoot.resolve(
516                            configuration.docPaths.moduleSummary(mdle)),
517                        contents.moduleLabel, "")
518                    : contents.moduleLabel));
519            } else if (!configuration.modules.isEmpty()) {
520                addPageLabel(target, contents.moduleLabel, true);
521            }
522        }
523    }
524
525    private void addModuleOfElementLink(Content target) {
526        if (configuration.showModules) {
527            target.add(HtmlTree.LI(navLinkModule));
528        }
529    }
530
531    private void addPackageLink(Content target) {
532        if (configuration.packages.size() == 1) {
533            PackageElement packageElement = configuration.packages.first();
534            boolean included = packageElement != null
535                && configuration.utils.isIncluded(packageElement);
536            if (!included) {
537                for (PackageElement p : configuration.packages) {
538                    if (p.equals(packageElement)) {
539                        included = true;
540                        break;
541                    }
542                }
543            }
544            if (included || packageElement == null) {
545                target.add(HtmlTree.LI(links.createLink(
546                    pathToRoot.resolve(
547                        configuration.docPaths.forPackage(packageElement)
548                            .resolve(DocPaths.PACKAGE_SUMMARY)),
549                    contents.packageLabel)));
550            } else {
551                DocLink crossPkgLink = configuration.extern.getExternalLink(
552                    packageElement, pathToRoot,
553                    DocPaths.PACKAGE_SUMMARY.getPath());
554                if (crossPkgLink != null) {
555                    target.add(HtmlTree.LI(
556                        links.createLink(crossPkgLink, contents.packageLabel)));
557                } else {
558                    target.add(HtmlTree.LI(contents.packageLabel));
559                }
560            }
561        } else if (!configuration.packages.isEmpty()) {
562            addPageLabel(target, contents.packageLabel, true);
563        }
564    }
565
566    private void addPackageOfElementLink(Content target) {
567        target.add(HtmlTree.LI(
568            links.createLink(DocPath.parent.resolve(DocPaths.PACKAGE_SUMMARY),
569                contents.packageLabel)));
570    }
571
572    private void addPackageSummaryLink(Content target) {
573        target.add(HtmlTree.LI(
574            links.createLink(DocPaths.PACKAGE_SUMMARY, contents.packageLabel)));
575    }
576
577    private void addTreeLink(Content target) {
578        if (options.createTree()) {
579            List<PackageElement> packages
580                = new ArrayList<>(configuration.getSpecifiedPackageElements());
581            DocPath docPath = packages.size() == 1
582                && configuration.getSpecifiedTypeElements().isEmpty()
583                    ? pathToRoot.resolve(
584                        configuration.docPaths.forPackage(packages.get(0))
585                            .resolve(DocPaths.PACKAGE_TREE))
586                    : pathToRoot.resolve(DocPaths.OVERVIEW_TREE);
587            target.add(
588                HtmlTree.LI(links.createLink(docPath, contents.treeLabel, "")));
589        }
590    }
591
592    private void addDeprecatedLink(Content target) {
593        if (configuration.conditionalPages
594            .contains(HtmlConfiguration.ConditionalPage.DEPRECATED)) {
595            target.add(HtmlTree.LI(
596                links.createLink(pathToRoot.resolve(DocPaths.DEPRECATED_LIST),
597                    contents.deprecatedLabel, "")));
598        }
599    }
600
601    private void addPreviewLink(Content target) {
602        if (configuration.conditionalPages
603            .contains(HtmlConfiguration.ConditionalPage.PREVIEW)) {
604            target.add(HtmlTree
605                .LI(links.createLink(pathToRoot.resolve(DocPaths.PREVIEW_LIST),
606                    contents.previewLabel, "")));
607        }
608    }
609
610    private void addNewLink(Content target) {
611        if (configuration.conditionalPages
612            .contains(HtmlConfiguration.ConditionalPage.NEW)) {
613            target.add(HtmlTree
614                .LI(links.createLink(pathToRoot.resolve(DocPaths.NEW_LIST),
615                    contents.newLabel, "")));
616        }
617    }
618
619    private void addIndexLink(Content target) {
620        if (options.createIndex()) {
621            target.add(HtmlTree.LI(links.createLink(pathToRoot.resolve(
622                (options.splitIndex()
623                    ? DocPaths.INDEX_FILES.resolve(DocPaths.indexN(1))
624                    : DocPaths.INDEX_ALL)),
625                contents.indexLabel, "")));
626        }
627    }
628
629    private void addHelpLink(Content target) {
630        if (!options.noHelp()) {
631            String helpfile = options.helpFile();
632            DocPath helpfilenm;
633            if (helpfile.isEmpty()) {
634                helpfilenm = DocPaths.HELP_DOC;
635            } else {
636                DocFile file
637                    = DocFile.createFileForInput(configuration, helpfile);
638                helpfilenm = DocPath.create(file.getName());
639            }
640            target.add(HtmlTree.LI(links.createLink(
641                new DocLink(pathToRoot.resolve(helpfilenm),
642                    htmlIds.forPage(documentedPage).name()),
643                contents.helpLabel, "")));
644        }
645    }
646
647    private void addSearch(Content target) {
648        String reset = "reset";
649        var inputText = HtmlTree.INPUT("text", HtmlIds.SEARCH_INPUT)
650            .put(HtmlAttr.PLACEHOLDER, searchPlaceholder);
651        var inputReset = HtmlTree.INPUT(reset, HtmlIds.RESET_BUTTON)
652            .put(HtmlAttr.VALUE, reset);
653        var searchDiv = HtmlTree.DIV(HtmlStyle.navListSearch,
654            links.createLink(pathToRoot.resolve(DocPaths.SEARCH_PAGE),
655                searchLabel, ""));
656        searchDiv.add(inputText);
657        searchDiv.add(inputReset);
658        target.add(searchDiv);
659    }
660
661    /**
662     * Returns the navigation content.
663     *
664     * @return the navigation content
665     */
666    public Content getContent() {
667        if (options.noNavbar()) {
668            return new ContentBuilder();
669        }
670        var navigationBar = HtmlTree.NAV();
671
672        var navDiv = new HtmlTree(TagName.DIV);
673        Content skipNavLinks
674            = contents.getContent("doclet.Skip_navigation_links");
675        String toggleNavLinks = configuration.getDocResources()
676            .getText("doclet.Toggle_navigation_links");
677        navigationBar.add(MarkerComments.START_OF_TOP_NAVBAR);
678        // The mobile menu button uses three empty spans to produce its animated
679        // icon
680        HtmlTree iconSpan = HtmlTree.SPAN(HtmlStyle.navBarToggleIcon)
681            .add(Entity.NO_BREAK_SPACE);
682        navDiv.setStyle(HtmlStyle.topNav)
683            .setId(HtmlIds.NAVBAR_TOP)
684            .add(
685                new HtmlTree(TagName.BUTTON).setId(HtmlIds.NAVBAR_TOGGLE_BUTTON)
686                    .put(HtmlAttr.ARIA_CONTROLS, HtmlIds.NAVBAR_TOP.name())
687                    .put(HtmlAttr.ARIA_EXPANDED, String.valueOf(false))
688                    .put(HtmlAttr.ARIA_LABEL, toggleNavLinks)
689                    .add(iconSpan)
690                    .add(iconSpan)
691                    .add(iconSpan))
692            .add(HtmlTree.DIV(HtmlStyle.skipNav,
693                links.createLink(HtmlIds.SKIP_NAVBAR_TOP, skipNavLinks,
694                    skipNavLinks.toString())));
695        Content aboutContent = userHeader;
696        boolean addSearch
697            = options.createIndex() && documentedPage != PageMode.SEARCH;
698
699        var aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, aboutContent);
700        navDiv.add(aboutDiv);
701        var navList = new HtmlTree(TagName.UL)
702            .setId(HtmlIds.NAVBAR_TOP_FIRSTROW)
703            .setStyle(HtmlStyle.navList)
704            .put(HtmlAttr.TITLE, rowListTitle);
705        addMainNavLinks(navList);
706        navDiv.add(navList);
707        var ulNavSummaryRight = HtmlTree.UL(HtmlStyle.subNavListSmall);
708        addSummaryLinks(ulNavSummaryRight, true);
709        addDetailLinks(ulNavSummaryRight, true);
710        navDiv.add(ulNavSummaryRight);
711        navigationBar.add(navDiv);
712
713        var subDiv = HtmlTree.DIV(HtmlStyle.subNav);
714
715        var div = new HtmlTree(TagName.DIV).setId(HtmlIds.NAVBAR_SUB_LIST);
716        // Add the summary links if present.
717        var ulNavSummary = HtmlTree.UL(HtmlStyle.subNavList);
718        addSummaryLinks(ulNavSummary, false);
719        div.add(ulNavSummary);
720        // Add the detail links if present.
721        var ulNavDetail = HtmlTree.UL(HtmlStyle.subNavList);
722        addDetailLinks(ulNavDetail, false);
723        div.add(ulNavDetail);
724        subDiv.add(div);
725
726        if (addSearch) {
727            addSearch(subDiv);
728        }
729        navigationBar.add(subDiv);
730
731        navigationBar.add(MarkerComments.END_OF_TOP_NAVBAR);
732        navigationBar.add(HtmlTree.SPAN(HtmlStyle.skipNav)
733            .addUnchecked(Text.EMPTY)
734            .setId(HtmlIds.SKIP_NAVBAR_TOP));
735
736        return navigationBar;
737    }
738}