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