duke@1: /* jjg@1521: * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this duke@1: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. duke@1: */ duke@1: duke@1: package com.sun.tools.doclets.formats.html; duke@1: duke@1: import java.io.*; duke@1: import java.text.SimpleDateFormat; duke@1: import java.util.*; duke@1: bpatel@233: import com.sun.javadoc.*; bpatel@233: import com.sun.tools.doclets.formats.html.markup.*; bpatel@233: import com.sun.tools.doclets.internal.toolkit.*; jjg@1357: import com.sun.tools.doclets.internal.toolkit.taglets.*; bpatel@233: import com.sun.tools.doclets.internal.toolkit.util.*; duke@1: duke@1: /** duke@1: * Class for the Html Format Code Generation specific to JavaDoc. duke@1: * This Class contains methods related to the Html Code Generation which duke@1: * are used extensively while generating the entire documentation. duke@1: * jjg@1359: *
This is NOT part of any supported API. jjg@1359: * If you write code that depends on this, you do so at your own risk. jjg@1359: * This code and its internal interfaces are subject to change or jjg@1359: * deletion without notice. jjg@1359: * duke@1: * @since 1.2 duke@1: * @author Atul M Dambalkar duke@1: * @author Robert Field bpatel@233: * @author Bhavesh Patel (Modified) duke@1: */ duke@1: public class HtmlDocletWriter extends HtmlDocWriter { duke@1: duke@1: /** duke@1: * Relative path from the file getting generated to the destination duke@1: * directory. For example, if the file getting generated is jjg@1372: * "java/lang/Object.html", then the path to the root is "../..". duke@1: * This string can be empty if the file getting generated is in duke@1: * the destination directory. duke@1: */ jjg@1372: public final DocPath pathToRoot; duke@1: duke@1: /** jjg@1372: * Platform-independent path from the current or the duke@1: * destination directory to the file getting generated. duke@1: * Used when creating the file. duke@1: */ jjg@1372: public final DocPath path; duke@1: duke@1: /** duke@1: * Name of the file getting generated. If the file getting generated is duke@1: * "java/lang/Object.html", then the filename is "Object.html". duke@1: */ jjg@1372: public final DocPath filename; duke@1: duke@1: /** duke@1: * The global configuration information for this run. duke@1: */ jjg@1410: public final ConfigurationImpl configuration; duke@1: duke@1: /** bpatel@766: * To check whether annotation heading is printed or not. bpatel@766: */ bpatel@766: protected boolean printedAnnotationHeading = false; bpatel@766: bpatel@766: /** bpatel@1477: * To check whether the repeated annotations is documented or not. bpatel@1477: */ bpatel@1477: private boolean isAnnotationDocumented = false; bpatel@1477: bpatel@1477: /** bpatel@1477: * To check whether the container annotations is documented or not. bpatel@1477: */ bpatel@1477: private boolean isContainerDocumented = false; bpatel@1477: bpatel@1477: /** duke@1: * Constructor to construct the HtmlStandardWriter object. duke@1: * jjg@1372: * @param path File to be generated. duke@1: */ jjg@1372: public HtmlDocletWriter(ConfigurationImpl configuration, DocPath path) jjg@1372: throws IOException { jjg@1372: super(configuration, path); duke@1: this.configuration = configuration; duke@1: this.path = path; jjg@1372: this.pathToRoot = path.parent().invert(); jjg@1372: this.filename = path.basename(); duke@1: } duke@1: duke@1: /** duke@1: * Replace {@docRoot} tag used in options that accept HTML text, such duke@1: * as -header, -footer, -top and -bottom, and when converting a relative duke@1: * HREF where commentTagsToString inserts a {@docRoot} where one was duke@1: * missing. (Also see DocRootTaglet for {@docRoot} tags in doc duke@1: * comments.) duke@1: *
duke@1: * Replace {@docRoot} tag in htmlstr with the relative path to the duke@1: * destination directory from the directory where the file is being duke@1: * written, looping to handle all such tags in htmlstr. duke@1: *
duke@1: * For example, for "-d docs" and -header containing {@docRoot}, when duke@1: * the HTML page for source file p/C1.java is being generated, the duke@1: * {@docRoot} tag would be inserted into the header as "../", duke@1: * the relative path from docs/p/ to docs/ (the document root). duke@1: *
duke@1: * Note: This doc comment was written with '@' representing '@'
duke@1: * to prevent the inline tag from being interpreted.
duke@1: */
duke@1: public String replaceDocRootDir(String htmlstr) {
duke@1: // Return if no inline tags exist
duke@1: int index = htmlstr.indexOf("{@");
duke@1: if (index < 0) {
duke@1: return htmlstr;
duke@1: }
duke@1: String lowerHtml = htmlstr.toLowerCase();
duke@1: // Return index of first occurrence of {@docroot}
duke@1: // Note: {@docRoot} is not case sensitive when passed in w/command line option
duke@1: index = lowerHtml.indexOf("{@docroot}", index);
duke@1: if (index < 0) {
duke@1: return htmlstr;
duke@1: }
jjg@910: StringBuilder buf = new StringBuilder();
duke@1: int previndex = 0;
duke@1: while (true) {
bpatel@997: if (configuration.docrootparent.length() > 0) {
jjg@1372: final String docroot_parent = "{@docroot}/..";
bpatel@997: // Search for lowercase version of {@docRoot}/..
jjg@1372: index = lowerHtml.indexOf(docroot_parent, previndex);
bpatel@997: // If next {@docRoot}/.. pattern not found, append rest of htmlstr and exit loop
bpatel@997: if (index < 0) {
bpatel@997: buf.append(htmlstr.substring(previndex));
bpatel@997: break;
bpatel@997: }
bpatel@997: // If next {@docroot}/.. pattern found, append htmlstr up to start of tag
bpatel@997: buf.append(htmlstr.substring(previndex, index));
jjg@1372: previndex = index + docroot_parent.length();
bpatel@997: // Insert docrootparent absolute path where {@docRoot}/.. was located
bpatel@997:
bpatel@997: buf.append(configuration.docrootparent);
bpatel@997: // Append slash if next character is not a slash
bpatel@997: if (previndex < htmlstr.length() && htmlstr.charAt(previndex) != '/') {
jjg@1372: buf.append('/');
bpatel@997: }
bpatel@997: } else {
jjg@1372: final String docroot = "{@docroot}";
bpatel@997: // Search for lowercase version of {@docRoot}
jjg@1372: index = lowerHtml.indexOf(docroot, previndex);
bpatel@997: // If next {@docRoot} tag not found, append rest of htmlstr and exit loop
bpatel@997: if (index < 0) {
bpatel@997: buf.append(htmlstr.substring(previndex));
bpatel@997: break;
bpatel@997: }
bpatel@997: // If next {@docroot} tag found, append htmlstr up to start of tag
bpatel@997: buf.append(htmlstr.substring(previndex, index));
jjg@1372: previndex = index + docroot.length();
bpatel@997: // Insert relative path where {@docRoot} was located
jjg@1372: buf.append(pathToRoot.isEmpty() ? "." : pathToRoot.getPath());
bpatel@997: // Append slash if next character is not a slash
jjg@1372: if (previndex < htmlstr.length() && htmlstr.charAt(previndex) != '/') {
jjg@1372: buf.append('/');
bpatel@997: }
duke@1: }
duke@1: }
duke@1: return buf.toString();
duke@1: }
duke@1:
duke@1: /**
bpatel@766: * Get the script to show or hide the All classes link.
bpatel@766: *
bpatel@766: * @param id id of the element to show or hide
bpatel@766: * @return a content tree for the script
bpatel@766: */
bpatel@766: public Content getAllClassesLinkScript(String id) {
bpatel@766: HtmlTree script = new HtmlTree(HtmlTag.SCRIPT);
bpatel@766: script.addAttr(HtmlAttr.TYPE, "text/javascript");
bpatel@793: String scriptCode = "" + DocletConstants.NL;
bpatel@766: Content scriptContent = new RawHtml(scriptCode);
bpatel@766: script.addContent(scriptContent);
bpatel@766: Content div = HtmlTree.DIV(script);
bpatel@766: return div;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Add method information.
bpatel@766: *
bpatel@766: * @param method the method to be documented
bpatel@766: * @param dl the content tree to which the method information will be added
bpatel@766: */
bpatel@766: private void addMethodInfo(MethodDoc method, Content dl) {
duke@1: ClassDoc[] intfacs = method.containingClass().interfaces();
duke@1: MethodDoc overriddenMethod = method.overriddenMethod();
bpatel@233: // Check whether there is any implementation or overridden info to be
bpatel@233: // printed. If no overridden or implementation info needs to be
bpatel@233: // printed, do not print this section.
bpatel@233: if ((intfacs.length > 0 &&
bpatel@233: new ImplementedMethods(method, this.configuration).build().length > 0) ||
bpatel@233: overriddenMethod != null) {
bpatel@766: MethodWriterImpl.addImplementsInfo(this, method, dl);
duke@1: if (overriddenMethod != null) {
bpatel@766: MethodWriterImpl.addOverridden(this,
bpatel@766: method.overriddenType(), overriddenMethod, dl);
duke@1: }
duke@1: }
duke@1: }
duke@1:
bpatel@766: /**
bpatel@766: * Adds the tags information.
bpatel@766: *
bpatel@766: * @param doc the doc for which the tags will be generated
bpatel@766: * @param htmltree the documentation tree to which the tags will be added
bpatel@766: */
bpatel@766: protected void addTagsInfo(Doc doc, Content htmltree) {
bpatel@766: if (configuration.nocomment) {
duke@1: return;
duke@1: }
bpatel@766: Content dl = new HtmlTree(HtmlTag.DL);
duke@1: if (doc instanceof MethodDoc) {
bpatel@766: addMethodInfo((MethodDoc) doc, dl);
duke@1: }
jjg@1744: TagletOutputImpl output = new TagletOutputImpl();
duke@1: TagletWriter.genTagOuput(configuration.tagletManager, doc,
duke@1: configuration.tagletManager.getCustomTags(doc),
duke@1: getTagletWriterInstance(false), output);
jjg@1744: dl.addContent(output.getContent());
bpatel@766: htmltree.addContent(dl);
duke@1: }
duke@1:
duke@1: /**
bpatel@233: * Check whether there are any tags for Serialization Overview
bpatel@233: * section to be printed.
bpatel@222: *
bpatel@233: * @param field the FieldDoc object to check for tags.
bpatel@222: * @return true if there are tags to be printed else return false.
bpatel@222: */
bpatel@233: protected boolean hasSerializationOverviewTags(FieldDoc field) {
jjg@1744: TagletOutputImpl output = new TagletOutputImpl();
bpatel@233: TagletWriter.genTagOuput(configuration.tagletManager, field,
bpatel@233: configuration.tagletManager.getCustomTags(field),
bpatel@222: getTagletWriterInstance(false), output);
jjg@1744: return !output.getContent().isEmpty();
bpatel@222: }
bpatel@222:
bpatel@222: /**
duke@1: * Returns a TagletWriter that knows how to write HTML.
duke@1: *
duke@1: * @return a TagletWriter that knows how to write HTML.
duke@1: */
duke@1: public TagletWriter getTagletWriterInstance(boolean isFirstSentence) {
duke@1: return new TagletWriterImpl(this, isFirstSentence);
duke@1: }
duke@1:
duke@1: /**
bpatel@766: * Get Package link, with target frame.
duke@1: *
bpatel@766: * @param pd The link will be to the "package-summary.html" page for this package
bpatel@766: * @param target name of the target frame
bpatel@766: * @param label tag for the link
bpatel@766: * @return a content for the target package link
duke@1: */
bpatel@766: public Content getTargetPackageLink(PackageDoc pd, String target,
bpatel@766: Content label) {
jjg@1373: return getHyperLink(pathString(pd, DocPaths.PACKAGE_SUMMARY), label, "", target);
duke@1: }
duke@1:
duke@1: /**
bpatel@1568: * Get Profile Package link, with target frame.
bpatel@1568: *
bpatel@1568: * @param pd the packageDoc object
bpatel@1568: * @param target name of the target frame
bpatel@1568: * @param label tag for the link
bpatel@1568: * @param profileName the name of the profile being documented
bpatel@1568: * @return a content for the target profile packages link
bpatel@1568: */
bpatel@1568: public Content getTargetProfilePackageLink(PackageDoc pd, String target,
bpatel@1568: Content label, String profileName) {
bpatel@1568: return getHyperLink(pathString(pd, DocPaths.profilePackageSummary(profileName)),
bpatel@1568: label, "", target);
bpatel@1568: }
bpatel@1568:
bpatel@1568: /**
bpatel@1568: * Get Profile link, with target frame.
bpatel@1568: *
bpatel@1568: * @param target name of the target frame
bpatel@1568: * @param label tag for the link
bpatel@1568: * @param profileName the name of the profile being documented
bpatel@1568: * @return a content for the target profile link
bpatel@1568: */
bpatel@1568: public Content getTargetProfileLink(String target, Content label,
bpatel@1568: String profileName) {
bpatel@1568: return getHyperLink(pathToRoot.resolve(
bpatel@1568: DocPaths.profileSummary(profileName)), label, "", target);
bpatel@1568: }
bpatel@1568:
bpatel@1568: /**
bpatel@1568: * Get the type name for profile search.
bpatel@1568: *
bpatel@1568: * @param cd the classDoc object for which the type name conversion is needed
bpatel@1568: * @return a type name string for the type
bpatel@1568: */
bpatel@1568: public String getTypeNameForProfile(ClassDoc cd) {
bpatel@1568: StringBuilder typeName =
bpatel@1568: new StringBuilder((cd.containingPackage()).name().replace(".", "/"));
bpatel@1568: typeName.append("/")
bpatel@1568: .append(cd.name().replace(".", "$"));
bpatel@1568: return typeName.toString();
bpatel@1568: }
bpatel@1568:
bpatel@1568: /**
bpatel@1568: * Check if a type belongs to a profile.
bpatel@1568: *
bpatel@1568: * @param cd the classDoc object that needs to be checked
bpatel@1568: * @param profileValue the profile in which the type needs to be checked
bpatel@1568: * @return true if the type is in the profile
bpatel@1568: */
bpatel@1568: public boolean isTypeInProfile(ClassDoc cd, int profileValue) {
bpatel@1568: return (configuration.profiles.getProfile(getTypeNameForProfile(cd)) <= profileValue);
bpatel@1568: }
bpatel@1568:
bpatel@1568: public void addClassesSummary(ClassDoc[] classes, String label,
bpatel@1568: String tableSummary, String[] tableHeader, Content summaryContentTree,
bpatel@1568: int profileValue) {
bpatel@1568: if(classes.length > 0) {
bpatel@1568: Arrays.sort(classes);
bpatel@1568: Content caption = getTableCaption(label);
bpatel@1568: Content table = HtmlTree.TABLE(HtmlStyle.packageSummary, 0, 3, 0,
bpatel@1568: tableSummary, caption);
bpatel@1568: table.addContent(getSummaryTableHeader(tableHeader, "col"));
bpatel@1568: Content tbody = new HtmlTree(HtmlTag.TBODY);
bpatel@1568: for (int i = 0; i < classes.length; i++) {
bpatel@1568: if (!isTypeInProfile(classes[i], profileValue)) {
bpatel@1568: continue;
bpatel@1568: }
bpatel@1568: if (!Util.isCoreClass(classes[i]) ||
bpatel@1568: !configuration.isGeneratedDoc(classes[i])) {
bpatel@1568: continue;
bpatel@1568: }
jjg@1736: Content classContent = getLink(new LinkInfoImpl(
jjg@1738: configuration, LinkInfoImpl.Kind.PACKAGE, classes[i]));
bpatel@1568: Content tdClass = HtmlTree.TD(HtmlStyle.colFirst, classContent);
bpatel@1568: HtmlTree tr = HtmlTree.TR(tdClass);
bpatel@1568: if (i%2 == 0)
bpatel@1568: tr.addStyle(HtmlStyle.altColor);
bpatel@1568: else
bpatel@1568: tr.addStyle(HtmlStyle.rowColor);
bpatel@1568: HtmlTree tdClassDescription = new HtmlTree(HtmlTag.TD);
bpatel@1568: tdClassDescription.addStyle(HtmlStyle.colLast);
bpatel@1568: if (Util.isDeprecated(classes[i])) {
bpatel@1568: tdClassDescription.addContent(deprecatedLabel);
bpatel@1568: if (classes[i].tags("deprecated").length > 0) {
bpatel@1568: addSummaryDeprecatedComment(classes[i],
bpatel@1568: classes[i].tags("deprecated")[0], tdClassDescription);
bpatel@1568: }
bpatel@1568: }
bpatel@1568: else
bpatel@1568: addSummaryComment(classes[i], tdClassDescription);
bpatel@1568: tr.addContent(tdClassDescription);
bpatel@1568: tbody.addContent(tr);
bpatel@1568: }
bpatel@1568: table.addContent(tbody);
bpatel@1568: Content li = HtmlTree.LI(HtmlStyle.blockList, table);
bpatel@1568: summaryContentTree.addContent(li);
bpatel@1568: }
bpatel@1568: }
bpatel@1568:
bpatel@1568: /**
bpatel@766: * Generates the HTML document tree and prints it out.
bpatel@766: *
bpatel@766: * @param metakeywords Array of String keywords for META tag. Each element
bpatel@766: * of the array is assigned to a separate META tag.
bpatel@766: * Pass in null for no array
bpatel@766: * @param includeScript true if printing windowtitle script
bpatel@766: * false for files that appear in the left-hand frames
bpatel@766: * @param body the body htmltree to be included in the document
bpatel@766: */
bpatel@766: public void printHtmlDocument(String[] metakeywords, boolean includeScript,
jjg@1364: Content body) throws IOException {
jjg@1410: Content htmlDocType = DocType.TRANSITIONAL;
bpatel@766: Content htmlComment = new Comment(configuration.getText("doclet.New_Page"));
bpatel@766: Content head = new HtmlTree(HtmlTag.HEAD);
bpatel@766: if (!configuration.notimestamp) {
jjg@1361: Content headComment = new Comment(getGeneratedByString());
bpatel@766: head.addContent(headComment);
bpatel@766: }
bpatel@766: if (configuration.charset.length() > 0) {
bpatel@766: Content meta = HtmlTree.META("Content-Type", "text/html",
bpatel@766: configuration.charset);
bpatel@766: head.addContent(meta);
bpatel@766: }
bpatel@766: head.addContent(getTitle());
bpatel@766: if (!configuration.notimestamp) {
bpatel@766: SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
bpatel@766: Content meta = HtmlTree.META("date", dateFormat.format(new Date()));
bpatel@766: head.addContent(meta);
bpatel@766: }
bpatel@766: if (metakeywords != null) {
bpatel@766: for (int i=0; i < metakeywords.length; i++) {
bpatel@766: Content meta = HtmlTree.META("keywords", metakeywords[i]);
bpatel@766: head.addContent(meta);
bpatel@766: }
bpatel@766: }
bpatel@766: head.addContent(getStyleSheetProperties());
bpatel@1417: head.addContent(getScriptProperties());
bpatel@766: Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(),
bpatel@766: head, body);
bpatel@766: Content htmlDocument = new HtmlDocument(htmlDocType,
bpatel@766: htmlComment, htmlTree);
jjg@1365: write(htmlDocument);
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get the window title.
bpatel@766: *
bpatel@766: * @param title the title string to construct the complete window title
bpatel@766: * @return the window title string
bpatel@766: */
bpatel@766: public String getWindowTitle(String title) {
bpatel@766: if (configuration.windowtitle.length() > 0) {
bpatel@766: title += " (" + configuration.windowtitle + ")";
bpatel@766: }
bpatel@766: return title;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get user specified header and the footer.
bpatel@766: *
bpatel@766: * @param header if true print the user provided header else print the
bpatel@766: * user provided footer.
bpatel@766: */
bpatel@766: public Content getUserHeaderFooter(boolean header) {
bpatel@766: String content;
bpatel@766: if (header) {
bpatel@766: content = replaceDocRootDir(configuration.header);
bpatel@766: } else {
bpatel@766: if (configuration.footer.length() != 0) {
bpatel@766: content = replaceDocRootDir(configuration.footer);
bpatel@766: } else {
bpatel@766: content = replaceDocRootDir(configuration.header);
bpatel@766: }
bpatel@766: }
bpatel@766: Content rawContent = new RawHtml(content);
bpatel@766: Content em = HtmlTree.EM(rawContent);
bpatel@766: return em;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Adds the user specified top.
bpatel@766: *
bpatel@766: * @param body the content tree to which user specified top will be added
bpatel@766: */
bpatel@766: public void addTop(Content body) {
bpatel@766: Content top = new RawHtml(replaceDocRootDir(configuration.top));
bpatel@766: body.addContent(top);
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Adds the user specified bottom.
bpatel@766: *
bpatel@766: * @param body the content tree to which user specified bottom will be added
bpatel@766: */
bpatel@766: public void addBottom(Content body) {
bpatel@766: Content bottom = new RawHtml(replaceDocRootDir(configuration.bottom));
bpatel@766: Content small = HtmlTree.SMALL(bottom);
bpatel@766: Content p = HtmlTree.P(HtmlStyle.legalCopy, small);
bpatel@766: body.addContent(p);
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Adds the navigation bar for the Html page at the top and and the bottom.
bpatel@766: *
bpatel@766: * @param header If true print navigation bar at the top of the page else
bpatel@766: * @param body the HtmlTree to which the nav links will be added
bpatel@766: */
bpatel@766: protected void addNavLinks(boolean header, Content body) {
bpatel@766: if (!configuration.nonavbar) {
bpatel@766: String allClassesId = "allclasses_";
bpatel@766: HtmlTree navDiv = new HtmlTree(HtmlTag.DIV);
bpatel@766: if (header) {
bpatel@766: body.addContent(HtmlConstants.START_OF_TOP_NAVBAR);
bpatel@766: navDiv.addStyle(HtmlStyle.topNav);
bpatel@766: allClassesId += "navbar_top";
bpatel@766: Content a = getMarkerAnchor("navbar_top");
bpatel@766: navDiv.addContent(a);
jjg@1373: Content skipLinkContent = getHyperLink(DocLink.fragment("skip-navbar_top"),
jjg@1373: HtmlTree.EMPTY,
jjg@1373: configuration.getText("doclet.Skip_navigation_links"),
jjg@1373: "");
bpatel@766: navDiv.addContent(skipLinkContent);
bpatel@766: } else {
bpatel@766: body.addContent(HtmlConstants.START_OF_BOTTOM_NAVBAR);
bpatel@766: navDiv.addStyle(HtmlStyle.bottomNav);
bpatel@766: allClassesId += "navbar_bottom";
bpatel@766: Content a = getMarkerAnchor("navbar_bottom");
bpatel@766: navDiv.addContent(a);
jjg@1373: Content skipLinkContent = getHyperLink(DocLink.fragment("skip-navbar_bottom"),
jjg@1373: HtmlTree.EMPTY,
jjg@1373: configuration.getText("doclet.Skip_navigation_links"),
jjg@1373: "");
bpatel@766: navDiv.addContent(skipLinkContent);
bpatel@766: }
bpatel@766: if (header) {
bpatel@766: navDiv.addContent(getMarkerAnchor("navbar_top_firstrow"));
bpatel@766: } else {
bpatel@766: navDiv.addContent(getMarkerAnchor("navbar_bottom_firstrow"));
bpatel@766: }
bpatel@766: HtmlTree navList = new HtmlTree(HtmlTag.UL);
bpatel@766: navList.addStyle(HtmlStyle.navList);
jjg@1606: navList.addAttr(HtmlAttr.TITLE,
jjg@1606: configuration.getText("doclet.Navigation"));
bpatel@766: if (configuration.createoverview) {
bpatel@766: navList.addContent(getNavLinkContents());
bpatel@766: }
bpatel@766: if (configuration.packages.length == 1) {
bpatel@766: navList.addContent(getNavLinkPackage(configuration.packages[0]));
bpatel@766: } else if (configuration.packages.length > 1) {
bpatel@766: navList.addContent(getNavLinkPackage());
bpatel@766: }
bpatel@766: navList.addContent(getNavLinkClass());
bpatel@766: if(configuration.classuse) {
bpatel@766: navList.addContent(getNavLinkClassUse());
bpatel@766: }
bpatel@766: if(configuration.createtree) {
bpatel@766: navList.addContent(getNavLinkTree());
bpatel@766: }
bpatel@766: if(!(configuration.nodeprecated ||
bpatel@766: configuration.nodeprecatedlist)) {
bpatel@766: navList.addContent(getNavLinkDeprecated());
bpatel@766: }
bpatel@766: if(configuration.createindex) {
bpatel@766: navList.addContent(getNavLinkIndex());
bpatel@766: }
bpatel@766: if (!configuration.nohelp) {
bpatel@766: navList.addContent(getNavLinkHelp());
bpatel@766: }
bpatel@766: navDiv.addContent(navList);
bpatel@766: Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, getUserHeaderFooter(header));
bpatel@766: navDiv.addContent(aboutDiv);
bpatel@766: body.addContent(navDiv);
bpatel@766: Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious());
bpatel@766: ulNav.addContent(getNavLinkNext());
bpatel@766: Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav);
bpatel@766: Content ulFrames = HtmlTree.UL(HtmlStyle.navList, getNavShowLists());
bpatel@766: ulFrames.addContent(getNavHideLists(filename));
bpatel@766: subDiv.addContent(ulFrames);
bpatel@766: HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex());
bpatel@766: ulAllClasses.addAttr(HtmlAttr.ID, allClassesId.toString());
bpatel@766: subDiv.addContent(ulAllClasses);
bpatel@766: subDiv.addContent(getAllClassesLinkScript(allClassesId.toString()));
bpatel@766: addSummaryDetailLinks(subDiv);
bpatel@766: if (header) {
bpatel@766: subDiv.addContent(getMarkerAnchor("skip-navbar_top"));
bpatel@766: body.addContent(subDiv);
bpatel@766: body.addContent(HtmlConstants.END_OF_TOP_NAVBAR);
bpatel@766: } else {
bpatel@766: subDiv.addContent(getMarkerAnchor("skip-navbar_bottom"));
bpatel@766: body.addContent(subDiv);
bpatel@766: body.addContent(HtmlConstants.END_OF_BOTTOM_NAVBAR);
bpatel@766: }
bpatel@766: }
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get the word "NEXT" to indicate that no link is available. Override
bpatel@766: * this method to customize next link.
bpatel@766: *
bpatel@766: * @return a content tree for the link
bpatel@766: */
bpatel@766: protected Content getNavLinkNext() {
bpatel@766: return getNavLinkNext(null);
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get the word "PREV" to indicate that no link is available. Override
bpatel@766: * this method to customize prev link.
bpatel@766: *
bpatel@766: * @return a content tree for the link
bpatel@766: */
bpatel@766: protected Content getNavLinkPrevious() {
bpatel@766: return getNavLinkPrevious(null);
bpatel@766: }
bpatel@766:
bpatel@766: /**
duke@1: * Do nothing. This is the default method.
duke@1: */
bpatel@766: protected void addSummaryDetailLinks(Content navDiv) {
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get link to the "overview-summary.html" page.
bpatel@766: *
bpatel@766: * @return a content tree for the link
bpatel@766: */
bpatel@766: protected Content getNavLinkContents() {
jjg@1372: Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_SUMMARY),
jjg@1373: overviewLabel, "", "");
bpatel@766: Content li = HtmlTree.LI(linkContent);
bpatel@766: return li;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get link to the "package-summary.html" page for the package passed.
bpatel@766: *
bpatel@766: * @param pkg Package to which link will be generated
bpatel@766: * @return a content tree for the link
bpatel@766: */
bpatel@766: protected Content getNavLinkPackage(PackageDoc pkg) {
bpatel@766: Content linkContent = getPackageLink(pkg,
bpatel@766: packageLabel);
bpatel@766: Content li = HtmlTree.LI(linkContent);
bpatel@766: return li;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get the word "Package" , to indicate that link is not available here.
bpatel@766: *
bpatel@766: * @return a content tree for the link
bpatel@766: */
bpatel@766: protected Content getNavLinkPackage() {
bpatel@766: Content li = HtmlTree.LI(packageLabel);
bpatel@766: return li;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get the word "Use", to indicate that link is not available.
bpatel@766: *
bpatel@766: * @return a content tree for the link
bpatel@766: */
bpatel@766: protected Content getNavLinkClassUse() {
bpatel@766: Content li = HtmlTree.LI(useLabel);
bpatel@766: return li;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get link for previous file.
bpatel@766: *
bpatel@766: * @param prev File name for the prev link
bpatel@766: * @return a content tree for the link
bpatel@766: */
jjg@1372: public Content getNavLinkPrevious(DocPath prev) {
bpatel@766: Content li;
bpatel@766: if (prev != null) {
jjg@1373: li = HtmlTree.LI(getHyperLink(prev, prevLabel, "", ""));
bpatel@766: }
bpatel@766: else
bpatel@766: li = HtmlTree.LI(prevLabel);
bpatel@766: return li;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get link for next file. If next is null, just print the label
bpatel@766: * without linking it anywhere.
bpatel@766: *
bpatel@766: * @param next File name for the next link
bpatel@766: * @return a content tree for the link
bpatel@766: */
jjg@1372: public Content getNavLinkNext(DocPath next) {
bpatel@766: Content li;
bpatel@766: if (next != null) {
jjg@1373: li = HtmlTree.LI(getHyperLink(next, nextLabel, "", ""));
bpatel@766: }
bpatel@766: else
bpatel@766: li = HtmlTree.LI(nextLabel);
bpatel@766: return li;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get "FRAMES" link, to switch to the frame version of the output.
bpatel@766: *
bpatel@766: * @param link File to be linked, "index.html"
bpatel@766: * @return a content tree for the link
bpatel@766: */
jjg@1372: protected Content getNavShowLists(DocPath link) {
jjg@1373: DocLink dl = new DocLink(link, path.getPath(), null);
jjg@1373: Content framesContent = getHyperLink(dl, framesLabel, "", "_top");
bpatel@766: Content li = HtmlTree.LI(framesContent);
bpatel@766: return li;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get "FRAMES" link, to switch to the frame version of the output.
bpatel@766: *
bpatel@766: * @return a content tree for the link
bpatel@766: */
bpatel@766: protected Content getNavShowLists() {
jjg@1372: return getNavShowLists(pathToRoot.resolve(DocPaths.INDEX));
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get "NO FRAMES" link, to switch to the non-frame version of the output.
bpatel@766: *
bpatel@766: * @param link File to be linked
bpatel@766: * @return a content tree for the link
bpatel@766: */
jjg@1372: protected Content getNavHideLists(DocPath link) {
jjg@1373: Content noFramesContent = getHyperLink(link, noframesLabel, "", "_top");
bpatel@766: Content li = HtmlTree.LI(noFramesContent);
bpatel@766: return li;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get "Tree" link in the navigation bar. If there is only one package
bpatel@766: * specified on the command line, then the "Tree" link will be to the
bpatel@766: * only "package-tree.html" file otherwise it will be to the
bpatel@766: * "overview-tree.html" file.
bpatel@766: *
bpatel@766: * @return a content tree for the link
duke@1: */
bpatel@766: protected Content getNavLinkTree() {
bpatel@766: Content treeLinkContent;
bpatel@766: PackageDoc[] packages = configuration.root.specifiedPackages();
bpatel@766: if (packages.length == 1 && configuration.root.specifiedClasses().length == 0) {
bpatel@766: treeLinkContent = getHyperLink(pathString(packages[0],
jjg@1373: DocPaths.PACKAGE_TREE), treeLabel,
bpatel@766: "", "");
bpatel@766: } else {
jjg@1372: treeLinkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE),
jjg@1373: treeLabel, "", "");
bpatel@766: }
bpatel@766: Content li = HtmlTree.LI(treeLinkContent);
bpatel@766: return li;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get the overview tree link for the main tree.
bpatel@766: *
bpatel@766: * @param label the label for the link
bpatel@766: * @return a content tree for the link
bpatel@766: */
bpatel@766: protected Content getNavLinkMainTree(String label) {
jjg@1372: Content mainTreeContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE),
bpatel@766: new StringContent(label));
bpatel@766: Content li = HtmlTree.LI(mainTreeContent);
bpatel@766: return li;
duke@1: }
duke@1:
duke@1: /**
bpatel@766: * Get the word "Class", to indicate that class link is not available.
bpatel@766: *
bpatel@766: * @return a content tree for the link
bpatel@766: */
bpatel@766: protected Content getNavLinkClass() {
bpatel@766: Content li = HtmlTree.LI(classLabel);
bpatel@766: return li;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get "Deprecated" API link in the navigation bar.
bpatel@766: *
bpatel@766: * @return a content tree for the link
bpatel@766: */
bpatel@766: protected Content getNavLinkDeprecated() {
jjg@1372: Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.DEPRECATED_LIST),
jjg@1373: deprecatedLabel, "", "");
bpatel@766: Content li = HtmlTree.LI(linkContent);
bpatel@766: return li;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get link for generated index. If the user has used "-splitindex"
bpatel@766: * command line option, then link to file "index-files/index-1.html" is
bpatel@766: * generated otherwise link to file "index-all.html" is generated.
bpatel@766: *
bpatel@766: * @return a content tree for the link
bpatel@766: */
bpatel@766: protected Content getNavLinkClassIndex() {
jjg@1372: Content allClassesContent = getHyperLink(pathToRoot.resolve(
jjg@1373: DocPaths.ALLCLASSES_NOFRAME),
bpatel@766: allclassesLabel, "", "");
bpatel@766: Content li = HtmlTree.LI(allClassesContent);
bpatel@766: return li;
bpatel@766: }
duke@1:
duke@1: /**
bpatel@766: * Get link for generated class index.
bpatel@766: *
bpatel@766: * @return a content tree for the link
bpatel@766: */
bpatel@766: protected Content getNavLinkIndex() {
jjg@1372: Content linkContent = getHyperLink(pathToRoot.resolve(
jjg@1372: (configuration.splitindex
jjg@1372: ? DocPaths.INDEX_FILES.resolve(DocPaths.indexN(1))
jjg@1373: : DocPaths.INDEX_ALL)),
bpatel@766: indexLabel, "", "");
bpatel@766: Content li = HtmlTree.LI(linkContent);
bpatel@766: return li;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get help file link. If user has provided a help file, then generate a
bpatel@766: * link to the user given file, which is already copied to current or
bpatel@766: * destination directory.
bpatel@766: *
bpatel@766: * @return a content tree for the link
bpatel@766: */
bpatel@766: protected Content getNavLinkHelp() {
jjg@1372: String helpfile = configuration.helpfile;
jjg@1372: DocPath helpfilenm;
jjg@1372: if (helpfile.isEmpty()) {
jjg@1372: helpfilenm = DocPaths.HELP_DOC;
bpatel@766: } else {
jjg@1383: DocFile file = DocFile.createFileForInput(configuration, helpfile);
jjg@1383: helpfilenm = DocPath.create(file.getName());
bpatel@766: }
jjg@1373: Content linkContent = getHyperLink(pathToRoot.resolve(helpfilenm),
bpatel@766: helpLabel, "", "");
bpatel@766: Content li = HtmlTree.LI(linkContent);
bpatel@766: return li;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get summary table header.
bpatel@766: *
bpatel@766: * @param header the header for the table
bpatel@766: * @param scope the scope of the headers
bpatel@766: * @return a content tree for the header
bpatel@766: */
bpatel@766: public Content getSummaryTableHeader(String[] header, String scope) {
bpatel@766: Content tr = new HtmlTree(HtmlTag.TR);
bpatel@766: int size = header.length;
bpatel@766: Content tableHeader;
bpatel@766: if (size == 1) {
bpatel@766: tableHeader = new StringContent(header[0]);
bpatel@766: tr.addContent(HtmlTree.TH(HtmlStyle.colOne, scope, tableHeader));
bpatel@766: return tr;
bpatel@766: }
bpatel@766: for (int i = 0; i < size; i++) {
bpatel@766: tableHeader = new StringContent(header[i]);
bpatel@766: if(i == 0)
bpatel@766: tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader));
bpatel@766: else if(i == (size - 1))
bpatel@766: tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader));
bpatel@766: else
bpatel@766: tr.addContent(HtmlTree.TH(scope, tableHeader));
bpatel@766: }
bpatel@766: return tr;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get table caption.
bpatel@766: *
bpatel@766: * @param rawText the caption for the table which could be raw Html
bpatel@766: * @return a content tree for the caption
bpatel@766: */
bpatel@766: public Content getTableCaption(String rawText) {
bpatel@766: Content title = new RawHtml(rawText);
bpatel@766: Content captionSpan = HtmlTree.SPAN(title);
bpatel@766: Content space = getSpace();
bpatel@766: Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space);
bpatel@766: Content caption = HtmlTree.CAPTION(captionSpan);
bpatel@766: caption.addContent(tabSpan);
bpatel@766: return caption;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get the marker anchor which will be added to the documentation tree.
bpatel@766: *
bpatel@766: * @param anchorName the anchor name attribute
bpatel@766: * @return a content tree for the marker anchor
bpatel@766: */
bpatel@766: public Content getMarkerAnchor(String anchorName) {
bpatel@766: return getMarkerAnchor(anchorName, null);
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Get the marker anchor which will be added to the documentation tree.
bpatel@766: *
bpatel@766: * @param anchorName the anchor name attribute
bpatel@766: * @param anchorContent the content that should be added to the anchor
bpatel@766: * @return a content tree for the marker anchor
bpatel@766: */
bpatel@766: public Content getMarkerAnchor(String anchorName, Content anchorContent) {
bpatel@766: if (anchorContent == null)
bpatel@766: anchorContent = new Comment(" ");
bpatel@766: Content markerAnchor = HtmlTree.A_NAME(anchorName, anchorContent);
bpatel@766: return markerAnchor;
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Returns a packagename content.
bpatel@766: *
bpatel@766: * @param packageDoc the package to check
bpatel@766: * @return package name content
bpatel@766: */
bpatel@766: public Content getPackageName(PackageDoc packageDoc) {
bpatel@766: return packageDoc == null || packageDoc.name().length() == 0 ?
bpatel@766: defaultPackageLabel :
bpatel@766: getPackageLabel(packageDoc.name());
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@766: * Returns a package name label.
bpatel@766: *
jjg@1358: * @param packageName the package name
bpatel@766: * @return the package name content
bpatel@766: */
bpatel@766: public Content getPackageLabel(String packageName) {
bpatel@766: return new StringContent(packageName);
bpatel@766: }
bpatel@766:
bpatel@766: /**
bpatel@995: * Add package deprecation information to the documentation tree
bpatel@995: *
bpatel@995: * @param deprPkgs list of deprecated packages
bpatel@995: * @param headingKey the caption for the deprecated package table
bpatel@995: * @param tableSummary the summary for the deprecated package table
bpatel@995: * @param tableHeader table headers for the deprecated package table
bpatel@995: * @param contentTree the content tree to which the deprecated package table will be added
bpatel@995: */
bpatel@995: protected void addPackageDeprecatedAPI(List
duke@1: * Here is the algorithm used to fix the link:
duke@1: *
jjg@1326: * {@literal
duke@1: * For example, suppose com.sun.javadoc.RootDoc has this link:
jjg@1326: * {@literal The package Page }
duke@1: *
duke@1: * If this link appeared in the index, we would redirect
duke@1: * the link like this:
duke@1: *
jjg@1326: * {@literal The package Page}
duke@1: *
duke@1: * @param doc the Doc object whose documentation is being written.
duke@1: * @param text the text being written.
duke@1: *
duke@1: * @return the text, with all the relative links redirected to work.
duke@1: */
duke@1: private String redirectRelativeLinks(Doc doc, String text) {
duke@1: if (doc == null || shouldNotRedirectRelativeLinks()) {
duke@1: return text;
duke@1: }
duke@1:
jjg@1372: DocPath redirectPathFromRoot;
duke@1: if (doc instanceof ClassDoc) {
jjg@1372: redirectPathFromRoot = DocPath.forPackage(((ClassDoc) doc).containingPackage());
duke@1: } else if (doc instanceof MemberDoc) {
jjg@1372: redirectPathFromRoot = DocPath.forPackage(((MemberDoc) doc).containingPackage());
duke@1: } else if (doc instanceof PackageDoc) {
jjg@1372: redirectPathFromRoot = DocPath.forPackage((PackageDoc) doc);
duke@1: } else {
duke@1: return text;
duke@1: }
duke@1:
duke@1: //Redirect all relative links.
duke@1: int end, begin = text.toLowerCase().indexOf("= 0){
jjg@1362: StringBuilder textBuff = new StringBuilder(text);
duke@1:
duke@1: while(begin >=0){
duke@1: if (textBuff.length() > begin + 2 && ! Character.isWhitespace(textBuff.charAt(begin+2))) {
duke@1: begin = textBuff.toString().toLowerCase().indexOf("", begin +1);
duke@1: if(begin == 0){
duke@1: //Link has no equal symbol.
duke@1: configuration.root.printWarning(
duke@1: doc.position(),
duke@1: configuration.getText("doclet.malformed_html_link_tag", text));
duke@1: break;
duke@1: }
duke@1: if (end == -1) {
duke@1: //Break without warning. This tag is not necessarily malformed. The text
duke@1: //might be missing '>' character because the href has an inline tag.
duke@1: break;
duke@1: }
jjg@1372: if (textBuff.substring(begin, end).indexOf("\"") != -1){
duke@1: begin = textBuff.indexOf("\"", begin) + 1;
duke@1: end = textBuff.indexOf("\"", begin +1);
jjg@1372: if (begin == 0 || end == -1){
duke@1: //Link is missing a quote.
duke@1: break;
duke@1: }
duke@1: }
duke@1: String relativeLink = textBuff.substring(begin, end);
jjg@1372: if (!(relativeLink.toLowerCase().startsWith("mailto:") ||
jjg@1372: relativeLink.toLowerCase().startsWith("http:") ||
jjg@1372: relativeLink.toLowerCase().startsWith("https:") ||
jjg@1372: relativeLink.toLowerCase().startsWith("file:"))) {
jjg@1372: relativeLink = "{@"+(new DocRootTaglet()).getName() + "}/"
jjg@1372: + redirectPathFromRoot.resolve(relativeLink).getPath();
duke@1: textBuff.replace(begin, end, relativeLink);
duke@1: }
duke@1: begin = textBuff.toString().toLowerCase().indexOf("", "", " ", " ", "", "
",
duke@1: "", "
", "", "
",
duke@1: "", " ", "", " ",
duke@1: "", " ", "", "
", "", "
",
duke@1: "", "
", "", "
",
duke@1: "", "
", "", "
",
duke@1: "", "
", "",
duke@1: "
",
duke@1: "", "
",
duke@1: "", "
", "", "
",
duke@1: "", "
", "", "
",
duke@1: "", " ", "", " ",
duke@1: "", " ", "", "
", "", "
",
duke@1: "", "
", "", "
",
duke@1: "", "
", "", "
",
duke@1: "", "
", "",
duke@1: "
",
duke@1: "", "
",
duke@1: "