1 /*
   2  * Copyright (c) 1997, 2013, 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 
  26 package com.sun.tools.doclets.formats.html;
  27 
  28 import java.util.*;
  29 
  30 import com.sun.javadoc.*;
  31 import com.sun.tools.javac.jvm.Profile;
  32 import com.sun.tools.doclets.formats.html.markup.*;
  33 import com.sun.tools.doclets.internal.toolkit.*;
  34 import com.sun.tools.doclets.internal.toolkit.builders.*;
  35 import com.sun.tools.doclets.internal.toolkit.taglets.*;
  36 import com.sun.tools.doclets.internal.toolkit.util.*;
  37 import java.io.IOException;
  38 
  39 /**
  40  * Generate the Class Information Page.
  41  *
  42  *  <p><b>This is NOT part of any supported API.
  43  *  If you write code that depends on this, you do so at your own risk.
  44  *  This code and its internal interfaces are subject to change or
  45  *  deletion without notice.</b>
  46  *
  47  * @see com.sun.javadoc.ClassDoc
  48  * @see java.util.Collections
  49  * @see java.util.List
  50  * @see java.util.ArrayList
  51  * @see java.util.HashMap
  52  *
  53  * @author Atul M Dambalkar
  54  * @author Robert Field
  55  * @author Bhavesh Patel (Modified)
  56  */
  57 public class ClassWriterImpl extends SubWriterHolderWriter
  58         implements ClassWriter {
  59 
  60     protected final ClassDoc classDoc;
  61 
  62     protected final ClassTree classtree;
  63 
  64     protected final ClassDoc prev;
  65 
  66     protected final ClassDoc next;
  67 
  68     /**
  69      * @param configuration the configuration data for the doclet
  70      * @param classDoc the class being documented.
  71      * @param prevClass the previous class that was documented.
  72      * @param nextClass the next class being documented.
  73      * @param classTree the class tree for the given class.
  74      */
  75     public ClassWriterImpl (ConfigurationImpl configuration, ClassDoc classDoc,
  76             ClassDoc prevClass, ClassDoc nextClass, ClassTree classTree)
  77             throws IOException {
  78         super(configuration, DocPath.forClass(classDoc));
  79         this.classDoc = classDoc;
  80         configuration.currentcd = classDoc;
  81         this.classtree = classTree;
  82         this.prev = prevClass;
  83         this.next = nextClass;
  84     }
  85 
  86     /**
  87      * Get this package link.
  88      *
  89      * @return a content tree for the package link
  90      */
  91     protected Content getNavLinkPackage() {
  92         Content linkContent = getHyperLink(DocPaths.PACKAGE_SUMMARY,
  93                 packageLabel);
  94         Content li = HtmlTree.LI(linkContent);
  95         return li;
  96     }
  97 
  98     /**
  99      * Get the class link.
 100      *
 101      * @return a content tree for the class link
 102      */
 103     protected Content getNavLinkClass() {
 104         Content li = HtmlTree.LI(HtmlStyle.navBarCell1Rev, classLabel);
 105         return li;
 106     }
 107 
 108     /**
 109      * Get the class use link.
 110      *
 111      * @return a content tree for the class use link
 112      */
 113     protected Content getNavLinkClassUse() {
 114         Content linkContent = getHyperLink(DocPaths.CLASS_USE.resolve(filename), useLabel);
 115         Content li = HtmlTree.LI(linkContent);
 116         return li;
 117     }
 118 
 119     /**
 120      * Get link to previous class.
 121      *
 122      * @return a content tree for the previous class link
 123      */
 124     public Content getNavLinkPrevious() {
 125         Content li;
 126         if (prev != null) {
 127             Content prevLink = new RawHtml(getLink(new LinkInfoImpl(configuration,
 128                     LinkInfoImpl.CONTEXT_CLASS, prev, "",
 129                     configuration.getText("doclet.Prev_Class"), true)));
 130             li = HtmlTree.LI(prevLink);
 131         }
 132         else
 133             li = HtmlTree.LI(prevclassLabel);
 134         return li;
 135     }
 136 
 137     /**
 138      * Get link to next class.
 139      *
 140      * @return a content tree for the next class link
 141      */
 142     public Content getNavLinkNext() {
 143         Content li;
 144         if (next != null) {
 145             Content nextLink = new RawHtml(getLink(new LinkInfoImpl(configuration,
 146                     LinkInfoImpl.CONTEXT_CLASS, next, "",
 147                     configuration.getText("doclet.Next_Class"), true)));
 148             li = HtmlTree.LI(nextLink);
 149         }
 150         else
 151             li = HtmlTree.LI(nextclassLabel);
 152         return li;
 153     }
 154 
 155     /**
 156      * {@inheritDoc}
 157      */
 158     public Content getHeader(String header) {
 159         String pkgname = (classDoc.containingPackage() != null)?
 160             classDoc.containingPackage().name(): "";
 161         String clname = classDoc.name();
 162         Content bodyTree = getBody(true, getWindowTitle(clname));
 163         addTop(bodyTree);
 164         addNavLinks(true, bodyTree);
 165         bodyTree.addContent(HtmlConstants.START_OF_CLASS_DATA);
 166         HtmlTree div = new HtmlTree(HtmlTag.DIV);
 167         div.addStyle(HtmlStyle.header);
 168         if (configuration.showProfiles) {
 169             String sep = "";
 170             int profile = configuration.profiles.getProfile(getTypeNameForProfile(classDoc));
 171             if (profile > 0) {
 172                 Content profNameContent = new StringContent();
 173                 for (int i = profile; i < configuration.profiles.getProfileCount(); i++) {
 174                     profNameContent.addContent(sep);
 175                     profNameContent.addContent(Profile.lookup(i).name);
 176                     sep = ", ";
 177                 }
 178                 Content profileNameDiv = HtmlTree.DIV(HtmlStyle.subTitle, profNameContent);
 179                 div.addContent(profileNameDiv);
 180             }
 181         }
 182         if (pkgname.length() > 0) {
 183             Content pkgNameContent = new StringContent(pkgname);
 184             Content pkgNameDiv = HtmlTree.DIV(HtmlStyle.subTitle, pkgNameContent);
 185             div.addContent(pkgNameDiv);
 186         }
 187         LinkInfoImpl linkInfo = new LinkInfoImpl(configuration,
 188                 LinkInfoImpl.CONTEXT_CLASS_HEADER, classDoc, false);
 189         //Let's not link to ourselves in the header.
 190         linkInfo.linkToSelf = false;
 191         Content headerContent = new StringContent(header);
 192         Content heading = HtmlTree.HEADING(HtmlConstants.CLASS_PAGE_HEADING, true,
 193                 HtmlStyle.title, headerContent);
 194         heading.addContent(new RawHtml(getTypeParameterLinks(linkInfo)));
 195         div.addContent(heading);
 196         bodyTree.addContent(div);
 197         return bodyTree;
 198     }
 199 
 200     /**
 201      * {@inheritDoc}
 202      */
 203     public Content getClassContentHeader() {
 204         return getContentHeader();
 205     }
 206 
 207     /**
 208      * {@inheritDoc}
 209      */
 210     public void addFooter(Content contentTree) {
 211         contentTree.addContent(HtmlConstants.END_OF_CLASS_DATA);
 212         addNavLinks(false, contentTree);
 213         addBottom(contentTree);
 214     }
 215 
 216     /**
 217      * {@inheritDoc}
 218      */
 219     public void printDocument(Content contentTree) throws IOException {
 220         printHtmlDocument(configuration.metakeywords.getMetaKeywords(classDoc),
 221                 true, contentTree);
 222     }
 223 
 224     /**
 225      * {@inheritDoc}
 226      */
 227     public Content getClassInfoTreeHeader() {
 228         return getMemberTreeHeader();
 229     }
 230 
 231     /**
 232      * {@inheritDoc}
 233      */
 234     public Content getClassInfo(Content classInfoTree) {
 235         return getMemberTree(HtmlStyle.description, classInfoTree);
 236     }
 237 
 238     /**
 239      * {@inheritDoc}
 240      */
 241     public void addClassSignature(String modifiers, Content classInfoTree) {
 242         boolean isInterface = classDoc.isInterface();
 243         classInfoTree.addContent(new HtmlTree(HtmlTag.BR));
 244         Content pre = new HtmlTree(HtmlTag.PRE);
 245         addAnnotationInfo(classDoc, pre);
 246         pre.addContent(modifiers);
 247         LinkInfoImpl linkInfo = new LinkInfoImpl(configuration,
 248                 LinkInfoImpl.CONTEXT_CLASS_SIGNATURE, classDoc, false);
 249         //Let's not link to ourselves in the signature.
 250         linkInfo.linkToSelf = false;
 251         Content className = new StringContent(classDoc.name());
 252         Content parameterLinks = new RawHtml(getTypeParameterLinks(linkInfo));
 253         if (configuration.linksource) {
 254             addSrcLink(classDoc, className, pre);
 255             pre.addContent(parameterLinks);
 256         } else {
 257             Content span = HtmlTree.SPAN(HtmlStyle.strong, className);
 258             span.addContent(parameterLinks);
 259             pre.addContent(span);
 260         }
 261         if (!isInterface) {
 262             Type superclass = Util.getFirstVisibleSuperClass(classDoc,
 263                     configuration);
 264             if (superclass != null) {
 265                 pre.addContent(DocletConstants.NL);
 266                 pre.addContent("extends ");
 267                 Content link = new RawHtml(getLink(new LinkInfoImpl(configuration,
 268                         LinkInfoImpl.CONTEXT_CLASS_SIGNATURE_PARENT_NAME,
 269                         superclass)));
 270                 pre.addContent(link);
 271             }
 272         }
 273         Type[] implIntfacs = classDoc.interfaceTypes();
 274         if (implIntfacs != null && implIntfacs.length > 0) {
 275             int counter = 0;
 276             for (int i = 0; i < implIntfacs.length; i++) {
 277                 ClassDoc classDoc = implIntfacs[i].asClassDoc();
 278                 if (! (classDoc.isPublic() ||
 279                         Util.isLinkable(classDoc, configuration))) {
 280                     continue;
 281                 }
 282                 if (counter == 0) {
 283                     pre.addContent(DocletConstants.NL);
 284                     pre.addContent(isInterface? "extends " : "implements ");
 285                 } else {
 286                     pre.addContent(", ");
 287                 }
 288                 Content link = new RawHtml(getLink(new LinkInfoImpl(configuration,
 289                         LinkInfoImpl.CONTEXT_CLASS_SIGNATURE_PARENT_NAME,
 290                         implIntfacs[i])));
 291                 pre.addContent(link);
 292                 counter++;
 293             }
 294         }
 295         classInfoTree.addContent(pre);
 296     }
 297 
 298     /**
 299      * {@inheritDoc}
 300      */
 301     public void addClassDescription(Content classInfoTree) {
 302         if(!configuration.nocomment) {
 303             // generate documentation for the class.
 304             if (classDoc.inlineTags().length > 0) {
 305                 addInlineComment(classDoc, classInfoTree);
 306             }
 307         }
 308     }
 309 
 310     /**
 311      * {@inheritDoc}
 312      */
 313     public void addClassTagInfo(Content classInfoTree) {
 314         if(!configuration.nocomment) {
 315             // Print Information about all the tags here
 316             addTagsInfo(classDoc, classInfoTree);
 317         }
 318     }
 319 
 320     /**
 321      * Get the class hierarchy tree for the given class.
 322      *
 323      * @param type the class to print the hierarchy for
 324      * @return a content tree for class inheritence
 325      */
 326     private Content getClassInheritenceTree(Type type) {
 327         Type sup;
 328         HtmlTree classTreeUl = new HtmlTree(HtmlTag.UL);
 329         classTreeUl.addStyle(HtmlStyle.inheritance);
 330         Content liTree = null;
 331         do {
 332             sup = Util.getFirstVisibleSuperClass(
 333                     type instanceof ClassDoc ? (ClassDoc) type : type.asClassDoc(),
 334                     configuration);
 335             if (sup != null) {
 336                 HtmlTree ul = new HtmlTree(HtmlTag.UL);
 337                 ul.addStyle(HtmlStyle.inheritance);
 338                 ul.addContent(getTreeForClassHelper(type));
 339                 if (liTree != null)
 340                     ul.addContent(liTree);
 341                 Content li = HtmlTree.LI(ul);
 342                 liTree = li;
 343                 type = sup;
 344             }
 345             else
 346                 classTreeUl.addContent(getTreeForClassHelper(type));
 347         }
 348         while (sup != null);
 349         if (liTree != null)
 350             classTreeUl.addContent(liTree);
 351         return classTreeUl;
 352     }
 353 
 354     /**
 355      * Get the class helper tree for the given class.
 356      *
 357      * @param type the class to print the helper for
 358      * @return a content tree for class helper
 359      */
 360     private Content getTreeForClassHelper(Type type) {
 361         Content li = new HtmlTree(HtmlTag.LI);
 362         if (type.equals(classDoc)) {
 363             String typeParameters = getTypeParameterLinks(
 364                     new LinkInfoImpl(configuration, LinkInfoImpl.CONTEXT_TREE,
 365                     classDoc, false));
 366             if (configuration.shouldExcludeQualifier(
 367                     classDoc.containingPackage().name())) {
 368                 li.addContent(type.asClassDoc().name());
 369                 li.addContent(new RawHtml(typeParameters));
 370             } else {
 371                 li.addContent(type.asClassDoc().qualifiedName());
 372                 li.addContent(new RawHtml(typeParameters));
 373             }
 374         } else {
 375             Content link = new RawHtml(getLink(new LinkInfoImpl(configuration,
 376                     LinkInfoImpl.CONTEXT_CLASS_TREE_PARENT,
 377                     type instanceof ClassDoc ? (ClassDoc) type : type,
 378                     configuration.getClassName(type.asClassDoc()), false)));
 379             li.addContent(link);
 380         }
 381         return li;
 382     }
 383 
 384     /**
 385      * {@inheritDoc}
 386      */
 387     public void addClassTree(Content classContentTree) {
 388         if (!classDoc.isClass()) {
 389             return;
 390         }
 391         classContentTree.addContent(getClassInheritenceTree(classDoc));
 392     }
 393 
 394     /**
 395      * {@inheritDoc}
 396      */
 397     public void addTypeParamInfo(Content classInfoTree) {
 398         if (classDoc.typeParamTags().length > 0) {
 399             TagletOutput output = (new ParamTaglet()).getTagletOutput(classDoc,
 400                     getTagletWriterInstance(false));
 401             Content typeParam = new RawHtml(output.toString());
 402             Content dl = HtmlTree.DL(typeParam);
 403             classInfoTree.addContent(dl);
 404         }
 405     }
 406 
 407     /**
 408      * {@inheritDoc}
 409      */
 410     public void addSubClassInfo(Content classInfoTree) {
 411         if (classDoc.isClass()) {
 412             if (classDoc.qualifiedName().equals("java.lang.Object") ||
 413                     classDoc.qualifiedName().equals("org.omg.CORBA.Object")) {
 414                 return;    // Don't generate the list, too huge
 415             }
 416             List<ClassDoc> subclasses = classtree.subs(classDoc, false);
 417             if (subclasses.size() > 0) {
 418                 Content label = getResource(
 419                         "doclet.Subclasses");
 420                 Content dt = HtmlTree.DT(label);
 421                 Content dl = HtmlTree.DL(dt);
 422                 dl.addContent(getClassLinks(LinkInfoImpl.CONTEXT_SUBCLASSES,
 423                         subclasses));
 424                 classInfoTree.addContent(dl);
 425             }
 426         }
 427     }
 428 
 429     /**
 430      * {@inheritDoc}
 431      */
 432     public void addSubInterfacesInfo(Content classInfoTree) {
 433         if (classDoc.isInterface()) {
 434             List<ClassDoc> subInterfaces = classtree.allSubs(classDoc, false);
 435             if (subInterfaces.size() > 0) {
 436                 Content label = getResource(
 437                         "doclet.Subinterfaces");
 438                 Content dt = HtmlTree.DT(label);
 439                 Content dl = HtmlTree.DL(dt);
 440                 dl.addContent(getClassLinks(LinkInfoImpl.CONTEXT_SUBINTERFACES,
 441                         subInterfaces));
 442                 classInfoTree.addContent(dl);
 443             }
 444         }
 445     }
 446 
 447     /**
 448      * {@inheritDoc}
 449      */
 450     public void addInterfaceUsageInfo (Content classInfoTree) {
 451         if (! classDoc.isInterface()) {
 452             return;
 453         }
 454         if (classDoc.qualifiedName().equals("java.lang.Cloneable") ||
 455                 classDoc.qualifiedName().equals("java.io.Serializable")) {
 456             return;   // Don't generate the list, too big
 457         }
 458         List<ClassDoc> implcl = classtree.implementingclasses(classDoc);
 459         if (implcl.size() > 0) {
 460             Content label = getResource(
 461                     "doclet.Implementing_Classes");
 462             Content dt = HtmlTree.DT(label);
 463             Content dl = HtmlTree.DL(dt);
 464             dl.addContent(getClassLinks(LinkInfoImpl.CONTEXT_IMPLEMENTED_CLASSES,
 465                     implcl));
 466             classInfoTree.addContent(dl);
 467         }
 468     }
 469 
 470     /**
 471      * {@inheritDoc}
 472      */
 473     public void addImplementedInterfacesInfo(Content classInfoTree) {
 474         //NOTE:  we really should be using ClassDoc.interfaceTypes() here, but
 475         //       it doesn't walk up the tree like we want it to.
 476         List<Type> interfaceArray = Util.getAllInterfaces(classDoc, configuration);
 477         if (classDoc.isClass() && interfaceArray.size() > 0) {
 478             Content label = getResource(
 479                     "doclet.All_Implemented_Interfaces");
 480             Content dt = HtmlTree.DT(label);
 481             Content dl = HtmlTree.DL(dt);
 482             dl.addContent(getClassLinks(LinkInfoImpl.CONTEXT_IMPLEMENTED_INTERFACES,
 483                     interfaceArray));
 484             classInfoTree.addContent(dl);
 485         }
 486     }
 487 
 488     /**
 489      * {@inheritDoc}
 490      */
 491     public void addSuperInterfacesInfo(Content classInfoTree) {
 492         //NOTE:  we really should be using ClassDoc.interfaceTypes() here, but
 493         //       it doesn't walk up the tree like we want it to.
 494         List<Type> interfaceArray = Util.getAllInterfaces(classDoc, configuration);
 495         if (classDoc.isInterface() && interfaceArray.size() > 0) {
 496             Content label = getResource(
 497                     "doclet.All_Superinterfaces");
 498             Content dt = HtmlTree.DT(label);
 499             Content dl = HtmlTree.DL(dt);
 500             dl.addContent(getClassLinks(LinkInfoImpl.CONTEXT_SUPER_INTERFACES,
 501                     interfaceArray));
 502             classInfoTree.addContent(dl);
 503         }
 504     }
 505 
 506     /**
 507      * {@inheritDoc}
 508      */
 509     public void addNestedClassInfo(Content classInfoTree) {
 510         ClassDoc outerClass = classDoc.containingClass();
 511         if (outerClass != null) {
 512             Content label;
 513             if (outerClass.isInterface()) {
 514                 label = getResource(
 515                         "doclet.Enclosing_Interface");
 516             } else {
 517                 label = getResource(
 518                         "doclet.Enclosing_Class");
 519             }
 520             Content dt = HtmlTree.DT(label);
 521             Content dl = HtmlTree.DL(dt);
 522             Content dd = new HtmlTree(HtmlTag.DD);
 523             dd.addContent(new RawHtml(getLink(new LinkInfoImpl(configuration,
 524                     LinkInfoImpl.CONTEXT_CLASS, outerClass, false))));
 525             dl.addContent(dd);
 526             classInfoTree.addContent(dl);
 527         }
 528     }
 529 
 530     /**
 531      * {@inheritDoc}
 532      */
 533     public void addFunctionalInterfaceInfo (Content classInfoTree) {
 534         if (classDoc.isFunctionalInterface()) {
 535             Content dt = HtmlTree.DT(getResource("doclet.Functional_Interface"));
 536             Content dl = HtmlTree.DL(dt);
 537             Content dd = new HtmlTree(HtmlTag.DD);
 538             dd.addContent(getResource("doclet.Functional_Interface_Message"));
 539             dl.addContent(dd);
 540             classInfoTree.addContent(dl);
 541         }
 542     }
 543 
 544     /**
 545      * {@inheritDoc}
 546      */
 547     public void addClassDeprecationInfo(Content classInfoTree) {
 548         Content hr = new HtmlTree(HtmlTag.HR);
 549         classInfoTree.addContent(hr);
 550         Tag[] deprs = classDoc.tags("deprecated");
 551         if (Util.isDeprecated(classDoc)) {
 552             Content strong = HtmlTree.STRONG(deprecatedPhrase);
 553             Content div = HtmlTree.DIV(HtmlStyle.block, strong);
 554             if (deprs.length > 0) {
 555                 Tag[] commentTags = deprs[0].inlineTags();
 556                 if (commentTags.length > 0) {
 557                     div.addContent(getSpace());
 558                     addInlineDeprecatedComment(classDoc, deprs[0], div);
 559                 }
 560             }
 561             classInfoTree.addContent(div);
 562         }
 563     }
 564 
 565     /**
 566      * Get links to the given classes.
 567      *
 568      * @param context the id of the context where the link will be printed
 569      * @param list the list of classes
 570      * @return a content tree for the class list
 571      */
 572     private Content getClassLinks(int context, List<?> list) {
 573         Object[] typeList = list.toArray();
 574         Content dd = new HtmlTree(HtmlTag.DD);
 575         for (int i = 0; i < list.size(); i++) {
 576             if (i > 0) {
 577                 Content separator = new StringContent(", ");
 578                 dd.addContent(separator);
 579             }
 580             if (typeList[i] instanceof ClassDoc) {
 581                 Content link = new RawHtml(getLink(
 582                         new LinkInfoImpl(configuration, context, (ClassDoc)(typeList[i]))));
 583                 dd.addContent(link);
 584             } else {
 585                 Content link = new RawHtml(getLink(
 586                         new LinkInfoImpl(configuration, context, (Type)(typeList[i]))));
 587                 dd.addContent(link);
 588             }
 589         }
 590         return dd;
 591     }
 592 
 593     /**
 594      * {@inheritDoc}
 595      */
 596     protected Content getNavLinkTree() {
 597         Content treeLinkContent = getHyperLink(DocPaths.PACKAGE_TREE,
 598                 treeLabel, "", "");
 599         Content li = HtmlTree.LI(treeLinkContent);
 600         return li;
 601     }
 602 
 603     /**
 604      * Add summary details to the navigation bar.
 605      *
 606      * @param subDiv the content tree to which the summary detail links will be added
 607      */
 608     protected void addSummaryDetailLinks(Content subDiv) {
 609         try {
 610             Content div = HtmlTree.DIV(getNavSummaryLinks());
 611             div.addContent(getNavDetailLinks());
 612             subDiv.addContent(div);
 613         } catch (Exception e) {
 614             e.printStackTrace();
 615             throw new DocletAbortException();
 616         }
 617     }
 618 
 619     /**
 620      * Get summary links for navigation bar.
 621      *
 622      * @return the content tree for the navigation summary links
 623      */
 624     protected Content getNavSummaryLinks() throws Exception {
 625         Content li = HtmlTree.LI(summaryLabel);
 626         li.addContent(getSpace());
 627         Content ulNav = HtmlTree.UL(HtmlStyle.subNavList, li);
 628         MemberSummaryBuilder memberSummaryBuilder = (MemberSummaryBuilder)
 629                 configuration.getBuilderFactory().getMemberSummaryBuilder(this);
 630         String[] navLinkLabels =  new String[] {
 631             "doclet.navNested", "doclet.navEnum", "doclet.navField", "doclet.navConstructor",
 632             "doclet.navMethod"
 633         };
 634         for (int i = 0; i < navLinkLabels.length; i++ ) {
 635             Content liNav = new HtmlTree(HtmlTag.LI);
 636             if (i == VisibleMemberMap.ENUM_CONSTANTS && ! classDoc.isEnum()) {
 637                 continue;
 638             }
 639             if (i == VisibleMemberMap.CONSTRUCTORS && classDoc.isEnum()) {
 640                 continue;
 641             }
 642             AbstractMemberWriter writer =
 643                 ((AbstractMemberWriter) memberSummaryBuilder.
 644                 getMemberSummaryWriter(i));
 645             if (writer == null) {
 646                 liNav.addContent(getResource(navLinkLabels[i]));
 647             } else {
 648                 writer.addNavSummaryLink(
 649                         memberSummaryBuilder.members(i),
 650                         memberSummaryBuilder.getVisibleMemberMap(i), liNav);
 651             }
 652             if (i < navLinkLabels.length-1) {
 653                 addNavGap(liNav);
 654             }
 655             ulNav.addContent(liNav);
 656         }
 657         return ulNav;
 658     }
 659 
 660     /**
 661      * Get detail links for the navigation bar.
 662      *
 663      * @return the content tree for the detail links
 664      */
 665     protected Content getNavDetailLinks() throws Exception {
 666         Content li = HtmlTree.LI(detailLabel);
 667         li.addContent(getSpace());
 668         Content ulNav = HtmlTree.UL(HtmlStyle.subNavList, li);
 669         MemberSummaryBuilder memberSummaryBuilder = (MemberSummaryBuilder)
 670                 configuration.getBuilderFactory().getMemberSummaryBuilder(this);
 671         String[] navLinkLabels =  new String[] {
 672             "doclet.navNested", "doclet.navEnum", "doclet.navField", "doclet.navConstructor",
 673             "doclet.navMethod"
 674         };
 675         for (int i = 1; i < navLinkLabels.length; i++ ) {
 676             Content liNav = new HtmlTree(HtmlTag.LI);
 677             AbstractMemberWriter writer =
 678                     ((AbstractMemberWriter) memberSummaryBuilder.
 679                     getMemberSummaryWriter(i));
 680             if (i == VisibleMemberMap.ENUM_CONSTANTS && ! classDoc.isEnum()) {
 681                 continue;
 682             }
 683             if (i == VisibleMemberMap.CONSTRUCTORS && classDoc.isEnum()) {
 684                 continue;
 685             }
 686             if (writer == null) {
 687                 liNav.addContent(getResource(navLinkLabels[i]));
 688             } else {
 689                 writer.addNavDetailLink(memberSummaryBuilder.members(i), liNav);
 690             }
 691             if (i < navLinkLabels.length - 1) {
 692                 addNavGap(liNav);
 693             }
 694             ulNav.addContent(liNav);
 695         }
 696         return ulNav;
 697     }
 698 
 699     /**
 700      * Add gap between navigation bar elements.
 701      *
 702      * @param liNav the content tree to which the gap will be added
 703      */
 704     protected void addNavGap(Content liNav) {
 705         liNav.addContent(getSpace());
 706         liNav.addContent("|");
 707         liNav.addContent(getSpace());
 708     }
 709 
 710     /**
 711      * Return the classDoc being documented.
 712      *
 713      * @return the classDoc being documented.
 714      */
 715     public ClassDoc getClassDoc() {
 716         return classDoc;
 717     }
 718 }