duke@1: /* bpatel@3913: * Copyright (c) 1998, 2014, 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.*; jlahoda@2415: import java.util.regex.Matcher; jlahoda@2415: import java.util.regex.Pattern; 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.*; jlahoda@2413: import com.sun.tools.javac.util.StringUtils; 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@2035: * To check whether annotation field heading is printed or not. bpatel@2035: */ bpatel@2035: protected boolean printedAnnotationFieldHeading = false; bpatel@2035: bpatel@2035: /** 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 '&#064;' 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: } jlahoda@2415: Matcher docrootMatcher = docrootPattern.matcher(htmlstr); jlahoda@2415: if (!docrootMatcher.find()) { duke@1: return htmlstr; duke@1: } jjg@910: StringBuilder buf = new StringBuilder(); jlahoda@2415: int prevEnd = 0; jlahoda@2415: do { jlahoda@2415: int match = docrootMatcher.start(); jlahoda@2415: // append htmlstr up to start of next {@docroot} jlahoda@2415: buf.append(htmlstr.substring(prevEnd, match)); jlahoda@2415: prevEnd = docrootMatcher.end(); jlahoda@2415: if (configuration.docrootparent.length() > 0 && htmlstr.startsWith("/..", prevEnd)) { bpatel@2212: // Insert the absolute link if {@docRoot} is followed by "/..". bpatel@997: buf.append(configuration.docrootparent); jlahoda@2415: prevEnd += 3; bpatel@997: } else { bpatel@997: // Insert relative path where {@docRoot} was located jjg@1372: buf.append(pathToRoot.isEmpty() ? "." : pathToRoot.getPath()); bpatel@2212: } bpatel@2212: // Append slash if next character is not a slash jlahoda@2415: if (prevEnd < htmlstr.length() && htmlstr.charAt(prevEnd) != '/') { bpatel@2212: buf.append('/'); duke@1: } jlahoda@2415: } while (docrootMatcher.find()); jlahoda@2415: buf.append(htmlstr.substring(prevEnd)); duke@1: return buf.toString(); duke@1: } jlahoda@2415: //where: jlahoda@2415: // Note: {@docRoot} is not case sensitive when passed in w/command line option: jlahoda@2415: private static final Pattern docrootPattern = jlahoda@2415: Pattern.compile(Pattern.quote("{@docroot}"), Pattern.CASE_INSENSITIVE); 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@1751: Content output = new ContentBuilder(); duke@1: TagletWriter.genTagOuput(configuration.tagletManager, doc, jjg@1750: configuration.tagletManager.getCustomTaglets(doc), duke@1: getTagletWriterInstance(false), output); jjg@1751: dl.addContent(output); 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@1751: Content output = new ContentBuilder(); bpatel@233: TagletWriter.genTagOuput(configuration.tagletManager, field, jjg@1750: configuration.tagletManager.getCustomTaglets(field), bpatel@222: getTagletWriterInstance(false), output); jjg@1751: return !output.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); jjg@1747: Content caption = getTableCaption(new RawHtml(label)); bpatel@2084: Content table = HtmlTree.TABLE(HtmlStyle.typeSummary, 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@2163: summaryContentTree.addContent(table); 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); jjg@2006: head.addContent(getGeneratedBy(!configuration.notimestamp)); bpatel@766: if (configuration.charset.length() > 0) { bpatel@1981: Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, 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@1936: return rawContent; 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@1995: Content skipNavLinks = configuration.getResource("doclet.Skip_navigation_links"); 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@2101: Content a = getMarkerAnchor(SectionName.NAVBAR_TOP); bpatel@1995: //WCAG - Hyperlinks should contain text or an image with alt text - for AT tools bpatel@766: navDiv.addContent(a); bpatel@1995: Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav, getHyperLink( bpatel@2101: getDocLink(SectionName.SKIP_NAVBAR_TOP), skipNavLinks, bpatel@1995: skipNavLinks.toString(), "")); 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@2101: Content a = getMarkerAnchor(SectionName.NAVBAR_BOTTOM); bpatel@766: navDiv.addContent(a); bpatel@1995: Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav, getHyperLink( bpatel@2101: getDocLink(SectionName.SKIP_NAVBAR_BOTTOM), skipNavLinks, bpatel@1995: skipNavLinks.toString(), "")); bpatel@766: navDiv.addContent(skipLinkContent); bpatel@766: } bpatel@766: if (header) { bpatel@2101: navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_TOP_FIRSTROW)); bpatel@766: } else { bpatel@2101: navDiv.addContent(getMarkerAnchor(SectionName.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@2101: subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_TOP)); bpatel@766: body.addContent(subDiv); bpatel@766: body.addContent(HtmlConstants.END_OF_TOP_NAVBAR); bpatel@766: } else { bpatel@2101: subDiv.addContent(getMarkerAnchor(SectionName.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: */ jjg@1747: public Content getTableCaption(Content title) { 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@2101: return getMarkerAnchor(getName(anchorName), null); bpatel@2101: } bpatel@2101: bpatel@2101: /** bpatel@2101: * Get the marker anchor which will be added to the documentation tree. bpatel@2101: * bpatel@2101: * @param sectionName the section name anchor attribute for page bpatel@2101: * @return a content tree for the marker anchor bpatel@2101: */ bpatel@2101: public Content getMarkerAnchor(SectionName sectionName) { bpatel@2101: return getMarkerAnchor(sectionName.getName(), null); bpatel@2101: } bpatel@2101: bpatel@2101: /** bpatel@2101: * Get the marker anchor which will be added to the documentation tree. bpatel@2101: * bpatel@2101: * @param sectionName the section name anchor attribute for page bpatel@2101: * @param anchorName the anchor name combined with section name attribute for the page bpatel@2101: * @return a content tree for the marker anchor bpatel@2101: */ bpatel@2101: public Content getMarkerAnchor(SectionName sectionName, String anchorName) { bpatel@2101: return getMarkerAnchor(sectionName.getName() + getName(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 deprPkgs, String headingKey, bpatel@995: String tableSummary, String[] tableHeader, Content contentTree) { bpatel@995: if (deprPkgs.size() > 0) { bpatel@2084: Content table = HtmlTree.TABLE(HtmlStyle.deprecatedSummary, 0, 3, 0, tableSummary, jjg@1747: getTableCaption(configuration.getResource(headingKey))); bpatel@995: table.addContent(getSummaryTableHeader(tableHeader, "col")); bpatel@995: Content tbody = new HtmlTree(HtmlTag.TBODY); bpatel@995: for (int i = 0; i < deprPkgs.size(); i++) { bpatel@995: PackageDoc pkg = (PackageDoc) deprPkgs.get(i); bpatel@995: HtmlTree td = HtmlTree.TD(HtmlStyle.colOne, bpatel@995: getPackageLink(pkg, getPackageName(pkg))); bpatel@995: if (pkg.tags("deprecated").length > 0) { bpatel@995: addInlineDeprecatedComment(pkg, pkg.tags("deprecated")[0], td); bpatel@995: } bpatel@995: HtmlTree tr = HtmlTree.TR(td); bpatel@995: if (i % 2 == 0) { bpatel@995: tr.addStyle(HtmlStyle.altColor); bpatel@995: } else { bpatel@995: tr.addStyle(HtmlStyle.rowColor); bpatel@995: } bpatel@995: tbody.addContent(tr); bpatel@995: } bpatel@995: table.addContent(tbody); bpatel@995: Content li = HtmlTree.LI(HtmlStyle.blockList, table); bpatel@995: Content ul = HtmlTree.UL(HtmlStyle.blockList, li); bpatel@995: contentTree.addContent(ul); bpatel@995: } bpatel@995: } bpatel@995: bpatel@995: /** jjg@1381: * Return the path to the class page for a classdoc. duke@1: * duke@1: * @param cd Class to which the path is requested. duke@1: * @param name Name of the file(doesn't include path). duke@1: */ jjg@1372: protected DocPath pathString(ClassDoc cd, DocPath name) { duke@1: return pathString(cd.containingPackage(), name); duke@1: } duke@1: duke@1: /** duke@1: * Return path to the given file name in the given package. So if the name duke@1: * passed is "Object.html" and the name of the package is "java.lang", and duke@1: * if the relative path is "../.." then returned string will be duke@1: * "../../java/lang/Object.html" duke@1: * duke@1: * @param pd Package in which the file name is assumed to be. duke@1: * @param name File name, to which path string is. duke@1: */ jjg@1372: protected DocPath pathString(PackageDoc pd, DocPath name) { jjg@1372: return pathToRoot.resolve(DocPath.forPackage(pd).resolve(name)); duke@1: } duke@1: duke@1: /** duke@1: * Return the link to the given package. duke@1: * duke@1: * @param pkg the package to link to. duke@1: * @param label the label for the link. bpatel@766: * @return a content tree for the package link. bpatel@766: */ jjg@1740: public Content getPackageLink(PackageDoc pkg, String label) { jjg@1740: return getPackageLink(pkg, new StringContent(label)); jjg@1740: } jjg@1740: jjg@1740: /** jjg@1740: * Return the link to the given package. jjg@1740: * jjg@1740: * @param pkg the package to link to. jjg@1740: * @param label the label for the link. jjg@1740: * @return a content tree for the package link. jjg@1740: */ bpatel@766: public Content getPackageLink(PackageDoc pkg, Content label) { bpatel@766: boolean included = pkg != null && pkg.isIncluded(); bpatel@766: if (! included) { bpatel@766: PackageDoc[] packages = configuration.packages; bpatel@766: for (int i = 0; i < packages.length; i++) { bpatel@766: if (packages[i].equals(pkg)) { bpatel@766: included = true; bpatel@766: break; bpatel@766: } bpatel@766: } bpatel@766: } bpatel@766: if (included || pkg == null) { jjg@1372: return getHyperLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY), jjg@1373: label); bpatel@766: } else { jjg@1373: DocLink crossPkgLink = getCrossPackageLink(Util.getPackageName(pkg)); bpatel@766: if (crossPkgLink != null) { jjg@1373: return getHyperLink(crossPkgLink, label); duke@1: } else { duke@1: return label; duke@1: } duke@1: } duke@1: } duke@1: jjg@1737: public Content italicsClassName(ClassDoc cd, boolean qual) { jjg@1737: Content name = new StringContent((qual)? cd.qualifiedName(): cd.name()); bpatel@2147: return (cd.isInterface())? HtmlTree.SPAN(HtmlStyle.interfaceName, name): name; duke@1: } duke@1: duke@1: /** bpatel@766: * Add the link to the content tree. bpatel@766: * bpatel@766: * @param doc program element doc for which the link will be added bpatel@766: * @param label label for the link bpatel@766: * @param htmltree the content tree to which the link will be added bpatel@766: */ bpatel@766: public void addSrcLink(ProgramElementDoc doc, Content label, Content htmltree) { bpatel@766: if (doc == null) { bpatel@766: return; bpatel@766: } bpatel@766: ClassDoc cd = doc.containingClass(); bpatel@766: if (cd == null) { bpatel@766: //d must be a class doc since in has no containing class. bpatel@766: cd = (ClassDoc) doc; bpatel@766: } jjg@1372: DocPath href = pathToRoot jjg@1372: .resolve(DocPaths.SOURCE_OUTPUT) jjg@1372: .resolve(DocPath.forClass(cd)); jjg@1373: Content linkContent = getHyperLink(href.fragment(SourceToHTMLConverter.getAnchorName(doc)), label, "", ""); bpatel@766: htmltree.addContent(linkContent); bpatel@766: } bpatel@766: bpatel@766: /** duke@1: * Return the link to the given class. duke@1: * duke@1: * @param linkInfo the information about the link. duke@1: * duke@1: * @return the link for the given class. duke@1: */ jjg@1736: public Content getLink(LinkInfoImpl linkInfo) { duke@1: LinkFactoryImpl factory = new LinkFactoryImpl(this); jjg@1741: return factory.getLink(linkInfo); duke@1: } duke@1: duke@1: /** duke@1: * Return the type parameters for the given class. duke@1: * duke@1: * @param linkInfo the information about the link. duke@1: * @return the type for the given class. duke@1: */ jjg@1736: public Content getTypeParameterLinks(LinkInfoImpl linkInfo) { duke@1: LinkFactoryImpl factory = new LinkFactoryImpl(this); jjg@1736: return factory.getTypeParameterLinks(linkInfo, false); duke@1: } duke@1: duke@1: /************************************************************* duke@1: * Return a class cross link to external class documentation. duke@1: * The name must be fully qualified to determine which package duke@1: * the class is in. The -link option does not allow users to duke@1: * link to external classes in the "default" package. duke@1: * duke@1: * @param qualifiedClassName the qualified name of the external class. duke@1: * @param refMemName the name of the member being referenced. This should duke@1: * be null or empty string if no member is being referenced. duke@1: * @param label the label for the external link. bpatel@182: * @param strong true if the link should be strong. duke@1: * @param style the style of the link. duke@1: * @param code true if the label should be code font. duke@1: */ jjg@1737: public Content getCrossClassLink(String qualifiedClassName, String refMemName, jjg@1737: Content label, boolean strong, String style, duke@1: boolean code) { jjg@1373: String className = ""; jjg@1373: String packageName = qualifiedClassName == null ? "" : qualifiedClassName; duke@1: int periodIndex; jjg@1373: while ((periodIndex = packageName.lastIndexOf('.')) != -1) { duke@1: className = packageName.substring(periodIndex + 1, packageName.length()) + duke@1: (className.length() > 0 ? "." + className : ""); jjg@1737: Content defaultLabel = new StringContent(className); jjg@1737: if (code) jjg@1737: defaultLabel = HtmlTree.CODE(defaultLabel); duke@1: packageName = packageName.substring(0, periodIndex); duke@1: if (getCrossPackageLink(packageName) != null) { duke@1: //The package exists in external documentation, so link to the external duke@1: //class (assuming that it exists). This is definitely a limitation of duke@1: //the -link option. There are ways to determine if an external package duke@1: //exists, but no way to determine if the external class exists. We just duke@1: //have to assume that it does. jjg@1373: DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot, jjg@1373: className + ".html", refMemName); jjg@1737: return getHyperLink(link, jjg@1737: (label == null) || label.isEmpty() ? defaultLabel : label, bpatel@182: strong, style, duke@1: configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName), duke@1: ""); duke@1: } duke@1: } duke@1: return null; duke@1: } duke@1: duke@1: public boolean isClassLinkable(ClassDoc cd) { duke@1: if (cd.isIncluded()) { duke@1: return configuration.isGeneratedDoc(cd); duke@1: } duke@1: return configuration.extern.isExternal(cd); duke@1: } duke@1: jjg@1373: public DocLink getCrossPackageLink(String pkgName) { jjg@1372: return configuration.extern.getExternalLink(pkgName, pathToRoot, jjg@1373: DocPaths.PACKAGE_SUMMARY.getPath()); duke@1: } duke@1: bpatel@766: /** bpatel@766: * Get the class link. bpatel@766: * bpatel@766: * @param context the id of the context where the link will be added bpatel@766: * @param cd the class doc to link to bpatel@766: * @return a content tree for the link bpatel@766: */ jjg@1735: public Content getQualifiedClassLink(LinkInfoImpl.Kind context, ClassDoc cd) { jjg@1738: return getLink(new LinkInfoImpl(configuration, context, cd) jjg@1738: .label(configuration.getClassName(cd))); duke@1: } duke@1: duke@1: /** bpatel@766: * Add the class link. bpatel@766: * bpatel@766: * @param context the id of the context where the link will be added bpatel@766: * @param cd the class doc to link to bpatel@766: * @param contentTree the content tree to which the link will be added duke@1: */ jjg@1735: public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, ClassDoc cd, Content contentTree) { bpatel@766: addPreQualifiedClassLink(context, cd, false, contentTree); duke@1: } duke@1: duke@1: /** duke@1: * Retrieve the class link with the package portion of the label in jjg@1373: * plain text. If the qualifier is excluded, it will not be included in the duke@1: * link label. duke@1: * duke@1: * @param cd the class to link to. bpatel@182: * @param isStrong true if the link should be strong. duke@1: * @return the link with the package portion of the label in plain text. duke@1: */ jjg@1737: public Content getPreQualifiedClassLink(LinkInfoImpl.Kind context, bpatel@182: ClassDoc cd, boolean isStrong) { jjg@1737: ContentBuilder classlink = new ContentBuilder(); duke@1: PackageDoc pd = cd.containingPackage(); jjg@1737: if (pd != null && ! configuration.shouldExcludeQualifier(pd.name())) { jjg@1737: classlink.addContent(getPkgName(cd)); duke@1: } jjg@1737: classlink.addContent(getLink(new LinkInfoImpl(configuration, jjg@1738: context, cd).label(cd.name()).strong(isStrong))); duke@1: return classlink; duke@1: } duke@1: bpatel@766: /** bpatel@766: * Add the class link with the package portion of the label in bpatel@766: * plain text. If the qualifier is excluded, it will not be included in the bpatel@766: * link label. bpatel@766: * bpatel@766: * @param context the id of the context where the link will be added bpatel@766: * @param cd the class to link to bpatel@766: * @param isStrong true if the link should be strong bpatel@766: * @param contentTree the content tree to which the link with be added bpatel@766: */ jjg@1735: public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, bpatel@766: ClassDoc cd, boolean isStrong, Content contentTree) { bpatel@766: PackageDoc pd = cd.containingPackage(); bpatel@766: if(pd != null && ! configuration.shouldExcludeQualifier(pd.name())) { bpatel@766: contentTree.addContent(getPkgName(cd)); bpatel@766: } jjg@1736: contentTree.addContent(getLink(new LinkInfoImpl(configuration, jjg@1738: context, cd).label(cd.name()).strong(isStrong))); bpatel@766: } duke@1: duke@1: /** bpatel@766: * Add the class link, with only class name as the strong link and prefixing duke@1: * plain package name. bpatel@766: * bpatel@766: * @param context the id of the context where the link will be added bpatel@766: * @param cd the class to link to bpatel@766: * @param contentTree the content tree to which the link with be added duke@1: */ jjg@1735: public void addPreQualifiedStrongClassLink(LinkInfoImpl.Kind context, ClassDoc cd, Content contentTree) { bpatel@766: addPreQualifiedClassLink(context, cd, true, contentTree); duke@1: } duke@1: duke@1: /** bpatel@766: * Get the link for the given member. duke@1: * bpatel@766: * @param context the id of the context where the link will be added bpatel@766: * @param doc the member being linked to bpatel@766: * @param label the label for the link bpatel@766: * @return a content tree for the doc link duke@1: */ jjg@1735: public Content getDocLink(LinkInfoImpl.Kind context, MemberDoc doc, String label) { jjg@1737: return getDocLink(context, doc.containingClass(), doc, jjg@1737: new StringContent(label)); duke@1: } duke@1: duke@1: /** duke@1: * Return the link for the given member. duke@1: * duke@1: * @param context the id of the context where the link will be printed. duke@1: * @param doc the member being linked to. duke@1: * @param label the label for the link. bpatel@182: * @param strong true if the link should be strong. duke@1: * @return the link for the given member. duke@1: */ jjg@1736: public Content getDocLink(LinkInfoImpl.Kind context, MemberDoc doc, String label, jjg@1736: boolean strong) { bpatel@182: return getDocLink(context, doc.containingClass(), doc, label, strong); duke@1: } duke@1: duke@1: /** duke@1: * Return the link for the given member. duke@1: * duke@1: * @param context the id of the context where the link will be printed. duke@1: * @param classDoc the classDoc that we should link to. This is not duke@1: * necessarily equal to doc.containingClass(). We may be duke@1: * inheriting comments. duke@1: * @param doc the member being linked to. duke@1: * @param label the label for the link. bpatel@182: * @param strong true if the link should be strong. duke@1: * @return the link for the given member. duke@1: */ jjg@1736: public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, jjg@1736: String label, boolean strong) { jjg@1606: return getDocLink(context, classDoc, doc, label, strong, false); jjg@1606: } jjg@1746: public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, jjg@1746: Content label, boolean strong) { jjg@1746: return getDocLink(context, classDoc, doc, label, strong, false); jjg@1746: } jjg@1606: jjg@1606: /** jjg@1606: * Return the link for the given member. jjg@1606: * jjg@1606: * @param context the id of the context where the link will be printed. jjg@1606: * @param classDoc the classDoc that we should link to. This is not jjg@1606: * necessarily equal to doc.containingClass(). We may be jjg@1606: * inheriting comments. jjg@1606: * @param doc the member being linked to. jjg@1606: * @param label the label for the link. jjg@1606: * @param strong true if the link should be strong. jjg@1606: * @param isProperty true if the doc parameter is a JavaFX property. jjg@1606: * @return the link for the given member. jjg@1606: */ jjg@1736: public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, jjg@1737: String label, boolean strong, boolean isProperty) { jjg@1746: return getDocLink(context, classDoc, doc, new StringContent(check(label)), strong, isProperty); jjg@1746: } jjg@1746: jjg@1746: String check(String s) { jjg@1746: if (s.matches(".*[&<>].*"))throw new IllegalArgumentException(s); jjg@1746: return s; jjg@1737: } jjg@1737: jjg@1737: public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, jjg@1737: Content label, boolean strong, boolean isProperty) { duke@1: if (! (doc.isIncluded() || jjg@1410: Util.isLinkable(classDoc, configuration))) { jjg@1737: return label; duke@1: } else if (doc instanceof ExecutableMemberDoc) { duke@1: ExecutableMemberDoc emd = (ExecutableMemberDoc)doc; jjg@1738: return getLink(new LinkInfoImpl(configuration, context, classDoc) bpatel@2101: .label(label).where(getName(getAnchor(emd, isProperty))).strong(strong)); duke@1: } else if (doc instanceof MemberDoc) { jjg@1738: return getLink(new LinkInfoImpl(configuration, context, classDoc) bpatel@2101: .label(label).where(getName(doc.name())).strong(strong)); duke@1: } else { jjg@1737: return label; duke@1: } duke@1: } duke@1: bpatel@766: /** bpatel@766: * Return the link for the given member. bpatel@766: * bpatel@766: * @param context the id of the context where the link will be added bpatel@766: * @param classDoc the classDoc that we should link to. This is not bpatel@766: * necessarily equal to doc.containingClass(). We may be bpatel@766: * inheriting comments bpatel@766: * @param doc the member being linked to bpatel@766: * @param label the label for the link bpatel@766: * @return the link for the given member bpatel@766: */ jjg@1735: public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, jjg@1737: Content label) { bpatel@766: if (! (doc.isIncluded() || jjg@1410: Util.isLinkable(classDoc, configuration))) { jjg@1737: return label; bpatel@766: } else if (doc instanceof ExecutableMemberDoc) { jjg@1738: ExecutableMemberDoc emd = (ExecutableMemberDoc) doc; jjg@1738: return getLink(new LinkInfoImpl(configuration, context, classDoc) bpatel@2101: .label(label).where(getName(getAnchor(emd)))); bpatel@766: } else if (doc instanceof MemberDoc) { jjg@1738: return getLink(new LinkInfoImpl(configuration, context, classDoc) bpatel@2101: .label(label).where(getName(doc.name()))); bpatel@766: } else { jjg@1737: return label; bpatel@766: } bpatel@766: } bpatel@766: duke@1: public String getAnchor(ExecutableMemberDoc emd) { jjg@1606: return getAnchor(emd, false); jjg@1606: } jjg@1606: jjg@1606: public String getAnchor(ExecutableMemberDoc emd, boolean isProperty) { jjg@1606: if (isProperty) { jjg@1606: return emd.name(); jjg@1606: } duke@1: StringBuilder signature = new StringBuilder(emd.signature()); duke@1: StringBuilder signatureParsed = new StringBuilder(); duke@1: int counter = 0; duke@1: for (int i = 0; i < signature.length(); i++) { duke@1: char c = signature.charAt(i); duke@1: if (c == '<') { duke@1: counter++; duke@1: } else if (c == '>') { duke@1: counter--; duke@1: } else if (counter == 0) { duke@1: signatureParsed.append(c); duke@1: } duke@1: } duke@1: return emd.name() + signatureParsed.toString(); duke@1: } duke@1: jjg@1745: public Content seeTagToContent(SeeTag see) { duke@1: String tagName = see.name(); duke@1: if (! (tagName.startsWith("@link") || tagName.equals("@see"))) { jjg@1745: return new ContentBuilder(); duke@1: } jjg@1365: ksrini@2218: String seetext = replaceDocRootDir(Util.normalizeNewlines(see.text())); duke@1: duke@1: //Check if @see is an href or "string" duke@1: if (seetext.startsWith("<") || seetext.startsWith("\"")) { jjg@1745: return new RawHtml(seetext); duke@1: } duke@1: jjg@1365: boolean plain = tagName.equalsIgnoreCase("@linkplain"); jjg@1737: Content label = plainOrCode(plain, new RawHtml(see.label())); jjg@1365: duke@1: //The text from the @see tag. We will output this text when a label is not specified. jjg@1737: Content text = plainOrCode(plain, new RawHtml(seetext)); duke@1: duke@1: ClassDoc refClass = see.referencedClass(); duke@1: String refClassName = see.referencedClassName(); duke@1: MemberDoc refMem = see.referencedMember(); duke@1: String refMemName = see.referencedMemberName(); jjg@1365: duke@1: if (refClass == null) { duke@1: //@see is not referencing an included class duke@1: PackageDoc refPackage = see.referencedPackage(); duke@1: if (refPackage != null && refPackage.isIncluded()) { duke@1: //@see is referencing an included package jjg@1365: if (label.isEmpty()) jjg@1737: label = plainOrCode(plain, new StringContent(refPackage.name())); jjg@1745: return getPackageLink(refPackage, label); duke@1: } else { duke@1: //@see is not referencing an included class or package. Check for cross links. jjg@1737: Content classCrossLink; jjg@1373: DocLink packageCrossLink = getCrossPackageLink(refClassName); duke@1: if (packageCrossLink != null) { duke@1: //Package cross link found jjg@1737: return getHyperLink(packageCrossLink, jjg@1745: (label.isEmpty() ? text : label)); duke@1: } else if ((classCrossLink = getCrossClassLink(refClassName, jjg@1365: refMemName, label, false, "", !plain)) != null) { jjg@1365: //Class cross link found (possibly to a member in the class) jjg@1745: return classCrossLink; duke@1: } else { duke@1: //No cross link found so print warning duke@1: configuration.getDocletSpecificMsg().warning(see.position(), "doclet.see.class_or_package_not_found", duke@1: tagName, seetext); jjg@1745: return (label.isEmpty() ? text: label); duke@1: } duke@1: } duke@1: } else if (refMemName == null) { duke@1: // Must be a class reference since refClass is not null and refMemName is null. jjg@1365: if (label.isEmpty()) { jjg@1737: label = plainOrCode(plain, new StringContent(refClass.name())); duke@1: } jjg@1738: return getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, refClass) jjg@1745: .label(label)); duke@1: } else if (refMem == null) { duke@1: // Must be a member reference since refClass is not null and refMemName is not null. duke@1: // However, refMem is null, so this referenced member does not exist. jjg@1745: return (label.isEmpty() ? text: label); duke@1: } else { duke@1: // Must be a member reference since refClass is not null and refMemName is not null. duke@1: // refMem is not null, so this @see tag must be referencing a valid member. duke@1: ClassDoc containing = refMem.containingClass(); duke@1: if (see.text().trim().startsWith("#") && duke@1: ! (containing.isPublic() || jjg@1410: Util.isLinkable(containing, configuration))) { duke@1: // Since the link is relative and the holder is not even being duke@1: // documented, this must be an inherited link. Redirect it. duke@1: // The current class either overrides the referenced member or duke@1: // inherits it automatically. jjg@405: if (this instanceof ClassWriterImpl) { jjg@405: containing = ((ClassWriterImpl) this).getClassDoc(); jjg@405: } else if (!containing.isPublic()){ jjg@405: configuration.getDocletSpecificMsg().warning( jjg@405: see.position(), "doclet.see.class_or_package_not_accessible", jjg@405: tagName, containing.qualifiedName()); jjg@405: } else { jjg@405: configuration.getDocletSpecificMsg().warning( jjg@405: see.position(), "doclet.see.class_or_package_not_found", jjg@405: tagName, seetext); jjg@405: } duke@1: } duke@1: if (configuration.currentcd != containing) { bpatel@3913: refMemName = (refMem instanceof ConstructorDoc) ? bpatel@3913: refMemName : containing.name() + "." + refMemName; duke@1: } duke@1: if (refMem instanceof ExecutableMemberDoc) { duke@1: if (refMemName.indexOf('(') < 0) { duke@1: refMemName += ((ExecutableMemberDoc)refMem).signature(); duke@1: } duke@1: } duke@1: jjg@1737: text = plainOrCode(plain, new StringContent(refMemName)); jjg@1365: jjg@1735: return getDocLink(LinkInfoImpl.Kind.SEE_TAG, containing, jjg@1746: refMem, (label.isEmpty() ? text: label), false); duke@1: } duke@1: } duke@1: jjg@1737: private Content plainOrCode(boolean plain, Content body) { jjg@1737: return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body); jjg@1737: } jjg@1737: bpatel@766: /** bpatel@766: * Add the inline comment. bpatel@766: * bpatel@766: * @param doc the doc for which the inline comment will be added bpatel@766: * @param tag the inline tag to be added bpatel@766: * @param htmltree the content tree to which the comment will be added bpatel@766: */ bpatel@766: public void addInlineComment(Doc doc, Tag tag, Content htmltree) { jjg@1724: addCommentTags(doc, tag, tag.inlineTags(), false, false, htmltree); bpatel@766: } bpatel@766: bpatel@766: /** bpatel@766: * Add the inline deprecated comment. bpatel@766: * bpatel@766: * @param doc the doc for which the inline deprecated comment will be added bpatel@766: * @param tag the inline tag to be added bpatel@766: * @param htmltree the content tree to which the comment will be added bpatel@766: */ bpatel@766: public void addInlineDeprecatedComment(Doc doc, Tag tag, Content htmltree) { bpatel@766: addCommentTags(doc, tag.inlineTags(), true, false, htmltree); bpatel@766: } bpatel@766: bpatel@766: /** bpatel@766: * Adds the summary content. bpatel@766: * bpatel@766: * @param doc the doc for which the summary will be generated bpatel@766: * @param htmltree the documentation tree to which the summary will be added bpatel@766: */ bpatel@766: public void addSummaryComment(Doc doc, Content htmltree) { bpatel@766: addSummaryComment(doc, doc.firstSentenceTags(), htmltree); bpatel@766: } bpatel@766: bpatel@766: /** bpatel@766: * Adds the summary content. bpatel@766: * bpatel@766: * @param doc the doc for which the summary will be generated bpatel@766: * @param firstSentenceTags the first sentence tags for the doc bpatel@766: * @param htmltree the documentation tree to which the summary will be added bpatel@766: */ bpatel@766: public void addSummaryComment(Doc doc, Tag[] firstSentenceTags, Content htmltree) { bpatel@766: addCommentTags(doc, firstSentenceTags, false, true, htmltree); bpatel@766: } bpatel@766: bpatel@766: public void addSummaryDeprecatedComment(Doc doc, Tag tag, Content htmltree) { bpatel@766: addCommentTags(doc, tag.firstSentenceTags(), true, true, htmltree); bpatel@766: } bpatel@766: bpatel@766: /** bpatel@766: * Adds the inline comment. bpatel@766: * bpatel@766: * @param doc the doc for which the inline comments will be generated bpatel@766: * @param htmltree the documentation tree to which the inline comments will be added bpatel@766: */ bpatel@766: public void addInlineComment(Doc doc, Content htmltree) { bpatel@766: addCommentTags(doc, doc.inlineTags(), false, false, htmltree); bpatel@766: } bpatel@766: duke@1: /** bpatel@766: * Adds the comment tags. bpatel@766: * bpatel@766: * @param doc the doc for which the comment tags will be generated bpatel@766: * @param tags the first sentence tags for the doc bpatel@766: * @param depr true if it is deprecated jjg@1372: * @param first true if the first sentence tags should be added bpatel@766: * @param htmltree the documentation tree to which the comment tags will be added bpatel@766: */ bpatel@766: private void addCommentTags(Doc doc, Tag[] tags, boolean depr, bpatel@766: boolean first, Content htmltree) { jjg@1724: addCommentTags(doc, null, tags, depr, first, htmltree); jjg@1724: } jjg@1724: jjg@1724: /** jjg@1724: * Adds the comment tags. jjg@1724: * jjg@1724: * @param doc the doc for which the comment tags will be generated jjg@1724: * @param holderTag the block tag context for the inline tags jjg@1724: * @param tags the first sentence tags for the doc jjg@1724: * @param depr true if it is deprecated jjg@1724: * @param first true if the first sentence tags should be added jjg@1724: * @param htmltree the documentation tree to which the comment tags will be added jjg@1724: */ jjg@1724: private void addCommentTags(Doc doc, Tag holderTag, Tag[] tags, boolean depr, jjg@1724: boolean first, Content htmltree) { bpatel@766: if(configuration.nocomment){ bpatel@766: return; bpatel@766: } bpatel@766: Content div; jjg@1745: Content result = commentTagsToContent(null, doc, tags, first); bpatel@766: if (depr) { bpatel@2147: Content italic = HtmlTree.SPAN(HtmlStyle.deprecationComment, result); bpatel@766: div = HtmlTree.DIV(HtmlStyle.block, italic); bpatel@766: htmltree.addContent(div); bpatel@766: } bpatel@766: else { bpatel@766: div = HtmlTree.DIV(HtmlStyle.block, result); bpatel@766: htmltree.addContent(div); bpatel@766: } bpatel@766: if (tags.length == 0) { bpatel@766: htmltree.addContent(getSpace()); bpatel@766: } bpatel@766: } bpatel@766: bpatel@766: /** duke@1: * Converts inline tags and text to text strings, expanding the duke@1: * inline tags along the way. Called wherever text can contain duke@1: * an inline tag, such as in comments or in free-form text arguments duke@1: * to non-inline tags. duke@1: * duke@1: * @param holderTag specific tag where comment resides duke@1: * @param doc specific doc where comment resides duke@1: * @param tags array of text tags and inline tags (often alternating) duke@1: * present in the text of interest for this doc duke@1: * @param isFirstSentence true if text is first sentence duke@1: */ jjg@1745: public Content commentTagsToContent(Tag holderTag, Doc doc, Tag[] tags, duke@1: boolean isFirstSentence) { jjg@1747: Content result = new ContentBuilder(); bpatel@997: boolean textTagChange = false; duke@1: // Array of all possible inline tags for this javadoc run duke@1: configuration.tagletManager.checkTags(doc, tags, true); duke@1: for (int i = 0; i < tags.length; i++) { duke@1: Tag tagelem = tags[i]; duke@1: String tagName = tagelem.name(); duke@1: if (tagelem instanceof SeeTag) { jjg@1745: result.addContent(seeTagToContent((SeeTag) tagelem)); duke@1: } else if (! tagName.equals("Text")) { jjg@1745: boolean wasEmpty = result.isEmpty(); bpatel@2212: Content output; bpatel@2212: if (configuration.docrootparent.length() > 0 bpatel@2212: && tagelem.name().equals("@docRoot") bpatel@2212: && ((tags[i + 1]).text()).startsWith("/..")) { bpatel@2212: // If Xdocrootparent switch ON, set the flag to remove the /.. occurrence after bpatel@2212: // {@docRoot} tag in the very next Text tag. bpatel@2212: textTagChange = true; bpatel@2212: // Replace the occurrence of {@docRoot}/.. with the absolute link. bpatel@2212: output = new StringContent(configuration.docrootparent); bpatel@2212: } else { bpatel@2212: output = TagletWriter.getInlineTagOuput( bpatel@2212: configuration.tagletManager, holderTag, bpatel@2212: tagelem, getTagletWriterInstance(isFirstSentence)); bpatel@2212: } jjg@1745: if (output != null) jjg@1751: result.addContent(output); jjg@1745: if (wasEmpty && isFirstSentence && tagelem.name().equals("@inheritDoc") && !result.isEmpty()) { duke@1: break; duke@1: } else { bpatel@997: continue; duke@1: } duke@1: } else { bpatel@997: String text = tagelem.text(); bpatel@2212: //If Xdocrootparent switch ON, remove the /.. occurrence after {@docRoot} tag. bpatel@997: if (textTagChange) { bpatel@997: text = text.replaceFirst("/..", ""); bpatel@997: textTagChange = false; bpatel@997: } duke@1: //This is just a regular text tag. The text may contain html links () duke@1: //or inline tag {@docRoot}, which will be handled as special cases. bpatel@997: text = redirectRelativeLinks(tagelem.holder(), text); duke@1: duke@1: // Replace @docRoot only if not represented by an instance of DocRootTaglet, duke@1: // that is, only if it was not present in a source file doc comment. duke@1: // This happens when inserted by the doclet (a few lines duke@1: // above in this method). [It might also happen when passed in on the command duke@1: // line as a text argument to an option (like -header).] duke@1: text = replaceDocRootDir(text); duke@1: if (isFirstSentence) { duke@1: text = removeNonInlineHtmlTags(text); duke@1: } jjg@1748: text = Util.replaceTabs(configuration, text); jjg@1911: text = Util.normalizeNewlines(text); jjg@1748: result.addContent(new RawHtml(text)); duke@1: } duke@1: } jjg@1745: return result; duke@1: } duke@1: duke@1: /** duke@1: * Return true if relative links should not be redirected. duke@1: * duke@1: * @return Return true if a relative link should not be redirected. duke@1: */ duke@1: private boolean shouldNotRedirectRelativeLinks() { duke@1: return this instanceof AnnotationTypeWriter || duke@1: this instanceof ClassWriter || duke@1: this instanceof PackageSummaryWriter; duke@1: } duke@1: duke@1: /** duke@1: * Suppose a piece of documentation has a relative link. When you copy jjg@1372: * that documentation to another place such as the index or class-use page, duke@1: * that relative link will no longer work. We should redirect those links duke@1: * so that they will work again. duke@1: *

duke@1: * Here is the algorithm used to fix the link: duke@1: *

jjg@1326: * {@literal => docRoot + + } duke@1: *

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. jlahoda@2415: int end, begin = StringUtils.indexOfIgnoreCase(text, "= 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))) { jlahoda@2415: begin = StringUtils.indexOfIgnoreCase(textBuff.toString(), "", 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); jlahoda@2413: String relativeLinkLowerCase = StringUtils.toLowerCase(relativeLink); jlahoda@2413: if (!(relativeLinkLowerCase.startsWith("mailto:") || jlahoda@2413: relativeLinkLowerCase.startsWith("http:") || jlahoda@2413: relativeLinkLowerCase.startsWith("https:") || jlahoda@2413: relativeLinkLowerCase.startsWith("file:"))) { jjg@1372: relativeLink = "{@"+(new DocRootTaglet()).getName() + "}/" jjg@1372: + redirectPathFromRoot.resolve(relativeLink).getPath(); duke@1: textBuff.replace(begin, end, relativeLink); duke@1: } jlahoda@2415: begin = StringUtils.indexOfIgnoreCase(textBuff.toString(), " blockTags = new HashSet(); jjg@1748: static { jjg@1748: for (HtmlTag t: HtmlTag.values()) { jjg@1748: if (t.blockType == HtmlTag.BlockType.BLOCK) jjg@1748: blockTags.add(t.value); jjg@1748: } jjg@1748: } jjg@1748: jjg@1748: public static String removeNonInlineHtmlTags(String text) { jjg@1748: final int len = text.length(); jjg@1748: jjg@1748: int startPos = 0; // start of text to copy jjg@1748: int lessThanPos = text.indexOf('<'); // position of latest '<' jjg@1748: if (lessThanPos < 0) { duke@1: return text; duke@1: } jjg@1748: jjg@1748: StringBuilder result = new StringBuilder(); jjg@1748: main: while (lessThanPos != -1) { jjg@1748: int currPos = lessThanPos + 1; jjg@1748: if (currPos == len) jjg@1748: break; jjg@1748: char ch = text.charAt(currPos); jjg@1748: if (ch == '/') { jjg@1748: if (++currPos == len) jjg@1748: break; jjg@1748: ch = text.charAt(currPos); jjg@1748: } jjg@1748: int tagPos = currPos; jjg@1748: while (isHtmlTagLetterOrDigit(ch)) { jjg@1748: if (++currPos == len) jjg@1748: break main; jjg@1748: ch = text.charAt(currPos); jjg@1748: } jlahoda@2413: if (ch == '>' && blockTags.contains(StringUtils.toLowerCase(text.substring(tagPos, currPos)))) { jjg@1748: result.append(text, startPos, lessThanPos); jjg@1748: startPos = currPos + 1; jjg@1748: } jjg@1748: lessThanPos = text.indexOf('<', currPos); duke@1: } jjg@1748: result.append(text.substring(startPos)); jjg@1748: jjg@1748: return result.toString(); duke@1: } duke@1: jjg@1755: private static boolean isHtmlTagLetterOrDigit(char ch) { jjg@1748: return ('a' <= ch && ch <= 'z') || jjg@1748: ('A' <= ch && ch <= 'Z') || jjg@1748: ('1' <= ch && ch <= '6'); duke@1: } duke@1: duke@1: /** bpatel@766: * Returns a link to the stylesheet file. bpatel@766: * bpatel@766: * @return an HtmlTree for the lINK tag which provides the stylesheet location bpatel@766: */ bpatel@766: public HtmlTree getStyleSheetProperties() { jjg@1383: String stylesheetfile = configuration.stylesheetfile; jjg@1372: DocPath stylesheet; jjg@1383: if (stylesheetfile.isEmpty()) { jjg@1383: stylesheet = DocPaths.STYLESHEET; bpatel@766: } else { jjg@1383: DocFile file = DocFile.createFileForInput(configuration, stylesheetfile); jjg@1383: stylesheet = DocPath.create(file.getName()); bpatel@766: } jjg@1372: HtmlTree link = HtmlTree.LINK("stylesheet", "text/css", jjg@1372: pathToRoot.resolve(stylesheet).getPath(), jjg@1372: "Style"); bpatel@766: return link; bpatel@766: } bpatel@766: bpatel@766: /** bpatel@1417: * Returns a link to the JavaScript file. bpatel@1417: * bpatel@1417: * @return an HtmlTree for the Script tag which provides the JavaScript location bpatel@1417: */ bpatel@1417: public HtmlTree getScriptProperties() { bpatel@1417: HtmlTree script = HtmlTree.SCRIPT("text/javascript", bpatel@1417: pathToRoot.resolve(DocPaths.JAVASCRIPT).getPath()); bpatel@1417: return script; bpatel@1417: } bpatel@1417: bpatel@1417: /** jjh@972: * According to jjh@972: * The Java™ Language Specification, jjh@972: * all the outer classes and static nested classes are core classes. duke@1: */ duke@1: public boolean isCoreClass(ClassDoc cd) { duke@1: return cd.containingClass() == null || cd.isStatic(); duke@1: } duke@1: duke@1: /** bpatel@766: * Adds the annotatation types for the given packageDoc. bpatel@766: * bpatel@766: * @param packageDoc the package to write annotations for. bpatel@766: * @param htmltree the documentation tree to which the annotation info will be bpatel@766: * added bpatel@766: */ bpatel@766: public void addAnnotationInfo(PackageDoc packageDoc, Content htmltree) { bpatel@766: addAnnotationInfo(packageDoc, packageDoc.annotations(), htmltree); bpatel@766: } bpatel@766: bpatel@766: /** jjg@1521: * Add the annotation types of the executable receiver. jjg@1521: * jjg@1521: * @param method the executable to write the receiver annotations for. bpatel@1686: * @param descList list of annotation description. jjg@1521: * @param htmltree the documentation tree to which the annotation info will be jjg@1521: * added jjg@1521: */ bpatel@1686: public void addReceiverAnnotationInfo(ExecutableMemberDoc method, AnnotationDesc[] descList, bpatel@1686: Content htmltree) { bpatel@1686: addAnnotationInfo(0, method, descList, false, htmltree); jjg@1521: } jjg@1521: jjg@1521: /** bpatel@766: * Adds the annotatation types for the given doc. bpatel@766: * jjg@1358: * @param doc the package to write annotations for bpatel@766: * @param htmltree the content tree to which the annotation types will be added bpatel@766: */ bpatel@766: public void addAnnotationInfo(ProgramElementDoc doc, Content htmltree) { bpatel@766: addAnnotationInfo(doc, doc.annotations(), htmltree); bpatel@766: } bpatel@766: bpatel@766: /** bpatel@766: * Add the annotatation types for the given doc and parameter. bpatel@766: * bpatel@766: * @param indent the number of spaces to indent the parameters. bpatel@766: * @param doc the doc to write annotations for. bpatel@766: * @param param the parameter to write annotations for. bpatel@766: * @param tree the content tree to which the annotation types will be added bpatel@766: */ bpatel@766: public boolean addAnnotationInfo(int indent, Doc doc, Parameter param, bpatel@766: Content tree) { bpatel@766: return addAnnotationInfo(indent, doc, param.annotations(), false, tree); bpatel@766: } bpatel@766: bpatel@766: /** bpatel@766: * Adds the annotatation types for the given doc. bpatel@766: * bpatel@766: * @param doc the doc to write annotations for. bpatel@766: * @param descList the array of {@link AnnotationDesc}. bpatel@766: * @param htmltree the documentation tree to which the annotation info will be bpatel@766: * added bpatel@766: */ bpatel@766: private void addAnnotationInfo(Doc doc, AnnotationDesc[] descList, bpatel@766: Content htmltree) { bpatel@766: addAnnotationInfo(0, doc, descList, true, htmltree); bpatel@766: } bpatel@766: bpatel@766: /** jjg@1747: * Adds the annotation types for the given doc. bpatel@766: * bpatel@766: * @param indent the number of extra spaces to indent the annotations. bpatel@766: * @param doc the doc to write annotations for. bpatel@766: * @param descList the array of {@link AnnotationDesc}. bpatel@766: * @param htmltree the documentation tree to which the annotation info will be bpatel@766: * added bpatel@766: */ bpatel@766: private boolean addAnnotationInfo(int indent, Doc doc, bpatel@766: AnnotationDesc[] descList, boolean lineBreak, Content htmltree) { jjg@1747: List annotations = getAnnotations(indent, descList, lineBreak); bpatel@1686: String sep =""; jjg@1747: if (annotations.isEmpty()) { bpatel@766: return false; bpatel@766: } jjg@1747: for (Content annotation: annotations) { bpatel@1686: htmltree.addContent(sep); jjg@1747: htmltree.addContent(annotation); bpatel@1686: sep = " "; bpatel@766: } bpatel@766: return true; bpatel@766: } bpatel@766: bpatel@766: /** duke@1: * Return the string representations of the annotation types for duke@1: * the given doc. duke@1: * duke@1: * @param indent the number of extra spaces to indent the annotations. duke@1: * @param descList the array of {@link AnnotationDesc}. duke@1: * @param linkBreak if true, add new line between each member value. duke@1: * @return an array of strings representing the annotations being duke@1: * documented. duke@1: */ jjg@1747: private List getAnnotations(int indent, AnnotationDesc[] descList, boolean linkBreak) { jjg@1521: return getAnnotations(indent, descList, linkBreak, true); jjg@1521: } jjg@1521: jjg@1521: /** jjg@1521: * Return the string representations of the annotation types for jjg@1521: * the given doc. jjg@1521: * jjg@1521: * A {@code null} {@code elementType} indicates that all the jjg@1521: * annotations should be returned without any filtering. jjg@1521: * jjg@1521: * @param indent the number of extra spaces to indent the annotations. jjg@1521: * @param descList the array of {@link AnnotationDesc}. jjg@1521: * @param linkBreak if true, add new line between each member value. jjg@1521: * @param elementType the type of targeted element (used for filtering jjg@1521: * type annotations from declaration annotations) jjg@1521: * @return an array of strings representing the annotations being jjg@1521: * documented. jjg@1521: */ jjg@1747: public List getAnnotations(int indent, AnnotationDesc[] descList, boolean linkBreak, jjg@1521: boolean isJava5DeclarationLocation) { jjg@1747: List results = new ArrayList(); jjg@1747: ContentBuilder annotation; duke@1: for (int i = 0; i < descList.length; i++) { duke@1: AnnotationTypeDoc annotationDoc = descList[i].annotationType(); bpatel@1477: // If an annotation is not documented, do not add it to the list. If bpatel@1477: // the annotation is of a repeatable type, and if it is not documented bpatel@1477: // and also if its container annotation is not documented, do not add it bpatel@1477: // to the list. If an annotation of a repeatable type is not documented bpatel@1477: // but its container is documented, it will be added to the list. bpatel@1477: if (! Util.isDocumentedAnnotation(annotationDoc) && bpatel@1477: (!isAnnotationDocumented && !isContainerDocumented)) { duke@1: continue; duke@1: } jjg@1521: /* TODO: check logic here to correctly handle declaration jjg@1521: * and type annotations. jjg@1521: if (Util.isDeclarationAnnotation(annotationDoc, isJava5DeclarationLocation)) { jjg@1521: continue; jjg@1521: }*/ jjg@1747: annotation = new ContentBuilder(); bpatel@1477: isAnnotationDocumented = false; jjg@1410: LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, jjg@1735: LinkInfoImpl.Kind.ANNOTATION, annotationDoc); duke@1: AnnotationDesc.ElementValuePair[] pairs = descList[i].elementValues(); bpatel@1477: // If the annotation is synthesized, do not print the container. bpatel@1477: if (descList[i].isSynthesized()) { duke@1: for (int j = 0; j < pairs.length; j++) { duke@1: AnnotationValue annotationValue = pairs[j].value(); jjg@74: List annotationTypeValues = new ArrayList(); duke@1: if (annotationValue.value() instanceof AnnotationValue[]) { duke@1: AnnotationValue[] annotationArray = bpatel@1477: (AnnotationValue[]) annotationValue.value(); bpatel@1477: annotationTypeValues.addAll(Arrays.asList(annotationArray)); duke@1: } else { duke@1: annotationTypeValues.add(annotationValue); duke@1: } bpatel@1477: String sep = ""; bpatel@1477: for (AnnotationValue av : annotationTypeValues) { jjg@1747: annotation.addContent(sep); jjg@1747: annotation.addContent(annotationValueToContent(av)); bpatel@1477: sep = " "; duke@1: } duke@1: } bpatel@1477: } bpatel@1477: else if (isAnnotationArray(pairs)) { bpatel@1477: // If the container has 1 or more value defined and if the bpatel@1477: // repeatable type annotation is not documented, do not print bpatel@1477: // the container. bpatel@1477: if (pairs.length == 1 && isAnnotationDocumented) { bpatel@1477: AnnotationValue[] annotationArray = bpatel@1477: (AnnotationValue[]) (pairs[0].value()).value(); bpatel@1477: List annotationTypeValues = new ArrayList(); bpatel@1477: annotationTypeValues.addAll(Arrays.asList(annotationArray)); bpatel@1477: String sep = ""; bpatel@1477: for (AnnotationValue av : annotationTypeValues) { jjg@1747: annotation.addContent(sep); jjg@1747: annotation.addContent(annotationValueToContent(av)); bpatel@1477: sep = " "; bpatel@1477: } bpatel@1477: } bpatel@1477: // If the container has 1 or more value defined and if the bpatel@1477: // repeatable type annotation is not documented, print the container. bpatel@1477: else { bpatel@1477: addAnnotations(annotationDoc, linkInfo, annotation, pairs, bpatel@1477: indent, false); bpatel@1477: } bpatel@1477: } bpatel@1477: else { bpatel@1477: addAnnotations(annotationDoc, linkInfo, annotation, pairs, bpatel@1477: indent, linkBreak); duke@1: } jjg@1747: annotation.addContent(linkBreak ? DocletConstants.NL : ""); jjg@1747: results.add(annotation); duke@1: } duke@1: return results; duke@1: } duke@1: bpatel@1477: /** bpatel@1477: * Add annotation to the annotation string. bpatel@1477: * bpatel@1477: * @param annotationDoc the annotation being documented bpatel@1477: * @param linkInfo the information about the link bpatel@1477: * @param annotation the annotation string to which the annotation will be added bpatel@1477: * @param pairs annotation type element and value pairs bpatel@1477: * @param indent the number of extra spaces to indent the annotations. bpatel@1477: * @param linkBreak if true, add new line between each member value bpatel@1477: */ bpatel@1477: private void addAnnotations(AnnotationTypeDoc annotationDoc, LinkInfoImpl linkInfo, jjg@1747: ContentBuilder annotation, AnnotationDesc.ElementValuePair[] pairs, bpatel@1477: int indent, boolean linkBreak) { jjg@1737: linkInfo.label = new StringContent("@" + annotationDoc.name()); jjg@1747: annotation.addContent(getLink(linkInfo)); bpatel@1477: if (pairs.length > 0) { jjg@1747: annotation.addContent("("); bpatel@1477: for (int j = 0; j < pairs.length; j++) { bpatel@1477: if (j > 0) { jjg@1747: annotation.addContent(","); bpatel@1477: if (linkBreak) { jjg@1747: annotation.addContent(DocletConstants.NL); bpatel@1477: int spaces = annotationDoc.name().length() + 2; bpatel@1477: for (int k = 0; k < (spaces + indent); k++) { jjg@1747: annotation.addContent(" "); bpatel@1477: } bpatel@1477: } bpatel@1477: } jjg@1747: annotation.addContent(getDocLink(LinkInfoImpl.Kind.ANNOTATION, bpatel@1477: pairs[j].element(), pairs[j].element().name(), false)); jjg@1747: annotation.addContent("="); bpatel@1477: AnnotationValue annotationValue = pairs[j].value(); bpatel@1477: List annotationTypeValues = new ArrayList(); bpatel@1477: if (annotationValue.value() instanceof AnnotationValue[]) { bpatel@1477: AnnotationValue[] annotationArray = bpatel@1477: (AnnotationValue[]) annotationValue.value(); bpatel@1477: annotationTypeValues.addAll(Arrays.asList(annotationArray)); bpatel@1477: } else { bpatel@1477: annotationTypeValues.add(annotationValue); bpatel@1477: } jjg@1747: annotation.addContent(annotationTypeValues.size() == 1 ? "" : "{"); bpatel@1477: String sep = ""; bpatel@1477: for (AnnotationValue av : annotationTypeValues) { jjg@1747: annotation.addContent(sep); jjg@1747: annotation.addContent(annotationValueToContent(av)); bpatel@1477: sep = ","; bpatel@1477: } jjg@1747: annotation.addContent(annotationTypeValues.size() == 1 ? "" : "}"); bpatel@1477: isContainerDocumented = false; bpatel@1477: } jjg@1747: annotation.addContent(")"); bpatel@1477: } bpatel@1477: } bpatel@1477: bpatel@1477: /** bpatel@1477: * Check if the annotation contains an array of annotation as a value. This bpatel@1477: * check is to verify if a repeatable type annotation is present or not. bpatel@1477: * bpatel@1477: * @param pairs annotation type element and value pairs bpatel@1477: * bpatel@1477: * @return true if the annotation contains an array of annotation as a value. bpatel@1477: */ bpatel@1477: private boolean isAnnotationArray(AnnotationDesc.ElementValuePair[] pairs) { bpatel@1477: AnnotationValue annotationValue; bpatel@1477: for (int j = 0; j < pairs.length; j++) { bpatel@1477: annotationValue = pairs[j].value(); bpatel@1477: if (annotationValue.value() instanceof AnnotationValue[]) { bpatel@1477: AnnotationValue[] annotationArray = bpatel@1477: (AnnotationValue[]) annotationValue.value(); bpatel@1477: if (annotationArray.length > 1) { bpatel@1477: if (annotationArray[0].value() instanceof AnnotationDesc) { bpatel@1477: AnnotationTypeDoc annotationDoc = bpatel@1477: ((AnnotationDesc) annotationArray[0].value()).annotationType(); bpatel@1477: isContainerDocumented = true; bpatel@1477: if (Util.isDocumentedAnnotation(annotationDoc)) { bpatel@1477: isAnnotationDocumented = true; bpatel@1477: } bpatel@1477: return true; bpatel@1477: } bpatel@1477: } bpatel@1477: } bpatel@1477: } bpatel@1477: return false; bpatel@1477: } bpatel@1477: jjg@1747: private Content annotationValueToContent(AnnotationValue annotationValue) { duke@1: if (annotationValue.value() instanceof Type) { duke@1: Type type = (Type) annotationValue.value(); duke@1: if (type.asClassDoc() != null) { jjg@1410: LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, jjg@1735: LinkInfoImpl.Kind.ANNOTATION, type); jjg@1737: linkInfo.label = new StringContent((type.asClassDoc().isIncluded() ? jjg@1737: type.typeName() : jjg@1737: type.qualifiedTypeName()) + type.dimension() + ".class"); jjg@1747: return getLink(linkInfo); duke@1: } else { jjg@1747: return new StringContent(type.typeName() + type.dimension() + ".class"); duke@1: } duke@1: } else if (annotationValue.value() instanceof AnnotationDesc) { jjg@1747: List list = getAnnotations(0, duke@1: new AnnotationDesc[]{(AnnotationDesc) annotationValue.value()}, duke@1: false); jjg@1747: ContentBuilder buf = new ContentBuilder(); jjg@1747: for (Content c: list) { jjg@1747: buf.addContent(c); duke@1: } jjg@1747: return buf; duke@1: } else if (annotationValue.value() instanceof MemberDoc) { jjg@1735: return getDocLink(LinkInfoImpl.Kind.ANNOTATION, duke@1: (MemberDoc) annotationValue.value(), jjg@1747: ((MemberDoc) annotationValue.value()).name(), false); duke@1: } else { jjg@1747: return new StringContent(annotationValue.toString()); duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Return the configuation for this doclet. duke@1: * duke@1: * @return the configuration for this doclet. duke@1: */ duke@1: public Configuration configuration() { duke@1: return configuration; duke@1: } duke@1: }