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

Tue, 23 Oct 2012 13:20:37 -0700

author
jjg
date
Tue, 23 Oct 2012 13:20:37 -0700
changeset 1372
78962d89f283
parent 1365
2013982bee34
child 1373
4a1c57a1c410
permissions
-rw-r--r--

8000741: refactor javadoc to use abstraction to handle relative paths
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("",
   411                         "skip-navbar_top", HtmlTree.EMPTY, configuration.getText(
   412                         "doclet.Skip_navigation_links"), "");
   413                 navDiv.addContent(skipLinkContent);
   414             } else {
   415                 body.addContent(HtmlConstants.START_OF_BOTTOM_NAVBAR);
   416                 navDiv.addStyle(HtmlStyle.bottomNav);
   417                 allClassesId += "navbar_bottom";
   418                 Content a = getMarkerAnchor("navbar_bottom");
   419                 navDiv.addContent(a);
   420                 Content skipLinkContent = getHyperLink("",
   421                         "skip-navbar_bottom", HtmlTree.EMPTY, configuration.getText(
   422                         "doclet.Skip_navigation_links"), "");
   423                 navDiv.addContent(skipLinkContent);
   424             }
   425             if (header) {
   426                 navDiv.addContent(getMarkerAnchor("navbar_top_firstrow"));
   427             } else {
   428                 navDiv.addContent(getMarkerAnchor("navbar_bottom_firstrow"));
   429             }
   430             HtmlTree navList = new HtmlTree(HtmlTag.UL);
   431             navList.addStyle(HtmlStyle.navList);
   432             navList.addAttr(HtmlAttr.TITLE, "Navigation");
   433             if (configuration.createoverview) {
   434                 navList.addContent(getNavLinkContents());
   435             }
   436             if (configuration.packages.length == 1) {
   437                 navList.addContent(getNavLinkPackage(configuration.packages[0]));
   438             } else if (configuration.packages.length > 1) {
   439                 navList.addContent(getNavLinkPackage());
   440             }
   441             navList.addContent(getNavLinkClass());
   442             if(configuration.classuse) {
   443                 navList.addContent(getNavLinkClassUse());
   444             }
   445             if(configuration.createtree) {
   446                 navList.addContent(getNavLinkTree());
   447             }
   448             if(!(configuration.nodeprecated ||
   449                      configuration.nodeprecatedlist)) {
   450                 navList.addContent(getNavLinkDeprecated());
   451             }
   452             if(configuration.createindex) {
   453                 navList.addContent(getNavLinkIndex());
   454             }
   455             if (!configuration.nohelp) {
   456                 navList.addContent(getNavLinkHelp());
   457             }
   458             navDiv.addContent(navList);
   459             Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, getUserHeaderFooter(header));
   460             navDiv.addContent(aboutDiv);
   461             body.addContent(navDiv);
   462             Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious());
   463             ulNav.addContent(getNavLinkNext());
   464             Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav);
   465             Content ulFrames = HtmlTree.UL(HtmlStyle.navList, getNavShowLists());
   466             ulFrames.addContent(getNavHideLists(filename));
   467             subDiv.addContent(ulFrames);
   468             HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex());
   469             ulAllClasses.addAttr(HtmlAttr.ID, allClassesId.toString());
   470             subDiv.addContent(ulAllClasses);
   471             subDiv.addContent(getAllClassesLinkScript(allClassesId.toString()));
   472             addSummaryDetailLinks(subDiv);
   473             if (header) {
   474                 subDiv.addContent(getMarkerAnchor("skip-navbar_top"));
   475                 body.addContent(subDiv);
   476                 body.addContent(HtmlConstants.END_OF_TOP_NAVBAR);
   477             } else {
   478                 subDiv.addContent(getMarkerAnchor("skip-navbar_bottom"));
   479                 body.addContent(subDiv);
   480                 body.addContent(HtmlConstants.END_OF_BOTTOM_NAVBAR);
   481             }
   482         }
   483     }
   485     /**
   486      * Get the word "NEXT" to indicate that no link is available.  Override
   487      * this method to customize next link.
   488      *
   489      * @return a content tree for the link
   490      */
   491     protected Content getNavLinkNext() {
   492         return getNavLinkNext(null);
   493     }
   495     /**
   496      * Get the word "PREV" to indicate that no link is available.  Override
   497      * this method to customize prev link.
   498      *
   499      * @return a content tree for the link
   500      */
   501     protected Content getNavLinkPrevious() {
   502         return getNavLinkPrevious(null);
   503     }
   505     /**
   506      * Do nothing. This is the default method.
   507      */
   508     protected void addSummaryDetailLinks(Content navDiv) {
   509     }
   511     /**
   512      * Get link to the "overview-summary.html" page.
   513      *
   514      * @return a content tree for the link
   515      */
   516     protected Content getNavLinkContents() {
   517         Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_SUMMARY),
   518                 "", overviewLabel, "", "");
   519         Content li = HtmlTree.LI(linkContent);
   520         return li;
   521     }
   523     /**
   524      * Get link to the "package-summary.html" page for the package passed.
   525      *
   526      * @param pkg Package to which link will be generated
   527      * @return a content tree for the link
   528      */
   529     protected Content getNavLinkPackage(PackageDoc pkg) {
   530         Content linkContent = getPackageLink(pkg,
   531                 packageLabel);
   532         Content li = HtmlTree.LI(linkContent);
   533         return li;
   534     }
   536     /**
   537      * Get the word "Package" , to indicate that link is not available here.
   538      *
   539      * @return a content tree for the link
   540      */
   541     protected Content getNavLinkPackage() {
   542         Content li = HtmlTree.LI(packageLabel);
   543         return li;
   544     }
   546     /**
   547      * Get the word "Use", to indicate that link is not available.
   548      *
   549      * @return a content tree for the link
   550      */
   551     protected Content getNavLinkClassUse() {
   552         Content li = HtmlTree.LI(useLabel);
   553         return li;
   554     }
   556     /**
   557      * Get link for previous file.
   558      *
   559      * @param prev File name for the prev link
   560      * @return a content tree for the link
   561      */
   562     public Content getNavLinkPrevious(DocPath prev) {
   563         Content li;
   564         if (prev != null) {
   565             li = HtmlTree.LI(getHyperLink(prev, "", prevLabel, "", ""));
   566         }
   567         else
   568             li = HtmlTree.LI(prevLabel);
   569         return li;
   570     }
   572     /**
   573      * Get link for next file.  If next is null, just print the label
   574      * without linking it anywhere.
   575      *
   576      * @param next File name for the next link
   577      * @return a content tree for the link
   578      */
   579     public Content getNavLinkNext(DocPath next) {
   580         Content li;
   581         if (next != null) {
   582             li = HtmlTree.LI(getHyperLink(next, "", nextLabel, "", ""));
   583         }
   584         else
   585             li = HtmlTree.LI(nextLabel);
   586         return li;
   587     }
   589     /**
   590      * Get "FRAMES" link, to switch to the frame version of the output.
   591      *
   592      * @param link File to be linked, "index.html"
   593      * @return a content tree for the link
   594      */
   595     protected Content getNavShowLists(DocPath link) {
   596         Content framesContent = getHyperLink(link.getPath() + "?" + path.getPath(),
   597                 "", framesLabel, "", "_top");
   598         Content li = HtmlTree.LI(framesContent);
   599         return li;
   600     }
   602     /**
   603      * Get "FRAMES" link, to switch to the frame version of the output.
   604      *
   605      * @return a content tree for the link
   606      */
   607     protected Content getNavShowLists() {
   608         return getNavShowLists(pathToRoot.resolve(DocPaths.INDEX));
   609     }
   611     /**
   612      * Get "NO FRAMES" link, to switch to the non-frame version of the output.
   613      *
   614      * @param link File to be linked
   615      * @return a content tree for the link
   616      */
   617     protected Content getNavHideLists(DocPath link) {
   618         Content noFramesContent = getHyperLink(link, "", noframesLabel, "", "_top");
   619         Content li = HtmlTree.LI(noFramesContent);
   620         return li;
   621     }
   623     /**
   624      * Get "Tree" link in the navigation bar. If there is only one package
   625      * specified on the command line, then the "Tree" link will be to the
   626      * only "package-tree.html" file otherwise it will be to the
   627      * "overview-tree.html" file.
   628      *
   629      * @return a content tree for the link
   630      */
   631     protected Content getNavLinkTree() {
   632         Content treeLinkContent;
   633         PackageDoc[] packages = configuration.root.specifiedPackages();
   634         if (packages.length == 1 && configuration.root.specifiedClasses().length == 0) {
   635             treeLinkContent = getHyperLink(pathString(packages[0],
   636                     DocPaths.PACKAGE_TREE), "", treeLabel,
   637                     "", "");
   638         } else {
   639             treeLinkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE),
   640                     "", treeLabel, "", "");
   641         }
   642         Content li = HtmlTree.LI(treeLinkContent);
   643         return li;
   644     }
   646     /**
   647      * Get the overview tree link for the main tree.
   648      *
   649      * @param label the label for the link
   650      * @return a content tree for the link
   651      */
   652     protected Content getNavLinkMainTree(String label) {
   653         Content mainTreeContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE),
   654                 new StringContent(label));
   655         Content li = HtmlTree.LI(mainTreeContent);
   656         return li;
   657     }
   659     /**
   660      * Get the word "Class", to indicate that class link is not available.
   661      *
   662      * @return a content tree for the link
   663      */
   664     protected Content getNavLinkClass() {
   665         Content li = HtmlTree.LI(classLabel);
   666         return li;
   667     }
   669     /**
   670      * Get "Deprecated" API link in the navigation bar.
   671      *
   672      * @return a content tree for the link
   673      */
   674     protected Content getNavLinkDeprecated() {
   675         Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.DEPRECATED_LIST),
   676                 "", deprecatedLabel, "", "");
   677         Content li = HtmlTree.LI(linkContent);
   678         return li;
   679     }
   681     /**
   682      * Get link for generated index. If the user has used "-splitindex"
   683      * command line option, then link to file "index-files/index-1.html" is
   684      * generated otherwise link to file "index-all.html" is generated.
   685      *
   686      * @return a content tree for the link
   687      */
   688     protected Content getNavLinkClassIndex() {
   689         Content allClassesContent = getHyperLink(pathToRoot.resolve(
   690                 DocPaths.ALLCLASSES_NOFRAME), "",
   691                 allclassesLabel, "", "");
   692         Content li = HtmlTree.LI(allClassesContent);
   693         return li;
   694     }
   696     /**
   697      * Get link for generated class index.
   698      *
   699      * @return a content tree for the link
   700      */
   701     protected Content getNavLinkIndex() {
   702         Content linkContent = getHyperLink(pathToRoot.resolve(
   703                 (configuration.splitindex
   704                     ? DocPaths.INDEX_FILES.resolve(DocPaths.indexN(1))
   705                     : DocPaths.INDEX_ALL)), "",
   706             indexLabel, "", "");
   707         Content li = HtmlTree.LI(linkContent);
   708         return li;
   709     }
   711     /**
   712      * Get help file link. If user has provided a help file, then generate a
   713      * link to the user given file, which is already copied to current or
   714      * destination directory.
   715      *
   716      * @return a content tree for the link
   717      */
   718     protected Content getNavLinkHelp() {
   719         String helpfile = configuration.helpfile;
   720         DocPath helpfilenm;
   721         if (helpfile.isEmpty()) {
   722             helpfilenm = DocPaths.HELP_DOC;
   723         } else {
   724             helpfilenm = DocPath.create(new File(helpfile).getName());
   725         }
   726         Content linkContent = getHyperLink(pathToRoot.resolve(helpfilenm), "",
   727                 helpLabel, "", "");
   728         Content li = HtmlTree.LI(linkContent);
   729         return li;
   730     }
   732     /**
   733      * Get summary table header.
   734      *
   735      * @param header the header for the table
   736      * @param scope the scope of the headers
   737      * @return a content tree for the header
   738      */
   739     public Content getSummaryTableHeader(String[] header, String scope) {
   740         Content tr = new HtmlTree(HtmlTag.TR);
   741         int size = header.length;
   742         Content tableHeader;
   743         if (size == 1) {
   744             tableHeader = new StringContent(header[0]);
   745             tr.addContent(HtmlTree.TH(HtmlStyle.colOne, scope, tableHeader));
   746             return tr;
   747         }
   748         for (int i = 0; i < size; i++) {
   749             tableHeader = new StringContent(header[i]);
   750             if(i == 0)
   751                 tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader));
   752             else if(i == (size - 1))
   753                 tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader));
   754             else
   755                 tr.addContent(HtmlTree.TH(scope, tableHeader));
   756         }
   757         return tr;
   758     }
   760     /**
   761      * Get table caption.
   762      *
   763      * @param rawText the caption for the table which could be raw Html
   764      * @return a content tree for the caption
   765      */
   766     public Content getTableCaption(String rawText) {
   767         Content title = new RawHtml(rawText);
   768         Content captionSpan = HtmlTree.SPAN(title);
   769         Content space = getSpace();
   770         Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space);
   771         Content caption = HtmlTree.CAPTION(captionSpan);
   772         caption.addContent(tabSpan);
   773         return caption;
   774     }
   776     /**
   777      * Get the marker anchor which will be added to the documentation tree.
   778      *
   779      * @param anchorName the anchor name attribute
   780      * @return a content tree for the marker anchor
   781      */
   782     public Content getMarkerAnchor(String anchorName) {
   783         return getMarkerAnchor(anchorName, null);
   784     }
   786     /**
   787      * Get the marker anchor which will be added to the documentation tree.
   788      *
   789      * @param anchorName the anchor name attribute
   790      * @param anchorContent the content that should be added to the anchor
   791      * @return a content tree for the marker anchor
   792      */
   793     public Content getMarkerAnchor(String anchorName, Content anchorContent) {
   794         if (anchorContent == null)
   795             anchorContent = new Comment(" ");
   796         Content markerAnchor = HtmlTree.A_NAME(anchorName, anchorContent);
   797         return markerAnchor;
   798     }
   800     /**
   801      * Returns a packagename content.
   802      *
   803      * @param packageDoc the package to check
   804      * @return package name content
   805      */
   806     public Content getPackageName(PackageDoc packageDoc) {
   807         return packageDoc == null || packageDoc.name().length() == 0 ?
   808             defaultPackageLabel :
   809             getPackageLabel(packageDoc.name());
   810     }
   812     /**
   813      * Returns a package name label.
   814      *
   815      * @param packageName the package name
   816      * @return the package name content
   817      */
   818     public Content getPackageLabel(String packageName) {
   819         return new StringContent(packageName);
   820     }
   822     /**
   823      * Add package deprecation information to the documentation tree
   824      *
   825      * @param deprPkgs list of deprecated packages
   826      * @param headingKey the caption for the deprecated package table
   827      * @param tableSummary the summary for the deprecated package table
   828      * @param tableHeader table headers for the deprecated package table
   829      * @param contentTree the content tree to which the deprecated package table will be added
   830      */
   831     protected void addPackageDeprecatedAPI(List<Doc> deprPkgs, String headingKey,
   832             String tableSummary, String[] tableHeader, Content contentTree) {
   833         if (deprPkgs.size() > 0) {
   834             Content table = HtmlTree.TABLE(0, 3, 0, tableSummary,
   835                     getTableCaption(configuration().getText(headingKey)));
   836             table.addContent(getSummaryTableHeader(tableHeader, "col"));
   837             Content tbody = new HtmlTree(HtmlTag.TBODY);
   838             for (int i = 0; i < deprPkgs.size(); i++) {
   839                 PackageDoc pkg = (PackageDoc) deprPkgs.get(i);
   840                 HtmlTree td = HtmlTree.TD(HtmlStyle.colOne,
   841                         getPackageLink(pkg, getPackageName(pkg)));
   842                 if (pkg.tags("deprecated").length > 0) {
   843                     addInlineDeprecatedComment(pkg, pkg.tags("deprecated")[0], td);
   844                 }
   845                 HtmlTree tr = HtmlTree.TR(td);
   846                 if (i % 2 == 0) {
   847                     tr.addStyle(HtmlStyle.altColor);
   848                 } else {
   849                     tr.addStyle(HtmlStyle.rowColor);
   850                 }
   851                 tbody.addContent(tr);
   852             }
   853             table.addContent(tbody);
   854             Content li = HtmlTree.LI(HtmlStyle.blockList, table);
   855             Content ul = HtmlTree.UL(HtmlStyle.blockList, li);
   856             contentTree.addContent(ul);
   857         }
   858     }
   860     /**
   861      * Return the path to the class page for a classdoc. Works same as
   862      * {@link #pathToClass(ClassDoc)}.
   863      *
   864      * @param cd   Class to which the path is requested.
   865      * @param name Name of the file(doesn't include path).
   866      */
   867     protected DocPath pathString(ClassDoc cd, DocPath name) {
   868         return pathString(cd.containingPackage(), name);
   869     }
   871     /**
   872      * Return path to the given file name in the given package. So if the name
   873      * passed is "Object.html" and the name of the package is "java.lang", and
   874      * if the relative path is "../.." then returned string will be
   875      * "../../java/lang/Object.html"
   876      *
   877      * @param pd Package in which the file name is assumed to be.
   878      * @param name File name, to which path string is.
   879      */
   880     protected DocPath pathString(PackageDoc pd, DocPath name) {
   881         return pathToRoot.resolve(DocPath.forPackage(pd).resolve(name));
   882     }
   884     /**
   885      * Return the link to the given package.
   886      *
   887      * @param pkg the package to link to.
   888      * @param label the label for the link.
   889      * @param isStrong true if the label should be strong.
   890      * @return the link to the given package.
   891      */
   892     public String getPackageLinkString(PackageDoc pkg, String label,
   893                                  boolean isStrong) {
   894         return getPackageLinkString(pkg, label, isStrong, "");
   895     }
   897     /**
   898      * Return the link to the given package.
   899      *
   900      * @param pkg the package to link to.
   901      * @param label the label for the link.
   902      * @param isStrong true if the label should be strong.
   903      * @param style  the font of the package link label.
   904      * @return the link to the given package.
   905      */
   906     public String getPackageLinkString(PackageDoc pkg, String label, boolean isStrong,
   907             String style) {
   908         boolean included = pkg != null && pkg.isIncluded();
   909         if (! included) {
   910             PackageDoc[] packages = configuration.packages;
   911             for (int i = 0; i < packages.length; i++) {
   912                 if (packages[i].equals(pkg)) {
   913                     included = true;
   914                     break;
   915                 }
   916             }
   917         }
   918         if (included || pkg == null) {
   919             return getHyperLinkString(pathString(pkg, DocPaths.PACKAGE_SUMMARY),
   920                                 "", label, isStrong, style);
   921         } else {
   922             String crossPkgLink = getCrossPackageLink(Util.getPackageName(pkg));
   923             if (crossPkgLink != null) {
   924                 return getHyperLinkString(/*TEMP*/ DocPath.create(crossPkgLink), "", label, isStrong, style);
   925             } else {
   926                 return label;
   927             }
   928         }
   929     }
   931     /**
   932      * Return the link to the given package.
   933      *
   934      * @param pkg the package to link to.
   935      * @param label the label for the link.
   936      * @return a content tree for the package link.
   937      */
   938     public Content getPackageLink(PackageDoc pkg, Content label) {
   939         boolean included = pkg != null && pkg.isIncluded();
   940         if (! included) {
   941             PackageDoc[] packages = configuration.packages;
   942             for (int i = 0; i < packages.length; i++) {
   943                 if (packages[i].equals(pkg)) {
   944                     included = true;
   945                     break;
   946                 }
   947             }
   948         }
   949         if (included || pkg == null) {
   950             return getHyperLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY),
   951                                 "", label);
   952         } else {
   953             String crossPkgLink = getCrossPackageLink(Util.getPackageName(pkg));
   954             if (crossPkgLink != null) {
   955                 return getHyperLink(/*TEMP*/ DocPath.create(crossPkgLink), "", label);
   956             } else {
   957                 return label;
   958             }
   959         }
   960     }
   962     public String italicsClassName(ClassDoc cd, boolean qual) {
   963         String name = (qual)? cd.qualifiedName(): cd.name();
   964         return (cd.isInterface())?  italicsText(name): name;
   965     }
   967     /**
   968      * Add the link to the content tree.
   969      *
   970      * @param doc program element doc for which the link will be added
   971      * @param label label for the link
   972      * @param htmltree the content tree to which the link will be added
   973      */
   974     public void addSrcLink(ProgramElementDoc doc, Content label, Content htmltree) {
   975         if (doc == null) {
   976             return;
   977         }
   978         ClassDoc cd = doc.containingClass();
   979         if (cd == null) {
   980             //d must be a class doc since in has no containing class.
   981             cd = (ClassDoc) doc;
   982         }
   983         DocPath href = pathToRoot
   984                 .resolve(DocPaths.SOURCE_OUTPUT)
   985                 .resolve(DocPath.forClass(cd));
   986         Content linkContent = getHyperLink(href, SourceToHTMLConverter.getAnchorName(doc), label, "", "");
   987         htmltree.addContent(linkContent);
   988     }
   990     /**
   991      * Return the link to the given class.
   992      *
   993      * @param linkInfo the information about the link.
   994      *
   995      * @return the link for the given class.
   996      */
   997     public String getLink(LinkInfoImpl linkInfo) {
   998         LinkFactoryImpl factory = new LinkFactoryImpl(this);
   999         String link = ((LinkOutputImpl) factory.getLinkOutput(linkInfo)).toString();
  1000         displayLength += linkInfo.displayLength;
  1001         return link;
  1004     /**
  1005      * Return the type parameters for the given class.
  1007      * @param linkInfo the information about the link.
  1008      * @return the type for the given class.
  1009      */
  1010     public String getTypeParameterLinks(LinkInfoImpl linkInfo) {
  1011         LinkFactoryImpl factory = new LinkFactoryImpl(this);
  1012         return ((LinkOutputImpl)
  1013             factory.getTypeParameterLinks(linkInfo, false)).toString();
  1016     /*************************************************************
  1017      * Return a class cross link to external class documentation.
  1018      * The name must be fully qualified to determine which package
  1019      * the class is in.  The -link option does not allow users to
  1020      * link to external classes in the "default" package.
  1022      * @param qualifiedClassName the qualified name of the external class.
  1023      * @param refMemName the name of the member being referenced.  This should
  1024      * be null or empty string if no member is being referenced.
  1025      * @param label the label for the external link.
  1026      * @param strong true if the link should be strong.
  1027      * @param style the style of the link.
  1028      * @param code true if the label should be code font.
  1029      */
  1030     public String getCrossClassLink(String qualifiedClassName, String refMemName,
  1031                                     String label, boolean strong, String style,
  1032                                     boolean code) {
  1033         String className = "",
  1034             packageName = qualifiedClassName == null ? "" : qualifiedClassName;
  1035         int periodIndex;
  1036         while((periodIndex = packageName.lastIndexOf('.')) != -1) {
  1037             className = packageName.substring(periodIndex + 1, packageName.length()) +
  1038                 (className.length() > 0 ? "." + className : "");
  1039             String defaultLabel = code ? codeText(className) : className;
  1040             packageName = packageName.substring(0, periodIndex);
  1041             if (getCrossPackageLink(packageName) != null) {
  1042                 //The package exists in external documentation, so link to the external
  1043                 //class (assuming that it exists).  This is definitely a limitation of
  1044                 //the -link option.  There are ways to determine if an external package
  1045                 //exists, but no way to determine if the external class exists.  We just
  1046                 //have to assume that it does.
  1047                 return getHyperLinkString(
  1048                     configuration.extern.getExternalLink(packageName, pathToRoot,
  1049                                 className + ".html?is-external=true"),
  1050                     refMemName == null ? "" : refMemName,
  1051                     label == null || label.length() == 0 ? defaultLabel : label,
  1052                     strong, style,
  1053                     configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName),
  1054                     "");
  1057         return null;
  1060     public boolean isClassLinkable(ClassDoc cd) {
  1061         if (cd.isIncluded()) {
  1062             return configuration.isGeneratedDoc(cd);
  1064         return configuration.extern.isExternal(cd);
  1067     public String getCrossPackageLink(String pkgName) {
  1068         return configuration.extern.getExternalLink(pkgName, pathToRoot,
  1069             "package-summary.html?is-external=true");
  1072     /**
  1073      * Get the class link.
  1075      * @param context the id of the context where the link will be added
  1076      * @param cd the class doc to link to
  1077      * @return a content tree for the link
  1078      */
  1079     public Content getQualifiedClassLink(int context, ClassDoc cd) {
  1080         return new RawHtml(getLink(new LinkInfoImpl(context, cd,
  1081                 configuration.getClassName(cd), "")));
  1084     /**
  1085      * Add the class link.
  1087      * @param context the id of the context where the link will be added
  1088      * @param cd the class doc to link to
  1089      * @param contentTree the content tree to which the link will be added
  1090      */
  1091     public void addPreQualifiedClassLink(int context, ClassDoc cd, Content contentTree) {
  1092         addPreQualifiedClassLink(context, cd, false, contentTree);
  1095     /**
  1096      * Retrieve the class link with the package portion of the label in
  1097      * plain text.  If the qualifier is excluded, it willnot be included in the
  1098      * link label.
  1100      * @param cd the class to link to.
  1101      * @param isStrong true if the link should be strong.
  1102      * @return the link with the package portion of the label in plain text.
  1103      */
  1104     public String getPreQualifiedClassLink(int context,
  1105             ClassDoc cd, boolean isStrong) {
  1106         String classlink = "";
  1107         PackageDoc pd = cd.containingPackage();
  1108         if(pd != null && ! configuration.shouldExcludeQualifier(pd.name())) {
  1109             classlink = getPkgName(cd);
  1111         classlink += getLink(new LinkInfoImpl(context, cd, cd.name(), isStrong));
  1112         return classlink;
  1115     /**
  1116      * Add the class link with the package portion of the label in
  1117      * plain text. If the qualifier is excluded, it will not be included in the
  1118      * link label.
  1120      * @param context the id of the context where the link will be added
  1121      * @param cd the class to link to
  1122      * @param isStrong true if the link should be strong
  1123      * @param contentTree the content tree to which the link with be added
  1124      */
  1125     public void addPreQualifiedClassLink(int context,
  1126             ClassDoc cd, boolean isStrong, Content contentTree) {
  1127         PackageDoc pd = cd.containingPackage();
  1128         if(pd != null && ! configuration.shouldExcludeQualifier(pd.name())) {
  1129             contentTree.addContent(getPkgName(cd));
  1131         contentTree.addContent(new RawHtml(getLink(new LinkInfoImpl(
  1132                 context, cd, cd.name(), isStrong))));
  1135     /**
  1136      * Add the class link, with only class name as the strong link and prefixing
  1137      * plain package name.
  1139      * @param context the id of the context where the link will be added
  1140      * @param cd the class to link to
  1141      * @param contentTree the content tree to which the link with be added
  1142      */
  1143     public void addPreQualifiedStrongClassLink(int context, ClassDoc cd, Content contentTree) {
  1144         addPreQualifiedClassLink(context, cd, true, contentTree);
  1147     /**
  1148      * Get the link for the given member.
  1150      * @param context the id of the context where the link will be added
  1151      * @param doc the member being linked to
  1152      * @param label the label for the link
  1153      * @return a content tree for the doc link
  1154      */
  1155     public Content getDocLink(int context, MemberDoc doc, String label) {
  1156         return getDocLink(context, doc.containingClass(), doc, label);
  1159     /**
  1160      * Return the link for the given member.
  1162      * @param context the id of the context where the link will be printed.
  1163      * @param doc the member being linked to.
  1164      * @param label the label for the link.
  1165      * @param strong true if the link should be strong.
  1166      * @return the link for the given member.
  1167      */
  1168     public String getDocLink(int context, MemberDoc doc, String label,
  1169                 boolean strong) {
  1170         return getDocLink(context, doc.containingClass(), doc, label, strong);
  1173     /**
  1174      * Return the link for the given member.
  1176      * @param context the id of the context where the link will be printed.
  1177      * @param classDoc the classDoc that we should link to.  This is not
  1178      *                 necessarily equal to doc.containingClass().  We may be
  1179      *                 inheriting comments.
  1180      * @param doc the member being linked to.
  1181      * @param label the label for the link.
  1182      * @param strong true if the link should be strong.
  1183      * @return the link for the given member.
  1184      */
  1185     public String getDocLink(int context, ClassDoc classDoc, MemberDoc doc,
  1186         String label, boolean strong) {
  1187         if (! (doc.isIncluded() ||
  1188             Util.isLinkable(classDoc, configuration()))) {
  1189             return label;
  1190         } else if (doc instanceof ExecutableMemberDoc) {
  1191             ExecutableMemberDoc emd = (ExecutableMemberDoc)doc;
  1192             return getLink(new LinkInfoImpl(context, classDoc,
  1193                 getAnchor(emd), label, strong));
  1194         } else if (doc instanceof MemberDoc) {
  1195             return getLink(new LinkInfoImpl(context, classDoc,
  1196                 doc.name(), label, strong));
  1197         } else {
  1198             return label;
  1202     /**
  1203      * Return the link for the given member.
  1205      * @param context the id of the context where the link will be added
  1206      * @param classDoc the classDoc that we should link to.  This is not
  1207      *                 necessarily equal to doc.containingClass().  We may be
  1208      *                 inheriting comments
  1209      * @param doc the member being linked to
  1210      * @param label the label for the link
  1211      * @return the link for the given member
  1212      */
  1213     public Content getDocLink(int context, ClassDoc classDoc, MemberDoc doc,
  1214         String label) {
  1215         if (! (doc.isIncluded() ||
  1216             Util.isLinkable(classDoc, configuration()))) {
  1217             return new StringContent(label);
  1218         } else if (doc instanceof ExecutableMemberDoc) {
  1219             ExecutableMemberDoc emd = (ExecutableMemberDoc)doc;
  1220             return new RawHtml(getLink(new LinkInfoImpl(context, classDoc,
  1221                 getAnchor(emd), label, false)));
  1222         } else if (doc instanceof MemberDoc) {
  1223             return new RawHtml(getLink(new LinkInfoImpl(context, classDoc,
  1224                 doc.name(), label, false)));
  1225         } else {
  1226             return new StringContent(label);
  1230     public String getAnchor(ExecutableMemberDoc emd) {
  1231         StringBuilder signature = new StringBuilder(emd.signature());
  1232         StringBuilder signatureParsed = new StringBuilder();
  1233         int counter = 0;
  1234         for (int i = 0; i < signature.length(); i++) {
  1235             char c = signature.charAt(i);
  1236             if (c == '<') {
  1237                 counter++;
  1238             } else if (c == '>') {
  1239                 counter--;
  1240             } else if (counter == 0) {
  1241                 signatureParsed.append(c);
  1244         return emd.name() + signatureParsed.toString();
  1247     public String seeTagToString(SeeTag see) {
  1248         String tagName = see.name();
  1249         if (! (tagName.startsWith("@link") || tagName.equals("@see"))) {
  1250             return "";
  1253         String seetext = replaceDocRootDir(see.text());
  1255         //Check if @see is an href or "string"
  1256         if (seetext.startsWith("<") || seetext.startsWith("\"")) {
  1257             return seetext;
  1260         boolean plain = tagName.equalsIgnoreCase("@linkplain");
  1261         String label = plainOrCodeText(plain, see.label());
  1263         //The text from the @see tag.  We will output this text when a label is not specified.
  1264         String text = plainOrCodeText(plain, seetext);
  1266         ClassDoc refClass = see.referencedClass();
  1267         String refClassName = see.referencedClassName();
  1268         MemberDoc refMem = see.referencedMember();
  1269         String refMemName = see.referencedMemberName();
  1271         if (refClass == null) {
  1272             //@see is not referencing an included class
  1273             PackageDoc refPackage = see.referencedPackage();
  1274             if (refPackage != null && refPackage.isIncluded()) {
  1275                 //@see is referencing an included package
  1276                 if (label.isEmpty())
  1277                     label = plainOrCodeText(plain, refPackage.name());
  1278                 return getPackageLinkString(refPackage, label, false);
  1279             } else {
  1280                 //@see is not referencing an included class or package.  Check for cross links.
  1281                 String classCrossLink, packageCrossLink = getCrossPackageLink(refClassName);
  1282                 if (packageCrossLink != null) {
  1283                     //Package cross link found
  1284                     return getHyperLinkString(/*TEMP*/ DocPath.create(packageCrossLink), "",
  1285                         (label.isEmpty() ? text : label), false);
  1286                 } else if ((classCrossLink = getCrossClassLink(refClassName,
  1287                         refMemName, label, false, "", !plain)) != null) {
  1288                     //Class cross link found (possibly to a member in the class)
  1289                     return classCrossLink;
  1290                 } else {
  1291                     //No cross link found so print warning
  1292                     configuration.getDocletSpecificMsg().warning(see.position(), "doclet.see.class_or_package_not_found",
  1293                             tagName, seetext);
  1294                     return (label.isEmpty() ? text: label);
  1297         } else if (refMemName == null) {
  1298             // Must be a class reference since refClass is not null and refMemName is null.
  1299             if (label.isEmpty()) {
  1300                 label = plainOrCodeText(plain, refClass.name());
  1302             return getLink(new LinkInfoImpl(refClass, label));
  1303         } else if (refMem == null) {
  1304             // Must be a member reference since refClass is not null and refMemName is not null.
  1305             // However, refMem is null, so this referenced member does not exist.
  1306             return (label.isEmpty() ? text: label);
  1307         } else {
  1308             // Must be a member reference since refClass is not null and refMemName is not null.
  1309             // refMem is not null, so this @see tag must be referencing a valid member.
  1310             ClassDoc containing = refMem.containingClass();
  1311             if (see.text().trim().startsWith("#") &&
  1312                 ! (containing.isPublic() ||
  1313                 Util.isLinkable(containing, configuration()))) {
  1314                 // Since the link is relative and the holder is not even being
  1315                 // documented, this must be an inherited link.  Redirect it.
  1316                 // The current class either overrides the referenced member or
  1317                 // inherits it automatically.
  1318                 if (this instanceof ClassWriterImpl) {
  1319                     containing = ((ClassWriterImpl) this).getClassDoc();
  1320                 } else if (!containing.isPublic()){
  1321                     configuration.getDocletSpecificMsg().warning(
  1322                         see.position(), "doclet.see.class_or_package_not_accessible",
  1323                         tagName, containing.qualifiedName());
  1324                 } else {
  1325                     configuration.getDocletSpecificMsg().warning(
  1326                         see.position(), "doclet.see.class_or_package_not_found",
  1327                         tagName, seetext);
  1330             if (configuration.currentcd != containing) {
  1331                 refMemName = containing.name() + "." + refMemName;
  1333             if (refMem instanceof ExecutableMemberDoc) {
  1334                 if (refMemName.indexOf('(') < 0) {
  1335                     refMemName += ((ExecutableMemberDoc)refMem).signature();
  1339             text = plainOrCodeText(plain, Util.escapeHtmlChars(refMemName));
  1341             return getDocLink(LinkInfoImpl.CONTEXT_SEE_TAG, containing,
  1342                 refMem, (label.isEmpty() ? text: label), false);
  1346     private String plainOrCodeText(boolean plain, String text) {
  1347         return (plain || text.isEmpty()) ? text : codeText(text);
  1350     /**
  1351      * Add the inline comment.
  1353      * @param doc the doc for which the inline comment will be added
  1354      * @param tag the inline tag to be added
  1355      * @param htmltree the content tree to which the comment will be added
  1356      */
  1357     public void addInlineComment(Doc doc, Tag tag, Content htmltree) {
  1358         addCommentTags(doc, tag.inlineTags(), false, false, htmltree);
  1361     /**
  1362      * Add the inline deprecated comment.
  1364      * @param doc the doc for which the inline deprecated comment will be added
  1365      * @param tag the inline tag to be added
  1366      * @param htmltree the content tree to which the comment will be added
  1367      */
  1368     public void addInlineDeprecatedComment(Doc doc, Tag tag, Content htmltree) {
  1369         addCommentTags(doc, tag.inlineTags(), true, false, htmltree);
  1372     /**
  1373      * Adds the summary content.
  1375      * @param doc the doc for which the summary will be generated
  1376      * @param htmltree the documentation tree to which the summary will be added
  1377      */
  1378     public void addSummaryComment(Doc doc, Content htmltree) {
  1379         addSummaryComment(doc, doc.firstSentenceTags(), htmltree);
  1382     /**
  1383      * Adds the summary content.
  1385      * @param doc the doc for which the summary will be generated
  1386      * @param firstSentenceTags the first sentence tags for the doc
  1387      * @param htmltree the documentation tree to which the summary will be added
  1388      */
  1389     public void addSummaryComment(Doc doc, Tag[] firstSentenceTags, Content htmltree) {
  1390         addCommentTags(doc, firstSentenceTags, false, true, htmltree);
  1393     public void addSummaryDeprecatedComment(Doc doc, Tag tag, Content htmltree) {
  1394         addCommentTags(doc, tag.firstSentenceTags(), true, true, htmltree);
  1397     /**
  1398      * Adds the inline comment.
  1400      * @param doc the doc for which the inline comments will be generated
  1401      * @param htmltree the documentation tree to which the inline comments will be added
  1402      */
  1403     public void addInlineComment(Doc doc, Content htmltree) {
  1404         addCommentTags(doc, doc.inlineTags(), false, false, htmltree);
  1407     /**
  1408      * Adds the comment tags.
  1410      * @param doc the doc for which the comment tags will be generated
  1411      * @param tags the first sentence tags for the doc
  1412      * @param depr true if it is deprecated
  1413      * @param first true if the first sentence tags should be added
  1414      * @param htmltree the documentation tree to which the comment tags will be added
  1415      */
  1416     private void addCommentTags(Doc doc, Tag[] tags, boolean depr,
  1417             boolean first, Content htmltree) {
  1418         if(configuration.nocomment){
  1419             return;
  1421         Content div;
  1422         Content result = new RawHtml(commentTagsToString(null, doc, tags, first));
  1423         if (depr) {
  1424             Content italic = HtmlTree.I(result);
  1425             div = HtmlTree.DIV(HtmlStyle.block, italic);
  1426             htmltree.addContent(div);
  1428         else {
  1429             div = HtmlTree.DIV(HtmlStyle.block, result);
  1430             htmltree.addContent(div);
  1432         if (tags.length == 0) {
  1433             htmltree.addContent(getSpace());
  1437     /**
  1438      * Converts inline tags and text to text strings, expanding the
  1439      * inline tags along the way.  Called wherever text can contain
  1440      * an inline tag, such as in comments or in free-form text arguments
  1441      * to non-inline tags.
  1443      * @param holderTag    specific tag where comment resides
  1444      * @param doc    specific doc where comment resides
  1445      * @param tags   array of text tags and inline tags (often alternating)
  1446      *               present in the text of interest for this doc
  1447      * @param isFirstSentence  true if text is first sentence
  1448      */
  1449     public String commentTagsToString(Tag holderTag, Doc doc, Tag[] tags,
  1450             boolean isFirstSentence) {
  1451         StringBuilder result = new StringBuilder();
  1452         boolean textTagChange = false;
  1453         // Array of all possible inline tags for this javadoc run
  1454         configuration.tagletManager.checkTags(doc, tags, true);
  1455         for (int i = 0; i < tags.length; i++) {
  1456             Tag tagelem = tags[i];
  1457             String tagName = tagelem.name();
  1458             if (tagelem instanceof SeeTag) {
  1459                 result.append(seeTagToString((SeeTag)tagelem));
  1460             } else if (! tagName.equals("Text")) {
  1461                 int originalLength = result.length();
  1462                 TagletOutput output = TagletWriter.getInlineTagOuput(
  1463                     configuration.tagletManager, holderTag,
  1464                     tagelem, getTagletWriterInstance(isFirstSentence));
  1465                 result.append(output == null ? "" : output.toString());
  1466                 if (originalLength == 0 && isFirstSentence && tagelem.name().equals("@inheritDoc") && result.length() > 0) {
  1467                     break;
  1468                 } else if (configuration.docrootparent.length() > 0 &&
  1469                         tagelem.name().equals("@docRoot") &&
  1470                         ((tags[i + 1]).text()).startsWith("/..")) {
  1471                     //If Xdocrootparent switch ON, set the flag to remove the /.. occurance after
  1472                     //{@docRoot} tag in the very next Text tag.
  1473                     textTagChange = true;
  1474                     continue;
  1475                 } else {
  1476                     continue;
  1478             } else {
  1479                 String text = tagelem.text();
  1480                 //If Xdocrootparent switch ON, remove the /.. occurance after {@docRoot} tag.
  1481                 if (textTagChange) {
  1482                     text = text.replaceFirst("/..", "");
  1483                     textTagChange = false;
  1485                 //This is just a regular text tag.  The text may contain html links (<a>)
  1486                 //or inline tag {@docRoot}, which will be handled as special cases.
  1487                 text = redirectRelativeLinks(tagelem.holder(), text);
  1489                 // Replace @docRoot only if not represented by an instance of DocRootTaglet,
  1490                 // that is, only if it was not present in a source file doc comment.
  1491                 // This happens when inserted by the doclet (a few lines
  1492                 // above in this method).  [It might also happen when passed in on the command
  1493                 // line as a text argument to an option (like -header).]
  1494                 text = replaceDocRootDir(text);
  1495                 if (isFirstSentence) {
  1496                     text = removeNonInlineHtmlTags(text);
  1498                 StringTokenizer lines = new StringTokenizer(text, "\r\n", true);
  1499                 StringBuilder textBuff = new StringBuilder();
  1500                 while (lines.hasMoreTokens()) {
  1501                     StringBuilder line = new StringBuilder(lines.nextToken());
  1502                     Util.replaceTabs(configuration.sourcetab, line);
  1503                     textBuff.append(line.toString());
  1505                 result.append(textBuff);
  1508         return result.toString();
  1511     /**
  1512      * Return true if relative links should not be redirected.
  1514      * @return Return true if a relative link should not be redirected.
  1515      */
  1516     private boolean shouldNotRedirectRelativeLinks() {
  1517         return  this instanceof AnnotationTypeWriter ||
  1518                 this instanceof ClassWriter ||
  1519                 this instanceof PackageSummaryWriter;
  1522     /**
  1523      * Suppose a piece of documentation has a relative link.  When you copy
  1524      * that documentation to another place such as the index or class-use page,
  1525      * that relative link will no longer work.  We should redirect those links
  1526      * so that they will work again.
  1527      * <p>
  1528      * Here is the algorithm used to fix the link:
  1529      * <p>
  1530      * {@literal <relative link> => docRoot + <relative path to file> + <relative link> }
  1531      * <p>
  1532      * For example, suppose com.sun.javadoc.RootDoc has this link:
  1533      * {@literal <a href="package-summary.html">The package Page</a> }
  1534      * <p>
  1535      * If this link appeared in the index, we would redirect
  1536      * the link like this:
  1538      * {@literal <a href="./com/sun/javadoc/package-summary.html">The package Page</a>}
  1540      * @param doc the Doc object whose documentation is being written.
  1541      * @param text the text being written.
  1543      * @return the text, with all the relative links redirected to work.
  1544      */
  1545     private String redirectRelativeLinks(Doc doc, String text) {
  1546         if (doc == null || shouldNotRedirectRelativeLinks()) {
  1547             return text;
  1550         DocPath redirectPathFromRoot;
  1551         if (doc instanceof ClassDoc) {
  1552             redirectPathFromRoot = DocPath.forPackage(((ClassDoc) doc).containingPackage());
  1553         } else if (doc instanceof MemberDoc) {
  1554             redirectPathFromRoot = DocPath.forPackage(((MemberDoc) doc).containingPackage());
  1555         } else if (doc instanceof PackageDoc) {
  1556             redirectPathFromRoot = DocPath.forPackage((PackageDoc) doc);
  1557         } else {
  1558             return text;
  1561         //Redirect all relative links.
  1562         int end, begin = text.toLowerCase().indexOf("<a");
  1563         if(begin >= 0){
  1564             StringBuilder textBuff = new StringBuilder(text);
  1566             while(begin >=0){
  1567                 if (textBuff.length() > begin + 2 && ! Character.isWhitespace(textBuff.charAt(begin+2))) {
  1568                     begin = textBuff.toString().toLowerCase().indexOf("<a", begin + 1);
  1569                     continue;
  1572                 begin = textBuff.indexOf("=", begin) + 1;
  1573                 end = textBuff.indexOf(">", begin +1);
  1574                 if(begin == 0){
  1575                     //Link has no equal symbol.
  1576                     configuration.root.printWarning(
  1577                         doc.position(),
  1578                         configuration.getText("doclet.malformed_html_link_tag", text));
  1579                     break;
  1581                 if (end == -1) {
  1582                     //Break without warning.  This <a> tag is not necessarily malformed.  The text
  1583                     //might be missing '>' character because the href has an inline tag.
  1584                     break;
  1586                 if (textBuff.substring(begin, end).indexOf("\"") != -1){
  1587                     begin = textBuff.indexOf("\"", begin) + 1;
  1588                     end = textBuff.indexOf("\"", begin +1);
  1589                     if (begin == 0 || end == -1){
  1590                         //Link is missing a quote.
  1591                         break;
  1594                 String relativeLink = textBuff.substring(begin, end);
  1595                 if (!(relativeLink.toLowerCase().startsWith("mailto:") ||
  1596                         relativeLink.toLowerCase().startsWith("http:") ||
  1597                         relativeLink.toLowerCase().startsWith("https:") ||
  1598                         relativeLink.toLowerCase().startsWith("file:"))) {
  1599                     relativeLink = "{@"+(new DocRootTaglet()).getName() + "}/"
  1600                         + redirectPathFromRoot.resolve(relativeLink).getPath();
  1601                     textBuff.replace(begin, end, relativeLink);
  1603                 begin = textBuff.toString().toLowerCase().indexOf("<a", begin + 1);
  1605             return textBuff.toString();
  1607         return text;
  1610     public String removeNonInlineHtmlTags(String text) {
  1611         if (text.indexOf('<') < 0) {
  1612             return text;
  1614         String noninlinetags[] = { "<ul>", "</ul>", "<ol>", "</ol>",
  1615                 "<dl>", "</dl>", "<table>", "</table>",
  1616                 "<tr>", "</tr>", "<td>", "</td>",
  1617                 "<th>", "</th>", "<p>", "</p>",
  1618                 "<li>", "</li>", "<dd>", "</dd>",
  1619                 "<dir>", "</dir>", "<dt>", "</dt>",
  1620                 "<h1>", "</h1>", "<h2>", "</h2>",
  1621                 "<h3>", "</h3>", "<h4>", "</h4>",
  1622                 "<h5>", "</h5>", "<h6>", "</h6>",
  1623                 "<pre>", "</pre>", "<menu>", "</menu>",
  1624                 "<listing>", "</listing>", "<hr>",
  1625                 "<blockquote>", "</blockquote>",
  1626                 "<center>", "</center>",
  1627                 "<UL>", "</UL>", "<OL>", "</OL>",
  1628                 "<DL>", "</DL>", "<TABLE>", "</TABLE>",
  1629                 "<TR>", "</TR>", "<TD>", "</TD>",
  1630                 "<TH>", "</TH>", "<P>", "</P>",
  1631                 "<LI>", "</LI>", "<DD>", "</DD>",
  1632                 "<DIR>", "</DIR>", "<DT>", "</DT>",
  1633                 "<H1>", "</H1>", "<H2>", "</H2>",
  1634                 "<H3>", "</H3>", "<H4>", "</H4>",
  1635                 "<H5>", "</H5>", "<H6>", "</H6>",
  1636                 "<PRE>", "</PRE>", "<MENU>", "</MENU>",
  1637                 "<LISTING>", "</LISTING>", "<HR>",
  1638                 "<BLOCKQUOTE>", "</BLOCKQUOTE>",
  1639                 "<CENTER>", "</CENTER>"
  1640         };
  1641         for (int i = 0; i < noninlinetags.length; i++) {
  1642             text = replace(text, noninlinetags[i], "");
  1644         return text;
  1647     public String replace(String text, String tobe, String by) {
  1648         while (true) {
  1649             int startindex = text.indexOf(tobe);
  1650             if (startindex < 0) {
  1651                 return text;
  1653             int endindex = startindex + tobe.length();
  1654             StringBuilder replaced = new StringBuilder();
  1655             if (startindex > 0) {
  1656                 replaced.append(text.substring(0, startindex));
  1658             replaced.append(by);
  1659             if (text.length() > endindex) {
  1660                 replaced.append(text.substring(endindex));
  1662             text = replaced.toString();
  1666     /**
  1667      * Returns a link to the stylesheet file.
  1669      * @return an HtmlTree for the lINK tag which provides the stylesheet location
  1670      */
  1671     public HtmlTree getStyleSheetProperties() {
  1672         String filename = configuration.stylesheetfile;
  1673         DocPath stylesheet;
  1674         if (filename.length() > 0) {
  1675             stylesheet = DocPath.create(new File(filename).getName());
  1676         } else {
  1677             stylesheet = DocPaths.STYLESHEET;
  1679         HtmlTree link = HtmlTree.LINK("stylesheet", "text/css",
  1680                 pathToRoot.resolve(stylesheet).getPath(),
  1681                 "Style");
  1682         return link;
  1685     /**
  1686      * According to
  1687      * <cite>The Java&trade; Language Specification</cite>,
  1688      * all the outer classes and static nested classes are core classes.
  1689      */
  1690     public boolean isCoreClass(ClassDoc cd) {
  1691         return cd.containingClass() == null || cd.isStatic();
  1694     /**
  1695      * Adds the annotatation types for the given packageDoc.
  1697      * @param packageDoc the package to write annotations for.
  1698      * @param htmltree the documentation tree to which the annotation info will be
  1699      *        added
  1700      */
  1701     public void addAnnotationInfo(PackageDoc packageDoc, Content htmltree) {
  1702         addAnnotationInfo(packageDoc, packageDoc.annotations(), htmltree);
  1705     /**
  1706      * Adds the annotatation types for the given doc.
  1708      * @param doc the package to write annotations for
  1709      * @param htmltree the content tree to which the annotation types will be added
  1710      */
  1711     public void addAnnotationInfo(ProgramElementDoc doc, Content htmltree) {
  1712         addAnnotationInfo(doc, doc.annotations(), htmltree);
  1715     /**
  1716      * Add the annotatation types for the given doc and parameter.
  1718      * @param indent the number of spaces to indent the parameters.
  1719      * @param doc the doc to write annotations for.
  1720      * @param param the parameter to write annotations for.
  1721      * @param tree the content tree to which the annotation types will be added
  1722      */
  1723     public boolean addAnnotationInfo(int indent, Doc doc, Parameter param,
  1724             Content tree) {
  1725         return addAnnotationInfo(indent, doc, param.annotations(), false, tree);
  1728     /**
  1729      * Adds the annotatation types for the given doc.
  1731      * @param doc the doc to write annotations for.
  1732      * @param descList the array of {@link AnnotationDesc}.
  1733      * @param htmltree the documentation tree to which the annotation info will be
  1734      *        added
  1735      */
  1736     private void addAnnotationInfo(Doc doc, AnnotationDesc[] descList,
  1737             Content htmltree) {
  1738         addAnnotationInfo(0, doc, descList, true, htmltree);
  1741     /**
  1742      * Adds the annotatation types for the given doc.
  1744      * @param indent the number of extra spaces to indent the annotations.
  1745      * @param doc the doc to write annotations for.
  1746      * @param descList the array of {@link AnnotationDesc}.
  1747      * @param htmltree the documentation tree to which the annotation info will be
  1748      *        added
  1749      */
  1750     private boolean addAnnotationInfo(int indent, Doc doc,
  1751             AnnotationDesc[] descList, boolean lineBreak, Content htmltree) {
  1752         List<String> annotations = getAnnotations(indent, descList, lineBreak);
  1753         if (annotations.size() == 0) {
  1754             return false;
  1756         Content annotationContent;
  1757         for (Iterator<String> iter = annotations.iterator(); iter.hasNext();) {
  1758             annotationContent = new RawHtml(iter.next());
  1759             htmltree.addContent(annotationContent);
  1761         return true;
  1764    /**
  1765      * Return the string representations of the annotation types for
  1766      * the given doc.
  1768      * @param indent the number of extra spaces to indent the annotations.
  1769      * @param descList the array of {@link AnnotationDesc}.
  1770      * @param linkBreak if true, add new line between each member value.
  1771      * @return an array of strings representing the annotations being
  1772      *         documented.
  1773      */
  1774     private List<String> getAnnotations(int indent, AnnotationDesc[] descList, boolean linkBreak) {
  1775         List<String> results = new ArrayList<String>();
  1776         StringBuilder annotation;
  1777         for (int i = 0; i < descList.length; i++) {
  1778             AnnotationTypeDoc annotationDoc = descList[i].annotationType();
  1779             if (! Util.isDocumentedAnnotation(annotationDoc)){
  1780                 continue;
  1782             annotation = new StringBuilder();
  1783             LinkInfoImpl linkInfo = new LinkInfoImpl(
  1784                 LinkInfoImpl.CONTEXT_ANNOTATION, annotationDoc);
  1785             linkInfo.label = "@" + annotationDoc.name();
  1786             annotation.append(getLink(linkInfo));
  1787             AnnotationDesc.ElementValuePair[] pairs = descList[i].elementValues();
  1788             if (pairs.length > 0) {
  1789                 annotation.append('(');
  1790                 for (int j = 0; j < pairs.length; j++) {
  1791                     if (j > 0) {
  1792                         annotation.append(",");
  1793                         if (linkBreak) {
  1794                             annotation.append(DocletConstants.NL);
  1795                             int spaces = annotationDoc.name().length() + 2;
  1796                             for (int k = 0; k < (spaces + indent); k++) {
  1797                                 annotation.append(' ');
  1801                     annotation.append(getDocLink(LinkInfoImpl.CONTEXT_ANNOTATION,
  1802                         pairs[j].element(), pairs[j].element().name(), false));
  1803                     annotation.append('=');
  1804                     AnnotationValue annotationValue = pairs[j].value();
  1805                     List<AnnotationValue> annotationTypeValues = new ArrayList<AnnotationValue>();
  1806                     if (annotationValue.value() instanceof AnnotationValue[]) {
  1807                         AnnotationValue[] annotationArray =
  1808                             (AnnotationValue[]) annotationValue.value();
  1809                         for (int k = 0; k < annotationArray.length; k++) {
  1810                             annotationTypeValues.add(annotationArray[k]);
  1812                     } else {
  1813                         annotationTypeValues.add(annotationValue);
  1815                     annotation.append(annotationTypeValues.size() == 1 ? "" : "{");
  1816                     for (Iterator<AnnotationValue> iter = annotationTypeValues.iterator(); iter.hasNext(); ) {
  1817                         annotation.append(annotationValueToString(iter.next()));
  1818                         annotation.append(iter.hasNext() ? "," : "");
  1820                     annotation.append(annotationTypeValues.size() == 1 ? "" : "}");
  1822                 annotation.append(")");
  1824             annotation.append(linkBreak ? DocletConstants.NL : "");
  1825             results.add(annotation.toString());
  1827         return results;
  1830     private String annotationValueToString(AnnotationValue annotationValue) {
  1831         if (annotationValue.value() instanceof Type) {
  1832             Type type = (Type) annotationValue.value();
  1833             if (type.asClassDoc() != null) {
  1834                 LinkInfoImpl linkInfo = new LinkInfoImpl(
  1835                     LinkInfoImpl.CONTEXT_ANNOTATION, type);
  1836                     linkInfo.label = (type.asClassDoc().isIncluded() ?
  1837                         type.typeName() :
  1838                         type.qualifiedTypeName()) + type.dimension() + ".class";
  1839                 return getLink(linkInfo);
  1840             } else {
  1841                 return type.typeName() + type.dimension() + ".class";
  1843         } else if (annotationValue.value() instanceof AnnotationDesc) {
  1844             List<String> list = getAnnotations(0,
  1845                 new AnnotationDesc[]{(AnnotationDesc) annotationValue.value()},
  1846                     false);
  1847             StringBuilder buf = new StringBuilder();
  1848             for (String s: list) {
  1849                 buf.append(s);
  1851             return buf.toString();
  1852         } else if (annotationValue.value() instanceof MemberDoc) {
  1853             return getDocLink(LinkInfoImpl.CONTEXT_ANNOTATION,
  1854                 (MemberDoc) annotationValue.value(),
  1855                 ((MemberDoc) annotationValue.value()).name(), false);
  1856          } else {
  1857             return annotationValue.toString();
  1861     /**
  1862      * Return the configuation for this doclet.
  1864      * @return the configuration for this doclet.
  1865      */
  1866     public Configuration configuration() {
  1867         return configuration;

mercurial