--- old/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/HtmlDocletWriter.java Fri Jan 22 12:17:50 2016 +++ /dev/null Fri Jan 22 12:17:50 2016 @@ -1,2227 +0,0 @@ -/* - * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.tools.doclets.formats.html; - -import java.io.*; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.sun.javadoc.*; -import com.sun.tools.doclets.formats.html.markup.*; -import com.sun.tools.doclets.internal.toolkit.*; -import com.sun.tools.doclets.internal.toolkit.taglets.*; -import com.sun.tools.doclets.internal.toolkit.util.*; -import com.sun.tools.javac.util.StringUtils; - -/** - * Class for the Html Format Code Generation specific to JavaDoc. - * This Class contains methods related to the Html Code Generation which - * are used extensively while generating the entire documentation. - * - *

This is NOT part of any supported API. - * If you write code that depends on this, you do so at your own risk. - * This code and its internal interfaces are subject to change or - * deletion without notice. - * - * @since 1.2 - * @author Atul M Dambalkar - * @author Robert Field - * @author Bhavesh Patel (Modified) - */ -public class HtmlDocletWriter extends HtmlDocWriter { - - /** - * Relative path from the file getting generated to the destination - * directory. For example, if the file getting generated is - * "java/lang/Object.html", then the path to the root is "../..". - * This string can be empty if the file getting generated is in - * the destination directory. - */ - public final DocPath pathToRoot; - - /** - * Platform-independent path from the current or the - * destination directory to the file getting generated. - * Used when creating the file. - */ - public final DocPath path; - - /** - * Name of the file getting generated. If the file getting generated is - * "java/lang/Object.html", then the filename is "Object.html". - */ - public final DocPath filename; - - /** - * The global configuration information for this run. - */ - public final ConfigurationImpl configuration; - - protected final Utils utils; - - /** - * To check whether annotation heading is printed or not. - */ - protected boolean printedAnnotationHeading = false; - - /** - * To check whether annotation field heading is printed or not. - */ - protected boolean printedAnnotationFieldHeading = false; - - /** - * To check whether the repeated annotations is documented or not. - */ - private boolean isAnnotationDocumented = false; - - /** - * To check whether the container annotations is documented or not. - */ - private boolean isContainerDocumented = false; - - HtmlTree fixedNavDiv = new HtmlTree(HtmlTag.DIV); - - /** - * Constructor to construct the HtmlStandardWriter object. - * - * @param path File to be generated. - */ - public HtmlDocletWriter(ConfigurationImpl configuration, DocPath path) - throws IOException { - super(configuration, path); - this.configuration = configuration; - this.utils = configuration.utils; - this.path = path; - this.pathToRoot = path.parent().invert(); - this.filename = path.basename(); - } - - /** - * Replace {@docRoot} tag used in options that accept HTML text, such - * as -header, -footer, -top and -bottom, and when converting a relative - * HREF where commentTagsToString inserts a {@docRoot} where one was - * missing. (Also see DocRootTaglet for {@docRoot} tags in doc - * comments.) - *

- * Replace {@docRoot} tag in htmlstr with the relative path to the - * destination directory from the directory where the file is being - * written, looping to handle all such tags in htmlstr. - *

- * For example, for "-d docs" and -header containing {@docRoot}, when - * the HTML page for source file p/C1.java is being generated, the - * {@docRoot} tag would be inserted into the header as "../", - * the relative path from docs/p/ to docs/ (the document root). - *

- * Note: This doc comment was written with '&#064;' representing '@' - * to prevent the inline tag from being interpreted. - */ - public String replaceDocRootDir(String htmlstr) { - // Return if no inline tags exist - int index = htmlstr.indexOf("{@"); - if (index < 0) { - return htmlstr; - } - Matcher docrootMatcher = docrootPattern.matcher(htmlstr); - if (!docrootMatcher.find()) { - return htmlstr; - } - StringBuilder buf = new StringBuilder(); - int prevEnd = 0; - do { - int match = docrootMatcher.start(); - // append htmlstr up to start of next {@docroot} - buf.append(htmlstr.substring(prevEnd, match)); - prevEnd = docrootMatcher.end(); - if (configuration.docrootparent.length() > 0 && htmlstr.startsWith("/..", prevEnd)) { - // Insert the absolute link if {@docRoot} is followed by "/..". - buf.append(configuration.docrootparent); - prevEnd += 3; - } else { - // Insert relative path where {@docRoot} was located - buf.append(pathToRoot.isEmpty() ? "." : pathToRoot.getPath()); - } - // Append slash if next character is not a slash - if (prevEnd < htmlstr.length() && htmlstr.charAt(prevEnd) != '/') { - buf.append('/'); - } - } while (docrootMatcher.find()); - buf.append(htmlstr.substring(prevEnd)); - return buf.toString(); - } - //where: - // Note: {@docRoot} is not case sensitive when passed in w/command line option: - private static final Pattern docrootPattern = - Pattern.compile(Pattern.quote("{@docroot}"), Pattern.CASE_INSENSITIVE); - - /** - * Get the script to show or hide the All classes link. - * - * @param id id of the element to show or hide - * @return a content tree for the script - */ - public Content getAllClassesLinkScript(String id) { - HtmlTree script = HtmlTree.SCRIPT(); - String scriptCode = "" + DocletConstants.NL; - Content scriptContent = new RawHtml(scriptCode); - script.addContent(scriptContent); - Content div = HtmlTree.DIV(script); - Content div_noscript = HtmlTree.DIV(getResource("doclet.No_Script_Message")); - Content noScript = HtmlTree.NOSCRIPT(div_noscript); - div.addContent(noScript); - return div; - } - - /** - * Add method information. - * - * @param method the method to be documented - * @param dl the content tree to which the method information will be added - */ - private void addMethodInfo(MethodDoc method, Content dl) { - ClassDoc[] intfacs = method.containingClass().interfaces(); - MethodDoc overriddenMethod = method.overriddenMethod(); - // Check whether there is any implementation or overridden info to be - // printed. If no overridden or implementation info needs to be - // printed, do not print this section. - if ((intfacs.length > 0 && - new ImplementedMethods(method, this.configuration).build().length > 0) || - overriddenMethod != null) { - MethodWriterImpl.addImplementsInfo(this, method, dl); - if (overriddenMethod != null) { - MethodWriterImpl.addOverridden(this, - method.overriddenType(), overriddenMethod, dl); - } - } - } - - /** - * Adds the tags information. - * - * @param doc the doc for which the tags will be generated - * @param htmltree the documentation tree to which the tags will be added - */ - protected void addTagsInfo(Doc doc, Content htmltree) { - if (configuration.nocomment) { - return; - } - Content dl = new HtmlTree(HtmlTag.DL); - if (doc instanceof MethodDoc) { - addMethodInfo((MethodDoc) doc, dl); - } - Content output = new ContentBuilder(); - TagletWriter.genTagOuput(configuration.tagletManager, doc, - configuration.tagletManager.getCustomTaglets(doc), - getTagletWriterInstance(false), output); - dl.addContent(output); - htmltree.addContent(dl); - } - - /** - * Check whether there are any tags for Serialization Overview - * section to be printed. - * - * @param field the FieldDoc object to check for tags. - * @return true if there are tags to be printed else return false. - */ - protected boolean hasSerializationOverviewTags(FieldDoc field) { - Content output = new ContentBuilder(); - TagletWriter.genTagOuput(configuration.tagletManager, field, - configuration.tagletManager.getCustomTaglets(field), - getTagletWriterInstance(false), output); - return !output.isEmpty(); - } - - /** - * Returns a TagletWriter that knows how to write HTML. - * - * @return a TagletWriter that knows how to write HTML. - */ - public TagletWriter getTagletWriterInstance(boolean isFirstSentence) { - return new TagletWriterImpl(this, isFirstSentence); - } - - /** - * Get Package link, with target frame. - * - * @param pd The link will be to the "package-summary.html" page for this package - * @param target name of the target frame - * @param label tag for the link - * @return a content for the target package link - */ - public Content getTargetPackageLink(PackageDoc pd, String target, - Content label) { - return getHyperLink(pathString(pd, DocPaths.PACKAGE_SUMMARY), label, "", target); - } - - /** - * Get Profile Package link, with target frame. - * - * @param pd the packageDoc object - * @param target name of the target frame - * @param label tag for the link - * @param profileName the name of the profile being documented - * @return a content for the target profile packages link - */ - public Content getTargetProfilePackageLink(PackageDoc pd, String target, - Content label, String profileName) { - return getHyperLink(pathString(pd, DocPaths.profilePackageSummary(profileName)), - label, "", target); - } - - /** - * Get Profile link, with target frame. - * - * @param target name of the target frame - * @param label tag for the link - * @param profileName the name of the profile being documented - * @return a content for the target profile link - */ - public Content getTargetProfileLink(String target, Content label, - String profileName) { - return getHyperLink(pathToRoot.resolve( - DocPaths.profileSummary(profileName)), label, "", target); - } - - /** - * Get the type name for profile search. - * - * @param cd the classDoc object for which the type name conversion is needed - * @return a type name string for the type - */ - public String getTypeNameForProfile(ClassDoc cd) { - StringBuilder typeName = - new StringBuilder((cd.containingPackage()).name().replace(".", "/")); - typeName.append("/") - .append(cd.name().replace(".", "$")); - return typeName.toString(); - } - - /** - * Check if a type belongs to a profile. - * - * @param cd the classDoc object that needs to be checked - * @param profileValue the profile in which the type needs to be checked - * @return true if the type is in the profile - */ - public boolean isTypeInProfile(ClassDoc cd, int profileValue) { - return (configuration.profiles.getProfile(getTypeNameForProfile(cd)) <= profileValue); - } - - public void addClassesSummary(ClassDoc[] classes, String label, - String tableSummary, String[] tableHeader, Content summaryContentTree, - int profileValue) { - if(classes.length > 0) { - Arrays.sort(classes); - Content caption = getTableCaption(new RawHtml(label)); - Content table = (configuration.isOutputHtml5()) - ? HtmlTree.TABLE(HtmlStyle.typeSummary, caption) - : HtmlTree.TABLE(HtmlStyle.typeSummary, tableSummary, caption); - table.addContent(getSummaryTableHeader(tableHeader, "col")); - Content tbody = new HtmlTree(HtmlTag.TBODY); - for (int i = 0; i < classes.length; i++) { - if (!isTypeInProfile(classes[i], profileValue)) { - continue; - } - if (!utils.isCoreClass(classes[i]) || - !configuration.isGeneratedDoc(classes[i])) { - continue; - } - Content classContent = getLink(new LinkInfoImpl( - configuration, LinkInfoImpl.Kind.PACKAGE, classes[i])); - Content tdClass = HtmlTree.TD(HtmlStyle.colFirst, classContent); - HtmlTree tr = HtmlTree.TR(tdClass); - if (i%2 == 0) - tr.addStyle(HtmlStyle.altColor); - else - tr.addStyle(HtmlStyle.rowColor); - HtmlTree tdClassDescription = new HtmlTree(HtmlTag.TD); - tdClassDescription.addStyle(HtmlStyle.colLast); - if (utils.isDeprecated(classes[i])) { - tdClassDescription.addContent(deprecatedLabel); - if (classes[i].tags("deprecated").length > 0) { - addSummaryDeprecatedComment(classes[i], - classes[i].tags("deprecated")[0], tdClassDescription); - } - } - else - addSummaryComment(classes[i], tdClassDescription); - tr.addContent(tdClassDescription); - tbody.addContent(tr); - } - table.addContent(tbody); - summaryContentTree.addContent(table); - } - } - - /** - * Generates the HTML document tree and prints it out. - * - * @param metakeywords Array of String keywords for META tag. Each element - * of the array is assigned to a separate META tag. - * Pass in null for no array - * @param includeScript true if printing windowtitle script - * false for files that appear in the left-hand frames - * @param body the body htmltree to be included in the document - */ - public void printHtmlDocument(String[] metakeywords, boolean includeScript, - Content body) throws IOException { - Content htmlDocType = configuration.isOutputHtml5() - ? DocType.HTML5 - : DocType.TRANSITIONAL; - Content htmlComment = new Comment(configuration.getText("doclet.New_Page")); - Content head = new HtmlTree(HtmlTag.HEAD); - head.addContent(getGeneratedBy(!configuration.notimestamp)); - head.addContent(getTitle()); - Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, - (configuration.charset.length() > 0) ? - configuration.charset : HtmlConstants.HTML_DEFAULT_CHARSET); - head.addContent(meta); - if (!configuration.notimestamp) { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); - meta = HtmlTree.META(configuration.isOutputHtml5() - ? "dc.created" - : "date", dateFormat.format(new Date())); - head.addContent(meta); - } - if (metakeywords != null) { - for (String metakeyword : metakeywords) { - meta = HtmlTree.META("keywords", metakeyword); - head.addContent(meta); - } - } - addStyleSheetProperties(head); - addScriptProperties(head); - Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(), - head, body); - Content htmlDocument = new HtmlDocument(htmlDocType, - htmlComment, htmlTree); - write(htmlDocument); - } - - /** - * Get the window title. - * - * @param title the title string to construct the complete window title - * @return the window title string - */ - public String getWindowTitle(String title) { - if (configuration.windowtitle.length() > 0) { - title += " (" + configuration.windowtitle + ")"; - } - return title; - } - - /** - * Get user specified header and the footer. - * - * @param header if true print the user provided header else print the - * user provided footer. - */ - public Content getUserHeaderFooter(boolean header) { - String content; - if (header) { - content = replaceDocRootDir(configuration.header); - } else { - if (configuration.footer.length() != 0) { - content = replaceDocRootDir(configuration.footer); - } else { - content = replaceDocRootDir(configuration.header); - } - } - Content rawContent = new RawHtml(content); - return rawContent; - } - - /** - * Adds the user specified top. - * - * @param htmlTree the content tree to which user specified top will be added - */ - public void addTop(Content htmlTree) { - Content top = new RawHtml(replaceDocRootDir(configuration.top)); - fixedNavDiv.addContent(top); - } - - /** - * Adds the user specified bottom. - * - * @param htmlTree the content tree to which user specified bottom will be added - */ - public void addBottom(Content htmlTree) { - Content bottom = new RawHtml(replaceDocRootDir(configuration.bottom)); - Content small = HtmlTree.SMALL(bottom); - Content p = HtmlTree.P(HtmlStyle.legalCopy, small); - htmlTree.addContent(p); - } - - /** - * Adds the navigation bar for the Html page at the top and and the bottom. - * - * @param header If true print navigation bar at the top of the page else - * @param htmlTree the HtmlTree to which the nav links will be added - */ - protected void addNavLinks(boolean header, Content htmlTree) { - if (!configuration.nonavbar) { - Content tree = (configuration.allowTag(HtmlTag.NAV)) - ? HtmlTree.NAV() - : htmlTree; - String allClassesId = "allclasses_"; - HtmlTree navDiv = new HtmlTree(HtmlTag.DIV); - fixedNavDiv.addStyle(HtmlStyle.fixedNav); - Content skipNavLinks = configuration.getResource("doclet.Skip_navigation_links"); - if (header) { - fixedNavDiv.addContent(HtmlConstants.START_OF_TOP_NAVBAR); - navDiv.addStyle(HtmlStyle.topNav); - allClassesId += "navbar_top"; - Content a = getMarkerAnchor(SectionName.NAVBAR_TOP); - //WCAG - Hyperlinks should contain text or an image with alt text - for AT tools - navDiv.addContent(a); - Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav, getHyperLink( - getDocLink(SectionName.SKIP_NAVBAR_TOP), skipNavLinks, - skipNavLinks.toString(), "")); - navDiv.addContent(skipLinkContent); - } else { - tree.addContent(HtmlConstants.START_OF_BOTTOM_NAVBAR); - navDiv.addStyle(HtmlStyle.bottomNav); - allClassesId += "navbar_bottom"; - Content a = getMarkerAnchor(SectionName.NAVBAR_BOTTOM); - navDiv.addContent(a); - Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav, getHyperLink( - getDocLink(SectionName.SKIP_NAVBAR_BOTTOM), skipNavLinks, - skipNavLinks.toString(), "")); - navDiv.addContent(skipLinkContent); - } - if (header) { - navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_TOP_FIRSTROW)); - } else { - navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_BOTTOM_FIRSTROW)); - } - HtmlTree navList = new HtmlTree(HtmlTag.UL); - navList.addStyle(HtmlStyle.navList); - navList.addAttr(HtmlAttr.TITLE, - configuration.getText("doclet.Navigation")); - if (configuration.createoverview) { - navList.addContent(getNavLinkContents()); - } - if (configuration.packages.size() == 1) { - navList.addContent(getNavLinkPackage(configuration.packages.first())); - } else if (configuration.packages.size() > 1) { - navList.addContent(getNavLinkPackage()); - } - navList.addContent(getNavLinkClass()); - if(configuration.classuse) { - navList.addContent(getNavLinkClassUse()); - } - if(configuration.createtree) { - navList.addContent(getNavLinkTree()); - } - if(!(configuration.nodeprecated || - configuration.nodeprecatedlist)) { - navList.addContent(getNavLinkDeprecated()); - } - if(configuration.createindex) { - navList.addContent(getNavLinkIndex()); - } - if (!configuration.nohelp) { - navList.addContent(getNavLinkHelp()); - } - navDiv.addContent(navList); - Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, getUserHeaderFooter(header)); - navDiv.addContent(aboutDiv); - if (header) { - fixedNavDiv.addContent(navDiv); - } else { - tree.addContent(navDiv); - } - Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious()); - ulNav.addContent(getNavLinkNext()); - Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav); - Content ulFrames = HtmlTree.UL(HtmlStyle.navList, getNavShowLists()); - ulFrames.addContent(getNavHideLists(filename)); - subDiv.addContent(ulFrames); - HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex()); - ulAllClasses.addAttr(HtmlAttr.ID, allClassesId.toString()); - subDiv.addContent(ulAllClasses); - if (header && configuration.createindex) { - HtmlTree inputText = HtmlTree.INPUT("text", "search"); - HtmlTree inputReset = HtmlTree.INPUT("reset", "reset"); - Content searchTxt = configuration.getResource("doclet.search"); - searchTxt.addContent(getSpace()); - HtmlTree liInput = HtmlTree.LI(HtmlTree.SPAN(searchTxt)); - liInput.addContent(inputText); - liInput.addContent(inputReset); - HtmlTree ulSearch = HtmlTree.UL(HtmlStyle.navListSearch, liInput); - subDiv.addContent(ulSearch); - } - subDiv.addContent(getAllClassesLinkScript(allClassesId.toString())); - addSummaryDetailLinks(subDiv); - if (header) { - subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_TOP)); - fixedNavDiv.addContent(subDiv); - fixedNavDiv.addContent(HtmlConstants.END_OF_TOP_NAVBAR); - tree.addContent(fixedNavDiv); - } else { - subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_BOTTOM)); - tree.addContent(subDiv); - tree.addContent(HtmlConstants.END_OF_BOTTOM_NAVBAR); - } - if (configuration.allowTag(HtmlTag.NAV)) { - htmlTree.addContent(tree); - } - } - } - - /** - * Get the word "NEXT" to indicate that no link is available. Override - * this method to customize next link. - * - * @return a content tree for the link - */ - protected Content getNavLinkNext() { - return getNavLinkNext(null); - } - - /** - * Get the word "PREV" to indicate that no link is available. Override - * this method to customize prev link. - * - * @return a content tree for the link - */ - protected Content getNavLinkPrevious() { - return getNavLinkPrevious(null); - } - - /** - * Do nothing. This is the default method. - */ - protected void addSummaryDetailLinks(Content navDiv) { - } - - /** - * Get link to the "overview-summary.html" page. - * - * @return a content tree for the link - */ - protected Content getNavLinkContents() { - Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_SUMMARY), - overviewLabel, "", ""); - Content li = HtmlTree.LI(linkContent); - return li; - } - - /** - * Get link to the "package-summary.html" page for the package passed. - * - * @param pkg Package to which link will be generated - * @return a content tree for the link - */ - protected Content getNavLinkPackage(PackageDoc pkg) { - Content linkContent = getPackageLink(pkg, - packageLabel); - Content li = HtmlTree.LI(linkContent); - return li; - } - - /** - * Get the word "Package" , to indicate that link is not available here. - * - * @return a content tree for the link - */ - protected Content getNavLinkPackage() { - Content li = HtmlTree.LI(packageLabel); - return li; - } - - /** - * Get the word "Use", to indicate that link is not available. - * - * @return a content tree for the link - */ - protected Content getNavLinkClassUse() { - Content li = HtmlTree.LI(useLabel); - return li; - } - - /** - * Get link for previous file. - * - * @param prev File name for the prev link - * @return a content tree for the link - */ - public Content getNavLinkPrevious(DocPath prev) { - Content li; - if (prev != null) { - li = HtmlTree.LI(getHyperLink(prev, prevLabel, "", "")); - } - else - li = HtmlTree.LI(prevLabel); - return li; - } - - /** - * Get link for next file. If next is null, just print the label - * without linking it anywhere. - * - * @param next File name for the next link - * @return a content tree for the link - */ - public Content getNavLinkNext(DocPath next) { - Content li; - if (next != null) { - li = HtmlTree.LI(getHyperLink(next, nextLabel, "", "")); - } - else - li = HtmlTree.LI(nextLabel); - return li; - } - - /** - * Get "FRAMES" link, to switch to the frame version of the output. - * - * @param link File to be linked, "index.html" - * @return a content tree for the link - */ - protected Content getNavShowLists(DocPath link) { - DocLink dl = new DocLink(link, path.getPath(), null); - Content framesContent = getHyperLink(dl, framesLabel, "", "_top"); - Content li = HtmlTree.LI(framesContent); - return li; - } - - /** - * Get "FRAMES" link, to switch to the frame version of the output. - * - * @return a content tree for the link - */ - protected Content getNavShowLists() { - return getNavShowLists(pathToRoot.resolve(DocPaths.INDEX)); - } - - /** - * Get "NO FRAMES" link, to switch to the non-frame version of the output. - * - * @param link File to be linked - * @return a content tree for the link - */ - protected Content getNavHideLists(DocPath link) { - Content noFramesContent = getHyperLink(link, noframesLabel, "", "_top"); - Content li = HtmlTree.LI(noFramesContent); - return li; - } - - /** - * Get "Tree" link in the navigation bar. If there is only one package - * specified on the command line, then the "Tree" link will be to the - * only "package-tree.html" file otherwise it will be to the - * "overview-tree.html" file. - * - * @return a content tree for the link - */ - protected Content getNavLinkTree() { - Content treeLinkContent; - PackageDoc[] packages = configuration.root.specifiedPackages(); - if (packages.length == 1 && configuration.root.specifiedClasses().length == 0) { - treeLinkContent = getHyperLink(pathString(packages[0], - DocPaths.PACKAGE_TREE), treeLabel, - "", ""); - } else { - treeLinkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE), - treeLabel, "", ""); - } - Content li = HtmlTree.LI(treeLinkContent); - return li; - } - - /** - * Get the overview tree link for the main tree. - * - * @param label the label for the link - * @return a content tree for the link - */ - protected Content getNavLinkMainTree(String label) { - Content mainTreeContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE), - new StringContent(label)); - Content li = HtmlTree.LI(mainTreeContent); - return li; - } - - /** - * Get the word "Class", to indicate that class link is not available. - * - * @return a content tree for the link - */ - protected Content getNavLinkClass() { - Content li = HtmlTree.LI(classLabel); - return li; - } - - /** - * Get "Deprecated" API link in the navigation bar. - * - * @return a content tree for the link - */ - protected Content getNavLinkDeprecated() { - Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.DEPRECATED_LIST), - deprecatedLabel, "", ""); - Content li = HtmlTree.LI(linkContent); - return li; - } - - /** - * Get link for generated index. If the user has used "-splitindex" - * command line option, then link to file "index-files/index-1.html" is - * generated otherwise link to file "index-all.html" is generated. - * - * @return a content tree for the link - */ - protected Content getNavLinkClassIndex() { - Content allClassesContent = getHyperLink(pathToRoot.resolve( - DocPaths.ALLCLASSES_NOFRAME), - allclassesLabel, "", ""); - Content li = HtmlTree.LI(allClassesContent); - return li; - } - - /** - * Get link for generated class index. - * - * @return a content tree for the link - */ - protected Content getNavLinkIndex() { - Content linkContent = getHyperLink(pathToRoot.resolve( - (configuration.splitindex - ? DocPaths.INDEX_FILES.resolve(DocPaths.indexN(1)) - : DocPaths.INDEX_ALL)), - indexLabel, "", ""); - Content li = HtmlTree.LI(linkContent); - return li; - } - - /** - * Get help file link. If user has provided a help file, then generate a - * link to the user given file, which is already copied to current or - * destination directory. - * - * @return a content tree for the link - */ - protected Content getNavLinkHelp() { - String helpfile = configuration.helpfile; - DocPath helpfilenm; - if (helpfile.isEmpty()) { - helpfilenm = DocPaths.HELP_DOC; - } else { - DocFile file = DocFile.createFileForInput(configuration, helpfile); - helpfilenm = DocPath.create(file.getName()); - } - Content linkContent = getHyperLink(pathToRoot.resolve(helpfilenm), - helpLabel, "", ""); - Content li = HtmlTree.LI(linkContent); - return li; - } - - /** - * Get summary table header. - * - * @param header the header for the table - * @param scope the scope of the headers - * @return a content tree for the header - */ - public Content getSummaryTableHeader(String[] header, String scope) { - Content tr = new HtmlTree(HtmlTag.TR); - int size = header.length; - Content tableHeader; - if (size == 1) { - tableHeader = new StringContent(header[0]); - tr.addContent(HtmlTree.TH(HtmlStyle.colOne, scope, tableHeader)); - return tr; - } - for (int i = 0; i < size; i++) { - tableHeader = new StringContent(header[i]); - if(i == 0) - tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader)); - else if(i == (size - 1)) - tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader)); - else - tr.addContent(HtmlTree.TH(scope, tableHeader)); - } - return tr; - } - - /** - * Get table caption. - * - * @param rawText the caption for the table which could be raw Html - * @return a content tree for the caption - */ - public Content getTableCaption(Content title) { - Content captionSpan = HtmlTree.SPAN(title); - Content space = getSpace(); - Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space); - Content caption = HtmlTree.CAPTION(captionSpan); - caption.addContent(tabSpan); - return caption; - } - - /** - * Get the marker anchor which will be added to the documentation tree. - * - * @param anchorName the anchor name attribute - * @return a content tree for the marker anchor - */ - public Content getMarkerAnchor(String anchorName) { - return getMarkerAnchor(getName(anchorName), null); - } - - /** - * Get the marker anchor which will be added to the documentation tree. - * - * @param sectionName the section name anchor attribute for page - * @return a content tree for the marker anchor - */ - public Content getMarkerAnchor(SectionName sectionName) { - return getMarkerAnchor(sectionName.getName(), null); - } - - /** - * Get the marker anchor which will be added to the documentation tree. - * - * @param sectionName the section name anchor attribute for page - * @param anchorName the anchor name combined with section name attribute for the page - * @return a content tree for the marker anchor - */ - public Content getMarkerAnchor(SectionName sectionName, String anchorName) { - return getMarkerAnchor(sectionName.getName() + getName(anchorName), null); - } - - /** - * Get the marker anchor which will be added to the documentation tree. - * - * @param anchorName the anchor name or id attribute - * @param anchorContent the content that should be added to the anchor - * @return a content tree for the marker anchor - */ - public Content getMarkerAnchor(String anchorName, Content anchorContent) { - if (anchorContent == null) - anchorContent = new Comment(" "); - Content markerAnchor = HtmlTree.A(configuration.htmlVersion, anchorName, anchorContent); - return markerAnchor; - } - - /** - * Returns a packagename content. - * - * @param packageDoc the package to check - * @return package name content - */ - public Content getPackageName(PackageDoc packageDoc) { - return packageDoc == null || packageDoc.name().isEmpty() ? - defaultPackageLabel : - getPackageLabel(packageDoc.name()); - } - - /** - * Returns a package name label. - * - * @param packageName the package name - * @return the package name content - */ - public Content getPackageLabel(String packageName) { - return new StringContent(packageName); - } - - /** - * Add package deprecation information to the documentation tree - * - * @param deprPkgs list of deprecated packages - * @param headingKey the caption for the deprecated package table - * @param tableSummary the summary for the deprecated package table - * @param tableHeader table headers for the deprecated package table - * @param contentTree the content tree to which the deprecated package table will be added - */ - protected void addPackageDeprecatedAPI(List deprPkgs, String headingKey, - String tableSummary, String[] tableHeader, Content contentTree) { - if (deprPkgs.size() > 0) { - Content caption = getTableCaption(configuration.getResource(headingKey)); - Content table = (configuration.isOutputHtml5()) - ? HtmlTree.TABLE(HtmlStyle.deprecatedSummary, caption) - : HtmlTree.TABLE(HtmlStyle.deprecatedSummary, tableSummary, caption); - table.addContent(getSummaryTableHeader(tableHeader, "col")); - Content tbody = new HtmlTree(HtmlTag.TBODY); - for (int i = 0; i < deprPkgs.size(); i++) { - PackageDoc pkg = (PackageDoc) deprPkgs.get(i); - HtmlTree td = HtmlTree.TD(HtmlStyle.colOne, - getPackageLink(pkg, getPackageName(pkg))); - if (pkg.tags("deprecated").length > 0) { - addInlineDeprecatedComment(pkg, pkg.tags("deprecated")[0], td); - } - HtmlTree tr = HtmlTree.TR(td); - if (i % 2 == 0) { - tr.addStyle(HtmlStyle.altColor); - } else { - tr.addStyle(HtmlStyle.rowColor); - } - tbody.addContent(tr); - } - table.addContent(tbody); - Content li = HtmlTree.LI(HtmlStyle.blockList, table); - Content ul = HtmlTree.UL(HtmlStyle.blockList, li); - contentTree.addContent(ul); - } - } - - /** - * Return the path to the class page for a classdoc. - * - * @param cd Class to which the path is requested. - * @param name Name of the file(doesn't include path). - */ - protected DocPath pathString(ClassDoc cd, DocPath name) { - return pathString(cd.containingPackage(), name); - } - - /** - * Return path to the given file name in the given package. So if the name - * passed is "Object.html" and the name of the package is "java.lang", and - * if the relative path is "../.." then returned string will be - * "../../java/lang/Object.html" - * - * @param pd Package in which the file name is assumed to be. - * @param name File name, to which path string is. - */ - protected DocPath pathString(PackageDoc pd, DocPath name) { - return pathToRoot.resolve(DocPath.forPackage(pd).resolve(name)); - } - - /** - * Given a package, return the name to be used in HTML anchor tag. - * @param packageDoc the package. - * @return the name to be used in HTML anchor tag. - */ - public String getPackageAnchorName(PackageDoc packageDoc) { - return packageDoc == null || packageDoc.name().length() == 0 ? - SectionName.UNNAMED_PACKAGE_ANCHOR.getName() : packageDoc.name(); - } - - /** - * Return the link to the given package. - * - * @param pkg the package to link to. - * @param label the label for the link. - * @return a content tree for the package link. - */ - public Content getPackageLink(PackageDoc pkg, String label) { - return getPackageLink(pkg, new StringContent(label)); - } - - /** - * Return the link to the given package. - * - * @param pkg the package to link to. - * @param label the label for the link. - * @return a content tree for the package link. - */ - public Content getPackageLink(PackageDoc pkg, Content label) { - boolean included = pkg != null && pkg.isIncluded(); - if (! included) { - for (PackageDoc p : configuration.packages) { - if (p.equals(pkg)) { - included = true; - break; - } - } - } - if (included || pkg == null) { - return getHyperLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY), - label); - } else { - DocLink crossPkgLink = getCrossPackageLink(utils.getPackageName(pkg)); - if (crossPkgLink != null) { - return getHyperLink(crossPkgLink, label); - } else { - return label; - } - } - } - - public Content italicsClassName(ClassDoc cd, boolean qual) { - Content name = new StringContent((qual)? cd.qualifiedName(): cd.name()); - return (cd.isInterface())? HtmlTree.SPAN(HtmlStyle.interfaceName, name): name; - } - - /** - * Add the link to the content tree. - * - * @param doc program element doc for which the link will be added - * @param label label for the link - * @param htmltree the content tree to which the link will be added - */ - public void addSrcLink(ProgramElementDoc doc, Content label, Content htmltree) { - if (doc == null) { - return; - } - ClassDoc cd = doc.containingClass(); - if (cd == null) { - //d must be a class doc since in has no containing class. - cd = (ClassDoc) doc; - } - DocPath href = pathToRoot - .resolve(DocPaths.SOURCE_OUTPUT) - .resolve(DocPath.forClass(cd)); - Content linkContent = getHyperLink(href.fragment(SourceToHTMLConverter.getAnchorName(doc)), label, "", ""); - htmltree.addContent(linkContent); - } - - /** - * Return the link to the given class. - * - * @param linkInfo the information about the link. - * - * @return the link for the given class. - */ - public Content getLink(LinkInfoImpl linkInfo) { - LinkFactoryImpl factory = new LinkFactoryImpl(this); - return factory.getLink(linkInfo); - } - - /** - * Return the type parameters for the given class. - * - * @param linkInfo the information about the link. - * @return the type for the given class. - */ - public Content getTypeParameterLinks(LinkInfoImpl linkInfo) { - LinkFactoryImpl factory = new LinkFactoryImpl(this); - return factory.getTypeParameterLinks(linkInfo, false); - } - - /************************************************************* - * Return a class cross link to external class documentation. - * The name must be fully qualified to determine which package - * the class is in. The -link option does not allow users to - * link to external classes in the "default" package. - * - * @param qualifiedClassName the qualified name of the external class. - * @param refMemName the name of the member being referenced. This should - * be null or empty string if no member is being referenced. - * @param label the label for the external link. - * @param strong true if the link should be strong. - * @param style the style of the link. - * @param code true if the label should be code font. - */ - public Content getCrossClassLink(String qualifiedClassName, String refMemName, - Content label, boolean strong, String style, - boolean code) { - String className = ""; - String packageName = qualifiedClassName == null ? "" : qualifiedClassName; - int periodIndex; - while ((periodIndex = packageName.lastIndexOf('.')) != -1) { - className = packageName.substring(periodIndex + 1, packageName.length()) + - (className.length() > 0 ? "." + className : ""); - Content defaultLabel = new StringContent(className); - if (code) - defaultLabel = HtmlTree.CODE(defaultLabel); - packageName = packageName.substring(0, periodIndex); - if (getCrossPackageLink(packageName) != null) { - //The package exists in external documentation, so link to the external - //class (assuming that it exists). This is definitely a limitation of - //the -link option. There are ways to determine if an external package - //exists, but no way to determine if the external class exists. We just - //have to assume that it does. - DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot, - className + ".html", refMemName); - return getHyperLink(link, - (label == null) || label.isEmpty() ? defaultLabel : label, - strong, style, - configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName), - ""); - } - } - return null; - } - - public boolean isClassLinkable(ClassDoc cd) { - if (cd.isIncluded()) { - return configuration.isGeneratedDoc(cd); - } - return configuration.extern.isExternal(cd); - } - - public DocLink getCrossPackageLink(String pkgName) { - return configuration.extern.getExternalLink(pkgName, pathToRoot, - DocPaths.PACKAGE_SUMMARY.getPath()); - } - - /** - * Get the class link. - * - * @param context the id of the context where the link will be added - * @param cd the class doc to link to - * @return a content tree for the link - */ - public Content getQualifiedClassLink(LinkInfoImpl.Kind context, ClassDoc cd) { - return getLink(new LinkInfoImpl(configuration, context, cd) - .label(configuration.getClassName(cd))); - } - - /** - * Add the class link. - * - * @param context the id of the context where the link will be added - * @param cd the class doc to link to - * @param contentTree the content tree to which the link will be added - */ - public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, ClassDoc cd, Content contentTree) { - addPreQualifiedClassLink(context, cd, false, contentTree); - } - - /** - * Retrieve the class link with the package portion of the label in - * plain text. If the qualifier is excluded, it will not be included in the - * link label. - * - * @param cd the class to link to. - * @param isStrong true if the link should be strong. - * @return the link with the package portion of the label in plain text. - */ - public Content getPreQualifiedClassLink(LinkInfoImpl.Kind context, - ClassDoc cd, boolean isStrong) { - ContentBuilder classlink = new ContentBuilder(); - PackageDoc pd = cd.containingPackage(); - if (pd != null && ! configuration.shouldExcludeQualifier(pd.name())) { - classlink.addContent(getPkgName(cd)); - } - classlink.addContent(getLink(new LinkInfoImpl(configuration, - context, cd).label(cd.name()).strong(isStrong))); - return classlink; - } - - /** - * Add the class link with the package portion of the label in - * plain text. If the qualifier is excluded, it will not be included in the - * link label. - * - * @param context the id of the context where the link will be added - * @param cd the class to link to - * @param isStrong true if the link should be strong - * @param contentTree the content tree to which the link with be added - */ - public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, - ClassDoc cd, boolean isStrong, Content contentTree) { - PackageDoc pd = cd.containingPackage(); - if(pd != null && ! configuration.shouldExcludeQualifier(pd.name())) { - contentTree.addContent(getPkgName(cd)); - } - contentTree.addContent(getLink(new LinkInfoImpl(configuration, - context, cd).label(cd.name()).strong(isStrong))); - } - - /** - * Add the class link, with only class name as the strong link and prefixing - * plain package name. - * - * @param context the id of the context where the link will be added - * @param cd the class to link to - * @param contentTree the content tree to which the link with be added - */ - public void addPreQualifiedStrongClassLink(LinkInfoImpl.Kind context, ClassDoc cd, Content contentTree) { - addPreQualifiedClassLink(context, cd, true, contentTree); - } - - /** - * Get the link for the given member. - * - * @param context the id of the context where the link will be added - * @param doc the member being linked to - * @param label the label for the link - * @return a content tree for the doc link - */ - public Content getDocLink(LinkInfoImpl.Kind context, MemberDoc doc, String label) { - return getDocLink(context, doc.containingClass(), doc, - new StringContent(label)); - } - - /** - * Return the link for the given member. - * - * @param context the id of the context where the link will be printed. - * @param doc the member being linked to. - * @param label the label for the link. - * @param strong true if the link should be strong. - * @return the link for the given member. - */ - public Content getDocLink(LinkInfoImpl.Kind context, MemberDoc doc, String label, - boolean strong) { - return getDocLink(context, doc.containingClass(), doc, label, strong); - } - - /** - * Return the link for the given member. - * - * @param context the id of the context where the link will be printed. - * @param classDoc the classDoc that we should link to. This is not - * necessarily equal to doc.containingClass(). We may be - * inheriting comments. - * @param doc the member being linked to. - * @param label the label for the link. - * @param strong true if the link should be strong. - * @return the link for the given member. - */ - public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, - String label, boolean strong) { - return getDocLink(context, classDoc, doc, label, strong, false); - } - public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, - Content label, boolean strong) { - return getDocLink(context, classDoc, doc, label, strong, false); - } - - /** - * Return the link for the given member. - * - * @param context the id of the context where the link will be printed. - * @param classDoc the classDoc that we should link to. This is not - * necessarily equal to doc.containingClass(). We may be - * inheriting comments. - * @param doc the member being linked to. - * @param label the label for the link. - * @param strong true if the link should be strong. - * @param isProperty true if the doc parameter is a JavaFX property. - * @return the link for the given member. - */ - public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, - String label, boolean strong, boolean isProperty) { - return getDocLink(context, classDoc, doc, new StringContent(check(label)), strong, isProperty); - } - - String check(String s) { - if (s.matches(".*[&<>].*"))throw new IllegalArgumentException(s); - return s; - } - - public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, - Content label, boolean strong, boolean isProperty) { - if (! (doc.isIncluded() || - utils.isLinkable(classDoc, configuration))) { - return label; - } else if (doc instanceof ExecutableMemberDoc) { - ExecutableMemberDoc emd = (ExecutableMemberDoc)doc; - return getLink(new LinkInfoImpl(configuration, context, classDoc) - .label(label).where(getName(getAnchor(emd, isProperty))).strong(strong)); - } else if (doc instanceof MemberDoc) { - return getLink(new LinkInfoImpl(configuration, context, classDoc) - .label(label).where(getName(doc.name())).strong(strong)); - } else { - return label; - } - } - - /** - * Return the link for the given member. - * - * @param context the id of the context where the link will be added - * @param classDoc the classDoc that we should link to. This is not - * necessarily equal to doc.containingClass(). We may be - * inheriting comments - * @param doc the member being linked to - * @param label the label for the link - * @return the link for the given member - */ - public Content getDocLink(LinkInfoImpl.Kind context, ClassDoc classDoc, MemberDoc doc, - Content label) { - if (! (doc.isIncluded() || - utils.isLinkable(classDoc, configuration))) { - return label; - } else if (doc instanceof ExecutableMemberDoc) { - ExecutableMemberDoc emd = (ExecutableMemberDoc) doc; - return getLink(new LinkInfoImpl(configuration, context, classDoc) - .label(label).where(getName(getAnchor(emd)))); - } else if (doc instanceof MemberDoc) { - return getLink(new LinkInfoImpl(configuration, context, classDoc) - .label(label).where(getName(doc.name()))); - } else { - return label; - } - } - - public String getAnchor(ExecutableMemberDoc emd) { - return getAnchor(emd, false); - } - - public String getAnchor(ExecutableMemberDoc emd, boolean isProperty) { - if (isProperty) { - return emd.name(); - } - StringBuilder signature = new StringBuilder(emd.signature()); - StringBuilder signatureParsed = new StringBuilder(); - int counter = 0; - for (int i = 0; i < signature.length(); i++) { - char c = signature.charAt(i); - if (c == '<') { - counter++; - } else if (c == '>') { - counter--; - } else if (counter == 0) { - signatureParsed.append(c); - } - } - return emd.name() + signatureParsed.toString(); - } - - public Content seeTagToContent(SeeTag see) { - String tagName = see.name(); - if (! (tagName.startsWith("@link") || tagName.equals("@see"))) { - return new ContentBuilder(); - } - - String seetext = replaceDocRootDir(utils.normalizeNewlines(see.text())); - - //Check if @see is an href or "string" - if (seetext.startsWith("<") || seetext.startsWith("\"")) { - return new RawHtml(seetext); - } - - boolean plain = tagName.equalsIgnoreCase("@linkplain"); - Content label = plainOrCode(plain, new RawHtml(see.label())); - - //The text from the @see tag. We will output this text when a label is not specified. - Content text = plainOrCode(plain, new RawHtml(seetext)); - - ClassDoc refClass = see.referencedClass(); - String refClassName = see.referencedClassName(); - MemberDoc refMem = see.referencedMember(); - String refMemName = see.referencedMemberName(); - - if (refClass == null) { - //@see is not referencing an included class - PackageDoc refPackage = see.referencedPackage(); - if (refPackage != null && refPackage.isIncluded()) { - //@see is referencing an included package - if (label.isEmpty()) - label = plainOrCode(plain, new StringContent(refPackage.name())); - return getPackageLink(refPackage, label); - } else { - //@see is not referencing an included class or package. Check for cross links. - Content classCrossLink; - DocLink packageCrossLink = getCrossPackageLink(refClassName); - if (packageCrossLink != null) { - //Package cross link found - return getHyperLink(packageCrossLink, - (label.isEmpty() ? text : label)); - } else if ((classCrossLink = getCrossClassLink(refClassName, - refMemName, label, false, "", !plain)) != null) { - //Class cross link found (possibly to a member in the class) - return classCrossLink; - } else { - //No cross link found so print warning - configuration.getDocletSpecificMsg().warning(see.position(), "doclet.see.class_or_package_not_found", - tagName, seetext); - return (label.isEmpty() ? text: label); - } - } - } else if (refMemName == null) { - // Must be a class reference since refClass is not null and refMemName is null. - if (label.isEmpty()) { - label = plainOrCode(plain, new StringContent(refClass.name())); - } - return getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, refClass) - .label(label)); - } else if (refMem == null) { - // Must be a member reference since refClass is not null and refMemName is not null. - // However, refMem is null, so this referenced member does not exist. - return (label.isEmpty() ? text: label); - } else { - // Must be a member reference since refClass is not null and refMemName is not null. - // refMem is not null, so this @see tag must be referencing a valid member. - ClassDoc containing = refMem.containingClass(); - if (see.text().trim().startsWith("#") && - ! (containing.isPublic() || - utils.isLinkable(containing, configuration))) { - // Since the link is relative and the holder is not even being - // documented, this must be an inherited link. Redirect it. - // The current class either overrides the referenced member or - // inherits it automatically. - if (this instanceof ClassWriterImpl) { - containing = ((ClassWriterImpl) this).getClassDoc(); - } else if (!containing.isPublic()){ - configuration.getDocletSpecificMsg().warning( - see.position(), "doclet.see.class_or_package_not_accessible", - tagName, containing.qualifiedName()); - } else { - configuration.getDocletSpecificMsg().warning( - see.position(), "doclet.see.class_or_package_not_found", - tagName, seetext); - } - } - if (configuration.currentcd != containing) { - refMemName = (refMem instanceof ConstructorDoc) ? - refMemName : containing.name() + "." + refMemName; - } - if (refMem instanceof ExecutableMemberDoc) { - if (refMemName.indexOf('(') < 0) { - refMemName += ((ExecutableMemberDoc)refMem).signature(); - } - } - - text = plainOrCode(plain, new StringContent(refMemName)); - - return getDocLink(LinkInfoImpl.Kind.SEE_TAG, containing, - refMem, (label.isEmpty() ? text: label), false); - } - } - - private Content plainOrCode(boolean plain, Content body) { - return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body); - } - - /** - * Add the inline comment. - * - * @param doc the doc for which the inline comment will be added - * @param tag the inline tag to be added - * @param htmltree the content tree to which the comment will be added - */ - public void addInlineComment(Doc doc, Tag tag, Content htmltree) { - addCommentTags(doc, tag, tag.inlineTags(), false, false, htmltree); - } - - /** - * Add the inline deprecated comment. - * - * @param doc the doc for which the inline deprecated comment will be added - * @param tag the inline tag to be added - * @param htmltree the content tree to which the comment will be added - */ - public void addInlineDeprecatedComment(Doc doc, Tag tag, Content htmltree) { - addCommentTags(doc, tag.inlineTags(), true, false, htmltree); - } - - /** - * Adds the summary content. - * - * @param doc the doc for which the summary will be generated - * @param htmltree the documentation tree to which the summary will be added - */ - public void addSummaryComment(Doc doc, Content htmltree) { - addSummaryComment(doc, doc.firstSentenceTags(), htmltree); - } - - /** - * Adds the summary content. - * - * @param doc the doc for which the summary will be generated - * @param firstSentenceTags the first sentence tags for the doc - * @param htmltree the documentation tree to which the summary will be added - */ - public void addSummaryComment(Doc doc, Tag[] firstSentenceTags, Content htmltree) { - addCommentTags(doc, firstSentenceTags, false, true, htmltree); - } - - public void addSummaryDeprecatedComment(Doc doc, Tag tag, Content htmltree) { - addCommentTags(doc, tag.firstSentenceTags(), true, true, htmltree); - } - - /** - * Adds the inline comment. - * - * @param doc the doc for which the inline comments will be generated - * @param htmltree the documentation tree to which the inline comments will be added - */ - public void addInlineComment(Doc doc, Content htmltree) { - addCommentTags(doc, doc.inlineTags(), false, false, htmltree); - } - - /** - * Adds the comment tags. - * - * @param doc the doc for which the comment tags will be generated - * @param tags the first sentence tags for the doc - * @param depr true if it is deprecated - * @param first true if the first sentence tags should be added - * @param htmltree the documentation tree to which the comment tags will be added - */ - private void addCommentTags(Doc doc, Tag[] tags, boolean depr, - boolean first, Content htmltree) { - addCommentTags(doc, null, tags, depr, first, htmltree); - } - - /** - * Adds the comment tags. - * - * @param doc the doc for which the comment tags will be generated - * @param holderTag the block tag context for the inline tags - * @param tags the first sentence tags for the doc - * @param depr true if it is deprecated - * @param first true if the first sentence tags should be added - * @param htmltree the documentation tree to which the comment tags will be added - */ - private void addCommentTags(Doc doc, Tag holderTag, Tag[] tags, boolean depr, - boolean first, Content htmltree) { - if(configuration.nocomment){ - return; - } - Content div; - Content result = commentTagsToContent(null, doc, tags, first); - if (depr) { - Content italic = HtmlTree.SPAN(HtmlStyle.deprecationComment, result); - div = HtmlTree.DIV(HtmlStyle.block, italic); - htmltree.addContent(div); - } - else { - div = HtmlTree.DIV(HtmlStyle.block, result); - htmltree.addContent(div); - } - if (tags.length == 0) { - htmltree.addContent(getSpace()); - } - } - - /** - * Converts inline tags and text to text strings, expanding the - * inline tags along the way. Called wherever text can contain - * an inline tag, such as in comments or in free-form text arguments - * to non-inline tags. - * - * @param holderTag specific tag where comment resides - * @param doc specific doc where comment resides - * @param tags array of text tags and inline tags (often alternating) - * present in the text of interest for this doc - * @param isFirstSentence true if text is first sentence - */ - public Content commentTagsToContent(Tag holderTag, Doc doc, Tag[] tags, - boolean isFirstSentence) { - Content result = new ContentBuilder(); - boolean textTagChange = false; - // Array of all possible inline tags for this javadoc run - configuration.tagletManager.checkTags(doc, tags, true); - for (int i = 0; i < tags.length; i++) { - Tag tagelem = tags[i]; - String tagName = tagelem.name(); - if (tagelem instanceof SeeTag) { - result.addContent(seeTagToContent((SeeTag) tagelem)); - } else if (! tagName.equals("Text")) { - boolean wasEmpty = result.isEmpty(); - Content output; - if (configuration.docrootparent.length() > 0 - && tagelem.name().equals("@docRoot") - && ((tags[i + 1]).text()).startsWith("/..")) { - // If Xdocrootparent switch ON, set the flag to remove the /.. occurrence after - // {@docRoot} tag in the very next Text tag. - textTagChange = true; - // Replace the occurrence of {@docRoot}/.. with the absolute link. - output = new StringContent(configuration.docrootparent); - } else { - output = TagletWriter.getInlineTagOuput( - configuration.tagletManager, holderTag, - tagelem, getTagletWriterInstance(isFirstSentence)); - } - if (output != null) - result.addContent(output); - if (wasEmpty && isFirstSentence && tagelem.name().equals("@inheritDoc") && !result.isEmpty()) { - break; - } else { - continue; - } - } else { - String text = tagelem.text(); - //If Xdocrootparent switch ON, remove the /.. occurrence after {@docRoot} tag. - if (textTagChange) { - text = text.replaceFirst("/..", ""); - textTagChange = false; - } - //This is just a regular text tag. The text may contain html links () - //or inline tag {@docRoot}, which will be handled as special cases. - text = redirectRelativeLinks(tagelem.holder(), text); - - // Replace @docRoot only if not represented by an instance of DocRootTaglet, - // that is, only if it was not present in a source file doc comment. - // This happens when inserted by the doclet (a few lines - // above in this method). [It might also happen when passed in on the command - // line as a text argument to an option (like -header).] - text = replaceDocRootDir(text); - if (isFirstSentence) { - text = removeNonInlineHtmlTags(text); - } - text = utils.replaceTabs(configuration, text); - text = utils.normalizeNewlines(text); - result.addContent(new RawHtml(text)); - } - } - return result; - } - - /** - * Return true if relative links should not be redirected. - * - * @return Return true if a relative link should not be redirected. - */ - private boolean shouldNotRedirectRelativeLinks() { - return this instanceof AnnotationTypeWriter || - this instanceof ClassWriter || - this instanceof PackageSummaryWriter; - } - - /** - * Suppose a piece of documentation has a relative link. When you copy - * that documentation to another place such as the index or class-use page, - * that relative link will no longer work. We should redirect those links - * so that they will work again. - *

- * Here is the algorithm used to fix the link: - *

- * {@literal => docRoot + + } - *

- * For example, suppose com.sun.javadoc.RootDoc has this link: - * {@literal The package Page } - *

- * If this link appeared in the index, we would redirect - * the link like this: - * - * {@literal The package Page} - * - * @param doc the Doc object whose documentation is being written. - * @param text the text being written. - * - * @return the text, with all the relative links redirected to work. - */ - private String redirectRelativeLinks(Doc doc, String text) { - if (doc == null || shouldNotRedirectRelativeLinks()) { - return text; - } - - DocPath redirectPathFromRoot; - if (doc instanceof ClassDoc) { - redirectPathFromRoot = DocPath.forPackage(((ClassDoc) doc).containingPackage()); - } else if (doc instanceof MemberDoc) { - redirectPathFromRoot = DocPath.forPackage(((MemberDoc) doc).containingPackage()); - } else if (doc instanceof PackageDoc) { - redirectPathFromRoot = DocPath.forPackage((PackageDoc) doc); - } else { - return text; - } - - //Redirect all relative links. - int end, begin = StringUtils.indexOfIgnoreCase(text, "= 0){ - StringBuilder textBuff = new StringBuilder(text); - - while(begin >=0){ - if (textBuff.length() > begin + 2 && ! Character.isWhitespace(textBuff.charAt(begin+2))) { - begin = StringUtils.indexOfIgnoreCase(textBuff.toString(), "", begin +1); - if(begin == 0){ - //Link has no equal symbol. - configuration.root.printWarning( - doc.position(), - configuration.getText("doclet.malformed_html_link_tag", text)); - break; - } - if (end == -1) { - //Break without warning. This tag is not necessarily malformed. The text - //might be missing '>' character because the href has an inline tag. - break; - } - - String quote = textBuff.substring(begin, end); - quote = quote.contains("\"") ? "\"" : - quote.contains("\'") ? "\'" : null; - if (quote != null) { - begin = textBuff.indexOf(quote, begin) + 1; - end = textBuff.indexOf(quote, begin +1); - if (begin == 0 || end == -1){ - //Link is missing a quote. - break; - } - } - String relativeLink = textBuff.substring(begin, end); - String relativeLinkLowerCase = StringUtils.toLowerCase(relativeLink); - if (!(relativeLinkLowerCase.startsWith("mailto:") || - relativeLinkLowerCase.startsWith("http:") || - relativeLinkLowerCase.startsWith("https:") || - relativeLinkLowerCase.startsWith("file:"))) { - relativeLink = "{@"+(new DocRootTaglet()).getName() + "}/" - + redirectPathFromRoot.resolve(relativeLink).getPath(); - textBuff.replace(begin, end, relativeLink); - } - begin = StringUtils.indexOfIgnoreCase(textBuff.toString(), " blockTags = new HashSet<>(); - static { - for (HtmlTag t: HtmlTag.values()) { - if (t.blockType == HtmlTag.BlockType.BLOCK) - blockTags.add(t.value); - } - } - - public static String removeNonInlineHtmlTags(String text) { - final int len = text.length(); - - int startPos = 0; // start of text to copy - int lessThanPos = text.indexOf('<'); // position of latest '<' - if (lessThanPos < 0) { - return text; - } - - StringBuilder result = new StringBuilder(); - main: while (lessThanPos != -1) { - int currPos = lessThanPos + 1; - if (currPos == len) - break; - char ch = text.charAt(currPos); - if (ch == '/') { - if (++currPos == len) - break; - ch = text.charAt(currPos); - } - int tagPos = currPos; - while (isHtmlTagLetterOrDigit(ch)) { - if (++currPos == len) - break main; - ch = text.charAt(currPos); - } - if (ch == '>' && blockTags.contains(StringUtils.toLowerCase(text.substring(tagPos, currPos)))) { - result.append(text, startPos, lessThanPos); - startPos = currPos + 1; - } - lessThanPos = text.indexOf('<', currPos); - } - result.append(text.substring(startPos)); - - return result.toString(); - } - - private static boolean isHtmlTagLetterOrDigit(char ch) { - return ('a' <= ch && ch <= 'z') || - ('A' <= ch && ch <= 'Z') || - ('1' <= ch && ch <= '6'); - } - - /** - * Add a link to the stylesheet file. - * - * @param head the content tree to which the files will be added - */ - public void addStyleSheetProperties(Content head) { - String stylesheetfile = configuration.stylesheetfile; - DocPath stylesheet; - if (stylesheetfile.isEmpty()) { - stylesheet = DocPaths.STYLESHEET; - } else { - DocFile file = DocFile.createFileForInput(configuration, stylesheetfile); - stylesheet = DocPath.create(file.getName()); - } - HtmlTree link = HtmlTree.LINK("stylesheet", "text/css", - pathToRoot.resolve(stylesheet).getPath(), - "Style"); - head.addContent(link); - if (configuration.createindex) { - HtmlTree jq_link = HtmlTree.LINK("stylesheet", "text/css", - pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(DocPaths.JQUERY_STYLESHEET_FILE)).getPath(), - "Style"); - head.addContent(jq_link); - } - } - - /** - * Add a link to the JavaScript file. - * - * @param head the content tree to which the files will be added - */ - public void addScriptProperties(Content head) { - HtmlTree javascript = HtmlTree.SCRIPT(pathToRoot.resolve(DocPaths.JAVASCRIPT).getPath()); - head.addContent(javascript); - if (configuration.createindex) { - if (pathToRoot != null && script != null) { - String path = pathToRoot.isEmpty() ? "." : pathToRoot.getPath(); - script.addContent(new RawHtml("var pathtoroot = \"" + path + "/\";loadScripts(document, \'script\');")); - } - addJQueryFile(head, DocPaths.JSZIP_MIN); - addJQueryFile(head, DocPaths.JSZIPUTILS_MIN); - head.addContent(new RawHtml("")); - addJQueryFile(head, DocPaths.JQUERY_JS_1_10); - addJQueryFile(head, DocPaths.JQUERY_JS); - } - } - - /** - * Add a link to the JQuery javascript file. - * - * @param head the content tree to which the files will be added - * @param filePath the DocPath of the file that needs to be added - */ - private void addJQueryFile(Content head, DocPath filePath) { - HtmlTree jqyeryScriptFile = HtmlTree.SCRIPT( - pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(filePath)).getPath()); - head.addContent(jqyeryScriptFile); - } - - /** - * According to - * The Java™ Language Specification, - * all the outer classes and static nested classes are core classes. - */ - public boolean isCoreClass(ClassDoc cd) { - return cd.containingClass() == null || cd.isStatic(); - } - - /** - * Adds the annotatation types for the given packageDoc. - * - * @param packageDoc the package to write annotations for. - * @param htmltree the documentation tree to which the annotation info will be - * added - */ - public void addAnnotationInfo(PackageDoc packageDoc, Content htmltree) { - addAnnotationInfo(packageDoc, packageDoc.annotations(), htmltree); - } - - /** - * Add the annotation types of the executable receiver. - * - * @param method the executable to write the receiver annotations for. - * @param descList list of annotation description. - * @param htmltree the documentation tree to which the annotation info will be - * added - */ - public void addReceiverAnnotationInfo(ExecutableMemberDoc method, AnnotationDesc[] descList, - Content htmltree) { - addAnnotationInfo(0, method, descList, false, htmltree); - } - - /** - * Adds the annotatation types for the given doc. - * - * @param doc the package to write annotations for - * @param htmltree the content tree to which the annotation types will be added - */ - public void addAnnotationInfo(ProgramElementDoc doc, Content htmltree) { - addAnnotationInfo(doc, doc.annotations(), htmltree); - } - - /** - * Add the annotatation types for the given doc and parameter. - * - * @param indent the number of spaces to indent the parameters. - * @param doc the doc to write annotations for. - * @param param the parameter to write annotations for. - * @param tree the content tree to which the annotation types will be added - */ - public boolean addAnnotationInfo(int indent, Doc doc, Parameter param, - Content tree) { - return addAnnotationInfo(indent, doc, param.annotations(), false, tree); - } - - /** - * Adds the annotatation types for the given doc. - * - * @param doc the doc to write annotations for. - * @param descList the array of {@link AnnotationDesc}. - * @param htmltree the documentation tree to which the annotation info will be - * added - */ - private void addAnnotationInfo(Doc doc, AnnotationDesc[] descList, - Content htmltree) { - addAnnotationInfo(0, doc, descList, true, htmltree); - } - - /** - * Adds the annotation types for the given doc. - * - * @param indent the number of extra spaces to indent the annotations. - * @param doc the doc to write annotations for. - * @param descList the array of {@link AnnotationDesc}. - * @param htmltree the documentation tree to which the annotation info will be - * added - */ - private boolean addAnnotationInfo(int indent, Doc doc, - AnnotationDesc[] descList, boolean lineBreak, Content htmltree) { - List annotations = getAnnotations(indent, descList, lineBreak); - String sep =""; - if (annotations.isEmpty()) { - return false; - } - for (Content annotation: annotations) { - htmltree.addContent(sep); - htmltree.addContent(annotation); - if (!lineBreak) { - sep = " "; - } - } - return true; - } - - /** - * Return the string representations of the annotation types for - * the given doc. - * - * @param indent the number of extra spaces to indent the annotations. - * @param descList the array of {@link AnnotationDesc}. - * @param linkBreak if true, add new line between each member value. - * @return an array of strings representing the annotations being - * documented. - */ - private List getAnnotations(int indent, AnnotationDesc[] descList, boolean linkBreak) { - return getAnnotations(indent, descList, linkBreak, true); - } - - /** - * Return the string representations of the annotation types for - * the given doc. - * - * A {@code null} {@code elementType} indicates that all the - * annotations should be returned without any filtering. - * - * @param indent the number of extra spaces to indent the annotations. - * @param descList the array of {@link AnnotationDesc}. - * @param linkBreak if true, add new line between each member value. - * @param elementType the type of targeted element (used for filtering - * type annotations from declaration annotations) - * @return an array of strings representing the annotations being - * documented. - */ - public List getAnnotations(int indent, AnnotationDesc[] descList, boolean linkBreak, - boolean isJava5DeclarationLocation) { - List results = new ArrayList<>(); - ContentBuilder annotation; - for (AnnotationDesc aDesc : descList) { - AnnotationTypeDoc annotationDoc = aDesc.annotationType(); - // If an annotation is not documented, do not add it to the list. If - // the annotation is of a repeatable type, and if it is not documented - // and also if its container annotation is not documented, do not add it - // to the list. If an annotation of a repeatable type is not documented - // but its container is documented, it will be added to the list. - if (!utils.isDocumentedAnnotation(annotationDoc) && - (!isAnnotationDocumented && !isContainerDocumented)) { - continue; - } - /* TODO: check logic here to correctly handle declaration - * and type annotations. - if (util.isDeclarationAnnotation(annotationDoc, isJava5DeclarationLocation)) { - continue; - }*/ - annotation = new ContentBuilder(); - isAnnotationDocumented = false; - LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, - LinkInfoImpl.Kind.ANNOTATION, annotationDoc); - AnnotationDesc.ElementValuePair[] pairs = aDesc.elementValues(); - // If the annotation is synthesized, do not print the container. - if (aDesc.isSynthesized()) { - for (AnnotationDesc.ElementValuePair pair : pairs) { - AnnotationValue annotationValue = pair.value(); - List annotationTypeValues = new ArrayList<>(); - if (annotationValue.value() instanceof AnnotationValue[]) { - AnnotationValue[] annotationArray = - (AnnotationValue[]) annotationValue.value(); - annotationTypeValues.addAll(Arrays.asList(annotationArray)); - } else { - annotationTypeValues.add(annotationValue); - } - String sep = ""; - for (AnnotationValue av : annotationTypeValues) { - annotation.addContent(sep); - annotation.addContent(annotationValueToContent(av)); - sep = " "; - } - } - } - else if (isAnnotationArray(pairs)) { - // If the container has 1 or more value defined and if the - // repeatable type annotation is not documented, do not print - // the container. - if (pairs.length == 1 && isAnnotationDocumented) { - AnnotationValue[] annotationArray = - (AnnotationValue[]) (pairs[0].value()).value(); - List annotationTypeValues = new ArrayList<>(); - annotationTypeValues.addAll(Arrays.asList(annotationArray)); - String sep = ""; - for (AnnotationValue av : annotationTypeValues) { - annotation.addContent(sep); - annotation.addContent(annotationValueToContent(av)); - sep = " "; - } - } - // If the container has 1 or more value defined and if the - // repeatable type annotation is not documented, print the container. - else { - addAnnotations(annotationDoc, linkInfo, annotation, pairs, - indent, false); - } - } - else { - addAnnotations(annotationDoc, linkInfo, annotation, pairs, - indent, linkBreak); - } - annotation.addContent(linkBreak ? DocletConstants.NL : ""); - results.add(annotation); - } - return results; - } - - /** - * Add annotation to the annotation string. - * - * @param annotationDoc the annotation being documented - * @param linkInfo the information about the link - * @param annotation the annotation string to which the annotation will be added - * @param pairs annotation type element and value pairs - * @param indent the number of extra spaces to indent the annotations. - * @param linkBreak if true, add new line between each member value - */ - private void addAnnotations(AnnotationTypeDoc annotationDoc, LinkInfoImpl linkInfo, - ContentBuilder annotation, AnnotationDesc.ElementValuePair[] pairs, - int indent, boolean linkBreak) { - linkInfo.label = new StringContent("@" + annotationDoc.name()); - annotation.addContent(getLink(linkInfo)); - if (pairs.length > 0) { - annotation.addContent("("); - for (int j = 0; j < pairs.length; j++) { - if (j > 0) { - annotation.addContent(","); - if (linkBreak) { - annotation.addContent(DocletConstants.NL); - int spaces = annotationDoc.name().length() + 2; - for (int k = 0; k < (spaces + indent); k++) { - annotation.addContent(" "); - } - } - } - annotation.addContent(getDocLink(LinkInfoImpl.Kind.ANNOTATION, - pairs[j].element(), pairs[j].element().name(), false)); - annotation.addContent("="); - AnnotationValue annotationValue = pairs[j].value(); - List annotationTypeValues = new ArrayList<>(); - if (annotationValue.value() instanceof AnnotationValue[]) { - AnnotationValue[] annotationArray = - (AnnotationValue[]) annotationValue.value(); - annotationTypeValues.addAll(Arrays.asList(annotationArray)); - } else { - annotationTypeValues.add(annotationValue); - } - annotation.addContent(annotationTypeValues.size() == 1 ? "" : "{"); - String sep = ""; - for (AnnotationValue av : annotationTypeValues) { - annotation.addContent(sep); - annotation.addContent(annotationValueToContent(av)); - sep = ","; - } - annotation.addContent(annotationTypeValues.size() == 1 ? "" : "}"); - isContainerDocumented = false; - } - annotation.addContent(")"); - } - } - - /** - * Check if the annotation contains an array of annotation as a value. This - * check is to verify if a repeatable type annotation is present or not. - * - * @param pairs annotation type element and value pairs - * - * @return true if the annotation contains an array of annotation as a value. - */ - private boolean isAnnotationArray(AnnotationDesc.ElementValuePair[] pairs) { - AnnotationValue annotationValue; - for (AnnotationDesc.ElementValuePair pair : pairs) { - annotationValue = pair.value(); - if (annotationValue.value() instanceof AnnotationValue[]) { - AnnotationValue[] annotationArray = - (AnnotationValue[]) annotationValue.value(); - if (annotationArray.length > 1) { - if (annotationArray[0].value() instanceof AnnotationDesc) { - AnnotationTypeDoc annotationDoc = - ((AnnotationDesc) annotationArray[0].value()).annotationType(); - isContainerDocumented = true; - if (utils.isDocumentedAnnotation(annotationDoc)) { - isAnnotationDocumented = true; - } - return true; - } - } - } - } - return false; - } - - private Content annotationValueToContent(AnnotationValue annotationValue) { - if (annotationValue.value() instanceof Type) { - Type type = (Type) annotationValue.value(); - if (type.asClassDoc() != null) { - LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, - LinkInfoImpl.Kind.ANNOTATION, type); - linkInfo.label = new StringContent((type.asClassDoc().isIncluded() ? - type.typeName() : - type.qualifiedTypeName()) + type.dimension() + ".class"); - return getLink(linkInfo); - } else { - return new StringContent(type.typeName() + type.dimension() + ".class"); - } - } else if (annotationValue.value() instanceof AnnotationDesc) { - List list = getAnnotations(0, - new AnnotationDesc[]{(AnnotationDesc) annotationValue.value()}, - false); - ContentBuilder buf = new ContentBuilder(); - for (Content c: list) { - buf.addContent(c); - } - return buf; - } else if (annotationValue.value() instanceof MemberDoc) { - return getDocLink(LinkInfoImpl.Kind.ANNOTATION, - (MemberDoc) annotationValue.value(), - ((MemberDoc) annotationValue.value()).name(), false); - } else { - return new StringContent(annotationValue.toString()); - } - } - - /** - * Return the configuation for this doclet. - * - * @return the configuration for this doclet. - */ - public Configuration configuration() { - return configuration; - } -} --- /dev/null Fri Jan 22 12:17:50 2016 +++ new/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java Fri Jan 22 12:17:50 2016 @@ -0,0 +1,2525 @@ +/* + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Name; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.SimpleAnnotationValueVisitor9; +import javax.lang.model.util.SimpleElementVisitor9; +import javax.lang.model.util.SimpleTypeVisitor9; + +import com.sun.source.doctree.AttributeTree; +import com.sun.source.doctree.AttributeTree.ValueKind; +import com.sun.source.doctree.CommentTree; +import com.sun.source.doctree.DocRootTree; +import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.DocTree.Kind; +import com.sun.source.doctree.EndElementTree; +import com.sun.source.doctree.EntityTree; +import com.sun.source.doctree.ErroneousTree; +import com.sun.source.doctree.InheritDocTree; +import com.sun.source.doctree.IndexTree; +import com.sun.source.doctree.LinkTree; +import com.sun.source.doctree.LiteralTree; +import com.sun.source.doctree.SeeTree; +import com.sun.source.doctree.StartElementTree; +import com.sun.source.doctree.TextTree; +import com.sun.source.util.SimpleDocTreeVisitor; +import com.sun.tools.javac.util.DefinedBy; +import com.sun.tools.javac.util.DefinedBy.Api; + +import jdk.javadoc.internal.doclets.formats.html.markup.Comment; +import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; +import jdk.javadoc.internal.doclets.formats.html.markup.DocType; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocWriter; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocument; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter; +import jdk.javadoc.internal.doclets.toolkit.ClassWriter; +import jdk.javadoc.internal.doclets.toolkit.Configuration; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.PackageSummaryWriter; +import jdk.javadoc.internal.doclets.toolkit.taglets.DocRootTaglet; +import jdk.javadoc.internal.doclets.toolkit.taglets.TagletWriter; +import jdk.javadoc.internal.doclets.toolkit.util.DocFile; +import jdk.javadoc.internal.doclets.toolkit.util.DocLink; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; +import jdk.javadoc.internal.doclets.toolkit.util.ImplementedMethods; +import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; + +import static com.sun.source.doctree.AttributeTree.ValueKind.*; +import static com.sun.source.doctree.DocTree.Kind.*; +import static jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocWriter.CONTENT_TYPE; +import static jdk.javadoc.internal.doclets.toolkit.util.CommentHelper.SPACER; + + +/** + * Class for the Html Format Code Generation specific to JavaDoc. + * This Class contains methods related to the Html Code Generation which + * are used extensively while generating the entire documentation. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @since 1.2 + * @author Atul M Dambalkar + * @author Robert Field + * @author Bhavesh Patel (Modified) + */ +public class HtmlDocletWriter extends HtmlDocWriter { + + /** + * Relative path from the file getting generated to the destination + * directory. For example, if the file getting generated is + * "java/lang/Object.html", then the path to the root is "../..". + * This string can be empty if the file getting generated is in + * the destination directory. + */ + public final DocPath pathToRoot; + + /** + * Platform-independent path from the current or the + * destination directory to the file getting generated. + * Used when creating the file. + */ + public final DocPath path; + + /** + * Name of the file getting generated. If the file getting generated is + * "java/lang/Object.html", then the filename is "Object.html". + */ + public final DocPath filename; + + /** + * The global configuration information for this run. + */ + public final ConfigurationImpl configuration; + + protected final Utils utils; + + /** + * To check whether annotation heading is printed or not. + */ + protected boolean printedAnnotationHeading = false; + + /** + * To check whether annotation field heading is printed or not. + */ + protected boolean printedAnnotationFieldHeading = false; + + /** + * To check whether the repeated annotations is documented or not. + */ + private boolean isAnnotationDocumented = false; + + /** + * To check whether the container annotations is documented or not. + */ + private boolean isContainerDocumented = false; + + HtmlTree fixedNavDiv = new HtmlTree(HtmlTag.DIV); + + /** + * Constructor to construct the HtmlStandardWriter object. + * + * @param path File to be generated. + */ + public HtmlDocletWriter(ConfigurationImpl configuration, DocPath path) + throws IOException { + super(configuration, path); + this.configuration = configuration; + this.utils = configuration.utils; + this.path = path; + this.pathToRoot = path.parent().invert(); + this.filename = path.basename(); + } + + /** + * Replace {@docRoot} tag used in options that accept HTML text, such + * as -header, -footer, -top and -bottom, and when converting a relative + * HREF where commentTagsToString inserts a {@docRoot} where one was + * missing. (Also see DocRootTaglet for {@docRoot} tags in doc + * comments.) + *

+ * Replace {@docRoot} tag in htmlstr with the relative path to the + * destination directory from the directory where the file is being + * written, looping to handle all such tags in htmlstr. + *

+ * For example, for "-d docs" and -header containing {@docRoot}, when + * the HTML page for source file p/C1.java is being generated, the + * {@docRoot} tag would be inserted into the header as "../", + * the relative path from docs/p/ to docs/ (the document root). + *

+ * Note: This doc comment was written with '&#064;' representing '@' + * to prevent the inline tag from being interpreted. + */ + public String replaceDocRootDir(String htmlstr) { + // Return if no inline tags exist + int index = htmlstr.indexOf("{@"); + if (index < 0) { + return htmlstr; + } + Matcher docrootMatcher = docrootPattern.matcher(htmlstr); + if (!docrootMatcher.find()) { + return htmlstr; + } + StringBuilder buf = new StringBuilder(); + int prevEnd = 0; + do { + int match = docrootMatcher.start(); + // append htmlstr up to start of next {@docroot} + buf.append(htmlstr.substring(prevEnd, match)); + prevEnd = docrootMatcher.end(); + if (configuration.docrootparent.length() > 0 && htmlstr.startsWith("/..", prevEnd)) { + // Insert the absolute link if {@docRoot} is followed by "/..". + buf.append(configuration.docrootparent); + prevEnd += 3; + } else { + // Insert relative path where {@docRoot} was located + buf.append(pathToRoot.isEmpty() ? "." : pathToRoot.getPath()); + } + // Append slash if next character is not a slash + if (prevEnd < htmlstr.length() && htmlstr.charAt(prevEnd) != '/') { + buf.append('/'); + } + } while (docrootMatcher.find()); + buf.append(htmlstr.substring(prevEnd)); + return buf.toString(); + } + //where: + // Note: {@docRoot} is not case sensitive when passed in w/command line option: + private static final Pattern docrootPattern = + Pattern.compile(Pattern.quote("{@docroot}"), Pattern.CASE_INSENSITIVE); + + /** + * Get the script to show or hide the All classes link. + * + * @param id id of the element to show or hide + * @return a content tree for the script + */ + public Content getAllClassesLinkScript(String id) { + HtmlTree script = HtmlTree.SCRIPT(); + String scriptCode = "" + DocletConstants.NL; + Content scriptContent = new RawHtml(scriptCode); + script.addContent(scriptContent); + Content div = HtmlTree.DIV(script); + Content div_noscript = HtmlTree.DIV(getResource("doclet.No_Script_Message")); + Content noScript = HtmlTree.NOSCRIPT(div_noscript); + div.addContent(noScript); + return div; + } + + /** + * Add method information. + * + * @param method the method to be documented + * @param dl the content tree to which the method information will be added + */ + private void addMethodInfo(ExecutableElement method, Content dl) { + TypeElement enclosing = utils.getEnclosingTypeElement(method); + List intfacs = enclosing.getInterfaces(); + ExecutableElement overriddenMethod = utils.overriddenMethod(method); + // Check whether there is any implementation or overridden info to be + // printed. If no overridden or implementation info needs to be + // printed, do not print this section. + if ((!intfacs.isEmpty() + && new ImplementedMethods(method, this.configuration).build().isEmpty() == false) + || overriddenMethod != null) { + MethodWriterImpl.addImplementsInfo(this, method, dl); + if (overriddenMethod != null) { + MethodWriterImpl.addOverridden(this, + utils.overriddenType(method), + overriddenMethod, + dl); + } + } + } + + /** + * Adds the tags information. + * + * @param e the Element for which the tags will be generated + * @param htmltree the documentation tree to which the tags will be added + */ + protected void addTagsInfo(Element e, Content htmltree) { + if (configuration.nocomment) { + return; + } + Content dl = new HtmlTree(HtmlTag.DL); + if (utils.isExecutableElement(e) && !utils.isConstructor(e)) { + addMethodInfo((ExecutableElement)e, dl); + } + Content output = new ContentBuilder(); + TagletWriter.genTagOutput(configuration.tagletManager, e, + configuration.tagletManager.getCustomTaglets(e), + getTagletWriterInstance(false), output); + dl.addContent(output); + htmltree.addContent(dl); + } + + /** + * Check whether there are any tags for Serialization Overview + * section to be printed. + * + * @param field the VariableElement object to check for tags. + * @return true if there are tags to be printed else return false. + */ + protected boolean hasSerializationOverviewTags(VariableElement field) { + Content output = new ContentBuilder(); + TagletWriter.genTagOutput(configuration.tagletManager, field, + configuration.tagletManager.getCustomTaglets(field), + getTagletWriterInstance(false), output); + return !output.isEmpty(); + } + + /** + * Returns a TagletWriter that knows how to write HTML. + * + * @return a TagletWriter that knows how to write HTML. + */ + public TagletWriter getTagletWriterInstance(boolean isFirstSentence) { + return new TagletWriterImpl(this, isFirstSentence); + } + + /** + * Get Package link, with target frame. + * + * @param pkg The link will be to the "package-summary.html" page for this package + * @param target name of the target frame + * @param label tag for the link + * @return a content for the target package link + */ + public Content getTargetPackageLink(PackageElement pkg, String target, + Content label) { + return getHyperLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY), label, "", target); + } + + + public void addClassesSummary(SortedSet classes, String label, + String tableSummary, List tableHeader, Content summaryContentTree) { + if (!classes.isEmpty()) { + Content caption = getTableCaption(new RawHtml(label)); + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(HtmlStyle.typeSummary, caption) + : HtmlTree.TABLE(HtmlStyle.typeSummary, tableSummary, caption); + table.addContent(getSummaryTableHeader(tableHeader, "col")); + Content tbody = new HtmlTree(HtmlTag.TBODY); + boolean altColor = true; + for (TypeElement te : classes) { + if (!utils.isCoreClass(te) || + !configuration.isGeneratedDoc(te)) { + continue; + } + Content classContent = getLink(new LinkInfoImpl( + configuration, LinkInfoImpl.Kind.PACKAGE, te)); + Content tdClass = HtmlTree.TD(HtmlStyle.colFirst, classContent); + HtmlTree tr = HtmlTree.TR(tdClass); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + altColor = !altColor; + HtmlTree tdClassDescription = new HtmlTree(HtmlTag.TD); + tdClassDescription.addStyle(HtmlStyle.colLast); + if (utils.isDeprecated(te)) { + tdClassDescription.addContent(deprecatedLabel); + List tags = utils.getDeprecatedTrees(te); + if (!tags.isEmpty()) { + addSummaryDeprecatedComment(te, tags.get(0), tdClassDescription); + } + } else { + addSummaryComment(te, tdClassDescription); + } + tr.addContent(tdClassDescription); + tbody.addContent(tr); + } + table.addContent(tbody); + summaryContentTree.addContent(table); + } + } + + /** + * Generates the HTML document tree and prints it out. + * + * @param metakeywords Array of String keywords for META tag. Each element + * of the array is assigned to a separate META tag. + * Pass in null for no array + * @param includeScript true if printing windowtitle script + * false for files that appear in the left-hand frames + * @param body the body htmltree to be included in the document + */ + public void printHtmlDocument(List metakeywords, boolean includeScript, + Content body) throws IOException { + Content htmlDocType = configuration.isOutputHtml5() + ? DocType.HTML5 + : DocType.TRANSITIONAL; + Content htmlComment = new Comment(configuration.getText("doclet.New_Page")); + Content head = new HtmlTree(HtmlTag.HEAD); + head.addContent(getGeneratedBy(!configuration.notimestamp)); + head.addContent(getTitle()); + Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, + (configuration.charset.length() > 0) ? + configuration.charset : HtmlConstants.HTML_DEFAULT_CHARSET); + head.addContent(meta); + if (!configuration.notimestamp) { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + meta = HtmlTree.META(configuration.isOutputHtml5() + ? "dc.created" + : "date", dateFormat.format(new Date())); + head.addContent(meta); + } + if (metakeywords != null) { + for (String metakeyword : metakeywords) { + meta = HtmlTree.META("keywords", metakeyword); + head.addContent(meta); + } + } + addStyleSheetProperties(head); + addScriptProperties(head); + Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(), + head, body); + Content htmlDocument = new HtmlDocument(htmlDocType, + htmlComment, htmlTree); + write(htmlDocument); + } + + /** + * Get the window title. + * + * @param title the title string to construct the complete window title + * @return the window title string + */ + public String getWindowTitle(String title) { + if (configuration.windowtitle.length() > 0) { + title += " (" + configuration.windowtitle + ")"; + } + return title; + } + + /** + * Get user specified header and the footer. + * + * @param header if true print the user provided header else print the + * user provided footer. + */ + public Content getUserHeaderFooter(boolean header) { + String content; + if (header) { + content = replaceDocRootDir(configuration.header); + } else { + if (configuration.footer.length() != 0) { + content = replaceDocRootDir(configuration.footer); + } else { + content = replaceDocRootDir(configuration.header); + } + } + Content rawContent = new RawHtml(content); + return rawContent; + } + + /** + * Adds the user specified top. + * + * @param htmlTree the content tree to which user specified top will be added + */ + public void addTop(Content htmlTree) { + Content top = new RawHtml(replaceDocRootDir(configuration.top)); + fixedNavDiv.addContent(top); + } + + /** + * Adds the user specified bottom. + * + * @param htmlTree the content tree to which user specified bottom will be added + */ + public void addBottom(Content htmlTree) { + Content bottom = new RawHtml(replaceDocRootDir(configuration.bottom)); + Content small = HtmlTree.SMALL(bottom); + Content p = HtmlTree.P(HtmlStyle.legalCopy, small); + htmlTree.addContent(p); + } + + /** + * Adds the navigation bar for the Html page at the top and and the bottom. + * + * @param header If true print navigation bar at the top of the page else + * @param htmlTree the HtmlTree to which the nav links will be added + */ + protected void addNavLinks(boolean header, Content htmlTree) { + if (!configuration.nonavbar) { + Content tree = (configuration.allowTag(HtmlTag.NAV)) + ? HtmlTree.NAV() + : htmlTree; + String allClassesId = "allclasses_"; + HtmlTree navDiv = new HtmlTree(HtmlTag.DIV); + fixedNavDiv.addStyle(HtmlStyle.fixedNav); + Content skipNavLinks = configuration.getResource("doclet.Skip_navigation_links"); + if (header) { + fixedNavDiv.addContent(HtmlConstants.START_OF_TOP_NAVBAR); + navDiv.addStyle(HtmlStyle.topNav); + allClassesId += "navbar_top"; + Content a = getMarkerAnchor(SectionName.NAVBAR_TOP); + //WCAG - Hyperlinks should contain text or an image with alt text - for AT tools + navDiv.addContent(a); + Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav, getHyperLink( + getDocLink(SectionName.SKIP_NAVBAR_TOP), skipNavLinks, + skipNavLinks.toString(), "")); + navDiv.addContent(skipLinkContent); + } else { + tree.addContent(HtmlConstants.START_OF_BOTTOM_NAVBAR); + navDiv.addStyle(HtmlStyle.bottomNav); + allClassesId += "navbar_bottom"; + Content a = getMarkerAnchor(SectionName.NAVBAR_BOTTOM); + navDiv.addContent(a); + Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav, getHyperLink( + getDocLink(SectionName.SKIP_NAVBAR_BOTTOM), skipNavLinks, + skipNavLinks.toString(), "")); + navDiv.addContent(skipLinkContent); + } + if (header) { + navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_TOP_FIRSTROW)); + } else { + navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_BOTTOM_FIRSTROW)); + } + HtmlTree navList = new HtmlTree(HtmlTag.UL); + navList.addStyle(HtmlStyle.navList); + navList.addAttr(HtmlAttr.TITLE, + configuration.getText("doclet.Navigation")); + if (configuration.createoverview) { + navList.addContent(getNavLinkContents()); + } + if (configuration.packages.size() == 1) { + navList.addContent(getNavLinkPackage(configuration.packages.first())); + } else if (!configuration.packages.isEmpty()) { + navList.addContent(getNavLinkPackage()); + } + navList.addContent(getNavLinkClass()); + if(configuration.classuse) { + navList.addContent(getNavLinkClassUse()); + } + if(configuration.createtree) { + navList.addContent(getNavLinkTree()); + } + if(!(configuration.nodeprecated || + configuration.nodeprecatedlist)) { + navList.addContent(getNavLinkDeprecated()); + } + if(configuration.createindex) { + navList.addContent(getNavLinkIndex()); + } + if (!configuration.nohelp) { + navList.addContent(getNavLinkHelp()); + } + navDiv.addContent(navList); + Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, getUserHeaderFooter(header)); + navDiv.addContent(aboutDiv); + if (header) { + fixedNavDiv.addContent(navDiv); + } else { + tree.addContent(navDiv); + } + Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious()); + ulNav.addContent(getNavLinkNext()); + Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav); + Content ulFrames = HtmlTree.UL(HtmlStyle.navList, getNavShowLists()); + ulFrames.addContent(getNavHideLists(filename)); + subDiv.addContent(ulFrames); + HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex()); + ulAllClasses.addAttr(HtmlAttr.ID, allClassesId); + subDiv.addContent(ulAllClasses); + if (header && configuration.createindex) { + HtmlTree inputText = HtmlTree.INPUT("text", "search"); + HtmlTree inputReset = HtmlTree.INPUT("reset", "reset"); + Content searchTxt = configuration.getResource("doclet.search"); + searchTxt.addContent(getSpace()); + HtmlTree liInput = HtmlTree.LI(HtmlTree.SPAN(searchTxt)); + liInput.addContent(inputText); + liInput.addContent(inputReset); + HtmlTree ulSearch = HtmlTree.UL(HtmlStyle.navListSearch, liInput); + subDiv.addContent(ulSearch); + } + subDiv.addContent(getAllClassesLinkScript(allClassesId)); + addSummaryDetailLinks(subDiv); + if (header) { + subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_TOP)); + fixedNavDiv.addContent(subDiv); + fixedNavDiv.addContent(HtmlConstants.END_OF_TOP_NAVBAR); + tree.addContent(fixedNavDiv); + } else { + subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_BOTTOM)); + tree.addContent(subDiv); + tree.addContent(HtmlConstants.END_OF_BOTTOM_NAVBAR); + } + if (configuration.allowTag(HtmlTag.NAV)) { + htmlTree.addContent(tree); + } + } + } + + /** + * Get the word "NEXT" to indicate that no link is available. Override + * this method to customize next link. + * + * @return a content tree for the link + */ + protected Content getNavLinkNext() { + return getNavLinkNext(null); + } + + /** + * Get the word "PREV" to indicate that no link is available. Override + * this method to customize prev link. + * + * @return a content tree for the link + */ + protected Content getNavLinkPrevious() { + return getNavLinkPrevious(null); + } + + /** + * Do nothing. This is the default method. + */ + protected void addSummaryDetailLinks(Content navDiv) { + } + + /** + * Get link to the "overview-summary.html" page. + * + * @return a content tree for the link + */ + protected Content getNavLinkContents() { + Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_SUMMARY), + overviewLabel, "", ""); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get link to the "package-summary.html" page for the package passed. + * + * @param pkg Package to which link will be generated + * @return a content tree for the link + */ + protected Content getNavLinkPackage(PackageElement pkg) { + Content linkContent = getPackageLink(pkg, packageLabel); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get the word "Package" , to indicate that link is not available here. + * + * @return a content tree for the link + */ + protected Content getNavLinkPackage() { + Content li = HtmlTree.LI(packageLabel); + return li; + } + + /** + * Get the word "Use", to indicate that link is not available. + * + * @return a content tree for the link + */ + protected Content getNavLinkClassUse() { + Content li = HtmlTree.LI(useLabel); + return li; + } + + /** + * Get link for previous file. + * + * @param prev File name for the prev link + * @return a content tree for the link + */ + public Content getNavLinkPrevious(DocPath prev) { + Content li; + if (prev != null) { + li = HtmlTree.LI(getHyperLink(prev, prevLabel, "", "")); + } + else + li = HtmlTree.LI(prevLabel); + return li; + } + + /** + * Get link for next file. If next is null, just print the label + * without linking it anywhere. + * + * @param next File name for the next link + * @return a content tree for the link + */ + public Content getNavLinkNext(DocPath next) { + Content li; + if (next != null) { + li = HtmlTree.LI(getHyperLink(next, nextLabel, "", "")); + } + else + li = HtmlTree.LI(nextLabel); + return li; + } + + /** + * Get "FRAMES" link, to switch to the frame version of the output. + * + * @param link File to be linked, "index.html" + * @return a content tree for the link + */ + protected Content getNavShowLists(DocPath link) { + DocLink dl = new DocLink(link, path.getPath(), null); + Content framesContent = getHyperLink(dl, framesLabel, "", "_top"); + Content li = HtmlTree.LI(framesContent); + return li; + } + + /** + * Get "FRAMES" link, to switch to the frame version of the output. + * + * @return a content tree for the link + */ + protected Content getNavShowLists() { + return getNavShowLists(pathToRoot.resolve(DocPaths.INDEX)); + } + + /** + * Get "NO FRAMES" link, to switch to the non-frame version of the output. + * + * @param link File to be linked + * @return a content tree for the link + */ + protected Content getNavHideLists(DocPath link) { + Content noFramesContent = getHyperLink(link, noframesLabel, "", "_top"); + Content li = HtmlTree.LI(noFramesContent); + return li; + } + + /** + * Get "Tree" link in the navigation bar. If there is only one package + * specified on the command line, then the "Tree" link will be to the + * only "package-tree.html" file otherwise it will be to the + * "overview-tree.html" file. + * + * @return a content tree for the link + */ + protected Content getNavLinkTree() { + List packages = new ArrayList<>(utils.getSpecifiedPackages()); + DocPath docPath = packages.size() == 1 && utils.getSpecifiedClasses().isEmpty() + ? pathString(packages.get(0), DocPaths.PACKAGE_TREE) + : pathToRoot.resolve(DocPaths.OVERVIEW_TREE); + return HtmlTree.LI(getHyperLink(docPath, treeLabel, "", "")); + } + + /** + * Get the overview tree link for the main tree. + * + * @param label the label for the link + * @return a content tree for the link + */ + protected Content getNavLinkMainTree(String label) { + Content mainTreeContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE), + new StringContent(label)); + Content li = HtmlTree.LI(mainTreeContent); + return li; + } + + /** + * Get the word "Class", to indicate that class link is not available. + * + * @return a content tree for the link + */ + protected Content getNavLinkClass() { + Content li = HtmlTree.LI(classLabel); + return li; + } + + /** + * Get "Deprecated" API link in the navigation bar. + * + * @return a content tree for the link + */ + protected Content getNavLinkDeprecated() { + Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.DEPRECATED_LIST), + deprecatedLabel, "", ""); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get link for generated index. If the user has used "-splitindex" + * command line option, then link to file "index-files/index-1.html" is + * generated otherwise link to file "index-all.html" is generated. + * + * @return a content tree for the link + */ + protected Content getNavLinkClassIndex() { + Content allClassesContent = getHyperLink(pathToRoot.resolve( + DocPaths.ALLCLASSES_NOFRAME), + allclassesLabel, "", ""); + Content li = HtmlTree.LI(allClassesContent); + return li; + } + + /** + * Get link for generated class index. + * + * @return a content tree for the link + */ + protected Content getNavLinkIndex() { + Content linkContent = getHyperLink(pathToRoot.resolve( + (configuration.splitindex + ? DocPaths.INDEX_FILES.resolve(DocPaths.indexN(1)) + : DocPaths.INDEX_ALL)), + indexLabel, "", ""); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get help file link. If user has provided a help file, then generate a + * link to the user given file, which is already copied to current or + * destination directory. + * + * @return a content tree for the link + */ + protected Content getNavLinkHelp() { + String helpfile = configuration.helpfile; + DocPath helpfilenm; + if (helpfile.isEmpty()) { + helpfilenm = DocPaths.HELP_DOC; + } else { + DocFile file = DocFile.createFileForInput(configuration, helpfile); + helpfilenm = DocPath.create(file.getName()); + } + Content linkContent = getHyperLink(pathToRoot.resolve(helpfilenm), + helpLabel, "", ""); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get summary table header. + * + * @param header the header for the table + * @param scope the scope of the headers + * @return a content tree for the header + */ + public Content getSummaryTableHeader(List header, String scope) { + Content tr = new HtmlTree(HtmlTag.TR); + final int size = header.size(); + Content tableHeader; + if (size == 1) { + tableHeader = new StringContent(header.get(0)); + tr.addContent(HtmlTree.TH(HtmlStyle.colOne, scope, tableHeader)); + return tr; + } + for (int i = 0; i < size; i++) { + tableHeader = new StringContent(header.get(i)); + if(i == 0) + tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader)); + else if(i == (size - 1)) + tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader)); + else + tr.addContent(HtmlTree.TH(scope, tableHeader)); + } + return tr; + } + + /** + * Get table caption. + * + * @param rawText the caption for the table which could be raw Html + * @return a content tree for the caption + */ + public Content getTableCaption(Content title) { + Content captionSpan = HtmlTree.SPAN(title); + Content space = getSpace(); + Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space); + Content caption = HtmlTree.CAPTION(captionSpan); + caption.addContent(tabSpan); + return caption; + } + + /** + * Get the marker anchor which will be added to the documentation tree. + * + * @param anchorName the anchor name attribute + * @return a content tree for the marker anchor + */ + public Content getMarkerAnchor(String anchorName) { + return getMarkerAnchor(getName(anchorName), null); + } + + /** + * Get the marker anchor which will be added to the documentation tree. + * + * @param sectionName the section name anchor attribute for page + * @return a content tree for the marker anchor + */ + public Content getMarkerAnchor(SectionName sectionName) { + return getMarkerAnchor(sectionName.getName(), null); + } + + /** + * Get the marker anchor which will be added to the documentation tree. + * + * @param sectionName the section name anchor attribute for page + * @param anchorName the anchor name combined with section name attribute for the page + * @return a content tree for the marker anchor + */ + public Content getMarkerAnchor(SectionName sectionName, String anchorName) { + return getMarkerAnchor(sectionName.getName() + getName(anchorName), null); + } + + /** + * Get the marker anchor which will be added to the documentation tree. + * + * @param anchorName the anchor name or id attribute + * @param anchorContent the content that should be added to the anchor + * @return a content tree for the marker anchor + */ + public Content getMarkerAnchor(String anchorName, Content anchorContent) { + if (anchorContent == null) + anchorContent = new Comment(" "); + Content markerAnchor = HtmlTree.A(configuration.htmlVersion, anchorName, anchorContent); + return markerAnchor; + } + + /** + * Returns a packagename content. + * + * @param packageElement the package to check + * @return package name content + */ + public Content getPackageName(PackageElement packageElement) { + return packageElement == null || packageElement.isUnnamed() + ? defaultPackageLabel + : getPackageLabel(packageElement.getQualifiedName().toString()); + } + + /** + * Returns a package name label. + * + * @param packageName the package name + * @return the package name content + */ + public Content getPackageLabel(String packageName) { + return new StringContent(packageName); + } + + /** + * Add package deprecation information to the documentation tree + * + * @param deprPkgs list of deprecated packages + * @param headingKey the caption for the deprecated package table + * @param tableSummary the summary for the deprecated package table + * @param tableHeader table headers for the deprecated package table + * @param contentTree the content tree to which the deprecated package table will be added + */ + protected void addPackageDeprecatedAPI(SortedSet deprPkgs, String headingKey, + String tableSummary, List tableHeader, Content contentTree) { + if (deprPkgs.size() > 0) { + Content caption = getTableCaption(configuration.getResource(headingKey)); + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(HtmlStyle.deprecatedSummary, caption) + : HtmlTree.TABLE(HtmlStyle.deprecatedSummary, tableSummary, caption); + table.addContent(getSummaryTableHeader(tableHeader, "col")); + Content tbody = new HtmlTree(HtmlTag.TBODY); + boolean altColor = true; + for (Element e : deprPkgs) { + PackageElement pkg = (PackageElement) e; + HtmlTree td = HtmlTree.TD(HtmlStyle.colOne, + getPackageLink(pkg, getPackageName(pkg))); + List tags = utils.getDeprecatedTrees(pkg); + if (!tags.isEmpty()) { + addInlineDeprecatedComment(pkg, tags.get(0), td); + } + HtmlTree tr = HtmlTree.TR(td); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + altColor = !altColor; + tbody.addContent(tr); + } + table.addContent(tbody); + Content li = HtmlTree.LI(HtmlStyle.blockList, table); + Content ul = HtmlTree.UL(HtmlStyle.blockList, li); + contentTree.addContent(ul); + } + } + + /** + * Return the path to the class page for a typeElement. + * + * @param te TypeElement for which the path is requested. + * @param name Name of the file(doesn't include path). + */ + protected DocPath pathString(TypeElement te, DocPath name) { + return pathString(utils.containingPackage(te), name); + } + + /** + * Return path to the given file name in the given package. So if the name + * passed is "Object.html" and the name of the package is "java.lang", and + * if the relative path is "../.." then returned string will be + * "../../java/lang/Object.html" + * + * @param packageElement Package in which the file name is assumed to be. + * @param name File name, to which path string is. + */ + protected DocPath pathString(PackageElement packageElement, DocPath name) { + return pathToRoot.resolve(DocPath.forPackage(packageElement).resolve(name)); + } + + /** + * Given a package, return the name to be used in HTML anchor tag. + * @param packageElement the package. + * @return the name to be used in HTML anchor tag. + */ + public String getPackageAnchorName(PackageElement packageElement) { + return packageElement == null || packageElement.isUnnamed() + ? SectionName.UNNAMED_PACKAGE_ANCHOR.getName() + : utils.getPackageName(packageElement); + } + + /** + * Return the link to the given package. + * + * @param packageElement the package to link to. + * @param label the label for the link. + * @return a content tree for the package link. + */ + public Content getPackageLink(PackageElement packageElement, String label) { + return getPackageLink(packageElement, new StringContent(label)); + } + + public Content getPackageLink(PackageElement packageElement) { + StringContent content = packageElement.isUnnamed() + ? new StringContent() + : new StringContent(utils.getPackageName(packageElement)); + return getPackageLink(packageElement, content); + } + + /** + * Return the link to the given package. + * + * @param packageElement the package to link to. + * @param label the label for the link. + * @return a content tree for the package link. + */ + public Content getPackageLink(PackageElement packageElement, Content label) { + boolean included = packageElement != null && utils.isIncluded(packageElement); + if (!included) { + for (PackageElement p : configuration.packages) { + if (p.equals(packageElement)) { + included = true; + break; + } + } + } + if (included || packageElement == null) { + return getHyperLink(pathString(packageElement, DocPaths.PACKAGE_SUMMARY), + label); + } else { + DocLink crossPkgLink = getCrossPackageLink(utils.getPackageName(packageElement)); + if (crossPkgLink != null) { + return getHyperLink(crossPkgLink, label); + } else { + return label; + } + } + } + + public Content interfaceName(TypeElement typeElement, boolean qual) { + Content name = new StringContent((qual) + ? typeElement.getQualifiedName().toString() + : utils.getSimpleName(typeElement)); + return (utils.isInterface(typeElement)) ? HtmlTree.SPAN(HtmlStyle.interfaceName, name) : name; + } + + /** + * Add the link to the content tree. + * + * @param typeElement program element typeElement for which the link will be added + * @param label label for the link + * @param htmltree the content tree to which the link will be added + */ + public void addSrcLink(Element typeElement, Content label, Content htmltree) { + if (typeElement == null) { + return; + } + TypeElement te = utils.getEnclosingTypeElement(typeElement); + if (te == null) { + // must be a typeElement since in has no containing class. + te = (TypeElement) typeElement; + } + DocPath href = pathToRoot + .resolve(DocPaths.SOURCE_OUTPUT) + .resolve(DocPath.forClass(utils, te)); + Content linkContent = getHyperLink(href + .fragment(SourceToHTMLConverter.getAnchorName(utils, typeElement)), label, "", ""); + htmltree.addContent(linkContent); + } + + /** + * Return the link to the given class. + * + * @param linkInfo the information about the link. + * + * @return the link for the given class. + */ + public Content getLink(LinkInfoImpl linkInfo) { + LinkFactoryImpl factory = new LinkFactoryImpl(this); + return factory.getLink(linkInfo); + } + + /** + * Return the type parameters for the given class. + * + * @param linkInfo the information about the link. + * @return the type for the given class. + */ + public Content getTypeParameterLinks(LinkInfoImpl linkInfo) { + LinkFactoryImpl factory = new LinkFactoryImpl(this); + return factory.getTypeParameterLinks(linkInfo, false); + } + + /************************************************************* + * Return a class cross link to external class documentation. + * The name must be fully qualified to determine which package + * the class is in. The -link option does not allow users to + * link to external classes in the "default" package. + * + * @param qualifiedClassName the qualified name of the external class. + * @param refMemName the name of the member being referenced. This should + * be null or empty string if no member is being referenced. + * @param label the label for the external link. + * @param strong true if the link should be strong. + * @param style the style of the link. + * @param code true if the label should be code font. + */ + public Content getCrossClassLink(String qualifiedClassName, String refMemName, + Content label, boolean strong, String style, + boolean code) { + String className = ""; + String packageName = qualifiedClassName == null ? "" : qualifiedClassName; + int periodIndex; + while ((periodIndex = packageName.lastIndexOf('.')) != -1) { + className = packageName.substring(periodIndex + 1, packageName.length()) + + (className.length() > 0 ? "." + className : ""); + Content defaultLabel = new StringContent(className); + if (code) + defaultLabel = HtmlTree.CODE(defaultLabel); + packageName = packageName.substring(0, periodIndex); + if (getCrossPackageLink(packageName) != null) { + /* + The package exists in external documentation, so link to the external + class (assuming that it exists). This is definitely a limitation of + the -link option. There are ways to determine if an external package + exists, but no way to determine if the external class exists. We just + have to assume that it does. + */ + DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot, + className + ".html", refMemName); + return getHyperLink(link, + (label == null) || label.isEmpty() ? defaultLabel : label, + strong, style, + configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName), + ""); + } + } + return null; + } + + public boolean isClassLinkable(TypeElement typeElement) { + if (utils.isIncluded(typeElement)) { + return configuration.isGeneratedDoc(typeElement); + } + return configuration.extern.isExternal(typeElement); + } + + public DocLink getCrossPackageLink(String pkgName) { + return configuration.extern.getExternalLink(pkgName, pathToRoot, + DocPaths.PACKAGE_SUMMARY.getPath()); + } + + /** + * Get the class link. + * + * @param context the id of the context where the link will be added + * @param element to link to + * @return a content tree for the link + */ + public Content getQualifiedClassLink(LinkInfoImpl.Kind context, Element element) { + LinkInfoImpl linkInfoImpl = new LinkInfoImpl(configuration, context, (TypeElement)element); + return getLink(linkInfoImpl.label(utils.getFullyQualifiedName(element))); + } + + /** + * Add the class link. + * + * @param context the id of the context where the link will be added + * @param typeElement to link to + * @param contentTree the content tree to which the link will be added + */ + public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) { + addPreQualifiedClassLink(context, typeElement, false, contentTree); + } + + /** + * Retrieve the class link with the package portion of the label in + * plain text. If the qualifier is excluded, it will not be included in the + * link label. + * + * @param typeElement the class to link to. + * @param isStrong true if the link should be strong. + * @return the link with the package portion of the label in plain text. + */ + public Content getPreQualifiedClassLink(LinkInfoImpl.Kind context, + TypeElement typeElement, boolean isStrong) { + ContentBuilder classlink = new ContentBuilder(); + PackageElement pkg = utils.containingPackage(typeElement); + if (pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) { + classlink.addContent(getEnclosingPackageName(typeElement)); + } + classlink.addContent(getLink(new LinkInfoImpl(configuration, + context, typeElement).label(utils.getSimpleName(typeElement)).strong(isStrong))); + return classlink; + } + + /** + * Add the class link with the package portion of the label in + * plain text. If the qualifier is excluded, it will not be included in the + * link label. + * + * @param context the id of the context where the link will be added + * @param typeElement the class to link to + * @param isStrong true if the link should be strong + * @param contentTree the content tree to which the link with be added + */ + public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, + TypeElement typeElement, boolean isStrong, Content contentTree) { + PackageElement pkg = utils.containingPackage(typeElement); + if(pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) { + contentTree.addContent(getEnclosingPackageName(typeElement)); + } + LinkInfoImpl linkinfo = new LinkInfoImpl(configuration, context, typeElement) + .label(utils.getSimpleName(typeElement)) + .strong(isStrong); + Content link = getLink(linkinfo); + contentTree.addContent(link); + } + + /** + * Add the class link, with only class name as the strong link and prefixing + * plain package name. + * + * @param context the id of the context where the link will be added + * @param typeElement the class to link to + * @param contentTree the content tree to which the link with be added + */ + public void addPreQualifiedStrongClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) { + addPreQualifiedClassLink(context, typeElement, true, contentTree); + } + + /** + * Get the link for the given member. + * + * @param context the id of the context where the link will be added + * @param element the member being linked to + * @param label the label for the link + * @return a content tree for the element link + */ + public Content getDocLink(LinkInfoImpl.Kind context, Element element, String label) { + return getDocLink(context, utils.getEnclosingTypeElement(element), element, + new StringContent(label)); + } + + /** + * Return the link for the given member. + * + * @param context the id of the context where the link will be printed. + * @param element the member being linked to. + * @param label the label for the link. + * @param strong true if the link should be strong. + * @return the link for the given member. + */ + public Content getDocLink(LinkInfoImpl.Kind context, Element element, String label, + boolean strong) { + return getDocLink(context, utils.getEnclosingTypeElement(element), element, label, strong); + } + + /** + * Return the link for the given member. + * + * @param context the id of the context where the link will be printed. + * @param typeElement the typeElement that we should link to. This is not + necessarily equal to element.containingClass(). We may be + inheriting comments. + * @param element the member being linked to. + * @param label the label for the link. + * @param strong true if the link should be strong. + * @return the link for the given member. + */ + public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, + String label, boolean strong) { + return getDocLink(context, typeElement, element, label, strong, false); + } + + public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, + Content label, boolean strong) { + return getDocLink(context, typeElement, element, label, strong, false); + } + + /** + * Return the link for the given member. + * + * @param context the id of the context where the link will be printed. + * @param typeElement the typeElement that we should link to. This is not + necessarily equal to element.containingClass(). We may be + inheriting comments. + * @param element the member being linked to. + * @param label the label for the link. + * @param strong true if the link should be strong. + * @param isProperty true if the element parameter is a JavaFX property. + * @return the link for the given member. + */ + public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, + String label, boolean strong, boolean isProperty) { + return getDocLink(context, typeElement, element, new StringContent(check(label)), strong, isProperty); + } + + String check(String s) { + if (s.matches(".*[&<>].*")) { + throw new IllegalArgumentException(s); + } + return s; + } + + public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, + Content label, boolean strong, boolean isProperty) { + if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) { + return label; + } else if (utils.isExecutableElement(element)) { + ExecutableElement ee = (ExecutableElement)element; + return getLink(new LinkInfoImpl(configuration, context, typeElement) + .label(label) + .where(getName(getAnchor(ee, isProperty))) + .strong(strong)); + } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) { + return getLink(new LinkInfoImpl(configuration, context, typeElement) + .label(label) + .where(getName(element.getSimpleName().toString())) + .strong(strong)); + } else { + return label; + } + } + + /** + * Return the link for the given member. + * + * @param context the id of the context where the link will be added + * @param typeElement the typeElement that we should link to. This is not + necessarily equal to element.containingClass(). We may be + inheriting comments + * @param element the member being linked to + * @param label the label for the link + * @return the link for the given member + */ + public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, + Content label) { + if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) { + return label; + } else if (utils.isExecutableElement(element)) { + ExecutableElement emd = (ExecutableElement) element; + return getLink(new LinkInfoImpl(configuration, context, typeElement) + .label(label) + .where(getName(getAnchor(emd)))); + } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) { + return getLink(new LinkInfoImpl(configuration, context, typeElement) + .label(label).where(getName(element.getSimpleName().toString()))); + } else { + return label; + } + } + + public String getAnchor(ExecutableElement executableElement) { + return getAnchor(executableElement, false); + } + + public String getAnchor(ExecutableElement executableElement, boolean isProperty) { + if (isProperty) { + return executableElement.getSimpleName().toString(); + } + String signature = utils.signature(executableElement); + StringBuilder signatureParsed = new StringBuilder(); + int counter = 0; + for (int i = 0; i < signature.length(); i++) { + char c = signature.charAt(i); + if (c == '<') { + counter++; + } else if (c == '>') { + counter--; + } else if (counter == 0) { + signatureParsed.append(c); + } + } + return utils.getSimpleName(executableElement) + signatureParsed.toString(); + } + + public Content seeTagToContent(Element element, DocTree see) { + + Kind kind = see.getKind(); + if (!(kind == LINK || kind == SEE || kind == LINK_PLAIN)) { + return new ContentBuilder(); + } + + CommentHelper ch = utils.getCommentHelper(element); + String tagName = ch.getTagName(see); + String seetext = replaceDocRootDir(utils.normalizeNewlines(ch.getText(see))); + // Check if @see is an href or "string" + if (seetext.startsWith("<") || seetext.startsWith("\"")) { + return new RawHtml(seetext); + } + boolean isLinkPlain = kind == LINK_PLAIN; + Content label = plainOrCode(isLinkPlain, new RawHtml(ch.getLabel(configuration, see))); + + //The text from the @see tag. We will output this text when a label is not specified. + Content text = plainOrCode(kind == LINK_PLAIN, new RawHtml(seetext)); + + TypeElement refClass = ch.getReferencedClass(configuration, see); + String refClassName = ch.getReferencedClassName(configuration, see); + Element refMem = ch.getReferencedMember(configuration, see); + String refMemName = ch.getReferencedMemberName(see); + + if (refMemName == null && refMem != null) { + refMemName = refMem.toString(); + } + if (refClass == null) { + //@see is not referencing an included class + PackageElement refPackage = ch.getReferencedPackage(configuration, see); + if (refPackage != null && utils.isIncluded(refPackage)) { + //@see is referencing an included package + if (label.isEmpty()) + label = plainOrCode(isLinkPlain, + new StringContent(refPackage.getQualifiedName().toString())); + return getPackageLink(refPackage, label); + } else { + // @see is not referencing an included class or package. Check for cross links. + Content classCrossLink; + DocLink packageCrossLink = getCrossPackageLink(refClassName); + if (packageCrossLink != null) { + // Package cross link found + return getHyperLink(packageCrossLink, + (label.isEmpty() ? text : label)); + } else if ((classCrossLink = getCrossClassLink(refClassName, + refMemName, label, false, "", !isLinkPlain)) != null) { + // Class cross link found (possibly to a member in the class) + return classCrossLink; + } else { + // No cross link found so print warning + configuration.getDocletSpecificMsg().warning(ch.getDocTreePath(see), + "doclet.see.class_or_package_not_found", + "@" + tagName, + seetext); + return (label.isEmpty() ? text: label); + } + } + } else if (refMemName == null) { + // Must be a class reference since refClass is not null and refMemName is null. + if (label.isEmpty()) { + /* + * it seems to me this is the right thing to do, but it causes comparator failures. + */ + if (!configuration.backwardCompatibility) { + StringContent content = utils.isEnclosingPackageIncluded(refClass) + ? new StringContent(utils.getSimpleName(refClass)) + : new StringContent(utils.getFullyQualifiedName(refClass)); + label = plainOrCode(isLinkPlain, content); + } else { + label = plainOrCode(isLinkPlain, + new StringContent(utils.getSimpleName(refClass))); + } + + } + return getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, refClass) + .label(label)); + } else if (refMem == null) { + // Must be a member reference since refClass is not null and refMemName is not null. + // However, refMem is null, so this referenced member does not exist. + return (label.isEmpty() ? text: label); + } else { + // Must be a member reference since refClass is not null and refMemName is not null. + // refMem is not null, so this @see tag must be referencing a valid member. + TypeElement containing = utils.getEnclosingTypeElement(refMem); + if (ch.getText(see).trim().startsWith("#") && + ! (utils.isPublic(containing) || utils.isLinkable(containing))) { + // Since the link is relative and the holder is not even being + // documented, this must be an inherited link. Redirect it. + // The current class either overrides the referenced member or + // inherits it automatically. + if (this instanceof ClassWriterImpl) { + containing = ((ClassWriterImpl) this).getTypeElement(); + } else if (!utils.isPublic(containing)) { + configuration.getDocletSpecificMsg().warning( + ch.getDocTreePath(see), "doclet.see.class_or_package_not_accessible", + tagName, utils.getFullyQualifiedName(containing)); + } else { + configuration.getDocletSpecificMsg().warning( + ch.getDocTreePath(see), "doclet.see.class_or_package_not_found", + tagName, seetext); + } + } + if (configuration.currentTypeElement != containing) { + refMemName = (utils.isConstructor(refMem)) + ? refMemName + : utils.getSimpleName(containing) + "." + refMemName; + } + if (utils.isExecutableElement(refMem)) { + if (refMemName.indexOf('(') < 0) { + refMemName += utils.makeSignature((ExecutableElement)refMem, true); + } + } + + text = plainOrCode(kind == LINK_PLAIN, new StringContent(refMemName)); + + return getDocLink(LinkInfoImpl.Kind.SEE_TAG, containing, + refMem, (label.isEmpty() ? text: label), false); + } + } + + private Content plainOrCode(boolean plain, Content body) { + return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body); + } + + /** + * Add the inline comment. + * + * @param element the Element for which the inline comment will be added + * @param tag the inline tag to be added + * @param htmltree the content tree to which the comment will be added + */ + public void addInlineComment(Element element, DocTree tag, Content htmltree) { + CommentHelper ch = utils.getCommentHelper(element); + List description = ch.getDescription(configuration, tag); + addCommentTags(element, tag, description, false, false, htmltree); + } + + /** + * Add the inline deprecated comment. + * + * @param e the Element for which the inline deprecated comment will be added + * @param tag the inline tag to be added + * @param htmltree the content tree to which the comment will be added + */ + public void addInlineDeprecatedComment(Element e, DocTree tag, Content htmltree) { + CommentHelper ch = utils.getCommentHelper(e); + addCommentTags(e, ch.getBody(configuration, tag), true, false, htmltree); + } + + /** + * Adds the summary content. + * + * @param element the Element for which the summary will be generated + * @param htmltree the documentation tree to which the summary will be added + */ + public void addSummaryComment(Element element, Content htmltree) { + addSummaryComment(element, utils.getFirstSentenceTrees(element), htmltree); + } + + /** + * Adds the summary content. + * + * @param element the Element for which the summary will be generated + * @param firstSentenceTags the first sentence tags for the doc + * @param htmltree the documentation tree to which the summary will be added + */ + public void addSummaryComment(Element element, List firstSentenceTags, Content htmltree) { + addCommentTags(element, firstSentenceTags, false, true, htmltree); + } + + public void addSummaryDeprecatedComment(Element element, DocTree tag, Content htmltree) { + CommentHelper ch = utils.getCommentHelper(element); + List body = ch.getBody(configuration, tag); + addCommentTags(element, ch.getFirstSentenceTrees(configuration, body), true, true, htmltree); + } + + /** + * Adds the inline comment. + * + * @param element the Element for which the inline comments will be generated + * @param htmltree the documentation tree to which the inline comments will be added + */ + public void addInlineComment(Element element, Content htmltree) { + addCommentTags(element, utils.getBody(element), false, false, htmltree); + } + + /** + * Adds the comment tags. + * + * @param element the Element for which the comment tags will be generated + * @param tags the first sentence tags for the doc + * @param depr true if it is deprecated + * @param first true if the first sentence tags should be added + * @param htmltree the documentation tree to which the comment tags will be added + */ + private void addCommentTags(Element element, List tags, boolean depr, + boolean first, Content htmltree) { + addCommentTags(element, null, tags, depr, first, htmltree); + } + + /** + * Adds the comment tags. + * + * @param element for which the comment tags will be generated + * @param holderTag the block tag context for the inline tags + * @param tags the first sentence tags for the doc + * @param depr true if it is deprecated + * @param first true if the first sentence tags should be added + * @param htmltree the documentation tree to which the comment tags will be added + */ + private void addCommentTags(Element element, DocTree holderTag, List tags, boolean depr, + boolean first, Content htmltree) { + if(configuration.nocomment){ + return; + } + Content div; + Content result = commentTagsToContent(null, element, tags, first); + if (depr) { + Content italic = HtmlTree.SPAN(HtmlStyle.deprecationComment, result); + div = HtmlTree.DIV(HtmlStyle.block, italic); + htmltree.addContent(div); + } + else { + div = HtmlTree.DIV(HtmlStyle.block, result); + htmltree.addContent(div); + } + if (tags.isEmpty()) { + htmltree.addContent(getSpace()); + } + } + + boolean ignoreNonInlineTag(DocTree dtree) { + Name name = null; + if (dtree.getKind() == Kind.START_ELEMENT) { + StartElementTree setree = (StartElementTree)dtree; + name = setree.getName(); + } else if (dtree.getKind() == Kind.END_ELEMENT) { + EndElementTree eetree = (EndElementTree)dtree; + name = eetree.getName(); + } + + if (name != null) { + com.sun.tools.doclint.HtmlTag htmlTag = com.sun.tools.doclint.HtmlTag.get(name); + if (htmlTag != null && + htmlTag.blockType != com.sun.tools.doclint.HtmlTag.BlockType.INLINE) { + return true; + } + } + return false; + } + + boolean isAllWhiteSpace(String body) { + for (int i = 0 ; i < body.length(); i++) { + if (!Character.isWhitespace(body.charAt(i))) + return false; + } + return true; + } + + /** + * Converts inline tags and text to text strings, expanding the + * inline tags along the way. Called wherever text can contain + * an inline tag, such as in comments or in free-form text arguments + * to non-inline tags. + * + * @param holderTag specific tag where comment resides + * @param element specific element where comment resides + * @param tags array of text tags and inline tags (often alternating) + present in the text of interest for this element + * @param isFirstSentence true if text is first sentence + * @return a Content object + */ + public Content commentTagsToContent(DocTree holderTag, Element element, + List tags, boolean isFirstSentence) { + + final Content result = new ContentBuilder() { + @Override + public void addContent(String text) { + super.addContent(utils.normalizeNewlines(text)); + } + }; + CommentHelper ch = utils.getCommentHelper(element); + // Array of all possible inline tags for this javadoc run + configuration.tagletManager.checkTags(utils, element, tags, true); + for (ListIterator iterator = tags.listIterator(); iterator.hasNext();) { + DocTree tag = iterator.next(); + // zap block tags + if (isFirstSentence && ignoreNonInlineTag(tag)) + continue; + + if (isFirstSentence && iterator.nextIndex() == tags.size() && + (tag.getKind() == TEXT && isAllWhiteSpace(ch.getText(tag)))) + continue; + + boolean allDone = new SimpleDocTreeVisitor() { + // notify the next DocTree handler to take necessary action + boolean commentRemoved = false; + + private boolean isLast(DocTree node) { + return node.equals(tags.get(tags.size() - 1)); + } + + private boolean isFirst(DocTree node) { + return node.equals(tags.get(0)); + } + + private boolean inAnAtag() { + if (utils.isStartElement(tag)) { + StartElementTree st = (StartElementTree)tag; + Name name = st.getName(); + if (name != null) { + com.sun.tools.doclint.HtmlTag htag = + com.sun.tools.doclint.HtmlTag.get(name); + return htag != null && htag.equals(com.sun.tools.doclint.HtmlTag.A); + } + } + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitAttribute(AttributeTree node, Content c) { + StringBuilder sb = new StringBuilder(SPACER).append(node.getName()); + if (node.getValueKind() == ValueKind.EMPTY) { + result.addContent(sb.toString()); + return false; + } + sb.append("="); + String quote; + switch (node.getValueKind()) { + case DOUBLE: + quote = "\""; + break; + case SINGLE: + quote = "\'"; + break; + default: + quote = ""; + break; + } + sb.append(quote); + result.addContent(sb.toString()); + Content docRootContent = new ContentBuilder(); + + for (DocTree dt : node.getValue()) { + if (utils.isText(dt) && inAnAtag()) { + String text = ((TextTree) dt).getBody(); + if (text.startsWith("/..") && !configuration.docrootparent.isEmpty()) { + result.addContent(configuration.docrootparent); + docRootContent = new ContentBuilder(); + text = textCleanup(text.substring(3), isLast(node)); + } else { + if (!docRootContent.isEmpty()) { + docRootContent = copyDocRootContent(docRootContent); + } else { + text = redirectRelativeLinks(element, (TextTree) dt); + } + text = textCleanup(text, isLast(node)); + } + result.addContent(text); + } else { + docRootContent = copyDocRootContent(docRootContent); + dt.accept(this, docRootContent); + } + } + copyDocRootContent(docRootContent); + result.addContent(quote); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitComment(CommentTree node, Content c) { + if (isFirstSentence && isFirst(node)) { + commentRemoved = true; + return this.visit(iterator.next(), c); + } + result.addContent(new RawHtml(node.getBody())); + return false; + } + + private Content copyDocRootContent(Content content) { + if (!content.isEmpty()) { + result.addContent(content); + return new ContentBuilder(); + } + return content; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitDocRoot(DocRootTree node, Content c) { + Content docRootContent = TagletWriter.getInlineTagOutput(element, + configuration.tagletManager, + holderTag, + node, + getTagletWriterInstance(isFirstSentence)); + if (c != null) { + c.addContent(docRootContent); + } else { + result.addContent(docRootContent); + } + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitEndElement(EndElementTree node, Content c) { + RawHtml rawHtml = new RawHtml(""); + result.addContent(rawHtml); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitEntity(EntityTree node, Content c) { + result.addContent(new RawHtml(node.toString())); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitErroneous(ErroneousTree node, Content c) { + configuration.getDocletSpecificMsg().warning(ch.getDocTreePath(node), + "doclet.tag.invalid_usage", node); + result.addContent(new RawHtml(node.toString())); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitInheritDoc(InheritDocTree node, Content c) { + Content output = TagletWriter.getInlineTagOutput(element, + configuration.tagletManager, holderTag, + tag, getTagletWriterInstance(isFirstSentence)); + result.addContent(output); + // if we obtained the first sentence successfully, nothing more to do + return (isFirstSentence && !output.isEmpty()); + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitIndex(IndexTree node, Content p) { + Content output = TagletWriter.getInlineTagOutput(element, + configuration.tagletManager, holderTag, tag, + getTagletWriterInstance(isFirstSentence)); + if (output != null) { + result.addContent(output); + } + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitLink(LinkTree node, Content c) { + // we need to pass the DocTreeImpl here, so ignore node + result.addContent(seeTagToContent(element, tag)); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitLiteral(LiteralTree node, Content c) { + String s = node.getBody().toString(); + if (node.getKind() == CODE) { + result.addContent(HtmlTree.CODE(new StringContent(utils.normalizeNewlines(s)))); + + } else { + result.addContent(s); + } + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitSee(SeeTree node, Content c) { + // we need to pass the DocTreeImpl here, so ignore node + result.addContent(seeTagToContent(element, tag)); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitStartElement(StartElementTree node, Content c) { + String text = "<" + node.getName(); + text = utils.normalizeNewlines(text); + RawHtml rawHtml = new RawHtml(text); + result.addContent(rawHtml); + + for (DocTree dt : node.getAttributes()) { + dt.accept(this, null); + } + result.addContent(new RawHtml(node.isSelfClosing() ? "/>" : ">")); + return false; + } + + private String textCleanup(String text, boolean isLast) { + return textCleanup(text, isLast, false); + } + + private String textCleanup(String text, boolean isLast, boolean trimLeader) { + if (trimLeader) { + text = removeLeadingWhitespace(text); + } + if (isFirstSentence && isLast) { + text = removeTrailingWhitespace(text); + } + text = utils.replaceTabs(text); + text = utils.normalizeNewlines(text); + return text; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitText(TextTree node, Content c) { + String text = node.getBody(); + text = textCleanup(text, isLast(node), commentRemoved); + commentRemoved = false; + result.addContent(new RawHtml(text)); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + protected Boolean defaultAction(DocTree node, Content c) { + Content output = TagletWriter.getInlineTagOutput(element, + configuration.tagletManager, holderTag, tag, + getTagletWriterInstance(isFirstSentence)); + if (output != null) { + result.addContent(output); + } + return false; + } + + }.visit(tag, null); + if (allDone) + break; + } + return result; + } + + private String removeTrailingWhitespace(String text) { + char[] buf = text.toCharArray(); + for (int i = buf.length - 1; i > 0 ; i--) { + if (!Character.isWhitespace(buf[i])) + return text.substring(0, i + 1); + } + return text; + } + + private String removeLeadingWhitespace(String text) { + char[] buf = text.toCharArray(); + for (int i = 0; i < buf.length; i++) { + if (!Character.isWhitespace(buf[i])) { + return text.substring(i); + } + } + return text; + } + + /** + * Return true if relative links should not be redirected. + * + * @return Return true if a relative link should not be redirected. + */ + private boolean shouldNotRedirectRelativeLinks() { + return this instanceof AnnotationTypeWriter || + this instanceof ClassWriter || + this instanceof PackageSummaryWriter; + } + + /** + * Suppose a piece of documentation has a relative link. When you copy + * that documentation to another place such as the index or class-use page, + * that relative link will no longer work. We should redirect those links + * so that they will work again. + *

+ * Here is the algorithm used to fix the link: + *

+ * {@literal => docRoot + + } + *

+ * For example, suppose DocletEnvironment has this link: + * {@literal The package Page } + *

+ * If this link appeared in the index, we would redirect + * the link like this: + * + * {@literal The package Page} + * + * @param element the Element object whose documentation is being written. + * @param text the text being written. + * + * @return the text, with all the relative links redirected to work. + */ + private String redirectRelativeLinks(Element element, TextTree tt) { + String text = tt.getBody(); + if (element == null || utils.isOverviewElement(element) || shouldNotRedirectRelativeLinks()) { + return text; + } + + DocPath redirectPathFromRoot = new SimpleElementVisitor9() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public DocPath visitType(TypeElement e, Void p) { + return DocPath.forPackage(utils.containingPackage(e)); + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public DocPath visitPackage(PackageElement e, Void p) { + return DocPath.forPackage(e); + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public DocPath visitVariable(VariableElement e, Void p) { + return DocPath.forPackage(utils.containingPackage(e)); + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public DocPath visitExecutable(ExecutableElement e, Void p) { + return DocPath.forPackage(utils.containingPackage(e)); + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected DocPath defaultAction(Element e, Void p) { + return null; + } + }.visit(element); + if (redirectPathFromRoot == null) { + return text; + } + String lower = Utils.toLowerCase(text); + if (!(lower.startsWith("mailto:") + || lower.startsWith("http:") + || lower.startsWith("https:") + || lower.startsWith("file:"))) { + text = "{@" + (new DocRootTaglet()).getName() + "}/" + + redirectPathFromRoot.resolve(text).getPath(); + text = replaceDocRootDir(text); + } + return text; + } + + static final Set blockTags = new HashSet<>(); + static { + for (HtmlTag t: HtmlTag.values()) { + if (t.blockType == HtmlTag.BlockType.BLOCK) + blockTags.add(t.value); + } + } + + /** + * Add a link to the stylesheet file. + * + * @param head the content tree to which the files will be added + */ + public void addStyleSheetProperties(Content head) { + String stylesheetfile = configuration.stylesheetfile; + DocPath stylesheet; + if (stylesheetfile.isEmpty()) { + stylesheet = DocPaths.STYLESHEET; + } else { + DocFile file = DocFile.createFileForInput(configuration, stylesheetfile); + stylesheet = DocPath.create(file.getName()); + } + HtmlTree link = HtmlTree.LINK("stylesheet", "text/css", + pathToRoot.resolve(stylesheet).getPath(), + "Style"); + head.addContent(link); + if (configuration.createindex) { + HtmlTree jq_link = HtmlTree.LINK("stylesheet", "text/css", + pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(DocPaths.JQUERY_STYLESHEET_FILE)).getPath(), + "Style"); + head.addContent(jq_link); + } + } + + /** + * Add a link to the JavaScript file. + * + * @param head the content tree to which the files will be added + */ + public void addScriptProperties(Content head) { + HtmlTree javascript = HtmlTree.SCRIPT(pathToRoot.resolve(DocPaths.JAVASCRIPT).getPath()); + head.addContent(javascript); + if (configuration.createindex) { + if (pathToRoot != null && script != null) { + String path = pathToRoot.isEmpty() ? "." : pathToRoot.getPath(); + script.addContent(new RawHtml("var pathtoroot = \"" + path + "/\";loadScripts(document, \'script\');")); + } + addJQueryFile(head, DocPaths.JSZIP_MIN); + addJQueryFile(head, DocPaths.JSZIPUTILS_MIN); + head.addContent(new RawHtml("")); + addJQueryFile(head, DocPaths.JQUERY_JS_1_10); + addJQueryFile(head, DocPaths.JQUERY_JS); + } + } + + /** + * Add a link to the JQuery javascript file. + * + * @param head the content tree to which the files will be added + * @param filePath the DocPath of the file that needs to be added + */ + private void addJQueryFile(Content head, DocPath filePath) { + HtmlTree jqyeryScriptFile = HtmlTree.SCRIPT( + pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(filePath)).getPath()); + head.addContent(jqyeryScriptFile); + } + + /** + * According to + * The Java™ Language Specification, + * all the outer classes and static nested classes are core classes. + */ + public boolean isCoreClass(TypeElement typeElement) { + return utils.getEnclosingTypeElement(typeElement) == null || utils.isStatic(typeElement); + } + + /** + * Adds the annotation types for the given packageElement. + * + * @param packageElement the package to write annotations for. + * @param htmltree the documentation tree to which the annotation info will be + * added + */ + public void addAnnotationInfo(PackageElement packageElement, Content htmltree) { + addAnnotationInfo(packageElement, packageElement.getAnnotationMirrors(), htmltree); + } + + /** + * Add the annotation types of the executable receiver. + * + * @param method the executable to write the receiver annotations for. + * @param descList list of annotation description. + * @param htmltree the documentation tree to which the annotation info will be + * added + */ + public void addReceiverAnnotationInfo(ExecutableElement method, List descList, + Content htmltree) { + addAnnotationInfo(0, method, descList, false, htmltree); + } + + /* + * this is a hack to delay dealing with Annotations in the writers, the assumption + * is that all necessary checks have been made to get here. + */ + public void addReceiverAnnotationInfo(ExecutableElement method, TypeMirror rcvrTypeMirror, + List annotationMirrors, Content htmltree) { + TypeMirror rcvrType = method.getReceiverType(); + List annotationMirrors1 = rcvrType.getAnnotationMirrors(); + addAnnotationInfo(0, method, annotationMirrors1, false, htmltree); + } + + /** + * Adds the annotatation types for the given element. + * + * @param element the package to write annotations for + * @param htmltree the content tree to which the annotation types will be added + */ + public void addAnnotationInfo(Element element, Content htmltree) { + addAnnotationInfo(element, element.getAnnotationMirrors(), htmltree); + } + + /** + * Add the annotatation types for the given element and parameter. + * + * @param indent the number of spaces to indent the parameters. + * @param element the element to write annotations for. + * @param param the parameter to write annotations for. + * @param tree the content tree to which the annotation types will be added + */ + public boolean addAnnotationInfo(int indent, Element element, VariableElement param, + Content tree) { + return addAnnotationInfo(indent, element, param.getAnnotationMirrors(), false, tree); + } + + /** + * Adds the annotatation types for the given Element. + * + * @param element the element to write annotations for. + * @param descList the array of {@link AnnotationDesc}. + * @param htmltree the documentation tree to which the annotation info will be + * added + */ + private void addAnnotationInfo(Element element, List descList, + Content htmltree) { + addAnnotationInfo(0, element, descList, true, htmltree); + } + + /** + * Adds the annotation types for the given element. + * + * @param indent the number of extra spaces to indent the annotations. + * @param element the element to write annotations for. + * @param descList the array of {@link AnnotationDesc}. + * @param htmltree the documentation tree to which the annotation info will be + * added + */ + private boolean addAnnotationInfo(int indent, Element element, + List descList, boolean lineBreak, Content htmltree) { + List annotations = getAnnotations(indent, descList, lineBreak); + String sep = ""; + if (annotations.isEmpty()) { + return false; + } + for (Content annotation: annotations) { + htmltree.addContent(sep); + htmltree.addContent(annotation); + if (!lineBreak) { + sep = " "; + } + } + return true; + } + + /** + * Return the string representations of the annotation types for + * the given doc. + * + * @param indent the number of extra spaces to indent the annotations. + * @param descList the array of {@link AnnotationDesc}. + * @param linkBreak if true, add new line between each member value. + * @return an array of strings representing the annotations being + * documented. + */ + private List getAnnotations(int indent, List descList, boolean linkBreak) { + return getAnnotations(indent, descList, linkBreak, true); + } + + private List getAnnotations(int indent, AnnotationMirror amirror, boolean linkBreak) { + List descList = new ArrayList<>(); + descList.add(amirror); + return getAnnotations(indent, descList, linkBreak, true); + } + + /** + * Return the string representations of the annotation types for + * the given doc. + * + * A {@code null} {@code elementType} indicates that all the + * annotations should be returned without any filtering. + * + * @param indent the number of extra spaces to indent the annotations. + * @param descList the array of {@link AnnotationDesc}. + * @param linkBreak if true, add new line between each member value. + * @param isJava5DeclarationLocation + * @return an array of strings representing the annotations being + * documented. + */ + public List getAnnotations(int indent, List descList, + boolean linkBreak, boolean isJava5DeclarationLocation) { + List results = new ArrayList<>(); + ContentBuilder annotation; + for (AnnotationMirror aDesc : descList) { + TypeElement annotationElement = (TypeElement)aDesc.getAnnotationType().asElement(); + // If an annotation is not documented, do not add it to the list. If + // the annotation is of a repeatable type, and if it is not documented + // and also if its container annotation is not documented, do not add it + // to the list. If an annotation of a repeatable type is not documented + // but its container is documented, it will be added to the list. + if (!utils.isDocumentedAnnotation(annotationElement) && + (!isAnnotationDocumented && !isContainerDocumented)) { + continue; + } + /* TODO: check logic here to correctly handle declaration + * and type annotations. + if (utils.isDeclarationAnnotation(annotationElement, isJava5DeclarationLocation)) { + continue; + }*/ + annotation = new ContentBuilder(); + isAnnotationDocumented = false; + LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.ANNOTATION, annotationElement); + Map pairs = aDesc.getElementValues(); + // If the annotation is synthesized, do not print the container. + if (utils.configuration.workArounds.isSynthesized(aDesc)) { + for (ExecutableElement ee : pairs.keySet()) { + AnnotationValue annotationValue = pairs.get(ee); + List annotationTypeValues = new ArrayList<>(); + + new SimpleAnnotationValueVisitor9>() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Void visitArray(List vals, List p) { + p.addAll(vals); + return null; + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected Void defaultAction(Object o, List p) { + p.add(annotationValue); + return null; + } + }.visit(annotationValue, annotationTypeValues); + + String sep = ""; + for (AnnotationValue av : annotationTypeValues) { + annotation.addContent(sep); + annotation.addContent(annotationValueToContent(av)); + sep = " "; + } + } + } else if (isAnnotationArray(pairs)) { + // If the container has 1 or more value defined and if the + // repeatable type annotation is not documented, do not print + // the container. + if (pairs.size() == 1 && isAnnotationDocumented) { + List annotationTypeValues = new ArrayList<>(); + for (AnnotationValue a : pairs.values()) { + new SimpleAnnotationValueVisitor9>() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Void visitArray(List vals, List annotationTypeValues) { + for (AnnotationValue av : vals) { + annotationTypeValues.add(av); + } + return null; + } + }.visit(a, annotationTypeValues); + } + String sep = ""; + for (AnnotationValue av : annotationTypeValues) { + annotation.addContent(sep); + annotation.addContent(annotationValueToContent(av)); + sep = " "; + } + } + // If the container has 1 or more value defined and if the + // repeatable type annotation is not documented, print the container. + else { + addAnnotations(annotationElement, linkInfo, annotation, pairs, + indent, false); + } + } + else { + addAnnotations(annotationElement, linkInfo, annotation, pairs, + indent, linkBreak); + } + annotation.addContent(linkBreak ? DocletConstants.NL : ""); + results.add(annotation); + } + return results; + } + + /** + * Add annotation to the annotation string. + * + * @param annotationDoc the annotation being documented + * @param linkInfo the information about the link + * @param annotation the annotation string to which the annotation will be added + * @param pairs annotation type element and value pairs + * @param indent the number of extra spaces to indent the annotations. + * @param linkBreak if true, add new line between each member value + */ + private void addAnnotations(TypeElement annotationDoc, LinkInfoImpl linkInfo, + ContentBuilder annotation, Mapmap, + int indent, boolean linkBreak) { + linkInfo.label = new StringContent("@" + annotationDoc.getSimpleName().toString()); + annotation.addContent(getLink(linkInfo)); + if (!map.isEmpty()) { + annotation.addContent("("); + boolean isFirst = true; + for (ExecutableElement element : map.keySet()) { + if (isFirst) { + isFirst = false; + } else { + annotation.addContent(","); + if (linkBreak) { + annotation.addContent(DocletConstants.NL); + int spaces = annotationDoc.getSimpleName().toString().length() + 2; + for (int k = 0; k < (spaces + indent); k++) { + annotation.addContent(" "); + } + } + } + annotation.addContent(getDocLink(LinkInfoImpl.Kind.ANNOTATION, + element, element.getSimpleName().toString(), false)); + annotation.addContent("="); + AnnotationValue annotationValue = map.get(element); + List annotationTypeValues = new ArrayList<>(); + new SimpleAnnotationValueVisitor9() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Void visitArray(List vals, AnnotationValue p) { + annotationTypeValues.addAll(vals); + return null; + } + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected Void defaultAction(Object o, AnnotationValue p) { + annotationTypeValues.add(p); + return null; + } + }.visit(annotationValue, annotationValue); + annotation.addContent(annotationTypeValues.size() == 1 ? "" : "{"); + String sep = ""; + for (AnnotationValue av : annotationTypeValues) { + annotation.addContent(sep); + annotation.addContent(annotationValueToContent(av)); + sep = ","; + } + annotation.addContent(annotationTypeValues.size() == 1 ? "" : "}"); + isContainerDocumented = false; + } + annotation.addContent(")"); + } + } + + /** + * Check if the annotation contains an array of annotation as a value. This + * check is to verify if a repeatable type annotation is present or not. + * + * @param pairs annotation type element and value pairs + * + * @return true if the annotation contains an array of annotation as a value. + */ + private boolean isAnnotationArray(Map pairs) { + AnnotationValue annotationValue; + for (ExecutableElement ee : pairs.keySet()) { + annotationValue = pairs.get(ee); + boolean rvalue = new SimpleAnnotationValueVisitor9() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Boolean visitArray(List vals, Void p) { + if (vals.size() > 1) { + if (vals.get(0) instanceof AnnotationMirror) { + isContainerDocumented = true; + return new SimpleAnnotationValueVisitor9() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Boolean visitAnnotation(AnnotationMirror a, Void p) { + isContainerDocumented = true; + Element asElement = a.getAnnotationType().asElement(); + if (utils.isDocumentedAnnotation((TypeElement)asElement)) { + isAnnotationDocumented = true; + } + return true; + } + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected Boolean defaultAction(Object o, Void p) { + return false; + } + }.visit(vals.get(0)); + } + } + return false; + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected Boolean defaultAction(Object o, Void p) { + return false; + } + }.visit(annotationValue); + if (rvalue) { + return true; + } + } + return false; + } + + private Content annotationValueToContent(AnnotationValue annotationValue) { + return new SimpleAnnotationValueVisitor9() { + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Content visitType(TypeMirror t, Void p) { + return new SimpleTypeVisitor9() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Content visitDeclared(DeclaredType t, Void p) { + LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.ANNOTATION, t); + String name = utils.isIncluded(t.asElement()) + ? t.asElement().getSimpleName().toString() + : utils.getFullyQualifiedName(t.asElement()); + linkInfo.label = new StringContent(name + utils.getDimension(t) + ".class"); + return getLink(linkInfo); + } + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected Content defaultAction(TypeMirror e, Void p) { + return new StringContent(t + utils.getDimension(t) + ".class"); + } + }.visit(t); + } + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Content visitAnnotation(AnnotationMirror a, Void p) { + List list = getAnnotations(0, a, false); + ContentBuilder buf = new ContentBuilder(); + for (Content c : list) { + buf.addContent(c); + } + return buf; + } + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Content visitEnumConstant(VariableElement c, Void p) { + return getDocLink(LinkInfoImpl.Kind.ANNOTATION, + c, c.getSimpleName().toString(), false); + } + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Content visitArray(List vals, Void p) { + ContentBuilder buf = new ContentBuilder(); + String sep = ""; + for (AnnotationValue av : vals) { + buf.addContent(sep); + buf.addContent(visit(av)); + sep = " "; + } + return buf; + } + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected Content defaultAction(Object o, Void p) { + return new StringContent(annotationValue.toString()); + } + }.visit(annotationValue); + } + + /** + * Return the configuration for this doclet. + * + * @return the configuration for this doclet. + */ + public Configuration configuration() { + return configuration; + } +}