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