src/share/classes/com/sun/tools/doclets/formats/html/HtmlDocletWriter.java

Tue, 23 Oct 2012 13:58:56 -0700

author
jjg
date
Tue, 23 Oct 2012 13:58:56 -0700
changeset 1373
4a1c57a1c410
parent 1372
78962d89f283
child 1381
23fe1a96bc0f
permissions
-rw-r--r--

8000416: refactor javadoc to provide and use an abstraction for relative URIs
Reviewed-by: darcy

     1 /*
     2  * Copyright (c) 1998, 2012, 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  */
    26 package com.sun.tools.doclets.formats.html;
    28 import java.io.*;
    29 import java.text.SimpleDateFormat;
    30 import java.util.*;
    32 import com.sun.javadoc.*;
    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.taglets.*;
    36 import com.sun.tools.doclets.internal.toolkit.util.*;
    38 /**
    39  * Class for the Html Format Code Generation specific to JavaDoc.
    40  * This Class contains methods related to the Html Code Generation which
    41  * are used extensively while generating the entire documentation.
    42  *
    43  *  <p><b>This is NOT part of any supported API.
    44  *  If you write code that depends on this, you do so at your own risk.
    45  *  This code and its internal interfaces are subject to change or
    46  *  deletion without notice.</b>
    47  *
    48  * @since 1.2
    49  * @author Atul M Dambalkar
    50  * @author Robert Field
    51  * @author Bhavesh Patel (Modified)
    52  */
    53 public class HtmlDocletWriter extends HtmlDocWriter {
    55     /**
    56      * Relative path from the file getting generated to the destination
    57      * directory. For example, if the file getting generated is
    58      * "java/lang/Object.html", then the path to the root is "../..".
    59      * This string can be empty if the file getting generated is in
    60      * the destination directory.
    61      */
    62     public final DocPath pathToRoot;
    64     /**
    65      * Platform-independent path from the current or the
    66      * destination directory to the file getting generated.
    67      * Used when creating the file.
    68      */
    69     public final DocPath path;
    71     /**
    72      * Name of the file getting generated. If the file getting generated is
    73      * "java/lang/Object.html", then the filename is "Object.html".
    74      */
    75     public final DocPath filename;
    77     /**
    78      * The display length used for indentation while generating the class page.
    79      */
    80     public int displayLength = 0;
    82     /**
    83      * The global configuration information for this run.
    84      */
    85     public ConfigurationImpl configuration;
    87     /**
    88      * To check whether annotation heading is printed or not.
    89      */
    90     protected boolean printedAnnotationHeading = false;
    92     /**
    93      * Constructor to construct the HtmlStandardWriter object.
    94      *
    95      * @param path File to be generated.
    96      */
    97     public HtmlDocletWriter(ConfigurationImpl configuration, DocPath path)
    98             throws IOException {
    99         super(configuration, path);
   100         this.configuration = configuration;
   101         this.path = path;
   102         this.pathToRoot = path.parent().invert();
   103         this.filename = path.basename();
   104     }
   106     /**
   107      * Replace {&#064;docRoot} tag used in options that accept HTML text, such
   108      * as -header, -footer, -top and -bottom, and when converting a relative
   109      * HREF where commentTagsToString inserts a {&#064;docRoot} where one was
   110      * missing.  (Also see DocRootTaglet for {&#064;docRoot} tags in doc
   111      * comments.)
   112      * <p>
   113      * Replace {&#064;docRoot} tag in htmlstr with the relative path to the
   114      * destination directory from the directory where the file is being
   115      * written, looping to handle all such tags in htmlstr.
   116      * <p>
   117      * For example, for "-d docs" and -header containing {&#064;docRoot}, when
   118      * the HTML page for source file p/C1.java is being generated, the
   119      * {&#064;docRoot} tag would be inserted into the header as "../",
   120      * the relative path from docs/p/ to docs/ (the document root).
   121      * <p>
   122      * Note: This doc comment was written with '&amp;#064;' representing '@'
   123      * to prevent the inline tag from being interpreted.
   124      */
   125     public String replaceDocRootDir(String htmlstr) {
   126         // Return if no inline tags exist
   127         int index = htmlstr.indexOf("{@");
   128         if (index < 0) {
   129             return htmlstr;
   130         }
   131         String lowerHtml = htmlstr.toLowerCase();
   132         // Return index of first occurrence of {@docroot}
   133         // Note: {@docRoot} is not case sensitive when passed in w/command line option
   134         index = lowerHtml.indexOf("{@docroot}", index);
   135         if (index < 0) {
   136             return htmlstr;
   137         }
   138         StringBuilder buf = new StringBuilder();
   139         int previndex = 0;
   140         while (true) {
   141             if (configuration.docrootparent.length() > 0) {
   142                 final String docroot_parent = "{@docroot}/..";
   143                 // Search for lowercase version of {@docRoot}/..
   144                 index = lowerHtml.indexOf(docroot_parent, previndex);
   145                 // If next {@docRoot}/.. pattern not found, append rest of htmlstr and exit loop
   146                 if (index < 0) {
   147                     buf.append(htmlstr.substring(previndex));
   148                     break;
   149                 }
   150                 // If next {@docroot}/.. pattern found, append htmlstr up to start of tag
   151                 buf.append(htmlstr.substring(previndex, index));
   152                 previndex = index + docroot_parent.length();
   153                 // Insert docrootparent absolute path where {@docRoot}/.. was located
   155                 buf.append(configuration.docrootparent);
   156                 // Append slash if next character is not a slash
   157                 if (previndex < htmlstr.length() && htmlstr.charAt(previndex) != '/') {
   158                     buf.append('/');
   159                 }
   160             } else {
   161                 final String docroot = "{@docroot}";
   162                 // Search for lowercase version of {@docRoot}
   163                 index = lowerHtml.indexOf(docroot, previndex);
   164                 // If next {@docRoot} tag not found, append rest of htmlstr and exit loop
   165                 if (index < 0) {
   166                     buf.append(htmlstr.substring(previndex));
   167                     break;
   168                 }
   169                 // If next {@docroot} tag found, append htmlstr up to start of tag
   170                 buf.append(htmlstr.substring(previndex, index));
   171                 previndex = index + docroot.length();
   172                 // Insert relative path where {@docRoot} was located
   173                 buf.append(pathToRoot.isEmpty() ? "." : pathToRoot.getPath());
   174                 // Append slash if next character is not a slash
   175                 if (previndex < htmlstr.length() && htmlstr.charAt(previndex) != '/') {
   176                     buf.append('/');
   177                 }
   178             }
   179         }
   180         return buf.toString();
   181     }
   183     /**
   184      * Get the script to show or hide the All classes link.
   185      *
   186      * @param id id of the element to show or hide
   187      * @return a content tree for the script
   188      */
   189     public Content getAllClassesLinkScript(String id) {
   190         HtmlTree script = new HtmlTree(HtmlTag.SCRIPT);
   191         script.addAttr(HtmlAttr.TYPE, "text/javascript");
   192         String scriptCode = "<!--" + DocletConstants.NL +
   193                 "  allClassesLink = document.getElementById(\"" + id + "\");" + DocletConstants.NL +
   194                 "  if(window==top) {" + DocletConstants.NL +
   195                 "    allClassesLink.style.display = \"block\";" + DocletConstants.NL +
   196                 "  }" + DocletConstants.NL +
   197                 "  else {" + DocletConstants.NL +
   198                 "    allClassesLink.style.display = \"none\";" + DocletConstants.NL +
   199                 "  }" + DocletConstants.NL +
   200                 "  //-->" + DocletConstants.NL;
   201         Content scriptContent = new RawHtml(scriptCode);
   202         script.addContent(scriptContent);
   203         Content div = HtmlTree.DIV(script);
   204         return div;
   205     }
   207     /**
   208      * Add method information.
   209      *
   210      * @param method the method to be documented
   211      * @param dl the content tree to which the method information will be added
   212      */
   213     private void addMethodInfo(MethodDoc method, Content dl) {
   214         ClassDoc[] intfacs = method.containingClass().interfaces();
   215         MethodDoc overriddenMethod = method.overriddenMethod();
   216         // Check whether there is any implementation or overridden info to be
   217         // printed. If no overridden or implementation info needs to be
   218         // printed, do not print this section.
   219         if ((intfacs.length > 0 &&
   220                 new ImplementedMethods(method, this.configuration).build().length > 0) ||
   221                 overriddenMethod != null) {
   222             MethodWriterImpl.addImplementsInfo(this, method, dl);
   223             if (overriddenMethod != null) {
   224                 MethodWriterImpl.addOverridden(this,
   225                         method.overriddenType(), overriddenMethod, dl);
   226             }
   227         }
   228     }
   230     /**
   231      * Adds the tags information.
   232      *
   233      * @param doc the doc for which the tags will be generated
   234      * @param htmltree the documentation tree to which the tags will be added
   235      */
   236     protected void addTagsInfo(Doc doc, Content htmltree) {
   237         if (configuration.nocomment) {
   238             return;
   239         }
   240         Content dl = new HtmlTree(HtmlTag.DL);
   241         if (doc instanceof MethodDoc) {
   242             addMethodInfo((MethodDoc) doc, dl);
   243         }
   244         TagletOutputImpl output = new TagletOutputImpl("");
   245         TagletWriter.genTagOuput(configuration.tagletManager, doc,
   246             configuration.tagletManager.getCustomTags(doc),
   247                 getTagletWriterInstance(false), output);
   248         String outputString = output.toString().trim();
   249         if (!outputString.isEmpty()) {
   250             Content resultString = new RawHtml(outputString);
   251             dl.addContent(resultString);
   252         }
   253         htmltree.addContent(dl);
   254     }
   256     /**
   257      * Check whether there are any tags for Serialization Overview
   258      * section to be printed.
   259      *
   260      * @param field the FieldDoc object to check for tags.
   261      * @return true if there are tags to be printed else return false.
   262      */
   263     protected boolean hasSerializationOverviewTags(FieldDoc field) {
   264         TagletOutputImpl output = new TagletOutputImpl("");
   265         TagletWriter.genTagOuput(configuration.tagletManager, field,
   266             configuration.tagletManager.getCustomTags(field),
   267                 getTagletWriterInstance(false), output);
   268         return (!output.toString().trim().isEmpty());
   269     }
   271     /**
   272      * Returns a TagletWriter that knows how to write HTML.
   273      *
   274      * @return a TagletWriter that knows how to write HTML.
   275      */
   276     public TagletWriter getTagletWriterInstance(boolean isFirstSentence) {
   277         return new TagletWriterImpl(this, isFirstSentence);
   278     }
   280     /**
   281      * Get Package link, with target frame.
   282      *
   283      * @param pd The link will be to the "package-summary.html" page for this package
   284      * @param target name of the target frame
   285      * @param label tag for the link
   286      * @return a content for the target package link
   287      */
   288     public Content getTargetPackageLink(PackageDoc pd, String target,
   289             Content label) {
   290         return getHyperLink(pathString(pd, DocPaths.PACKAGE_SUMMARY), label, "", target);
   291     }
   293     /**
   294      * Generates the HTML document tree and prints it out.
   295      *
   296      * @param metakeywords Array of String keywords for META tag. Each element
   297      *                     of the array is assigned to a separate META tag.
   298      *                     Pass in null for no array
   299      * @param includeScript true if printing windowtitle script
   300      *                      false for files that appear in the left-hand frames
   301      * @param body the body htmltree to be included in the document
   302      */
   303     public void printHtmlDocument(String[] metakeywords, boolean includeScript,
   304             Content body) throws IOException {
   305         Content htmlDocType = DocType.Transitional();
   306         Content htmlComment = new Comment(configuration.getText("doclet.New_Page"));
   307         Content head = new HtmlTree(HtmlTag.HEAD);
   308         if (!configuration.notimestamp) {
   309             Content headComment = new Comment(getGeneratedByString());
   310             head.addContent(headComment);
   311         }
   312         if (configuration.charset.length() > 0) {
   313             Content meta = HtmlTree.META("Content-Type", "text/html",
   314                     configuration.charset);
   315             head.addContent(meta);
   316         }
   317         head.addContent(getTitle());
   318         if (!configuration.notimestamp) {
   319             SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
   320             Content meta = HtmlTree.META("date", dateFormat.format(new Date()));
   321             head.addContent(meta);
   322         }
   323         if (metakeywords != null) {
   324             for (int i=0; i < metakeywords.length; i++) {
   325                 Content meta = HtmlTree.META("keywords", metakeywords[i]);
   326                 head.addContent(meta);
   327             }
   328         }
   329         head.addContent(getStyleSheetProperties());
   330         Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(),
   331                 head, body);
   332         Content htmlDocument = new HtmlDocument(htmlDocType,
   333                 htmlComment, htmlTree);
   334         write(htmlDocument);
   335     }
   337     /**
   338      * Get the window title.
   339      *
   340      * @param title the title string to construct the complete window title
   341      * @return the window title string
   342      */
   343     public String getWindowTitle(String title) {
   344         if (configuration.windowtitle.length() > 0) {
   345             title += " (" + configuration.windowtitle  + ")";
   346         }
   347         return title;
   348     }
   350     /**
   351      * Get user specified header and the footer.
   352      *
   353      * @param header if true print the user provided header else print the
   354      * user provided footer.
   355      */
   356     public Content getUserHeaderFooter(boolean header) {
   357         String content;
   358         if (header) {
   359             content = replaceDocRootDir(configuration.header);
   360         } else {
   361             if (configuration.footer.length() != 0) {
   362                 content = replaceDocRootDir(configuration.footer);
   363             } else {
   364                 content = replaceDocRootDir(configuration.header);
   365             }
   366         }
   367         Content rawContent = new RawHtml(content);
   368         Content em = HtmlTree.EM(rawContent);
   369         return em;
   370     }
   372     /**
   373      * Adds the user specified top.
   374      *
   375      * @param body the content tree to which user specified top will be added
   376      */
   377     public void addTop(Content body) {
   378         Content top = new RawHtml(replaceDocRootDir(configuration.top));
   379         body.addContent(top);
   380     }
   382     /**
   383      * Adds the user specified bottom.
   384      *
   385      * @param body the content tree to which user specified bottom will be added
   386      */
   387     public void addBottom(Content body) {
   388         Content bottom = new RawHtml(replaceDocRootDir(configuration.bottom));
   389         Content small = HtmlTree.SMALL(bottom);
   390         Content p = HtmlTree.P(HtmlStyle.legalCopy, small);
   391         body.addContent(p);
   392     }
   394     /**
   395      * Adds the navigation bar for the Html page at the top and and the bottom.
   396      *
   397      * @param header If true print navigation bar at the top of the page else
   398      * @param body the HtmlTree to which the nav links will be added
   399      */
   400     protected void addNavLinks(boolean header, Content body) {
   401         if (!configuration.nonavbar) {
   402             String allClassesId = "allclasses_";
   403             HtmlTree navDiv = new HtmlTree(HtmlTag.DIV);
   404             if (header) {
   405                 body.addContent(HtmlConstants.START_OF_TOP_NAVBAR);
   406                 navDiv.addStyle(HtmlStyle.topNav);
   407                 allClassesId += "navbar_top";
   408                 Content a = getMarkerAnchor("navbar_top");
   409                 navDiv.addContent(a);
   410                 Content skipLinkContent = getHyperLink(DocLink.fragment("skip-navbar_top"),
   411                         HtmlTree.EMPTY,
   412                         configuration.getText("doclet.Skip_navigation_links"),
   413                         "");
   414                 navDiv.addContent(skipLinkContent);
   415             } else {
   416                 body.addContent(HtmlConstants.START_OF_BOTTOM_NAVBAR);
   417                 navDiv.addStyle(HtmlStyle.bottomNav);
   418                 allClassesId += "navbar_bottom";
   419                 Content a = getMarkerAnchor("navbar_bottom");
   420                 navDiv.addContent(a);
   421                 Content skipLinkContent = getHyperLink(DocLink.fragment("skip-navbar_bottom"),
   422                         HtmlTree.EMPTY,
   423                         configuration.getText("doclet.Skip_navigation_links"),
   424                         "");
   425                 navDiv.addContent(skipLinkContent);
   426             }
   427             if (header) {
   428                 navDiv.addContent(getMarkerAnchor("navbar_top_firstrow"));
   429             } else {
   430                 navDiv.addContent(getMarkerAnchor("navbar_bottom_firstrow"));
   431             }
   432             HtmlTree navList = new HtmlTree(HtmlTag.UL);
   433             navList.addStyle(HtmlStyle.navList);
   434             navList.addAttr(HtmlAttr.TITLE, "Navigation");
   435             if (configuration.createoverview) {
   436                 navList.addContent(getNavLinkContents());
   437             }
   438             if (configuration.packages.length == 1) {
   439                 navList.addContent(getNavLinkPackage(configuration.packages[0]));
   440             } else if (configuration.packages.length > 1) {
   441                 navList.addContent(getNavLinkPackage());
   442             }
   443             navList.addContent(getNavLinkClass());
   444             if(configuration.classuse) {
   445                 navList.addContent(getNavLinkClassUse());
   446             }
   447             if(configuration.createtree) {
   448                 navList.addContent(getNavLinkTree());
   449             }
   450             if(!(configuration.nodeprecated ||
   451                      configuration.nodeprecatedlist)) {
   452                 navList.addContent(getNavLinkDeprecated());
   453             }
   454             if(configuration.createindex) {
   455                 navList.addContent(getNavLinkIndex());
   456             }
   457             if (!configuration.nohelp) {
   458                 navList.addContent(getNavLinkHelp());
   459             }
   460             navDiv.addContent(navList);
   461             Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, getUserHeaderFooter(header));
   462             navDiv.addContent(aboutDiv);
   463             body.addContent(navDiv);
   464             Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious());
   465             ulNav.addContent(getNavLinkNext());
   466             Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav);
   467             Content ulFrames = HtmlTree.UL(HtmlStyle.navList, getNavShowLists());
   468             ulFrames.addContent(getNavHideLists(filename));
   469             subDiv.addContent(ulFrames);
   470             HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex());
   471             ulAllClasses.addAttr(HtmlAttr.ID, allClassesId.toString());
   472             subDiv.addContent(ulAllClasses);
   473             subDiv.addContent(getAllClassesLinkScript(allClassesId.toString()));
   474             addSummaryDetailLinks(subDiv);
   475             if (header) {
   476                 subDiv.addContent(getMarkerAnchor("skip-navbar_top"));
   477                 body.addContent(subDiv);
   478                 body.addContent(HtmlConstants.END_OF_TOP_NAVBAR);
   479             } else {
   480                 subDiv.addContent(getMarkerAnchor("skip-navbar_bottom"));
   481                 body.addContent(subDiv);
   482                 body.addContent(HtmlConstants.END_OF_BOTTOM_NAVBAR);
   483             }
   484         }
   485     }
   487     /**
   488      * Get the word "NEXT" to indicate that no link is available.  Override
   489      * this method to customize next link.
   490      *
   491      * @return a content tree for the link
   492      */
   493     protected Content getNavLinkNext() {
   494         return getNavLinkNext(null);
   495     }
   497     /**
   498      * Get the word "PREV" to indicate that no link is available.  Override
   499      * this method to customize prev link.
   500      *
   501      * @return a content tree for the link
   502      */
   503     protected Content getNavLinkPrevious() {
   504         return getNavLinkPrevious(null);
   505     }
   507     /**
   508      * Do nothing. This is the default method.
   509      */
   510     protected void addSummaryDetailLinks(Content navDiv) {
   511     }
   513     /**
   514      * Get link to the "overview-summary.html" page.
   515      *
   516      * @return a content tree for the link
   517      */
   518     protected Content getNavLinkContents() {
   519         Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_SUMMARY),
   520                 overviewLabel, "", "");
   521         Content li = HtmlTree.LI(linkContent);
   522         return li;
   523     }
   525     /**
   526      * Get link to the "package-summary.html" page for the package passed.
   527      *
   528      * @param pkg Package to which link will be generated
   529      * @return a content tree for the link
   530      */
   531     protected Content getNavLinkPackage(PackageDoc pkg) {
   532         Content linkContent = getPackageLink(pkg,
   533                 packageLabel);
   534         Content li = HtmlTree.LI(linkContent);
   535         return li;
   536     }
   538     /**
   539      * Get the word "Package" , to indicate that link is not available here.
   540      *
   541      * @return a content tree for the link
   542      */
   543     protected Content getNavLinkPackage() {
   544         Content li = HtmlTree.LI(packageLabel);
   545         return li;
   546     }
   548     /**
   549      * Get the word "Use", to indicate that link is not available.
   550      *
   551      * @return a content tree for the link
   552      */
   553     protected Content getNavLinkClassUse() {
   554         Content li = HtmlTree.LI(useLabel);
   555         return li;
   556     }
   558     /**
   559      * Get link for previous file.
   560      *
   561      * @param prev File name for the prev link
   562      * @return a content tree for the link
   563      */
   564     public Content getNavLinkPrevious(DocPath prev) {
   565         Content li;
   566         if (prev != null) {
   567             li = HtmlTree.LI(getHyperLink(prev, prevLabel, "", ""));
   568         }
   569         else
   570             li = HtmlTree.LI(prevLabel);
   571         return li;
   572     }
   574     /**
   575      * Get link for next file.  If next is null, just print the label
   576      * without linking it anywhere.
   577      *
   578      * @param next File name for the next link
   579      * @return a content tree for the link
   580      */
   581     public Content getNavLinkNext(DocPath next) {
   582         Content li;
   583         if (next != null) {
   584             li = HtmlTree.LI(getHyperLink(next, nextLabel, "", ""));
   585         }
   586         else
   587             li = HtmlTree.LI(nextLabel);
   588         return li;
   589     }
   591     /**
   592      * Get "FRAMES" link, to switch to the frame version of the output.
   593      *
   594      * @param link File to be linked, "index.html"
   595      * @return a content tree for the link
   596      */
   597     protected Content getNavShowLists(DocPath link) {
   598         DocLink dl = new DocLink(link, path.getPath(), null);
   599         Content framesContent = getHyperLink(dl, framesLabel, "", "_top");
   600         Content li = HtmlTree.LI(framesContent);
   601         return li;
   602     }
   604     /**
   605      * Get "FRAMES" link, to switch to the frame version of the output.
   606      *
   607      * @return a content tree for the link
   608      */
   609     protected Content getNavShowLists() {
   610         return getNavShowLists(pathToRoot.resolve(DocPaths.INDEX));
   611     }
   613     /**
   614      * Get "NO FRAMES" link, to switch to the non-frame version of the output.
   615      *
   616      * @param link File to be linked
   617      * @return a content tree for the link
   618      */
   619     protected Content getNavHideLists(DocPath link) {
   620         Content noFramesContent = getHyperLink(link, noframesLabel, "", "_top");
   621         Content li = HtmlTree.LI(noFramesContent);
   622         return li;
   623     }
   625     /**
   626      * Get "Tree" link in the navigation bar. If there is only one package
   627      * specified on the command line, then the "Tree" link will be to the
   628      * only "package-tree.html" file otherwise it will be to the
   629      * "overview-tree.html" file.
   630      *
   631      * @return a content tree for the link
   632      */
   633     protected Content getNavLinkTree() {
   634         Content treeLinkContent;
   635         PackageDoc[] packages = configuration.root.specifiedPackages();
   636         if (packages.length == 1 && configuration.root.specifiedClasses().length == 0) {
   637             treeLinkContent = getHyperLink(pathString(packages[0],
   638                     DocPaths.PACKAGE_TREE), treeLabel,
   639                     "", "");
   640         } else {
   641             treeLinkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE),
   642                     treeLabel, "", "");
   643         }
   644         Content li = HtmlTree.LI(treeLinkContent);
   645         return li;
   646     }
   648     /**
   649      * Get the overview tree link for the main tree.
   650      *
   651      * @param label the label for the link
   652      * @return a content tree for the link
   653      */
   654     protected Content getNavLinkMainTree(String label) {
   655         Content mainTreeContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE),
   656                 new StringContent(label));
   657         Content li = HtmlTree.LI(mainTreeContent);
   658         return li;
   659     }
   661     /**
   662      * Get the word "Class", to indicate that class link is not available.
   663      *
   664      * @return a content tree for the link
   665      */
   666     protected Content getNavLinkClass() {
   667         Content li = HtmlTree.LI(classLabel);
   668         return li;
   669     }
   671     /**
   672      * Get "Deprecated" API link in the navigation bar.
   673      *
   674      * @return a content tree for the link
   675      */
   676     protected Content getNavLinkDeprecated() {
   677         Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.DEPRECATED_LIST),
   678                 deprecatedLabel, "", "");
   679         Content li = HtmlTree.LI(linkContent);
   680         return li;
   681     }
   683     /**
   684      * Get link for generated index. If the user has used "-splitindex"
   685      * command line option, then link to file "index-files/index-1.html" is
   686      * generated otherwise link to file "index-all.html" is generated.
   687      *
   688      * @return a content tree for the link
   689      */
   690     protected Content getNavLinkClassIndex() {
   691         Content allClassesContent = getHyperLink(pathToRoot.resolve(
   692                 DocPaths.ALLCLASSES_NOFRAME),
   693                 allclassesLabel, "", "");
   694         Content li = HtmlTree.LI(allClassesContent);
   695         return li;
   696     }
   698     /**
   699      * Get link for generated class index.
   700      *
   701      * @return a content tree for the link
   702      */
   703     protected Content getNavLinkIndex() {
   704         Content linkContent = getHyperLink(pathToRoot.resolve(
   705                 (configuration.splitindex
   706                     ? DocPaths.INDEX_FILES.resolve(DocPaths.indexN(1))
   707                     : DocPaths.INDEX_ALL)),
   708             indexLabel, "", "");
   709         Content li = HtmlTree.LI(linkContent);
   710         return li;
   711     }
   713     /**
   714      * Get help file link. If user has provided a help file, then generate a
   715      * link to the user given file, which is already copied to current or
   716      * destination directory.
   717      *
   718      * @return a content tree for the link
   719      */
   720     protected Content getNavLinkHelp() {
   721         String helpfile = configuration.helpfile;
   722         DocPath helpfilenm;
   723         if (helpfile.isEmpty()) {
   724             helpfilenm = DocPaths.HELP_DOC;
   725         } else {
   726             helpfilenm = DocPath.create(new File(helpfile).getName());
   727         }
   728         Content linkContent = getHyperLink(pathToRoot.resolve(helpfilenm),
   729                 helpLabel, "", "");
   730         Content li = HtmlTree.LI(linkContent);
   731         return li;
   732     }
   734     /**
   735      * Get summary table header.
   736      *
   737      * @param header the header for the table
   738      * @param scope the scope of the headers
   739      * @return a content tree for the header
   740      */
   741     public Content getSummaryTableHeader(String[] header, String scope) {
   742         Content tr = new HtmlTree(HtmlTag.TR);
   743         int size = header.length;
   744         Content tableHeader;
   745         if (size == 1) {
   746             tableHeader = new StringContent(header[0]);
   747             tr.addContent(HtmlTree.TH(HtmlStyle.colOne, scope, tableHeader));
   748             return tr;
   749         }
   750         for (int i = 0; i < size; i++) {
   751             tableHeader = new StringContent(header[i]);
   752             if(i == 0)
   753                 tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader));
   754             else if(i == (size - 1))
   755                 tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader));
   756             else
   757                 tr.addContent(HtmlTree.TH(scope, tableHeader));
   758         }
   759         return tr;
   760     }
   762     /**
   763      * Get table caption.
   764      *
   765      * @param rawText the caption for the table which could be raw Html
   766      * @return a content tree for the caption
   767      */
   768     public Content getTableCaption(String rawText) {
   769         Content title = new RawHtml(rawText);
   770         Content captionSpan = HtmlTree.SPAN(title);
   771         Content space = getSpace();
   772         Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space);
   773         Content caption = HtmlTree.CAPTION(captionSpan);
   774         caption.addContent(tabSpan);
   775         return caption;
   776     }
   778     /**
   779      * Get the marker anchor which will be added to the documentation tree.
   780      *
   781      * @param anchorName the anchor name attribute
   782      * @return a content tree for the marker anchor
   783      */
   784     public Content getMarkerAnchor(String anchorName) {
   785         return getMarkerAnchor(anchorName, null);
   786     }
   788     /**
   789      * Get the marker anchor which will be added to the documentation tree.
   790      *
   791      * @param anchorName the anchor name attribute
   792      * @param anchorContent the content that should be added to the anchor
   793      * @return a content tree for the marker anchor
   794      */
   795     public Content getMarkerAnchor(String anchorName, Content anchorContent) {
   796         if (anchorContent == null)
   797             anchorContent = new Comment(" ");
   798         Content markerAnchor = HtmlTree.A_NAME(anchorName, anchorContent);
   799         return markerAnchor;
   800     }
   802     /**
   803      * Returns a packagename content.
   804      *
   805      * @param packageDoc the package to check
   806      * @return package name content
   807      */
   808     public Content getPackageName(PackageDoc packageDoc) {
   809         return packageDoc == null || packageDoc.name().length() == 0 ?
   810             defaultPackageLabel :
   811             getPackageLabel(packageDoc.name());
   812     }
   814     /**
   815      * Returns a package name label.
   816      *
   817      * @param packageName the package name
   818      * @return the package name content
   819      */
   820     public Content getPackageLabel(String packageName) {
   821         return new StringContent(packageName);
   822     }
   824     /**
   825      * Add package deprecation information to the documentation tree
   826      *
   827      * @param deprPkgs list of deprecated packages
   828      * @param headingKey the caption for the deprecated package table
   829      * @param tableSummary the summary for the deprecated package table
   830      * @param tableHeader table headers for the deprecated package table
   831      * @param contentTree the content tree to which the deprecated package table will be added
   832      */
   833     protected void addPackageDeprecatedAPI(List<Doc> deprPkgs, String headingKey,
   834             String tableSummary, String[] tableHeader, Content contentTree) {
   835         if (deprPkgs.size() > 0) {
   836             Content table = HtmlTree.TABLE(0, 3, 0, tableSummary,
   837                     getTableCaption(configuration().getText(headingKey)));
   838             table.addContent(getSummaryTableHeader(tableHeader, "col"));
   839             Content tbody = new HtmlTree(HtmlTag.TBODY);
   840             for (int i = 0; i < deprPkgs.size(); i++) {
   841                 PackageDoc pkg = (PackageDoc) deprPkgs.get(i);
   842                 HtmlTree td = HtmlTree.TD(HtmlStyle.colOne,
   843                         getPackageLink(pkg, getPackageName(pkg)));
   844                 if (pkg.tags("deprecated").length > 0) {
   845                     addInlineDeprecatedComment(pkg, pkg.tags("deprecated")[0], td);
   846                 }
   847                 HtmlTree tr = HtmlTree.TR(td);
   848                 if (i % 2 == 0) {
   849                     tr.addStyle(HtmlStyle.altColor);
   850                 } else {
   851                     tr.addStyle(HtmlStyle.rowColor);
   852                 }
   853                 tbody.addContent(tr);
   854             }
   855             table.addContent(tbody);
   856             Content li = HtmlTree.LI(HtmlStyle.blockList, table);
   857             Content ul = HtmlTree.UL(HtmlStyle.blockList, li);
   858             contentTree.addContent(ul);
   859         }
   860     }
   862     /**
   863      * Return the path to the class page for a classdoc. Works same as
   864      * {@link #pathToClass(ClassDoc)}.
   865      *
   866      * @param cd   Class to which the path is requested.
   867      * @param name Name of the file(doesn't include path).
   868      */
   869     protected DocPath pathString(ClassDoc cd, DocPath name) {
   870         return pathString(cd.containingPackage(), name);
   871     }
   873     /**
   874      * Return path to the given file name in the given package. So if the name
   875      * passed is "Object.html" and the name of the package is "java.lang", and
   876      * if the relative path is "../.." then returned string will be
   877      * "../../java/lang/Object.html"
   878      *
   879      * @param pd Package in which the file name is assumed to be.
   880      * @param name File name, to which path string is.
   881      */
   882     protected DocPath pathString(PackageDoc pd, DocPath name) {
   883         return pathToRoot.resolve(DocPath.forPackage(pd).resolve(name));
   884     }
   886     /**
   887      * Return the link to the given package.
   888      *
   889      * @param pkg the package to link to.
   890      * @param label the label for the link.
   891      * @param isStrong true if the label should be strong.
   892      * @return the link to the given package.
   893      */
   894     public String getPackageLinkString(PackageDoc pkg, String label,
   895                                  boolean isStrong) {
   896         return getPackageLinkString(pkg, label, isStrong, "");
   897     }
   899     /**
   900      * Return the link to the given package.
   901      *
   902      * @param pkg the package to link to.
   903      * @param label the label for the link.
   904      * @param isStrong true if the label should be strong.
   905      * @param style  the font of the package link label.
   906      * @return the link to the given package.
   907      */
   908     public String getPackageLinkString(PackageDoc pkg, String label, boolean isStrong,
   909             String style) {
   910         boolean included = pkg != null && pkg.isIncluded();
   911         if (! included) {
   912             PackageDoc[] packages = configuration.packages;
   913             for (int i = 0; i < packages.length; i++) {
   914                 if (packages[i].equals(pkg)) {
   915                     included = true;
   916                     break;
   917                 }
   918             }
   919         }
   920         if (included || pkg == null) {
   921             return getHyperLinkString(pathString(pkg, DocPaths.PACKAGE_SUMMARY),
   922                                 label, isStrong, style);
   923         } else {
   924             DocLink crossPkgLink = getCrossPackageLink(Util.getPackageName(pkg));
   925             if (crossPkgLink != null) {
   926                 return getHyperLinkString(crossPkgLink, label, isStrong, style);
   927             } else {
   928                 return label;
   929             }
   930         }
   931     }
   933     /**
   934      * Return the link to the given package.
   935      *
   936      * @param pkg the package to link to.
   937      * @param label the label for the link.
   938      * @return a content tree for the package link.
   939      */
   940     public Content getPackageLink(PackageDoc pkg, Content label) {
   941         boolean included = pkg != null && pkg.isIncluded();
   942         if (! included) {
   943             PackageDoc[] packages = configuration.packages;
   944             for (int i = 0; i < packages.length; i++) {
   945                 if (packages[i].equals(pkg)) {
   946                     included = true;
   947                     break;
   948                 }
   949             }
   950         }
   951         if (included || pkg == null) {
   952             return getHyperLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY),
   953                     label);
   954         } else {
   955             DocLink crossPkgLink = getCrossPackageLink(Util.getPackageName(pkg));
   956             if (crossPkgLink != null) {
   957                 return getHyperLink(crossPkgLink, label);
   958             } else {
   959                 return label;
   960             }
   961         }
   962     }
   964     public String italicsClassName(ClassDoc cd, boolean qual) {
   965         String name = (qual)? cd.qualifiedName(): cd.name();
   966         return (cd.isInterface())?  italicsText(name): name;
   967     }
   969     /**
   970      * Add the link to the content tree.
   971      *
   972      * @param doc program element doc for which the link will be added
   973      * @param label label for the link
   974      * @param htmltree the content tree to which the link will be added
   975      */
   976     public void addSrcLink(ProgramElementDoc doc, Content label, Content htmltree) {
   977         if (doc == null) {
   978             return;
   979         }
   980         ClassDoc cd = doc.containingClass();
   981         if (cd == null) {
   982             //d must be a class doc since in has no containing class.
   983             cd = (ClassDoc) doc;
   984         }
   985         DocPath href = pathToRoot
   986                 .resolve(DocPaths.SOURCE_OUTPUT)
   987                 .resolve(DocPath.forClass(cd));
   988         Content linkContent = getHyperLink(href.fragment(SourceToHTMLConverter.getAnchorName(doc)), label, "", "");
   989         htmltree.addContent(linkContent);
   990     }
   992     /**
   993      * Return the link to the given class.
   994      *
   995      * @param linkInfo the information about the link.
   996      *
   997      * @return the link for the given class.
   998      */
   999     public String getLink(LinkInfoImpl linkInfo) {
  1000         LinkFactoryImpl factory = new LinkFactoryImpl(this);
  1001         String link = factory.getLinkOutput(linkInfo).toString();
  1002         displayLength += linkInfo.displayLength;
  1003         return link;
  1006     /**
  1007      * Return the type parameters for the given class.
  1009      * @param linkInfo the information about the link.
  1010      * @return the type for the given class.
  1011      */
  1012     public String getTypeParameterLinks(LinkInfoImpl linkInfo) {
  1013         LinkFactoryImpl factory = new LinkFactoryImpl(this);
  1014         return factory.getTypeParameterLinks(linkInfo, false).toString();
  1017     /*************************************************************
  1018      * Return a class cross link to external class documentation.
  1019      * The name must be fully qualified to determine which package
  1020      * the class is in.  The -link option does not allow users to
  1021      * link to external classes in the "default" package.
  1023      * @param qualifiedClassName the qualified name of the external class.
  1024      * @param refMemName the name of the member being referenced.  This should
  1025      * be null or empty string if no member is being referenced.
  1026      * @param label the label for the external link.
  1027      * @param strong true if the link should be strong.
  1028      * @param style the style of the link.
  1029      * @param code true if the label should be code font.
  1030      */
  1031     public String getCrossClassLink(String qualifiedClassName, String refMemName,
  1032                                     String label, boolean strong, String style,
  1033                                     boolean code) {
  1034         String className = "";
  1035         String packageName = qualifiedClassName == null ? "" : qualifiedClassName;
  1036         int periodIndex;
  1037         while ((periodIndex = packageName.lastIndexOf('.')) != -1) {
  1038             className = packageName.substring(periodIndex + 1, packageName.length()) +
  1039                 (className.length() > 0 ? "." + className : "");
  1040             String defaultLabel = code ? codeText(className) : className;
  1041             packageName = packageName.substring(0, periodIndex);
  1042             if (getCrossPackageLink(packageName) != null) {
  1043                 //The package exists in external documentation, so link to the external
  1044                 //class (assuming that it exists).  This is definitely a limitation of
  1045                 //the -link option.  There are ways to determine if an external package
  1046                 //exists, but no way to determine if the external class exists.  We just
  1047                 //have to assume that it does.
  1048                 DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot,
  1049                                 className + ".html", refMemName);
  1050                 return getHyperLinkString(link,
  1051                     (label == null) || label.length() == 0 ? defaultLabel : label,
  1054                     strong, style,
  1055                     configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName),
  1056                     "");
  1059         return null;
  1062     public boolean isClassLinkable(ClassDoc cd) {
  1063         if (cd.isIncluded()) {
  1064             return configuration.isGeneratedDoc(cd);
  1066         return configuration.extern.isExternal(cd);
  1069     public DocLink getCrossPackageLink(String pkgName) {
  1070         return configuration.extern.getExternalLink(pkgName, pathToRoot,
  1071             DocPaths.PACKAGE_SUMMARY.getPath());
  1074     /**
  1075      * Get the class link.
  1077      * @param context the id of the context where the link will be added
  1078      * @param cd the class doc to link to
  1079      * @return a content tree for the link
  1080      */
  1081     public Content getQualifiedClassLink(int context, ClassDoc cd) {
  1082         return new RawHtml(getLink(new LinkInfoImpl(context, cd,
  1083                 configuration.getClassName(cd), "")));
  1086     /**
  1087      * Add the class link.
  1089      * @param context the id of the context where the link will be added
  1090      * @param cd the class doc to link to
  1091      * @param contentTree the content tree to which the link will be added
  1092      */
  1093     public void addPreQualifiedClassLink(int context, ClassDoc cd, Content contentTree) {
  1094         addPreQualifiedClassLink(context, cd, false, contentTree);
  1097     /**
  1098      * Retrieve the class link with the package portion of the label in
  1099      * plain text.  If the qualifier is excluded, it will not be included in the
  1100      * link label.
  1102      * @param cd the class to link to.
  1103      * @param isStrong true if the link should be strong.
  1104      * @return the link with the package portion of the label in plain text.
  1105      */
  1106     public String getPreQualifiedClassLink(int context,
  1107             ClassDoc cd, boolean isStrong) {
  1108         String classlink = "";
  1109         PackageDoc pd = cd.containingPackage();
  1110         if(pd != null && ! configuration.shouldExcludeQualifier(pd.name())) {
  1111             classlink = getPkgName(cd);
  1113         classlink += getLink(new LinkInfoImpl(context, cd, cd.name(), isStrong));
  1114         return classlink;
  1117     /**
  1118      * Add the class link with the package portion of the label in
  1119      * plain text. If the qualifier is excluded, it will not be included in the
  1120      * link label.
  1122      * @param context the id of the context where the link will be added
  1123      * @param cd the class to link to
  1124      * @param isStrong true if the link should be strong
  1125      * @param contentTree the content tree to which the link with be added
  1126      */
  1127     public void addPreQualifiedClassLink(int context,
  1128             ClassDoc cd, boolean isStrong, Content contentTree) {
  1129         PackageDoc pd = cd.containingPackage();
  1130         if(pd != null && ! configuration.shouldExcludeQualifier(pd.name())) {
  1131             contentTree.addContent(getPkgName(cd));
  1133         contentTree.addContent(new RawHtml(getLink(new LinkInfoImpl(
  1134                 context, cd, cd.name(), isStrong))));
  1137     /**
  1138      * Add the class link, with only class name as the strong link and prefixing
  1139      * plain package name.
  1141      * @param context the id of the context where the link will be added
  1142      * @param cd the class to link to
  1143      * @param contentTree the content tree to which the link with be added
  1144      */
  1145     public void addPreQualifiedStrongClassLink(int context, ClassDoc cd, Content contentTree) {
  1146         addPreQualifiedClassLink(context, cd, true, contentTree);
  1149     /**
  1150      * Get the link for the given member.
  1152      * @param context the id of the context where the link will be added
  1153      * @param doc the member being linked to
  1154      * @param label the label for the link
  1155      * @return a content tree for the doc link
  1156      */
  1157     public Content getDocLink(int context, MemberDoc doc, String label) {
  1158         return getDocLink(context, doc.containingClass(), doc, label);
  1161     /**
  1162      * Return the link for the given member.
  1164      * @param context the id of the context where the link will be printed.
  1165      * @param doc the member being linked to.
  1166      * @param label the label for the link.
  1167      * @param strong true if the link should be strong.
  1168      * @return the link for the given member.
  1169      */
  1170     public String getDocLink(int context, MemberDoc doc, String label,
  1171                 boolean strong) {
  1172         return getDocLink(context, doc.containingClass(), doc, label, strong);
  1175     /**
  1176      * Return the link for the given member.
  1178      * @param context the id of the context where the link will be printed.
  1179      * @param classDoc the classDoc that we should link to.  This is not
  1180      *                 necessarily equal to doc.containingClass().  We may be
  1181      *                 inheriting comments.
  1182      * @param doc the member being linked to.
  1183      * @param label the label for the link.
  1184      * @param strong true if the link should be strong.
  1185      * @return the link for the given member.
  1186      */
  1187     public String getDocLink(int context, ClassDoc classDoc, MemberDoc doc,
  1188         String label, boolean strong) {
  1189         if (! (doc.isIncluded() ||
  1190             Util.isLinkable(classDoc, configuration()))) {
  1191             return label;
  1192         } else if (doc instanceof ExecutableMemberDoc) {
  1193             ExecutableMemberDoc emd = (ExecutableMemberDoc)doc;
  1194             return getLink(new LinkInfoImpl(context, classDoc,
  1195                 getAnchor(emd), label, strong));
  1196         } else if (doc instanceof MemberDoc) {
  1197             return getLink(new LinkInfoImpl(context, classDoc,
  1198                 doc.name(), label, strong));
  1199         } else {
  1200             return label;
  1204     /**
  1205      * Return the link for the given member.
  1207      * @param context the id of the context where the link will be added
  1208      * @param classDoc the classDoc that we should link to.  This is not
  1209      *                 necessarily equal to doc.containingClass().  We may be
  1210      *                 inheriting comments
  1211      * @param doc the member being linked to
  1212      * @param label the label for the link
  1213      * @return the link for the given member
  1214      */
  1215     public Content getDocLink(int context, ClassDoc classDoc, MemberDoc doc,
  1216         String label) {
  1217         if (! (doc.isIncluded() ||
  1218             Util.isLinkable(classDoc, configuration()))) {
  1219             return new StringContent(label);
  1220         } else if (doc instanceof ExecutableMemberDoc) {
  1221             ExecutableMemberDoc emd = (ExecutableMemberDoc)doc;
  1222             return new RawHtml(getLink(new LinkInfoImpl(context, classDoc,
  1223                 getAnchor(emd), label, false)));
  1224         } else if (doc instanceof MemberDoc) {
  1225             return new RawHtml(getLink(new LinkInfoImpl(context, classDoc,
  1226                 doc.name(), label, false)));
  1227         } else {
  1228             return new StringContent(label);
  1232     public String getAnchor(ExecutableMemberDoc emd) {
  1233         StringBuilder signature = new StringBuilder(emd.signature());
  1234         StringBuilder signatureParsed = new StringBuilder();
  1235         int counter = 0;
  1236         for (int i = 0; i < signature.length(); i++) {
  1237             char c = signature.charAt(i);
  1238             if (c == '<') {
  1239                 counter++;
  1240             } else if (c == '>') {
  1241                 counter--;
  1242             } else if (counter == 0) {
  1243                 signatureParsed.append(c);
  1246         return emd.name() + signatureParsed.toString();
  1249     public String seeTagToString(SeeTag see) {
  1250         String tagName = see.name();
  1251         if (! (tagName.startsWith("@link") || tagName.equals("@see"))) {
  1252             return "";
  1255         String seetext = replaceDocRootDir(see.text());
  1257         //Check if @see is an href or "string"
  1258         if (seetext.startsWith("<") || seetext.startsWith("\"")) {
  1259             return seetext;
  1262         boolean plain = tagName.equalsIgnoreCase("@linkplain");
  1263         String label = plainOrCodeText(plain, see.label());
  1265         //The text from the @see tag.  We will output this text when a label is not specified.
  1266         String text = plainOrCodeText(plain, seetext);
  1268         ClassDoc refClass = see.referencedClass();
  1269         String refClassName = see.referencedClassName();
  1270         MemberDoc refMem = see.referencedMember();
  1271         String refMemName = see.referencedMemberName();
  1273         if (refClass == null) {
  1274             //@see is not referencing an included class
  1275             PackageDoc refPackage = see.referencedPackage();
  1276             if (refPackage != null && refPackage.isIncluded()) {
  1277                 //@see is referencing an included package
  1278                 if (label.isEmpty())
  1279                     label = plainOrCodeText(plain, refPackage.name());
  1280                 return getPackageLinkString(refPackage, label, false);
  1281             } else {
  1282                 //@see is not referencing an included class or package.  Check for cross links.
  1283                 String classCrossLink;
  1284                 DocLink packageCrossLink = getCrossPackageLink(refClassName);
  1285                 if (packageCrossLink != null) {
  1286                     //Package cross link found
  1287                     return getHyperLinkString(packageCrossLink,
  1288                         (label.isEmpty() ? text : label), false);
  1289                 } else if ((classCrossLink = getCrossClassLink(refClassName,
  1290                         refMemName, label, false, "", !plain)) != null) {
  1291                     //Class cross link found (possibly to a member in the class)
  1292                     return classCrossLink;
  1293                 } else {
  1294                     //No cross link found so print warning
  1295                     configuration.getDocletSpecificMsg().warning(see.position(), "doclet.see.class_or_package_not_found",
  1296                             tagName, seetext);
  1297                     return (label.isEmpty() ? text: label);
  1300         } else if (refMemName == null) {
  1301             // Must be a class reference since refClass is not null and refMemName is null.
  1302             if (label.isEmpty()) {
  1303                 label = plainOrCodeText(plain, refClass.name());
  1305             return getLink(new LinkInfoImpl(refClass, label));
  1306         } else if (refMem == null) {
  1307             // Must be a member reference since refClass is not null and refMemName is not null.
  1308             // However, refMem is null, so this referenced member does not exist.
  1309             return (label.isEmpty() ? text: label);
  1310         } else {
  1311             // Must be a member reference since refClass is not null and refMemName is not null.
  1312             // refMem is not null, so this @see tag must be referencing a valid member.
  1313             ClassDoc containing = refMem.containingClass();
  1314             if (see.text().trim().startsWith("#") &&
  1315                 ! (containing.isPublic() ||
  1316                 Util.isLinkable(containing, configuration()))) {
  1317                 // Since the link is relative and the holder is not even being
  1318                 // documented, this must be an inherited link.  Redirect it.
  1319                 // The current class either overrides the referenced member or
  1320                 // inherits it automatically.
  1321                 if (this instanceof ClassWriterImpl) {
  1322                     containing = ((ClassWriterImpl) this).getClassDoc();
  1323                 } else if (!containing.isPublic()){
  1324                     configuration.getDocletSpecificMsg().warning(
  1325                         see.position(), "doclet.see.class_or_package_not_accessible",
  1326                         tagName, containing.qualifiedName());
  1327                 } else {
  1328                     configuration.getDocletSpecificMsg().warning(
  1329                         see.position(), "doclet.see.class_or_package_not_found",
  1330                         tagName, seetext);
  1333             if (configuration.currentcd != containing) {
  1334                 refMemName = containing.name() + "." + refMemName;
  1336             if (refMem instanceof ExecutableMemberDoc) {
  1337                 if (refMemName.indexOf('(') < 0) {
  1338                     refMemName += ((ExecutableMemberDoc)refMem).signature();
  1342             text = plainOrCodeText(plain, Util.escapeHtmlChars(refMemName));
  1344             return getDocLink(LinkInfoImpl.CONTEXT_SEE_TAG, containing,
  1345                 refMem, (label.isEmpty() ? text: label), false);
  1349     private String plainOrCodeText(boolean plain, String text) {
  1350         return (plain || text.isEmpty()) ? text : codeText(text);
  1353     /**
  1354      * Add the inline comment.
  1356      * @param doc the doc for which the inline comment will be added
  1357      * @param tag the inline tag to be added
  1358      * @param htmltree the content tree to which the comment will be added
  1359      */
  1360     public void addInlineComment(Doc doc, Tag tag, Content htmltree) {
  1361         addCommentTags(doc, tag.inlineTags(), false, false, htmltree);
  1364     /**
  1365      * Add the inline deprecated comment.
  1367      * @param doc the doc for which the inline deprecated comment will be added
  1368      * @param tag the inline tag to be added
  1369      * @param htmltree the content tree to which the comment will be added
  1370      */
  1371     public void addInlineDeprecatedComment(Doc doc, Tag tag, Content htmltree) {
  1372         addCommentTags(doc, tag.inlineTags(), true, false, htmltree);
  1375     /**
  1376      * Adds the summary content.
  1378      * @param doc the doc for which the summary will be generated
  1379      * @param htmltree the documentation tree to which the summary will be added
  1380      */
  1381     public void addSummaryComment(Doc doc, Content htmltree) {
  1382         addSummaryComment(doc, doc.firstSentenceTags(), htmltree);
  1385     /**
  1386      * Adds the summary content.
  1388      * @param doc the doc for which the summary will be generated
  1389      * @param firstSentenceTags the first sentence tags for the doc
  1390      * @param htmltree the documentation tree to which the summary will be added
  1391      */
  1392     public void addSummaryComment(Doc doc, Tag[] firstSentenceTags, Content htmltree) {
  1393         addCommentTags(doc, firstSentenceTags, false, true, htmltree);
  1396     public void addSummaryDeprecatedComment(Doc doc, Tag tag, Content htmltree) {
  1397         addCommentTags(doc, tag.firstSentenceTags(), true, true, htmltree);
  1400     /**
  1401      * Adds the inline comment.
  1403      * @param doc the doc for which the inline comments will be generated
  1404      * @param htmltree the documentation tree to which the inline comments will be added
  1405      */
  1406     public void addInlineComment(Doc doc, Content htmltree) {
  1407         addCommentTags(doc, doc.inlineTags(), false, false, htmltree);
  1410     /**
  1411      * Adds the comment tags.
  1413      * @param doc the doc for which the comment tags will be generated
  1414      * @param tags the first sentence tags for the doc
  1415      * @param depr true if it is deprecated
  1416      * @param first true if the first sentence tags should be added
  1417      * @param htmltree the documentation tree to which the comment tags will be added
  1418      */
  1419     private void addCommentTags(Doc doc, Tag[] tags, boolean depr,
  1420             boolean first, Content htmltree) {
  1421         if(configuration.nocomment){
  1422             return;
  1424         Content div;
  1425         Content result = new RawHtml(commentTagsToString(null, doc, tags, first));
  1426         if (depr) {
  1427             Content italic = HtmlTree.I(result);
  1428             div = HtmlTree.DIV(HtmlStyle.block, italic);
  1429             htmltree.addContent(div);
  1431         else {
  1432             div = HtmlTree.DIV(HtmlStyle.block, result);
  1433             htmltree.addContent(div);
  1435         if (tags.length == 0) {
  1436             htmltree.addContent(getSpace());
  1440     /**
  1441      * Converts inline tags and text to text strings, expanding the
  1442      * inline tags along the way.  Called wherever text can contain
  1443      * an inline tag, such as in comments or in free-form text arguments
  1444      * to non-inline tags.
  1446      * @param holderTag    specific tag where comment resides
  1447      * @param doc    specific doc where comment resides
  1448      * @param tags   array of text tags and inline tags (often alternating)
  1449      *               present in the text of interest for this doc
  1450      * @param isFirstSentence  true if text is first sentence
  1451      */
  1452     public String commentTagsToString(Tag holderTag, Doc doc, Tag[] tags,
  1453             boolean isFirstSentence) {
  1454         StringBuilder result = new StringBuilder();
  1455         boolean textTagChange = false;
  1456         // Array of all possible inline tags for this javadoc run
  1457         configuration.tagletManager.checkTags(doc, tags, true);
  1458         for (int i = 0; i < tags.length; i++) {
  1459             Tag tagelem = tags[i];
  1460             String tagName = tagelem.name();
  1461             if (tagelem instanceof SeeTag) {
  1462                 result.append(seeTagToString((SeeTag)tagelem));
  1463             } else if (! tagName.equals("Text")) {
  1464                 int originalLength = result.length();
  1465                 TagletOutput output = TagletWriter.getInlineTagOuput(
  1466                     configuration.tagletManager, holderTag,
  1467                     tagelem, getTagletWriterInstance(isFirstSentence));
  1468                 result.append(output == null ? "" : output.toString());
  1469                 if (originalLength == 0 && isFirstSentence && tagelem.name().equals("@inheritDoc") && result.length() > 0) {
  1470                     break;
  1471                 } else if (configuration.docrootparent.length() > 0 &&
  1472                         tagelem.name().equals("@docRoot") &&
  1473                         ((tags[i + 1]).text()).startsWith("/..")) {
  1474                     //If Xdocrootparent switch ON, set the flag to remove the /.. occurance after
  1475                     //{@docRoot} tag in the very next Text tag.
  1476                     textTagChange = true;
  1477                     continue;
  1478                 } else {
  1479                     continue;
  1481             } else {
  1482                 String text = tagelem.text();
  1483                 //If Xdocrootparent switch ON, remove the /.. occurance after {@docRoot} tag.
  1484                 if (textTagChange) {
  1485                     text = text.replaceFirst("/..", "");
  1486                     textTagChange = false;
  1488                 //This is just a regular text tag.  The text may contain html links (<a>)
  1489                 //or inline tag {@docRoot}, which will be handled as special cases.
  1490                 text = redirectRelativeLinks(tagelem.holder(), text);
  1492                 // Replace @docRoot only if not represented by an instance of DocRootTaglet,
  1493                 // that is, only if it was not present in a source file doc comment.
  1494                 // This happens when inserted by the doclet (a few lines
  1495                 // above in this method).  [It might also happen when passed in on the command
  1496                 // line as a text argument to an option (like -header).]
  1497                 text = replaceDocRootDir(text);
  1498                 if (isFirstSentence) {
  1499                     text = removeNonInlineHtmlTags(text);
  1501                 StringTokenizer lines = new StringTokenizer(text, "\r\n", true);
  1502                 StringBuilder textBuff = new StringBuilder();
  1503                 while (lines.hasMoreTokens()) {
  1504                     StringBuilder line = new StringBuilder(lines.nextToken());
  1505                     Util.replaceTabs(configuration.sourcetab, line);
  1506                     textBuff.append(line.toString());
  1508                 result.append(textBuff);
  1511         return result.toString();
  1514     /**
  1515      * Return true if relative links should not be redirected.
  1517      * @return Return true if a relative link should not be redirected.
  1518      */
  1519     private boolean shouldNotRedirectRelativeLinks() {
  1520         return  this instanceof AnnotationTypeWriter ||
  1521                 this instanceof ClassWriter ||
  1522                 this instanceof PackageSummaryWriter;
  1525     /**
  1526      * Suppose a piece of documentation has a relative link.  When you copy
  1527      * that documentation to another place such as the index or class-use page,
  1528      * that relative link will no longer work.  We should redirect those links
  1529      * so that they will work again.
  1530      * <p>
  1531      * Here is the algorithm used to fix the link:
  1532      * <p>
  1533      * {@literal <relative link> => docRoot + <relative path to file> + <relative link> }
  1534      * <p>
  1535      * For example, suppose com.sun.javadoc.RootDoc has this link:
  1536      * {@literal <a href="package-summary.html">The package Page</a> }
  1537      * <p>
  1538      * If this link appeared in the index, we would redirect
  1539      * the link like this:
  1541      * {@literal <a href="./com/sun/javadoc/package-summary.html">The package Page</a>}
  1543      * @param doc the Doc object whose documentation is being written.
  1544      * @param text the text being written.
  1546      * @return the text, with all the relative links redirected to work.
  1547      */
  1548     private String redirectRelativeLinks(Doc doc, String text) {
  1549         if (doc == null || shouldNotRedirectRelativeLinks()) {
  1550             return text;
  1553         DocPath redirectPathFromRoot;
  1554         if (doc instanceof ClassDoc) {
  1555             redirectPathFromRoot = DocPath.forPackage(((ClassDoc) doc).containingPackage());
  1556         } else if (doc instanceof MemberDoc) {
  1557             redirectPathFromRoot = DocPath.forPackage(((MemberDoc) doc).containingPackage());
  1558         } else if (doc instanceof PackageDoc) {
  1559             redirectPathFromRoot = DocPath.forPackage((PackageDoc) doc);
  1560         } else {
  1561             return text;
  1564         //Redirect all relative links.
  1565         int end, begin = text.toLowerCase().indexOf("<a");
  1566         if(begin >= 0){
  1567             StringBuilder textBuff = new StringBuilder(text);
  1569             while(begin >=0){
  1570                 if (textBuff.length() > begin + 2 && ! Character.isWhitespace(textBuff.charAt(begin+2))) {
  1571                     begin = textBuff.toString().toLowerCase().indexOf("<a", begin + 1);
  1572                     continue;
  1575                 begin = textBuff.indexOf("=", begin) + 1;
  1576                 end = textBuff.indexOf(">", begin +1);
  1577                 if(begin == 0){
  1578                     //Link has no equal symbol.
  1579                     configuration.root.printWarning(
  1580                         doc.position(),
  1581                         configuration.getText("doclet.malformed_html_link_tag", text));
  1582                     break;
  1584                 if (end == -1) {
  1585                     //Break without warning.  This <a> tag is not necessarily malformed.  The text
  1586                     //might be missing '>' character because the href has an inline tag.
  1587                     break;
  1589                 if (textBuff.substring(begin, end).indexOf("\"") != -1){
  1590                     begin = textBuff.indexOf("\"", begin) + 1;
  1591                     end = textBuff.indexOf("\"", begin +1);
  1592                     if (begin == 0 || end == -1){
  1593                         //Link is missing a quote.
  1594                         break;
  1597                 String relativeLink = textBuff.substring(begin, end);
  1598                 if (!(relativeLink.toLowerCase().startsWith("mailto:") ||
  1599                         relativeLink.toLowerCase().startsWith("http:") ||
  1600                         relativeLink.toLowerCase().startsWith("https:") ||
  1601                         relativeLink.toLowerCase().startsWith("file:"))) {
  1602                     relativeLink = "{@"+(new DocRootTaglet()).getName() + "}/"
  1603                         + redirectPathFromRoot.resolve(relativeLink).getPath();
  1604                     textBuff.replace(begin, end, relativeLink);
  1606                 begin = textBuff.toString().toLowerCase().indexOf("<a", begin + 1);
  1608             return textBuff.toString();
  1610         return text;
  1613     public String removeNonInlineHtmlTags(String text) {
  1614         if (text.indexOf('<') < 0) {
  1615             return text;
  1617         String noninlinetags[] = { "<ul>", "</ul>", "<ol>", "</ol>",
  1618                 "<dl>", "</dl>", "<table>", "</table>",
  1619                 "<tr>", "</tr>", "<td>", "</td>",
  1620                 "<th>", "</th>", "<p>", "</p>",
  1621                 "<li>", "</li>", "<dd>", "</dd>",
  1622                 "<dir>", "</dir>", "<dt>", "</dt>",
  1623                 "<h1>", "</h1>", "<h2>", "</h2>",
  1624                 "<h3>", "</h3>", "<h4>", "</h4>",
  1625                 "<h5>", "</h5>", "<h6>", "</h6>",
  1626                 "<pre>", "</pre>", "<menu>", "</menu>",
  1627                 "<listing>", "</listing>", "<hr>",
  1628                 "<blockquote>", "</blockquote>",
  1629                 "<center>", "</center>",
  1630                 "<UL>", "</UL>", "<OL>", "</OL>",
  1631                 "<DL>", "</DL>", "<TABLE>", "</TABLE>",
  1632                 "<TR>", "</TR>", "<TD>", "</TD>",
  1633                 "<TH>", "</TH>", "<P>", "</P>",
  1634                 "<LI>", "</LI>", "<DD>", "</DD>",
  1635                 "<DIR>", "</DIR>", "<DT>", "</DT>",
  1636                 "<H1>", "</H1>", "<H2>", "</H2>",
  1637                 "<H3>", "</H3>", "<H4>", "</H4>",
  1638                 "<H5>", "</H5>", "<H6>", "</H6>",
  1639                 "<PRE>", "</PRE>", "<MENU>", "</MENU>",
  1640                 "<LISTING>", "</LISTING>", "<HR>",
  1641                 "<BLOCKQUOTE>", "</BLOCKQUOTE>",
  1642                 "<CENTER>", "</CENTER>"
  1643         };
  1644         for (int i = 0; i < noninlinetags.length; i++) {
  1645             text = replace(text, noninlinetags[i], "");
  1647         return text;
  1650     public String replace(String text, String tobe, String by) {
  1651         while (true) {
  1652             int startindex = text.indexOf(tobe);
  1653             if (startindex < 0) {
  1654                 return text;
  1656             int endindex = startindex + tobe.length();
  1657             StringBuilder replaced = new StringBuilder();
  1658             if (startindex > 0) {
  1659                 replaced.append(text.substring(0, startindex));
  1661             replaced.append(by);
  1662             if (text.length() > endindex) {
  1663                 replaced.append(text.substring(endindex));
  1665             text = replaced.toString();
  1669     /**
  1670      * Returns a link to the stylesheet file.
  1672      * @return an HtmlTree for the lINK tag which provides the stylesheet location
  1673      */
  1674     public HtmlTree getStyleSheetProperties() {
  1675         String filename = configuration.stylesheetfile;
  1676         DocPath stylesheet;
  1677         if (filename.length() > 0) {
  1678             stylesheet = DocPath.create(new File(filename).getName());
  1679         } else {
  1680             stylesheet = DocPaths.STYLESHEET;
  1682         HtmlTree link = HtmlTree.LINK("stylesheet", "text/css",
  1683                 pathToRoot.resolve(stylesheet).getPath(),
  1684                 "Style");
  1685         return link;
  1688     /**
  1689      * According to
  1690      * <cite>The Java&trade; Language Specification</cite>,
  1691      * all the outer classes and static nested classes are core classes.
  1692      */
  1693     public boolean isCoreClass(ClassDoc cd) {
  1694         return cd.containingClass() == null || cd.isStatic();
  1697     /**
  1698      * Adds the annotatation types for the given packageDoc.
  1700      * @param packageDoc the package to write annotations for.
  1701      * @param htmltree the documentation tree to which the annotation info will be
  1702      *        added
  1703      */
  1704     public void addAnnotationInfo(PackageDoc packageDoc, Content htmltree) {
  1705         addAnnotationInfo(packageDoc, packageDoc.annotations(), htmltree);
  1708     /**
  1709      * Adds the annotatation types for the given doc.
  1711      * @param doc the package to write annotations for
  1712      * @param htmltree the content tree to which the annotation types will be added
  1713      */
  1714     public void addAnnotationInfo(ProgramElementDoc doc, Content htmltree) {
  1715         addAnnotationInfo(doc, doc.annotations(), htmltree);
  1718     /**
  1719      * Add the annotatation types for the given doc and parameter.
  1721      * @param indent the number of spaces to indent the parameters.
  1722      * @param doc the doc to write annotations for.
  1723      * @param param the parameter to write annotations for.
  1724      * @param tree the content tree to which the annotation types will be added
  1725      */
  1726     public boolean addAnnotationInfo(int indent, Doc doc, Parameter param,
  1727             Content tree) {
  1728         return addAnnotationInfo(indent, doc, param.annotations(), false, tree);
  1731     /**
  1732      * Adds the annotatation types for the given doc.
  1734      * @param doc the doc to write annotations for.
  1735      * @param descList the array of {@link AnnotationDesc}.
  1736      * @param htmltree the documentation tree to which the annotation info will be
  1737      *        added
  1738      */
  1739     private void addAnnotationInfo(Doc doc, AnnotationDesc[] descList,
  1740             Content htmltree) {
  1741         addAnnotationInfo(0, doc, descList, true, htmltree);
  1744     /**
  1745      * Adds the annotatation types for the given doc.
  1747      * @param indent the number of extra spaces to indent the annotations.
  1748      * @param doc the doc to write annotations for.
  1749      * @param descList the array of {@link AnnotationDesc}.
  1750      * @param htmltree the documentation tree to which the annotation info will be
  1751      *        added
  1752      */
  1753     private boolean addAnnotationInfo(int indent, Doc doc,
  1754             AnnotationDesc[] descList, boolean lineBreak, Content htmltree) {
  1755         List<String> annotations = getAnnotations(indent, descList, lineBreak);
  1756         if (annotations.size() == 0) {
  1757             return false;
  1759         Content annotationContent;
  1760         for (Iterator<String> iter = annotations.iterator(); iter.hasNext();) {
  1761             annotationContent = new RawHtml(iter.next());
  1762             htmltree.addContent(annotationContent);
  1764         return true;
  1767    /**
  1768      * Return the string representations of the annotation types for
  1769      * the given doc.
  1771      * @param indent the number of extra spaces to indent the annotations.
  1772      * @param descList the array of {@link AnnotationDesc}.
  1773      * @param linkBreak if true, add new line between each member value.
  1774      * @return an array of strings representing the annotations being
  1775      *         documented.
  1776      */
  1777     private List<String> getAnnotations(int indent, AnnotationDesc[] descList, boolean linkBreak) {
  1778         List<String> results = new ArrayList<String>();
  1779         StringBuilder annotation;
  1780         for (int i = 0; i < descList.length; i++) {
  1781             AnnotationTypeDoc annotationDoc = descList[i].annotationType();
  1782             if (! Util.isDocumentedAnnotation(annotationDoc)){
  1783                 continue;
  1785             annotation = new StringBuilder();
  1786             LinkInfoImpl linkInfo = new LinkInfoImpl(
  1787                 LinkInfoImpl.CONTEXT_ANNOTATION, annotationDoc);
  1788             linkInfo.label = "@" + annotationDoc.name();
  1789             annotation.append(getLink(linkInfo));
  1790             AnnotationDesc.ElementValuePair[] pairs = descList[i].elementValues();
  1791             if (pairs.length > 0) {
  1792                 annotation.append('(');
  1793                 for (int j = 0; j < pairs.length; j++) {
  1794                     if (j > 0) {
  1795                         annotation.append(",");
  1796                         if (linkBreak) {
  1797                             annotation.append(DocletConstants.NL);
  1798                             int spaces = annotationDoc.name().length() + 2;
  1799                             for (int k = 0; k < (spaces + indent); k++) {
  1800                                 annotation.append(' ');
  1804                     annotation.append(getDocLink(LinkInfoImpl.CONTEXT_ANNOTATION,
  1805                         pairs[j].element(), pairs[j].element().name(), false));
  1806                     annotation.append('=');
  1807                     AnnotationValue annotationValue = pairs[j].value();
  1808                     List<AnnotationValue> annotationTypeValues = new ArrayList<AnnotationValue>();
  1809                     if (annotationValue.value() instanceof AnnotationValue[]) {
  1810                         AnnotationValue[] annotationArray =
  1811                             (AnnotationValue[]) annotationValue.value();
  1812                         for (int k = 0; k < annotationArray.length; k++) {
  1813                             annotationTypeValues.add(annotationArray[k]);
  1815                     } else {
  1816                         annotationTypeValues.add(annotationValue);
  1818                     annotation.append(annotationTypeValues.size() == 1 ? "" : "{");
  1819                     for (Iterator<AnnotationValue> iter = annotationTypeValues.iterator(); iter.hasNext(); ) {
  1820                         annotation.append(annotationValueToString(iter.next()));
  1821                         annotation.append(iter.hasNext() ? "," : "");
  1823                     annotation.append(annotationTypeValues.size() == 1 ? "" : "}");
  1825                 annotation.append(")");
  1827             annotation.append(linkBreak ? DocletConstants.NL : "");
  1828             results.add(annotation.toString());
  1830         return results;
  1833     private String annotationValueToString(AnnotationValue annotationValue) {
  1834         if (annotationValue.value() instanceof Type) {
  1835             Type type = (Type) annotationValue.value();
  1836             if (type.asClassDoc() != null) {
  1837                 LinkInfoImpl linkInfo = new LinkInfoImpl(
  1838                     LinkInfoImpl.CONTEXT_ANNOTATION, type);
  1839                     linkInfo.label = (type.asClassDoc().isIncluded() ?
  1840                         type.typeName() :
  1841                         type.qualifiedTypeName()) + type.dimension() + ".class";
  1842                 return getLink(linkInfo);
  1843             } else {
  1844                 return type.typeName() + type.dimension() + ".class";
  1846         } else if (annotationValue.value() instanceof AnnotationDesc) {
  1847             List<String> list = getAnnotations(0,
  1848                 new AnnotationDesc[]{(AnnotationDesc) annotationValue.value()},
  1849                     false);
  1850             StringBuilder buf = new StringBuilder();
  1851             for (String s: list) {
  1852                 buf.append(s);
  1854             return buf.toString();
  1855         } else if (annotationValue.value() instanceof MemberDoc) {
  1856             return getDocLink(LinkInfoImpl.CONTEXT_ANNOTATION,
  1857                 (MemberDoc) annotationValue.value(),
  1858                 ((MemberDoc) annotationValue.value()).name(), false);
  1859          } else {
  1860             return annotationValue.toString();
  1864     /**
  1865      * Return the configuation for this doclet.
  1867      * @return the configuration for this doclet.
  1868      */
  1869     public Configuration configuration() {
  1870         return configuration;

mercurial