1 /*
   2  * Copyright (c) 2018, 2019, 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 package jdk.javadoc.internal.doclets.formats.html.markup;
  26 
  27 import java.util.ArrayDeque;
  28 import java.util.ArrayList;
  29 import java.util.Deque;
  30 import java.util.HashMap;
  31 import java.util.List;
  32 import java.util.Map;
  33 import java.util.Set;
  34 import java.util.SortedSet;
  35 
  36 import javax.lang.model.element.Element;
  37 import javax.lang.model.element.ElementKind;
  38 import javax.lang.model.element.ModuleElement;
  39 import javax.lang.model.element.PackageElement;
  40 import javax.lang.model.element.TypeElement;
  41 
  42 import jdk.javadoc.internal.doclets.formats.html.AbstractMemberWriter;
  43 import jdk.javadoc.internal.doclets.formats.html.Contents;
  44 import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
  45 import jdk.javadoc.internal.doclets.formats.html.MarkerComments;
  46 import jdk.javadoc.internal.doclets.formats.html.SectionName;
  47 import jdk.javadoc.internal.doclets.toolkit.Content;
  48 import jdk.javadoc.internal.doclets.toolkit.builders.MemberSummaryBuilder;
  49 import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
  50 import jdk.javadoc.internal.doclets.toolkit.util.DocLink;
  51 import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
  52 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
  53 import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable;
  54 
  55 import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.*;
  56 
  57 /**
  58  * Factory for navigation bar.
  59  *
  60  * <p>
  61  * <b>This is NOT part of any supported API. If you write code that depends on this, you do so at
  62  * your own risk. This code and its internal interfaces are subject to change or deletion without
  63  * notice.</b>
  64  */
  65 public class Navigation {
  66 
  67     private final HtmlConfiguration configuration;
  68     private final Element element;
  69     private final Contents contents;
  70     private final DocPath path;
  71     private final DocPath pathToRoot;
  72     private final Links links;
  73     private final PageMode documentedPage;
  74     private Content navLinkModule;
  75     private Content navLinkPackage;
  76     private Content navLinkClass;
  77     private MemberSummaryBuilder memberSummaryBuilder;
  78     private boolean displaySummaryModuleDescLink;
  79     private boolean displaySummaryModulesLink;
  80     private boolean displaySummaryPackagesLink;
  81     private boolean displaySummaryServicesLink;
  82     private final Map<Position, Deque<Content>> topBottomNavContents;
  83     private Content userHeader;
  84     private Content userFooter;
  85     private final String rowListTitle;
  86     private final Content searchLabel;
  87 
  88     public enum PageMode {
  89         ALLCLASSES,
  90         ALLPACKAGES,
  91         CLASS,
  92         CONSTANTVALUES,
  93         DEPRECATED,
  94         DOCFILE,
  95         HELP,
  96         INDEX,
  97         MODULE,
  98         OVERVIEW,
  99         PACKAGE,
 100         SERIALIZEDFORM,
 101         SYSTEMPROPERTIES,
 102         TREE,
 103         USE;
 104     }
 105 
 106     enum Position {
 107         BOTTOM(MarkerComments.START_OF_BOTTOM_NAVBAR, MarkerComments.END_OF_BOTTOM_NAVBAR),
 108         TOP(MarkerComments.START_OF_TOP_NAVBAR, MarkerComments.END_OF_TOP_NAVBAR);
 109 
 110         final Content startOfNav;
 111         final Content endOfNav;
 112 
 113         Position(Content startOfNav, Content endOfNav) {
 114             this.startOfNav = startOfNav;
 115             this.endOfNav = endOfNav;
 116         }
 117 
 118         Content startOfNav() {
 119             return startOfNav;
 120         }
 121 
 122         Content endOfNav() {
 123             return endOfNav;
 124         }
 125     }
 126 
 127     /**
 128      * Creates a {@code Navigation} object for a specific file, to be written in a specific HTML
 129      * version.
 130      *
 131      * @param element element being documented. null if its not an element documentation page
 132      * @param configuration the configuration object
 133      * @param page the kind of page being documented
 134      * @param path the DocPath object
 135      */
 136     public Navigation(Element element, HtmlConfiguration configuration, PageMode page, DocPath path) {
 137         this.configuration = configuration;
 138         this.element = element;
 139         this.contents = configuration.contents;
 140         this.documentedPage = page;
 141         this.path = path;
 142         this.pathToRoot = path.parent().invert();
 143         this.links = new Links(path);
 144         this.topBottomNavContents = new HashMap<>();
 145         this.rowListTitle = configuration.getResources().getText("doclet.Navigation");
 146         this.searchLabel = contents.getContent("doclet.search");
 147         populateNavContents(Position.TOP);
 148         populateNavContents(Position.BOTTOM);
 149     }
 150 
 151     /**
 152      * Populate the navigation contents for top and bottom navigation
 153      *
 154      * @param position the position of the navigation bar on the page
 155      */
 156     private void populateNavContents(Position position) {
 157         Deque<Content> queue = new ArrayDeque<>();
 158         Content skipNavLinks = contents.getContent("doclet.Skip_navigation_links");
 159         switch (position) {
 160             case TOP:
 161                 queue.addLast(links.createAnchor(SectionName.NAVBAR_TOP));
 162                 queue.addLast(links.createLink(SectionName.SKIP_NAVBAR_TOP, skipNavLinks,
 163                         skipNavLinks.toString(), ""));
 164                 queue.addLast(links.createAnchor(SectionName.NAVBAR_TOP_FIRSTROW));
 165                 queue.addLast(links.createAnchor(SectionName.SKIP_NAVBAR_TOP));
 166                 topBottomNavContents.put(position, queue);
 167                 break;
 168             case BOTTOM:
 169                 queue.addLast(links.createAnchor(SectionName.NAVBAR_BOTTOM));
 170                 queue.addLast(links.createLink(SectionName.SKIP_NAVBAR_BOTTOM, skipNavLinks,
 171                         skipNavLinks.toString(), ""));
 172                 queue.addLast(links.createAnchor(SectionName.NAVBAR_BOTTOM_FIRSTROW));
 173                 queue.addLast(links.createAnchor(SectionName.SKIP_NAVBAR_BOTTOM));
 174                 topBottomNavContents.put(position, queue);
 175                 break;
 176             default:
 177                 break;
 178         }
 179     }
 180 
 181     public Navigation setNavLinkModule(Content navLinkModule) {
 182         this.navLinkModule = navLinkModule;
 183         return this;
 184     }
 185 
 186     public Navigation setNavLinkPackage(Content navLinkPackage) {
 187         this.navLinkPackage = navLinkPackage;
 188         return this;
 189     }
 190 
 191     public Navigation setNavLinkClass(Content navLinkClass) {
 192         this.navLinkClass = navLinkClass;
 193         return this;
 194     }
 195 
 196     public Navigation setMemberSummaryBuilder(MemberSummaryBuilder memberSummaryBuilder) {
 197         this.memberSummaryBuilder = memberSummaryBuilder;
 198         return this;
 199     }
 200 
 201     public Navigation setDisplaySummaryModuleDescLink(boolean displaySummaryModuleDescLink) {
 202         this.displaySummaryModuleDescLink = displaySummaryModuleDescLink;
 203         return this;
 204     }
 205 
 206     public Navigation setDisplaySummaryModulesLink(boolean displaySummaryModulesLink) {
 207         this.displaySummaryModulesLink = displaySummaryModulesLink;
 208         return this;
 209     }
 210 
 211     public Navigation setDisplaySummaryPackagesLink(boolean displaySummaryPackagesLink) {
 212         this.displaySummaryPackagesLink = displaySummaryPackagesLink;
 213         return this;
 214     }
 215 
 216     public Navigation setDisplaySummaryServicesLink(boolean displaySummaryServicesLink) {
 217         this.displaySummaryServicesLink = displaySummaryServicesLink;
 218         return this;
 219     }
 220 
 221     public Navigation setUserHeader(Content userHeader) {
 222         this.userHeader = userHeader;
 223         return this;
 224     }
 225 
 226     public Navigation setUserFooter(Content userFooter) {
 227         this.userFooter = userFooter;
 228         return this;
 229     }
 230 
 231     /**
 232      * Add the links for the main navigation.
 233      *
 234      * @param tree the content tree to which the main navigation will added
 235      */
 236     private void addMainNavLinks(Content tree) {
 237         switch (documentedPage) {
 238             case OVERVIEW:
 239                 addActivePageLink(tree, contents.overviewLabel, configuration.createoverview);
 240                 addModuleLink(tree);
 241                 addPackageLink(tree);
 242                 addPageLabel(tree, contents.classLabel, true);
 243                 addPageLabel(tree, contents.useLabel, configuration.classuse);
 244                 addTreeLink(tree);
 245                 addDeprecatedLink(tree);
 246                 addIndexLink(tree);
 247                 addHelpLink(tree);
 248                 break;
 249             case MODULE:
 250                 addOverviewLink(tree);
 251                 addActivePageLink(tree, contents.moduleLabel, configuration.showModules);
 252                 addPackageLink(tree);
 253                 addPageLabel(tree, contents.classLabel, true);
 254                 addPageLabel(tree, contents.useLabel, configuration.classuse);
 255                 addTreeLink(tree);
 256                 addDeprecatedLink(tree);
 257                 addIndexLink(tree);
 258                 addHelpLink(tree);
 259                 break;
 260             case PACKAGE:
 261                 addOverviewLink(tree);
 262                 addModuleOfElementLink(tree);
 263                 addActivePageLink(tree, contents.packageLabel, true);
 264                 addPageLabel(tree, contents.classLabel, true);
 265                 if (configuration.classuse) {
 266                     addContentToTree(tree, links.createLink(DocPaths.PACKAGE_USE,
 267                             contents.useLabel, "", ""));
 268                 }
 269                 if (configuration.createtree) {
 270                     addContentToTree(tree, links.createLink(DocPaths.PACKAGE_TREE,
 271                             contents.treeLabel, "", ""));
 272                 }
 273                 addDeprecatedLink(tree);
 274                 addIndexLink(tree);
 275                 addHelpLink(tree);
 276                 break;
 277             case CLASS:
 278                 addOverviewLink(tree);
 279                 addModuleOfElementLink(tree);
 280                 addPackageSummaryLink(tree);
 281                 addActivePageLink(tree, contents.classLabel, true);
 282                 if (configuration.classuse) {
 283                     addContentToTree(tree, links.createLink(DocPaths.CLASS_USE.resolve(path.basename()),
 284                             contents.useLabel));
 285                 }
 286                 if (configuration.createtree) {
 287                     addContentToTree(tree, links.createLink(DocPaths.PACKAGE_TREE,
 288                             contents.treeLabel, "", ""));
 289                 }
 290                 addDeprecatedLink(tree);
 291                 addIndexLink(tree);
 292                 addHelpLink(tree);
 293                 break;
 294             case USE:
 295                 addOverviewLink(tree);
 296                 addModuleOfElementLink(tree);
 297                 if (element instanceof PackageElement) {
 298                     addPackageSummaryLink(tree);
 299                     addPageLabel(tree, contents.classLabel, true);
 300                 } else {
 301                     addPackageOfElementLink(tree);
 302                     addContentToTree(tree, navLinkClass);
 303                 }
 304                 addActivePageLink(tree, contents.useLabel, configuration.classuse);
 305                 if (element instanceof PackageElement) {
 306                     addContentToTree(tree, links.createLink(DocPaths.PACKAGE_TREE, contents.treeLabel));
 307                 } else {
 308                     addContentToTree(tree, configuration.utils.isEnclosingPackageIncluded((TypeElement) element)
 309                             ? links.createLink(DocPath.parent.resolve(DocPaths.PACKAGE_TREE), contents.treeLabel)
 310                             : links.createLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE), contents.treeLabel));
 311                 }
 312                 addDeprecatedLink(tree);
 313                 addIndexLink(tree);
 314                 addHelpLink(tree);
 315                 break;
 316             case TREE:
 317                 addOverviewLink(tree);
 318                 if (element == null) {
 319                     addPageLabel(tree, contents.moduleLabel, configuration.showModules);
 320                     addPageLabel(tree, contents.packageLabel, true);
 321                 } else {
 322                     addModuleOfElementLink(tree);
 323                     addPackageSummaryLink(tree);
 324                 }
 325                 addPageLabel(tree, contents.classLabel, true);
 326                 addPageLabel(tree, contents.useLabel, configuration.classuse);
 327                 addActivePageLink(tree, contents.treeLabel, configuration.createtree);
 328                 addDeprecatedLink(tree);
 329                 addIndexLink(tree);
 330                 addHelpLink(tree);
 331                 break;
 332             case DEPRECATED:
 333             case INDEX:
 334             case HELP:
 335                 addOverviewLink(tree);
 336                 addModuleLink(tree);
 337                 addPackageLink(tree);
 338                 addPageLabel(tree, contents.classLabel, true);
 339                 addPageLabel(tree, contents.useLabel, configuration.classuse);
 340                 addTreeLink(tree);
 341                 if (documentedPage == PageMode.DEPRECATED) {
 342                     addActivePageLink(tree, contents.deprecatedLabel, !(configuration.nodeprecated
 343                             || configuration.nodeprecatedlist));
 344                 } else {
 345                     addDeprecatedLink(tree);
 346                 }
 347                 if (documentedPage == PageMode.INDEX) {
 348                     addActivePageLink(tree, contents.indexLabel, configuration.createindex);
 349                 } else {
 350                     addIndexLink(tree);
 351                 }
 352                 if (documentedPage == PageMode.HELP) {
 353                     addActivePageLink(tree, contents.helpLabel, !configuration.nohelp);
 354                 } else {
 355                     addHelpLink(tree);
 356                 }
 357                 break;
 358             case ALLCLASSES:
 359             case ALLPACKAGES:
 360             case CONSTANTVALUES:
 361             case SERIALIZEDFORM:
 362             case SYSTEMPROPERTIES:
 363                 addOverviewLink(tree);
 364                 addModuleLink(tree);
 365                 addPackageLink(tree);
 366                 addPageLabel(tree, contents.classLabel, true);
 367                 addPageLabel(tree, contents.useLabel, configuration.classuse);
 368                 addTreeLink(tree);
 369                 addDeprecatedLink(tree);
 370                 addIndexLink(tree);
 371                 addHelpLink(tree);
 372                 break;
 373             case DOCFILE:
 374                 addOverviewLink(tree);
 375                 addModuleOfElementLink(tree);
 376                 addContentToTree(tree, navLinkPackage);
 377                 addPageLabel(tree, contents.classLabel, true);
 378                 addPageLabel(tree, contents.useLabel, configuration.classuse);
 379                 addTreeLink(tree);
 380                 addDeprecatedLink(tree);
 381                 addIndexLink(tree);
 382                 addHelpLink(tree);
 383                 break;
 384             default:
 385                 break;
 386         }
 387     }
 388 
 389     /**
 390      * Add the summary links to the sub-navigation.
 391      *
 392      * @param tree the content tree to which the sub-navigation will added
 393      */
 394     private void addSummaryLinks(Content tree) {
 395         List<Content> listContents = new ArrayList<>();
 396         switch (documentedPage) {
 397             case CLASS:
 398                 if (element.getKind() == ElementKind.ANNOTATION_TYPE) {
 399                     addAnnotationTypeSummaryLink("doclet.navField",
 400                             ANNOTATION_TYPE_FIELDS, listContents);
 401                     addAnnotationTypeSummaryLink("doclet.navAnnotationTypeRequiredMember",
 402                             ANNOTATION_TYPE_MEMBER_REQUIRED, listContents);
 403                     addAnnotationTypeSummaryLink("doclet.navAnnotationTypeOptionalMember",
 404                             ANNOTATION_TYPE_MEMBER_OPTIONAL, listContents);
 405                 } else {
 406                     TypeElement typeElement = (TypeElement) element;
 407                     for (VisibleMemberTable.Kind kind : summarySet) {
 408                         if (kind == ENUM_CONSTANTS && !configuration.utils.isEnum(typeElement)) {
 409                             continue;
 410                         }
 411                         if (kind == CONSTRUCTORS && configuration.utils.isEnum(typeElement)) {
 412                             continue;
 413                         }
 414                         AbstractMemberWriter writer
 415                                 = ((AbstractMemberWriter) memberSummaryBuilder.getMemberSummaryWriter(kind));
 416                         if (writer == null) {
 417                             addContentToList(listContents, contents.getNavLinkLabelContent(kind));
 418                         } else {
 419                             addTypeSummaryLink(memberSummaryBuilder.members(kind),
 420                                     memberSummaryBuilder.getVisibleMemberTable(),
 421                                     kind, listContents);
 422                         }
 423                     }
 424                 }
 425                 if (!listContents.isEmpty()) {
 426                     Content li = HtmlTree.LI(contents.summaryLabel);
 427                     li.add(Entity.NO_BREAK_SPACE);
 428                     tree.add(li);
 429                     addListToNav(listContents, tree);
 430                 }
 431                 break;
 432             case MODULE:
 433                 if (displaySummaryModuleDescLink) {
 434                     addContentToList(listContents,
 435                             links.createLink(SectionName.MODULE_DESCRIPTION, contents.navModuleDescription));
 436                 } else {
 437                     addContentToList(listContents, contents.navModuleDescription);
 438                 }
 439                 if (displaySummaryModulesLink) {
 440                     addContentToList(listContents,
 441                             links.createLink(SectionName.MODULES, contents.navModules));
 442                 } else {
 443                     addContentToList(listContents, contents.navModules);
 444                 }
 445                 if (displaySummaryPackagesLink) {
 446                     addContentToList(listContents,
 447                             links.createLink(SectionName.PACKAGES, contents.navPackages));
 448                 } else {
 449                     addContentToList(listContents, contents.navPackages);
 450                 }
 451                 if (displaySummaryServicesLink) {
 452                     addContentToList(listContents,
 453                             links.createLink(SectionName.SERVICES, contents.navServices));
 454                 } else {
 455                     addContentToList(listContents, contents.navServices);
 456                 }
 457                 if (!listContents.isEmpty()) {
 458                     Content li = HtmlTree.LI(contents.moduleSubNavLabel);
 459                     li.add(Entity.NO_BREAK_SPACE);
 460                     tree.add(li);
 461                     addListToNav(listContents, tree);
 462                 }
 463                 break;
 464             default:
 465                 break;
 466         }
 467     }
 468 
 469     /**
 470      * Add the navigation summary link.
 471      *
 472      * @param members members to be linked
 473      * @param vmt the visible member table
 474      * @param kind the visible member kind
 475      * @param listContents the list of contents
 476      */
 477     private void addTypeSummaryLink(SortedSet<? extends Element> members,
 478             VisibleMemberTable vmt,
 479             VisibleMemberTable.Kind kind, List<Content> listContents) {
 480         if (!members.isEmpty()) {
 481             addTypeSummaryLink(null, kind, true, listContents);
 482             return;
 483         }
 484         Set<TypeElement> visibleClasses = vmt.getVisibleTypeElements();
 485         for (TypeElement t : visibleClasses) {
 486             if (configuration.getVisibleMemberTable(t).hasVisibleMembers(kind)) {
 487                 addTypeSummaryLink(null, kind, true, listContents);
 488                 return;
 489             }
 490         }
 491         addTypeSummaryLink(null, kind, false, listContents);
 492     }
 493 
 494     /**
 495      * Add the navigation Type summary link.
 496      *
 497      * @param typeElement the Type being documented
 498      * @param kind the kind of member being documented
 499      * @param link true if the members are listed and need to be linked
 500      * @param listContents the list of contents to which the summary will be added
 501      */
 502     private void addTypeSummaryLink(TypeElement typeElement, VisibleMemberTable.Kind kind, boolean link,
 503             List<Content> listContents) {
 504         switch (kind) {
 505             case CONSTRUCTORS:
 506                 if (link) {
 507                     addContentToList(listContents, links.createLink(SectionName.CONSTRUCTOR_SUMMARY,
 508                             contents.navConstructor));
 509                 } else {
 510                     addContentToList(listContents, contents.navConstructor);
 511                 }
 512                 break;
 513             case ENUM_CONSTANTS:
 514                 if (link) {
 515                     if (typeElement == null) {
 516                         addContentToList(listContents, links.createLink(SectionName.ENUM_CONSTANT_SUMMARY,
 517                                 contents.navEnum));
 518                     } else {
 519                         addContentToList(listContents, links.createLink(
 520                                 SectionName.ENUM_CONSTANTS_INHERITANCE,
 521                                 configuration.getClassName(typeElement), contents.navEnum));
 522                     }
 523                 } else {
 524                     addContentToList(listContents, contents.navEnum);
 525                 }
 526                 break;
 527             case FIELDS:
 528                 if (link) {
 529                     if (typeElement == null) {
 530                         addContentToList(listContents,
 531                                 links.createLink(SectionName.FIELD_SUMMARY, contents.navField));
 532                     } else {
 533                         addContentToList(listContents, links.createLink(SectionName.FIELDS_INHERITANCE,
 534                                 configuration.getClassName(typeElement), contents.navField));
 535                     }
 536                 } else {
 537                     addContentToList(listContents, contents.navField);
 538                 }
 539                 break;
 540             case METHODS:
 541                 if (link) {
 542                     if (typeElement == null) {
 543                         addContentToList(listContents,
 544                                 links.createLink(SectionName.METHOD_SUMMARY, contents.navMethod));
 545                     } else {
 546                         addContentToList(listContents, links.createLink(SectionName.METHODS_INHERITANCE,
 547                                 configuration.getClassName(typeElement), contents.navMethod));
 548                     }
 549                 } else {
 550                     addContentToList(listContents, contents.navMethod);
 551                 }
 552                 break;
 553             case INNER_CLASSES:
 554                 if (link) {
 555                     if (typeElement == null) {
 556                         addContentToList(listContents,
 557                                 links.createLink(SectionName.NESTED_CLASS_SUMMARY, contents.navNested));
 558                     } else {
 559                         addContentToList(listContents, links.createLink(SectionName.NESTED_CLASSES_INHERITANCE,
 560                                 configuration.utils.getFullyQualifiedName(typeElement), contents.navNested));
 561                     }
 562                 } else {
 563                     addContentToList(listContents, contents.navNested);
 564                 }
 565                 break;
 566             case PROPERTIES:
 567                 if (link) {
 568                     if (typeElement == null) {
 569                         addContentToList(listContents,
 570                                 links.createLink(SectionName.PROPERTY_SUMMARY, contents.navProperty));
 571                     } else {
 572                         addContentToList(listContents, links.createLink(SectionName.PROPERTIES_INHERITANCE,
 573                                 configuration.getClassName(typeElement), contents.navProperty));
 574                     }
 575                 } else {
 576                     addContentToList(listContents, contents.navProperty);
 577                 }
 578                 break;
 579             default:
 580                 break;
 581         }
 582     }
 583 
 584     /**
 585      * Add the navigation Type summary link.
 586      *
 587      * @param label the label to be added
 588      * @param kind the kind of member being documented
 589      * @param listContents the list of contents to which the summary will be added
 590      */
 591     private void addAnnotationTypeSummaryLink(String label, VisibleMemberTable.Kind kind, List<Content> listContents) {
 592         AbstractMemberWriter writer = ((AbstractMemberWriter) memberSummaryBuilder.
 593                 getMemberSummaryWriter(kind));
 594         if (writer == null) {
 595             addContentToList(listContents, contents.getContent(label));
 596         } else {
 597             boolean link = memberSummaryBuilder.getVisibleMemberTable().hasVisibleMembers(kind);
 598             switch (kind) {
 599                 case ANNOTATION_TYPE_FIELDS:
 600                     if (link) {
 601                         addContentToList(listContents, links.createLink(SectionName.ANNOTATION_TYPE_FIELD_SUMMARY,
 602                                 contents.navField));
 603                     } else {
 604                         addContentToList(listContents, contents.navField);
 605                     }
 606                     break;
 607                 case ANNOTATION_TYPE_MEMBER_REQUIRED:
 608                     if (link) {
 609                         addContentToList(listContents, links.createLink(
 610                                 SectionName.ANNOTATION_TYPE_REQUIRED_ELEMENT_SUMMARY,
 611                                 contents.navAnnotationTypeRequiredMember));
 612                     } else {
 613                         addContentToList(listContents, contents.navAnnotationTypeRequiredMember);
 614                     }
 615                     break;
 616                 case ANNOTATION_TYPE_MEMBER_OPTIONAL:
 617                     if (link) {
 618                         addContentToList(listContents, links.createLink(
 619                                 SectionName.ANNOTATION_TYPE_OPTIONAL_ELEMENT_SUMMARY,
 620                                 contents.navAnnotationTypeOptionalMember));
 621                     } else {
 622                         addContentToList(listContents, contents.navAnnotationTypeOptionalMember);
 623                     }
 624                     break;
 625                 default:
 626                     break;
 627             }
 628         }
 629     }
 630 
 631     /**
 632      * Add the detail links to sub-navigation.
 633      *
 634      * @param tree the content tree to which the links will be added
 635      */
 636     private void addDetailLinks(Content tree) {
 637         switch (documentedPage) {
 638             case CLASS:
 639                 List<Content> listContents = new ArrayList<>();
 640                 if (element.getKind() == ElementKind.ANNOTATION_TYPE) {
 641                     addAnnotationTypeDetailLink(listContents);
 642                 } else {
 643                     TypeElement typeElement = (TypeElement) element;
 644                     for (VisibleMemberTable.Kind kind : detailSet) {
 645                         AbstractMemberWriter writer
 646                                 = ((AbstractMemberWriter) memberSummaryBuilder.
 647                                         getMemberSummaryWriter(kind));
 648                         if (kind == ENUM_CONSTANTS && !configuration.utils.isEnum(typeElement)) {
 649                             continue;
 650                         }
 651                         if (kind == CONSTRUCTORS && configuration.utils.isEnum(typeElement)) {
 652                             continue;
 653                         }
 654                         if (writer == null) {
 655                             addContentToList(listContents, contents.getNavLinkLabelContent(kind));
 656                         } else {
 657                             addTypeDetailLink(kind, memberSummaryBuilder.hasMembers(kind), listContents);
 658                         }
 659                     }
 660                 }
 661                 if (!listContents.isEmpty()) {
 662                     Content li = HtmlTree.LI(contents.detailLabel);
 663                     li.add(Entity.NO_BREAK_SPACE);
 664                     tree.add(li);
 665                     addListToNav(listContents, tree);
 666                 }
 667                 break;
 668             default:
 669                 break;
 670         }
 671     }
 672 
 673     /**
 674      * Add the navigation Type detail link.
 675      *
 676      * @param kind the kind of member being documented
 677      * @param link true if the members are listed and need to be linked
 678      * @param listContents the list of contents to which the detail will be added.
 679      */
 680     protected void addTypeDetailLink(VisibleMemberTable.Kind kind, boolean link, List<Content> listContents) {
 681         switch (kind) {
 682             case CONSTRUCTORS:
 683                 if (link) {
 684                     addContentToList(listContents, links.createLink(SectionName.CONSTRUCTOR_DETAIL, contents.navConstructor));
 685                 } else {
 686                     addContentToList(listContents, contents.navConstructor);
 687                 }
 688                 break;
 689             case ENUM_CONSTANTS:
 690                 if (link) {
 691                     addContentToList(listContents, links.createLink(SectionName.ENUM_CONSTANT_DETAIL, contents.navEnum));
 692                 } else {
 693                     addContentToList(listContents, contents.navEnum);
 694                 }
 695                 break;
 696             case FIELDS:
 697                 if (link) {
 698                     addContentToList(listContents, links.createLink(SectionName.FIELD_DETAIL, contents.navField));
 699                 } else {
 700                     addContentToList(listContents, contents.navField);
 701                 }
 702                 break;
 703             case METHODS:
 704                 if (link) {
 705                     addContentToList(listContents, links.createLink(SectionName.METHOD_DETAIL, contents.navMethod));
 706                 } else {
 707                     addContentToList(listContents, contents.navMethod);
 708                 }
 709                 break;
 710             case PROPERTIES:
 711                 if (link) {
 712                     addContentToList(listContents, links.createLink(SectionName.PROPERTY_DETAIL, contents.navProperty));
 713                 } else {
 714                     addContentToList(listContents, contents.navProperty);
 715                 }
 716                 break;
 717             default:
 718                 break;
 719         }
 720     }
 721 
 722     /**
 723      * Add the navigation Annotation Type detail link.
 724      *
 725      * @param listContents the list of contents to which the annotation detail will be added.
 726      */
 727     protected void addAnnotationTypeDetailLink(List<Content> listContents) {
 728         TypeElement annotationType = (TypeElement) element;
 729         AbstractMemberWriter writerField
 730                 = ((AbstractMemberWriter) memberSummaryBuilder.
 731                         getMemberSummaryWriter(ANNOTATION_TYPE_FIELDS));
 732         AbstractMemberWriter writerOptional
 733                 = ((AbstractMemberWriter) memberSummaryBuilder.
 734                         getMemberSummaryWriter(ANNOTATION_TYPE_MEMBER_OPTIONAL));
 735         AbstractMemberWriter writerRequired
 736                 = ((AbstractMemberWriter) memberSummaryBuilder.
 737                         getMemberSummaryWriter(ANNOTATION_TYPE_MEMBER_REQUIRED));
 738         if (writerField != null) {
 739             addAnnotationTypeDetailLink(ANNOTATION_TYPE_FIELDS,
 740                     !configuration.utils.getAnnotationFields(annotationType).isEmpty(),
 741                     listContents);
 742         } else {
 743             addContentToList(listContents, contents.navField);
 744         }
 745         if (writerOptional != null) {
 746             addAnnotationTypeDetailLink(ANNOTATION_TYPE_MEMBER_OPTIONAL,
 747                     !annotationType.getAnnotationMirrors().isEmpty(), listContents);
 748         } else if (writerRequired != null) {
 749             addAnnotationTypeDetailLink(ANNOTATION_TYPE_MEMBER_REQUIRED,
 750                     !annotationType.getAnnotationMirrors().isEmpty(), listContents);
 751         } else {
 752             addContentToList(listContents, contents.navAnnotationTypeMember);
 753         }
 754     }
 755 
 756     /**
 757      * Add the navigation Annotation Type detail link.
 758      *
 759      * @param type the kind of member being documented
 760      * @param link true if the member details need to be linked
 761      * @param listContents the list of contents to which the annotation detail will be added.
 762      */
 763     protected void addAnnotationTypeDetailLink(VisibleMemberTable.Kind type, boolean link, List<Content> listContents) {
 764         switch (type) {
 765             case ANNOTATION_TYPE_FIELDS:
 766                 if (link) {
 767                     addContentToList(listContents, links.createLink(SectionName.ANNOTATION_TYPE_FIELD_DETAIL,
 768                             contents.navField));
 769                 } else {
 770                     addContentToList(listContents, contents.navField);
 771                 }
 772                 break;
 773             case ANNOTATION_TYPE_MEMBER_REQUIRED:
 774             case ANNOTATION_TYPE_MEMBER_OPTIONAL:
 775                 if (link) {
 776                     addContentToList(listContents, links.createLink(SectionName.ANNOTATION_TYPE_ELEMENT_DETAIL,
 777                             contents.navAnnotationTypeMember));
 778                 } else {
 779                     addContentToList(listContents, contents.navAnnotationTypeMember);
 780                 }
 781                 break;
 782             default:
 783                 break;
 784         }
 785     }
 786 
 787     private void addContentToList(List<Content> listContents, Content tree) {
 788         listContents.add(HtmlTree.LI(tree));
 789     }
 790 
 791     private void addContentToTree(Content tree, Content content) {
 792         tree.add(HtmlTree.LI(content));
 793     }
 794 
 795     private void addListToNav(List<Content> listContents, Content tree) {
 796         int count = 0;
 797         for (Content liContent : listContents) {
 798             if (count < listContents.size() - 1) {
 799                 liContent.add(Entity.NO_BREAK_SPACE);
 800                 liContent.add("|");
 801                 liContent.add(Entity.NO_BREAK_SPACE);
 802             }
 803             tree.add(liContent);
 804             count++;
 805         }
 806     }
 807 
 808     private void addActivePageLink(Content tree, Content label, boolean display) {
 809         if (display) {
 810             tree.add(HtmlTree.LI(HtmlStyle.navBarCell1Rev, label));
 811         }
 812     }
 813 
 814     private void addPageLabel(Content tree, Content label, boolean display) {
 815         if (display) {
 816             tree.add(HtmlTree.LI(label));
 817         }
 818     }
 819 
 820     private void addOverviewLink(Content tree) {
 821         if (configuration.createoverview) {
 822             tree.add(HtmlTree.LI(links.createLink(pathToRoot.resolve(DocPaths.INDEX),
 823                     contents.overviewLabel, "", "")));
 824         }
 825     }
 826 
 827     private void addModuleLink(Content tree) {
 828         if (configuration.showModules) {
 829             if (configuration.modules.size() == 1) {
 830                 ModuleElement mdle = configuration.modules.first();
 831                 boolean included = configuration.utils.isIncluded(mdle);
 832                 tree.add(HtmlTree.LI((included)
 833                         ? links.createLink(pathToRoot.resolve(configuration.docPaths.moduleSummary(mdle)), contents.moduleLabel, "", "")
 834                         : contents.moduleLabel));
 835             } else if (!configuration.modules.isEmpty()) {
 836                 addPageLabel(tree, contents.moduleLabel, true);
 837             }
 838         }
 839     }
 840 
 841     private void addModuleOfElementLink(Content tree) {
 842         if (configuration.showModules) {
 843             tree.add(HtmlTree.LI(navLinkModule));
 844         }
 845     }
 846 
 847     private void addPackageLink(Content tree) {
 848         if (configuration.packages.size() == 1) {
 849             PackageElement packageElement = configuration.packages.first();
 850             boolean included = packageElement != null && configuration.utils.isIncluded(packageElement);
 851             if (!included) {
 852                 for (PackageElement p : configuration.packages) {
 853                     if (p.equals(packageElement)) {
 854                         included = true;
 855                         break;
 856                     }
 857                 }
 858             }
 859             if (included || packageElement == null) {
 860                 tree.add(HtmlTree.LI(links.createLink(
 861                         pathToRoot.resolve(configuration.docPaths.forPackage(packageElement).resolve(DocPaths.PACKAGE_SUMMARY)),
 862                         contents.packageLabel)));
 863             } else {
 864                 DocLink crossPkgLink = configuration.extern.getExternalLink(
 865                         packageElement, pathToRoot, DocPaths.PACKAGE_SUMMARY.getPath());
 866                 if (crossPkgLink != null) {
 867                     tree.add(HtmlTree.LI(links.createLink(crossPkgLink, contents.packageLabel)));
 868                 } else {
 869                     tree.add(HtmlTree.LI(contents.packageLabel));
 870                 }
 871             }
 872         } else if (!configuration.packages.isEmpty()) {
 873             addPageLabel(tree, contents.packageLabel, true);
 874         }
 875     }
 876 
 877     private void addPackageOfElementLink(Content tree) {
 878         tree.add(HtmlTree.LI(links.createLink(DocPath.parent.resolve(DocPaths.PACKAGE_SUMMARY),
 879                 contents.packageLabel)));
 880     }
 881 
 882     private void addPackageSummaryLink(Content tree) {
 883         tree.add(HtmlTree.LI(links.createLink(DocPaths.PACKAGE_SUMMARY, contents.packageLabel)));
 884     }
 885 
 886     private void addTreeLink(Content tree) {
 887         if (configuration.createtree) {
 888             List<PackageElement> packages = new ArrayList<>(configuration.getSpecifiedPackageElements());
 889             DocPath docPath = packages.size() == 1 && configuration.getSpecifiedTypeElements().isEmpty()
 890                     ? pathToRoot.resolve(configuration.docPaths.forPackage(packages.get(0)).resolve(DocPaths.PACKAGE_TREE))
 891                     : pathToRoot.resolve(DocPaths.OVERVIEW_TREE);
 892             tree.add(HtmlTree.LI(links.createLink(docPath, contents.treeLabel, "", "")));
 893         }
 894     }
 895 
 896     private void addDeprecatedLink(Content tree) {
 897         if (!(configuration.nodeprecated || configuration.nodeprecatedlist)) {
 898             tree.add(HtmlTree.LI(links.createLink(pathToRoot.resolve(DocPaths.DEPRECATED_LIST),
 899                     contents.deprecatedLabel, "", "")));
 900         }
 901     }
 902 
 903     private void addIndexLink(Content tree) {
 904         if (configuration.createindex) {
 905             tree.add(HtmlTree.LI(links.createLink(pathToRoot.resolve(
 906                     (configuration.splitindex
 907                             ? DocPaths.INDEX_FILES.resolve(DocPaths.indexN(1))
 908                             : DocPaths.INDEX_ALL)),
 909                     contents.indexLabel, "", "")));
 910         }
 911     }
 912 
 913     private void addHelpLink(Content tree) {
 914         if (!configuration.nohelp) {
 915             String helpfile = configuration.helpfile;
 916             DocPath helpfilenm;
 917             if (helpfile.isEmpty()) {
 918                 helpfilenm = DocPaths.HELP_DOC;
 919             } else {
 920                 DocFile file = DocFile.createFileForInput(configuration, helpfile);
 921                 helpfilenm = DocPath.create(file.getName());
 922             }
 923             tree.add(HtmlTree.LI(links.createLink(pathToRoot.resolve(helpfilenm),
 924                     contents.helpLabel, "", "")));
 925         }
 926     }
 927 
 928     private void addSearch(Content tree) {
 929         String searchValueId = "search";
 930         String reset = "reset";
 931         HtmlTree inputText = HtmlTree.INPUT("text", searchValueId, searchValueId);
 932         HtmlTree inputReset = HtmlTree.INPUT(reset, reset, reset);
 933         HtmlTree searchDiv = HtmlTree.DIV(HtmlStyle.navListSearch, HtmlTree.LABEL(searchValueId, searchLabel));
 934         searchDiv.add(inputText);
 935         searchDiv.add(inputReset);
 936         tree.add(searchDiv);
 937     }
 938 
 939     /**
 940      * Get the navigation content.
 941      *
 942      * @param top true if the top navigation bar is to be printed
 943      * @return the navigation contents
 944      */
 945     public Content getContent(boolean top) {
 946         if (configuration.nonavbar) {
 947             return new ContentBuilder();
 948         }
 949         Deque<Content> queue;
 950         Content tree = HtmlTree.NAV();
 951         HtmlTree navDiv = new HtmlTree(HtmlTag.DIV);
 952         if (top) {
 953             queue = topBottomNavContents.get(Position.TOP);
 954             tree.add(Position.TOP.startOfNav());
 955             navDiv.setStyle(HtmlStyle.topNav);
 956         } else {
 957             queue = topBottomNavContents.get(Position.BOTTOM);
 958             tree.add(Position.BOTTOM.startOfNav());
 959             navDiv.setStyle(HtmlStyle.bottomNav);
 960         }
 961         navDiv.add(queue.poll());
 962         HtmlTree skipLinkDiv = HtmlTree.DIV(HtmlStyle.skipNav, queue.poll());
 963         navDiv.add(skipLinkDiv);
 964         navDiv.add(queue.poll());
 965         HtmlTree navList = new HtmlTree(HtmlTag.UL);
 966         navList.setStyle(HtmlStyle.navList);
 967         navList.put(HtmlAttr.TITLE, rowListTitle);
 968         addMainNavLinks(navList);
 969         navDiv.add(navList);
 970         Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, top ? userHeader : userFooter);
 971         navDiv.add(aboutDiv);
 972         tree.add(navDiv);
 973         HtmlTree subDiv = new HtmlTree(HtmlTag.DIV);
 974         subDiv.setStyle(HtmlStyle.subNav);
 975         HtmlTree div = new HtmlTree(HtmlTag.DIV);
 976         // Add the summary links if present.
 977         HtmlTree ulNavSummary = new HtmlTree(HtmlTag.UL);
 978         ulNavSummary.setStyle(HtmlStyle.subNavList);
 979         addSummaryLinks(ulNavSummary);
 980         div.add(ulNavSummary);
 981         // Add the detail links if present.
 982         HtmlTree ulNavDetail = new HtmlTree(HtmlTag.UL);
 983         ulNavDetail.setStyle(HtmlStyle.subNavList);
 984         addDetailLinks(ulNavDetail);
 985         div.add(ulNavDetail);
 986         subDiv.add(div);
 987         if (top && configuration.createindex) {
 988             addSearch(subDiv);
 989         }
 990         tree.add(subDiv);
 991         if (top) {
 992             tree.add(Position.TOP.endOfNav());
 993             tree.add(HtmlTree.DIV(HtmlStyle.skipNav, queue.poll()));
 994         } else {
 995             tree.add(queue.poll());
 996             tree.add(Position.BOTTOM.endOfNav());
 997         }
 998         return tree;
 999     }
1000 }