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

Tue, 30 Oct 2012 10:15:19 -0700

author
jjg
date
Tue, 30 Oct 2012 10:15:19 -0700
changeset 1381
23fe1a96bc0f
parent 1373
4a1c57a1c410
child 1383
b980e8e6aabf
permissions
-rw-r--r--

8001929: fix doclint errors in langtools doc comments
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.
   864      *
   865      * @param cd   Class to which the path is requested.
   866      * @param name Name of the file(doesn't include path).
   867      */
   868     protected DocPath pathString(ClassDoc cd, DocPath name) {
   869         return pathString(cd.containingPackage(), name);
   870     }
   872     /**
   873      * Return path to the given file name in the given package. So if the name
   874      * passed is "Object.html" and the name of the package is "java.lang", and
   875      * if the relative path is "../.." then returned string will be
   876      * "../../java/lang/Object.html"
   877      *
   878      * @param pd Package in which the file name is assumed to be.
   879      * @param name File name, to which path string is.
   880      */
   881     protected DocPath pathString(PackageDoc pd, DocPath name) {
   882         return pathToRoot.resolve(DocPath.forPackage(pd).resolve(name));
   883     }
   885     /**
   886      * Return the link to the given package.
   887      *
   888      * @param pkg the package to link to.
   889      * @param label the label for the link.
   890      * @param isStrong true if the label should be strong.
   891      * @return the link to the given package.
   892      */
   893     public String getPackageLinkString(PackageDoc pkg, String label,
   894                                  boolean isStrong) {
   895         return getPackageLinkString(pkg, label, isStrong, "");
   896     }
   898     /**
   899      * Return the link to the given package.
   900      *
   901      * @param pkg the package to link to.
   902      * @param label the label for the link.
   903      * @param isStrong true if the label should be strong.
   904      * @param style  the font of the package link label.
   905      * @return the link to the given package.
   906      */
   907     public String getPackageLinkString(PackageDoc pkg, String label, boolean isStrong,
   908             String style) {
   909         boolean included = pkg != null && pkg.isIncluded();
   910         if (! included) {
   911             PackageDoc[] packages = configuration.packages;
   912             for (int i = 0; i < packages.length; i++) {
   913                 if (packages[i].equals(pkg)) {
   914                     included = true;
   915                     break;
   916                 }
   917             }
   918         }
   919         if (included || pkg == null) {
   920             return getHyperLinkString(pathString(pkg, DocPaths.PACKAGE_SUMMARY),
   921                                 label, isStrong, style);
   922         } else {
   923             DocLink crossPkgLink = getCrossPackageLink(Util.getPackageName(pkg));
   924             if (crossPkgLink != null) {
   925                 return getHyperLinkString(crossPkgLink, label, isStrong, style);
   926             } else {
   927                 return label;
   928             }
   929         }
   930     }
   932     /**
   933      * Return the link to the given package.
   934      *
   935      * @param pkg the package to link to.
   936      * @param label the label for the link.
   937      * @return a content tree for the package link.
   938      */
   939     public Content getPackageLink(PackageDoc pkg, Content label) {
   940         boolean included = pkg != null && pkg.isIncluded();
   941         if (! included) {
   942             PackageDoc[] packages = configuration.packages;
   943             for (int i = 0; i < packages.length; i++) {
   944                 if (packages[i].equals(pkg)) {
   945                     included = true;
   946                     break;
   947                 }
   948             }
   949         }
   950         if (included || pkg == null) {
   951             return getHyperLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY),
   952                     label);
   953         } else {
   954             DocLink crossPkgLink = getCrossPackageLink(Util.getPackageName(pkg));
   955             if (crossPkgLink != null) {
   956                 return getHyperLink(crossPkgLink, label);
   957             } else {
   958                 return label;
   959             }
   960         }
   961     }
   963     public String italicsClassName(ClassDoc cd, boolean qual) {
   964         String name = (qual)? cd.qualifiedName(): cd.name();
   965         return (cd.isInterface())?  italicsText(name): name;
   966     }
   968     /**
   969      * Add the link to the content tree.
   970      *
   971      * @param doc program element doc for which the link will be added
   972      * @param label label for the link
   973      * @param htmltree the content tree to which the link will be added
   974      */
   975     public void addSrcLink(ProgramElementDoc doc, Content label, Content htmltree) {
   976         if (doc == null) {
   977             return;
   978         }
   979         ClassDoc cd = doc.containingClass();
   980         if (cd == null) {
   981             //d must be a class doc since in has no containing class.
   982             cd = (ClassDoc) doc;
   983         }
   984         DocPath href = pathToRoot
   985                 .resolve(DocPaths.SOURCE_OUTPUT)
   986                 .resolve(DocPath.forClass(cd));
   987         Content linkContent = getHyperLink(href.fragment(SourceToHTMLConverter.getAnchorName(doc)), label, "", "");
   988         htmltree.addContent(linkContent);
   989     }
   991     /**
   992      * Return the link to the given class.
   993      *
   994      * @param linkInfo the information about the link.
   995      *
   996      * @return the link for the given class.
   997      */
   998     public String getLink(LinkInfoImpl linkInfo) {
   999         LinkFactoryImpl factory = new LinkFactoryImpl(this);
  1000         String link = factory.getLinkOutput(linkInfo).toString();
  1001         displayLength += linkInfo.displayLength;
  1002         return link;
  1005     /**
  1006      * Return the type parameters for the given class.
  1008      * @param linkInfo the information about the link.
  1009      * @return the type for the given class.
  1010      */
  1011     public String getTypeParameterLinks(LinkInfoImpl linkInfo) {
  1012         LinkFactoryImpl factory = new LinkFactoryImpl(this);
  1013         return 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         String 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                 DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot,
  1048                                 className + ".html", refMemName);
  1049                 return getHyperLinkString(link,
  1050                     (label == null) || label.length() == 0 ? defaultLabel : label,
  1053                     strong, style,
  1054                     configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName),
  1055                     "");
  1058         return null;
  1061     public boolean isClassLinkable(ClassDoc cd) {
  1062         if (cd.isIncluded()) {
  1063             return configuration.isGeneratedDoc(cd);
  1065         return configuration.extern.isExternal(cd);
  1068     public DocLink getCrossPackageLink(String pkgName) {
  1069         return configuration.extern.getExternalLink(pkgName, pathToRoot,
  1070             DocPaths.PACKAGE_SUMMARY.getPath());
  1073     /**
  1074      * Get the class link.
  1076      * @param context the id of the context where the link will be added
  1077      * @param cd the class doc to link to
  1078      * @return a content tree for the link
  1079      */
  1080     public Content getQualifiedClassLink(int context, ClassDoc cd) {
  1081         return new RawHtml(getLink(new LinkInfoImpl(context, cd,
  1082                 configuration.getClassName(cd), "")));
  1085     /**
  1086      * Add the class link.
  1088      * @param context the id of the context where the link will be added
  1089      * @param cd the class doc to link to
  1090      * @param contentTree the content tree to which the link will be added
  1091      */
  1092     public void addPreQualifiedClassLink(int context, ClassDoc cd, Content contentTree) {
  1093         addPreQualifiedClassLink(context, cd, false, contentTree);
  1096     /**
  1097      * Retrieve the class link with the package portion of the label in
  1098      * plain text.  If the qualifier is excluded, it will not be included in the
  1099      * link label.
  1101      * @param cd the class to link to.
  1102      * @param isStrong true if the link should be strong.
  1103      * @return the link with the package portion of the label in plain text.
  1104      */
  1105     public String getPreQualifiedClassLink(int context,
  1106             ClassDoc cd, boolean isStrong) {
  1107         String classlink = "";
  1108         PackageDoc pd = cd.containingPackage();
  1109         if(pd != null && ! configuration.shouldExcludeQualifier(pd.name())) {
  1110             classlink = getPkgName(cd);
  1112         classlink += getLink(new LinkInfoImpl(context, cd, cd.name(), isStrong));
  1113         return classlink;
  1116     /**
  1117      * Add the class link with the package portion of the label in
  1118      * plain text. If the qualifier is excluded, it will not be included in the
  1119      * link label.
  1121      * @param context the id of the context where the link will be added
  1122      * @param cd the class to link to
  1123      * @param isStrong true if the link should be strong
  1124      * @param contentTree the content tree to which the link with be added
  1125      */
  1126     public void addPreQualifiedClassLink(int context,
  1127             ClassDoc cd, boolean isStrong, Content contentTree) {
  1128         PackageDoc pd = cd.containingPackage();
  1129         if(pd != null && ! configuration.shouldExcludeQualifier(pd.name())) {
  1130             contentTree.addContent(getPkgName(cd));
  1132         contentTree.addContent(new RawHtml(getLink(new LinkInfoImpl(
  1133                 context, cd, cd.name(), isStrong))));
  1136     /**
  1137      * Add the class link, with only class name as the strong link and prefixing
  1138      * plain package name.
  1140      * @param context the id of the context where the link will be added
  1141      * @param cd the class to link to
  1142      * @param contentTree the content tree to which the link with be added
  1143      */
  1144     public void addPreQualifiedStrongClassLink(int context, ClassDoc cd, Content contentTree) {
  1145         addPreQualifiedClassLink(context, cd, true, contentTree);
  1148     /**
  1149      * Get the link for the given member.
  1151      * @param context the id of the context where the link will be added
  1152      * @param doc the member being linked to
  1153      * @param label the label for the link
  1154      * @return a content tree for the doc link
  1155      */
  1156     public Content getDocLink(int context, MemberDoc doc, String label) {
  1157         return getDocLink(context, doc.containingClass(), doc, label);
  1160     /**
  1161      * Return the link for the given member.
  1163      * @param context the id of the context where the link will be printed.
  1164      * @param doc the member being linked to.
  1165      * @param label the label for the link.
  1166      * @param strong true if the link should be strong.
  1167      * @return the link for the given member.
  1168      */
  1169     public String getDocLink(int context, MemberDoc doc, String label,
  1170                 boolean strong) {
  1171         return getDocLink(context, doc.containingClass(), doc, label, strong);
  1174     /**
  1175      * Return the link for the given member.
  1177      * @param context the id of the context where the link will be printed.
  1178      * @param classDoc the classDoc that we should link to.  This is not
  1179      *                 necessarily equal to doc.containingClass().  We may be
  1180      *                 inheriting comments.
  1181      * @param doc the member being linked to.
  1182      * @param label the label for the link.
  1183      * @param strong true if the link should be strong.
  1184      * @return the link for the given member.
  1185      */
  1186     public String getDocLink(int context, ClassDoc classDoc, MemberDoc doc,
  1187         String label, boolean strong) {
  1188         if (! (doc.isIncluded() ||
  1189             Util.isLinkable(classDoc, configuration()))) {
  1190             return label;
  1191         } else if (doc instanceof ExecutableMemberDoc) {
  1192             ExecutableMemberDoc emd = (ExecutableMemberDoc)doc;
  1193             return getLink(new LinkInfoImpl(context, classDoc,
  1194                 getAnchor(emd), label, strong));
  1195         } else if (doc instanceof MemberDoc) {
  1196             return getLink(new LinkInfoImpl(context, classDoc,
  1197                 doc.name(), label, strong));
  1198         } else {
  1199             return label;
  1203     /**
  1204      * Return the link for the given member.
  1206      * @param context the id of the context where the link will be added
  1207      * @param classDoc the classDoc that we should link to.  This is not
  1208      *                 necessarily equal to doc.containingClass().  We may be
  1209      *                 inheriting comments
  1210      * @param doc the member being linked to
  1211      * @param label the label for the link
  1212      * @return the link for the given member
  1213      */
  1214     public Content getDocLink(int context, ClassDoc classDoc, MemberDoc doc,
  1215         String label) {
  1216         if (! (doc.isIncluded() ||
  1217             Util.isLinkable(classDoc, configuration()))) {
  1218             return new StringContent(label);
  1219         } else if (doc instanceof ExecutableMemberDoc) {
  1220             ExecutableMemberDoc emd = (ExecutableMemberDoc)doc;
  1221             return new RawHtml(getLink(new LinkInfoImpl(context, classDoc,
  1222                 getAnchor(emd), label, false)));
  1223         } else if (doc instanceof MemberDoc) {
  1224             return new RawHtml(getLink(new LinkInfoImpl(context, classDoc,
  1225                 doc.name(), label, false)));
  1226         } else {
  1227             return new StringContent(label);
  1231     public String getAnchor(ExecutableMemberDoc emd) {
  1232         StringBuilder signature = new StringBuilder(emd.signature());
  1233         StringBuilder signatureParsed = new StringBuilder();
  1234         int counter = 0;
  1235         for (int i = 0; i < signature.length(); i++) {
  1236             char c = signature.charAt(i);
  1237             if (c == '<') {
  1238                 counter++;
  1239             } else if (c == '>') {
  1240                 counter--;
  1241             } else if (counter == 0) {
  1242                 signatureParsed.append(c);
  1245         return emd.name() + signatureParsed.toString();
  1248     public String seeTagToString(SeeTag see) {
  1249         String tagName = see.name();
  1250         if (! (tagName.startsWith("@link") || tagName.equals("@see"))) {
  1251             return "";
  1254         String seetext = replaceDocRootDir(see.text());
  1256         //Check if @see is an href or "string"
  1257         if (seetext.startsWith("<") || seetext.startsWith("\"")) {
  1258             return seetext;
  1261         boolean plain = tagName.equalsIgnoreCase("@linkplain");
  1262         String label = plainOrCodeText(plain, see.label());
  1264         //The text from the @see tag.  We will output this text when a label is not specified.
  1265         String text = plainOrCodeText(plain, seetext);
  1267         ClassDoc refClass = see.referencedClass();
  1268         String refClassName = see.referencedClassName();
  1269         MemberDoc refMem = see.referencedMember();
  1270         String refMemName = see.referencedMemberName();
  1272         if (refClass == null) {
  1273             //@see is not referencing an included class
  1274             PackageDoc refPackage = see.referencedPackage();
  1275             if (refPackage != null && refPackage.isIncluded()) {
  1276                 //@see is referencing an included package
  1277                 if (label.isEmpty())
  1278                     label = plainOrCodeText(plain, refPackage.name());
  1279                 return getPackageLinkString(refPackage, label, false);
  1280             } else {
  1281                 //@see is not referencing an included class or package.  Check for cross links.
  1282                 String classCrossLink;
  1283                 DocLink packageCrossLink = getCrossPackageLink(refClassName);
  1284                 if (packageCrossLink != null) {
  1285                     //Package cross link found
  1286                     return getHyperLinkString(packageCrossLink,
  1287                         (label.isEmpty() ? text : label), false);
  1288                 } else if ((classCrossLink = getCrossClassLink(refClassName,
  1289                         refMemName, label, false, "", !plain)) != null) {
  1290                     //Class cross link found (possibly to a member in the class)
  1291                     return classCrossLink;
  1292                 } else {
  1293                     //No cross link found so print warning
  1294                     configuration.getDocletSpecificMsg().warning(see.position(), "doclet.see.class_or_package_not_found",
  1295                             tagName, seetext);
  1296                     return (label.isEmpty() ? text: label);
  1299         } else if (refMemName == null) {
  1300             // Must be a class reference since refClass is not null and refMemName is null.
  1301             if (label.isEmpty()) {
  1302                 label = plainOrCodeText(plain, refClass.name());
  1304             return getLink(new LinkInfoImpl(refClass, label));
  1305         } else if (refMem == null) {
  1306             // Must be a member reference since refClass is not null and refMemName is not null.
  1307             // However, refMem is null, so this referenced member does not exist.
  1308             return (label.isEmpty() ? text: label);
  1309         } else {
  1310             // Must be a member reference since refClass is not null and refMemName is not null.
  1311             // refMem is not null, so this @see tag must be referencing a valid member.
  1312             ClassDoc containing = refMem.containingClass();
  1313             if (see.text().trim().startsWith("#") &&
  1314                 ! (containing.isPublic() ||
  1315                 Util.isLinkable(containing, configuration()))) {
  1316                 // Since the link is relative and the holder is not even being
  1317                 // documented, this must be an inherited link.  Redirect it.
  1318                 // The current class either overrides the referenced member or
  1319                 // inherits it automatically.
  1320                 if (this instanceof ClassWriterImpl) {
  1321                     containing = ((ClassWriterImpl) this).getClassDoc();
  1322                 } else if (!containing.isPublic()){
  1323                     configuration.getDocletSpecificMsg().warning(
  1324                         see.position(), "doclet.see.class_or_package_not_accessible",
  1325                         tagName, containing.qualifiedName());
  1326                 } else {
  1327                     configuration.getDocletSpecificMsg().warning(
  1328                         see.position(), "doclet.see.class_or_package_not_found",
  1329                         tagName, seetext);
  1332             if (configuration.currentcd != containing) {
  1333                 refMemName = containing.name() + "." + refMemName;
  1335             if (refMem instanceof ExecutableMemberDoc) {
  1336                 if (refMemName.indexOf('(') < 0) {
  1337                     refMemName += ((ExecutableMemberDoc)refMem).signature();
  1341             text = plainOrCodeText(plain, Util.escapeHtmlChars(refMemName));
  1343             return getDocLink(LinkInfoImpl.CONTEXT_SEE_TAG, containing,
  1344                 refMem, (label.isEmpty() ? text: label), false);
  1348     private String plainOrCodeText(boolean plain, String text) {
  1349         return (plain || text.isEmpty()) ? text : codeText(text);
  1352     /**
  1353      * Add the inline comment.
  1355      * @param doc the doc for which the inline comment will be added
  1356      * @param tag the inline tag to be added
  1357      * @param htmltree the content tree to which the comment will be added
  1358      */
  1359     public void addInlineComment(Doc doc, Tag tag, Content htmltree) {
  1360         addCommentTags(doc, tag.inlineTags(), false, false, htmltree);
  1363     /**
  1364      * Add the inline deprecated comment.
  1366      * @param doc the doc for which the inline deprecated comment will be added
  1367      * @param tag the inline tag to be added
  1368      * @param htmltree the content tree to which the comment will be added
  1369      */
  1370     public void addInlineDeprecatedComment(Doc doc, Tag tag, Content htmltree) {
  1371         addCommentTags(doc, tag.inlineTags(), true, false, htmltree);
  1374     /**
  1375      * Adds the summary content.
  1377      * @param doc the doc for which the summary will be generated
  1378      * @param htmltree the documentation tree to which the summary will be added
  1379      */
  1380     public void addSummaryComment(Doc doc, Content htmltree) {
  1381         addSummaryComment(doc, doc.firstSentenceTags(), htmltree);
  1384     /**
  1385      * Adds the summary content.
  1387      * @param doc the doc for which the summary will be generated
  1388      * @param firstSentenceTags the first sentence tags for the doc
  1389      * @param htmltree the documentation tree to which the summary will be added
  1390      */
  1391     public void addSummaryComment(Doc doc, Tag[] firstSentenceTags, Content htmltree) {
  1392         addCommentTags(doc, firstSentenceTags, false, true, htmltree);
  1395     public void addSummaryDeprecatedComment(Doc doc, Tag tag, Content htmltree) {
  1396         addCommentTags(doc, tag.firstSentenceTags(), true, true, htmltree);
  1399     /**
  1400      * Adds the inline comment.
  1402      * @param doc the doc for which the inline comments will be generated
  1403      * @param htmltree the documentation tree to which the inline comments will be added
  1404      */
  1405     public void addInlineComment(Doc doc, Content htmltree) {
  1406         addCommentTags(doc, doc.inlineTags(), false, false, htmltree);
  1409     /**
  1410      * Adds the comment tags.
  1412      * @param doc the doc for which the comment tags will be generated
  1413      * @param tags the first sentence tags for the doc
  1414      * @param depr true if it is deprecated
  1415      * @param first true if the first sentence tags should be added
  1416      * @param htmltree the documentation tree to which the comment tags will be added
  1417      */
  1418     private void addCommentTags(Doc doc, Tag[] tags, boolean depr,
  1419             boolean first, Content htmltree) {
  1420         if(configuration.nocomment){
  1421             return;
  1423         Content div;
  1424         Content result = new RawHtml(commentTagsToString(null, doc, tags, first));
  1425         if (depr) {
  1426             Content italic = HtmlTree.I(result);
  1427             div = HtmlTree.DIV(HtmlStyle.block, italic);
  1428             htmltree.addContent(div);
  1430         else {
  1431             div = HtmlTree.DIV(HtmlStyle.block, result);
  1432             htmltree.addContent(div);
  1434         if (tags.length == 0) {
  1435             htmltree.addContent(getSpace());
  1439     /**
  1440      * Converts inline tags and text to text strings, expanding the
  1441      * inline tags along the way.  Called wherever text can contain
  1442      * an inline tag, such as in comments or in free-form text arguments
  1443      * to non-inline tags.
  1445      * @param holderTag    specific tag where comment resides
  1446      * @param doc    specific doc where comment resides
  1447      * @param tags   array of text tags and inline tags (often alternating)
  1448      *               present in the text of interest for this doc
  1449      * @param isFirstSentence  true if text is first sentence
  1450      */
  1451     public String commentTagsToString(Tag holderTag, Doc doc, Tag[] tags,
  1452             boolean isFirstSentence) {
  1453         StringBuilder result = new StringBuilder();
  1454         boolean textTagChange = false;
  1455         // Array of all possible inline tags for this javadoc run
  1456         configuration.tagletManager.checkTags(doc, tags, true);
  1457         for (int i = 0; i < tags.length; i++) {
  1458             Tag tagelem = tags[i];
  1459             String tagName = tagelem.name();
  1460             if (tagelem instanceof SeeTag) {
  1461                 result.append(seeTagToString((SeeTag)tagelem));
  1462             } else if (! tagName.equals("Text")) {
  1463                 int originalLength = result.length();
  1464                 TagletOutput output = TagletWriter.getInlineTagOuput(
  1465                     configuration.tagletManager, holderTag,
  1466                     tagelem, getTagletWriterInstance(isFirstSentence));
  1467                 result.append(output == null ? "" : output.toString());
  1468                 if (originalLength == 0 && isFirstSentence && tagelem.name().equals("@inheritDoc") && result.length() > 0) {
  1469                     break;
  1470                 } else if (configuration.docrootparent.length() > 0 &&
  1471                         tagelem.name().equals("@docRoot") &&
  1472                         ((tags[i + 1]).text()).startsWith("/..")) {
  1473                     //If Xdocrootparent switch ON, set the flag to remove the /.. occurance after
  1474                     //{@docRoot} tag in the very next Text tag.
  1475                     textTagChange = true;
  1476                     continue;
  1477                 } else {
  1478                     continue;
  1480             } else {
  1481                 String text = tagelem.text();
  1482                 //If Xdocrootparent switch ON, remove the /.. occurance after {@docRoot} tag.
  1483                 if (textTagChange) {
  1484                     text = text.replaceFirst("/..", "");
  1485                     textTagChange = false;
  1487                 //This is just a regular text tag.  The text may contain html links (<a>)
  1488                 //or inline tag {@docRoot}, which will be handled as special cases.
  1489                 text = redirectRelativeLinks(tagelem.holder(), text);
  1491                 // Replace @docRoot only if not represented by an instance of DocRootTaglet,
  1492                 // that is, only if it was not present in a source file doc comment.
  1493                 // This happens when inserted by the doclet (a few lines
  1494                 // above in this method).  [It might also happen when passed in on the command
  1495                 // line as a text argument to an option (like -header).]
  1496                 text = replaceDocRootDir(text);
  1497                 if (isFirstSentence) {
  1498                     text = removeNonInlineHtmlTags(text);
  1500                 StringTokenizer lines = new StringTokenizer(text, "\r\n", true);
  1501                 StringBuilder textBuff = new StringBuilder();
  1502                 while (lines.hasMoreTokens()) {
  1503                     StringBuilder line = new StringBuilder(lines.nextToken());
  1504                     Util.replaceTabs(configuration.sourcetab, line);
  1505                     textBuff.append(line.toString());
  1507                 result.append(textBuff);
  1510         return result.toString();
  1513     /**
  1514      * Return true if relative links should not be redirected.
  1516      * @return Return true if a relative link should not be redirected.
  1517      */
  1518     private boolean shouldNotRedirectRelativeLinks() {
  1519         return  this instanceof AnnotationTypeWriter ||
  1520                 this instanceof ClassWriter ||
  1521                 this instanceof PackageSummaryWriter;
  1524     /**
  1525      * Suppose a piece of documentation has a relative link.  When you copy
  1526      * that documentation to another place such as the index or class-use page,
  1527      * that relative link will no longer work.  We should redirect those links
  1528      * so that they will work again.
  1529      * <p>
  1530      * Here is the algorithm used to fix the link:
  1531      * <p>
  1532      * {@literal <relative link> => docRoot + <relative path to file> + <relative link> }
  1533      * <p>
  1534      * For example, suppose com.sun.javadoc.RootDoc has this link:
  1535      * {@literal <a href="package-summary.html">The package Page</a> }
  1536      * <p>
  1537      * If this link appeared in the index, we would redirect
  1538      * the link like this:
  1540      * {@literal <a href="./com/sun/javadoc/package-summary.html">The package Page</a>}
  1542      * @param doc the Doc object whose documentation is being written.
  1543      * @param text the text being written.
  1545      * @return the text, with all the relative links redirected to work.
  1546      */
  1547     private String redirectRelativeLinks(Doc doc, String text) {
  1548         if (doc == null || shouldNotRedirectRelativeLinks()) {
  1549             return text;
  1552         DocPath redirectPathFromRoot;
  1553         if (doc instanceof ClassDoc) {
  1554             redirectPathFromRoot = DocPath.forPackage(((ClassDoc) doc).containingPackage());
  1555         } else if (doc instanceof MemberDoc) {
  1556             redirectPathFromRoot = DocPath.forPackage(((MemberDoc) doc).containingPackage());
  1557         } else if (doc instanceof PackageDoc) {
  1558             redirectPathFromRoot = DocPath.forPackage((PackageDoc) doc);
  1559         } else {
  1560             return text;
  1563         //Redirect all relative links.
  1564         int end, begin = text.toLowerCase().indexOf("<a");
  1565         if(begin >= 0){
  1566             StringBuilder textBuff = new StringBuilder(text);
  1568             while(begin >=0){
  1569                 if (textBuff.length() > begin + 2 && ! Character.isWhitespace(textBuff.charAt(begin+2))) {
  1570                     begin = textBuff.toString().toLowerCase().indexOf("<a", begin + 1);
  1571                     continue;
  1574                 begin = textBuff.indexOf("=", begin) + 1;
  1575                 end = textBuff.indexOf(">", begin +1);
  1576                 if(begin == 0){
  1577                     //Link has no equal symbol.
  1578                     configuration.root.printWarning(
  1579                         doc.position(),
  1580                         configuration.getText("doclet.malformed_html_link_tag", text));
  1581                     break;
  1583                 if (end == -1) {
  1584                     //Break without warning.  This <a> tag is not necessarily malformed.  The text
  1585                     //might be missing '>' character because the href has an inline tag.
  1586                     break;
  1588                 if (textBuff.substring(begin, end).indexOf("\"") != -1){
  1589                     begin = textBuff.indexOf("\"", begin) + 1;
  1590                     end = textBuff.indexOf("\"", begin +1);
  1591                     if (begin == 0 || end == -1){
  1592                         //Link is missing a quote.
  1593                         break;
  1596                 String relativeLink = textBuff.substring(begin, end);
  1597                 if (!(relativeLink.toLowerCase().startsWith("mailto:") ||
  1598                         relativeLink.toLowerCase().startsWith("http:") ||
  1599                         relativeLink.toLowerCase().startsWith("https:") ||
  1600                         relativeLink.toLowerCase().startsWith("file:"))) {
  1601                     relativeLink = "{@"+(new DocRootTaglet()).getName() + "}/"
  1602                         + redirectPathFromRoot.resolve(relativeLink).getPath();
  1603                     textBuff.replace(begin, end, relativeLink);
  1605                 begin = textBuff.toString().toLowerCase().indexOf("<a", begin + 1);
  1607             return textBuff.toString();
  1609         return text;
  1612     public String removeNonInlineHtmlTags(String text) {
  1613         if (text.indexOf('<') < 0) {
  1614             return text;
  1616         String noninlinetags[] = { "<ul>", "</ul>", "<ol>", "</ol>",
  1617                 "<dl>", "</dl>", "<table>", "</table>",
  1618                 "<tr>", "</tr>", "<td>", "</td>",
  1619                 "<th>", "</th>", "<p>", "</p>",
  1620                 "<li>", "</li>", "<dd>", "</dd>",
  1621                 "<dir>", "</dir>", "<dt>", "</dt>",
  1622                 "<h1>", "</h1>", "<h2>", "</h2>",
  1623                 "<h3>", "</h3>", "<h4>", "</h4>",
  1624                 "<h5>", "</h5>", "<h6>", "</h6>",
  1625                 "<pre>", "</pre>", "<menu>", "</menu>",
  1626                 "<listing>", "</listing>", "<hr>",
  1627                 "<blockquote>", "</blockquote>",
  1628                 "<center>", "</center>",
  1629                 "<UL>", "</UL>", "<OL>", "</OL>",
  1630                 "<DL>", "</DL>", "<TABLE>", "</TABLE>",
  1631                 "<TR>", "</TR>", "<TD>", "</TD>",
  1632                 "<TH>", "</TH>", "<P>", "</P>",
  1633                 "<LI>", "</LI>", "<DD>", "</DD>",
  1634                 "<DIR>", "</DIR>", "<DT>", "</DT>",
  1635                 "<H1>", "</H1>", "<H2>", "</H2>",
  1636                 "<H3>", "</H3>", "<H4>", "</H4>",
  1637                 "<H5>", "</H5>", "<H6>", "</H6>",
  1638                 "<PRE>", "</PRE>", "<MENU>", "</MENU>",
  1639                 "<LISTING>", "</LISTING>", "<HR>",
  1640                 "<BLOCKQUOTE>", "</BLOCKQUOTE>",
  1641                 "<CENTER>", "</CENTER>"
  1642         };
  1643         for (int i = 0; i < noninlinetags.length; i++) {
  1644             text = replace(text, noninlinetags[i], "");
  1646         return text;
  1649     public String replace(String text, String tobe, String by) {
  1650         while (true) {
  1651             int startindex = text.indexOf(tobe);
  1652             if (startindex < 0) {
  1653                 return text;
  1655             int endindex = startindex + tobe.length();
  1656             StringBuilder replaced = new StringBuilder();
  1657             if (startindex > 0) {
  1658                 replaced.append(text.substring(0, startindex));
  1660             replaced.append(by);
  1661             if (text.length() > endindex) {
  1662                 replaced.append(text.substring(endindex));
  1664             text = replaced.toString();
  1668     /**
  1669      * Returns a link to the stylesheet file.
  1671      * @return an HtmlTree for the lINK tag which provides the stylesheet location
  1672      */
  1673     public HtmlTree getStyleSheetProperties() {
  1674         String filename = configuration.stylesheetfile;
  1675         DocPath stylesheet;
  1676         if (filename.length() > 0) {
  1677             stylesheet = DocPath.create(new File(filename).getName());
  1678         } else {
  1679             stylesheet = DocPaths.STYLESHEET;
  1681         HtmlTree link = HtmlTree.LINK("stylesheet", "text/css",
  1682                 pathToRoot.resolve(stylesheet).getPath(),
  1683                 "Style");
  1684         return link;
  1687     /**
  1688      * According to
  1689      * <cite>The Java&trade; Language Specification</cite>,
  1690      * all the outer classes and static nested classes are core classes.
  1691      */
  1692     public boolean isCoreClass(ClassDoc cd) {
  1693         return cd.containingClass() == null || cd.isStatic();
  1696     /**
  1697      * Adds the annotatation types for the given packageDoc.
  1699      * @param packageDoc the package to write annotations for.
  1700      * @param htmltree the documentation tree to which the annotation info will be
  1701      *        added
  1702      */
  1703     public void addAnnotationInfo(PackageDoc packageDoc, Content htmltree) {
  1704         addAnnotationInfo(packageDoc, packageDoc.annotations(), htmltree);
  1707     /**
  1708      * Adds the annotatation types for the given doc.
  1710      * @param doc the package to write annotations for
  1711      * @param htmltree the content tree to which the annotation types will be added
  1712      */
  1713     public void addAnnotationInfo(ProgramElementDoc doc, Content htmltree) {
  1714         addAnnotationInfo(doc, doc.annotations(), htmltree);
  1717     /**
  1718      * Add the annotatation types for the given doc and parameter.
  1720      * @param indent the number of spaces to indent the parameters.
  1721      * @param doc the doc to write annotations for.
  1722      * @param param the parameter to write annotations for.
  1723      * @param tree the content tree to which the annotation types will be added
  1724      */
  1725     public boolean addAnnotationInfo(int indent, Doc doc, Parameter param,
  1726             Content tree) {
  1727         return addAnnotationInfo(indent, doc, param.annotations(), false, tree);
  1730     /**
  1731      * Adds the annotatation types for the given doc.
  1733      * @param doc the doc to write annotations for.
  1734      * @param descList the array of {@link AnnotationDesc}.
  1735      * @param htmltree the documentation tree to which the annotation info will be
  1736      *        added
  1737      */
  1738     private void addAnnotationInfo(Doc doc, AnnotationDesc[] descList,
  1739             Content htmltree) {
  1740         addAnnotationInfo(0, doc, descList, true, htmltree);
  1743     /**
  1744      * Adds the annotatation types for the given doc.
  1746      * @param indent the number of extra spaces to indent the annotations.
  1747      * @param doc the doc to write annotations for.
  1748      * @param descList the array of {@link AnnotationDesc}.
  1749      * @param htmltree the documentation tree to which the annotation info will be
  1750      *        added
  1751      */
  1752     private boolean addAnnotationInfo(int indent, Doc doc,
  1753             AnnotationDesc[] descList, boolean lineBreak, Content htmltree) {
  1754         List<String> annotations = getAnnotations(indent, descList, lineBreak);
  1755         if (annotations.size() == 0) {
  1756             return false;
  1758         Content annotationContent;
  1759         for (Iterator<String> iter = annotations.iterator(); iter.hasNext();) {
  1760             annotationContent = new RawHtml(iter.next());
  1761             htmltree.addContent(annotationContent);
  1763         return true;
  1766    /**
  1767      * Return the string representations of the annotation types for
  1768      * the given doc.
  1770      * @param indent the number of extra spaces to indent the annotations.
  1771      * @param descList the array of {@link AnnotationDesc}.
  1772      * @param linkBreak if true, add new line between each member value.
  1773      * @return an array of strings representing the annotations being
  1774      *         documented.
  1775      */
  1776     private List<String> getAnnotations(int indent, AnnotationDesc[] descList, boolean linkBreak) {
  1777         List<String> results = new ArrayList<String>();
  1778         StringBuilder annotation;
  1779         for (int i = 0; i < descList.length; i++) {
  1780             AnnotationTypeDoc annotationDoc = descList[i].annotationType();
  1781             if (! Util.isDocumentedAnnotation(annotationDoc)){
  1782                 continue;
  1784             annotation = new StringBuilder();
  1785             LinkInfoImpl linkInfo = new LinkInfoImpl(
  1786                 LinkInfoImpl.CONTEXT_ANNOTATION, annotationDoc);
  1787             linkInfo.label = "@" + annotationDoc.name();
  1788             annotation.append(getLink(linkInfo));
  1789             AnnotationDesc.ElementValuePair[] pairs = descList[i].elementValues();
  1790             if (pairs.length > 0) {
  1791                 annotation.append('(');
  1792                 for (int j = 0; j < pairs.length; j++) {
  1793                     if (j > 0) {
  1794                         annotation.append(",");
  1795                         if (linkBreak) {
  1796                             annotation.append(DocletConstants.NL);
  1797                             int spaces = annotationDoc.name().length() + 2;
  1798                             for (int k = 0; k < (spaces + indent); k++) {
  1799                                 annotation.append(' ');
  1803                     annotation.append(getDocLink(LinkInfoImpl.CONTEXT_ANNOTATION,
  1804                         pairs[j].element(), pairs[j].element().name(), false));
  1805                     annotation.append('=');
  1806                     AnnotationValue annotationValue = pairs[j].value();
  1807                     List<AnnotationValue> annotationTypeValues = new ArrayList<AnnotationValue>();
  1808                     if (annotationValue.value() instanceof AnnotationValue[]) {
  1809                         AnnotationValue[] annotationArray =
  1810                             (AnnotationValue[]) annotationValue.value();
  1811                         for (int k = 0; k < annotationArray.length; k++) {
  1812                             annotationTypeValues.add(annotationArray[k]);
  1814                     } else {
  1815                         annotationTypeValues.add(annotationValue);
  1817                     annotation.append(annotationTypeValues.size() == 1 ? "" : "{");
  1818                     for (Iterator<AnnotationValue> iter = annotationTypeValues.iterator(); iter.hasNext(); ) {
  1819                         annotation.append(annotationValueToString(iter.next()));
  1820                         annotation.append(iter.hasNext() ? "," : "");
  1822                     annotation.append(annotationTypeValues.size() == 1 ? "" : "}");
  1824                 annotation.append(")");
  1826             annotation.append(linkBreak ? DocletConstants.NL : "");
  1827             results.add(annotation.toString());
  1829         return results;
  1832     private String annotationValueToString(AnnotationValue annotationValue) {
  1833         if (annotationValue.value() instanceof Type) {
  1834             Type type = (Type) annotationValue.value();
  1835             if (type.asClassDoc() != null) {
  1836                 LinkInfoImpl linkInfo = new LinkInfoImpl(
  1837                     LinkInfoImpl.CONTEXT_ANNOTATION, type);
  1838                     linkInfo.label = (type.asClassDoc().isIncluded() ?
  1839                         type.typeName() :
  1840                         type.qualifiedTypeName()) + type.dimension() + ".class";
  1841                 return getLink(linkInfo);
  1842             } else {
  1843                 return type.typeName() + type.dimension() + ".class";
  1845         } else if (annotationValue.value() instanceof AnnotationDesc) {
  1846             List<String> list = getAnnotations(0,
  1847                 new AnnotationDesc[]{(AnnotationDesc) annotationValue.value()},
  1848                     false);
  1849             StringBuilder buf = new StringBuilder();
  1850             for (String s: list) {
  1851                 buf.append(s);
  1853             return buf.toString();
  1854         } else if (annotationValue.value() instanceof MemberDoc) {
  1855             return getDocLink(LinkInfoImpl.CONTEXT_ANNOTATION,
  1856                 (MemberDoc) annotationValue.value(),
  1857                 ((MemberDoc) annotationValue.value()).name(), false);
  1858          } else {
  1859             return annotationValue.toString();
  1863     /**
  1864      * Return the configuation for this doclet.
  1866      * @return the configuration for this doclet.
  1867      */
  1868     public Configuration configuration() {
  1869         return configuration;

mercurial