8190818: Introduce a new Script builder class

Reviewed-by: bpatel, ksrini
This commit is contained in:
Jonathan Gibbons 2017-11-16 15:06:17 -08:00
parent fbd053bd26
commit 7a89adf1a8
16 changed files with 416 additions and 266 deletions

View File

@ -592,7 +592,7 @@ public abstract class AbstractMemberWriter implements MemberSummaryWriter {
}
Table table = getSummaryTable();
if (table.needsScript()) {
writer.getScript().addContent(table.getScript());
writer.getMainBodyScript().append(table.getScript());
}
return table.toContent();
}

View File

@ -32,13 +32,11 @@ 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.formats.html.markup.Script;
import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
/**
@ -99,12 +97,12 @@ public class FrameOutputWriter extends HtmlDocletWriter {
HtmlTree body = new HtmlTree(HtmlTag.BODY);
body.addAttr(HtmlAttr.ONLOAD, "loadFrames()");
String topFilePath = configuration.topFile.getPath();
String javaScriptRefresh = "\nif (targetPage == \"\" || targetPage == \"undefined\")\n" +
" window.location.replace('" + topFilePath + "');\n";
RawHtml scriptContent = new RawHtml(javaScriptRefresh.replace("\n", DocletConstants.NL));
HtmlTree scriptTree = HtmlTree.SCRIPT();
scriptTree.addContent(scriptContent);
body.addContent(scriptTree);
Script script = new Script(
"\nif (targetPage == \"\" || targetPage == \"undefined\")\n" +
" window.location.replace(")
.appendStringLiteral(topFilePath, '\'')
.append(");\n");
body.addContent(script.asContent());
Content noScript = HtmlTree.NOSCRIPT(contents.noScriptMessage);
body.addContent(noScript);
if (configuration.allowTag(HtmlTag.MAIN)) {
@ -135,12 +133,12 @@ public class FrameOutputWriter extends HtmlDocletWriter {
Content htmlComment = new Comment(configuration.getText("doclet.New_Page"));
Content head = new HtmlTree(HtmlTag.HEAD);
head.addContent(getGeneratedBy(!configuration.notimestamp));
Content windowTitle = HtmlTree.TITLE(new StringContent(title));
Content windowTitle = HtmlTree.TITLE(title);
head.addContent(windowTitle);
Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, configuration.charset);
head.addContent(meta);
addStyleSheetProperties(configuration, head);
head.addContent(getFramesJavaScript());
head.addContent(getFramesScript().asContent());
Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(),
head, body);
Content htmlDocument = new HtmlDocument(htmlDocType,
@ -217,4 +215,64 @@ public class FrameOutputWriter extends HtmlDocletWriter {
frame.setStyle(HtmlStyle.rightIframe);
contentTree.addContent(frame);
}
/**
* Returns a content tree for the SCRIPT tag for the main page(index.html).
*
* @return a content for the SCRIPT tag
*/
protected Script getFramesScript() {
return new Script("\n" +
" tmpTargetPage = \"\" + window.location.search;\n" +
" if (tmpTargetPage != \"\" && tmpTargetPage != \"undefined\")\n" +
" tmpTargetPage = tmpTargetPage.substring(1);\n" +
" if (tmpTargetPage.indexOf(\":\") != -1 || (tmpTargetPage != \"\" && !validURL(tmpTargetPage)))\n" +
" tmpTargetPage = \"undefined\";\n" +
" targetPage = tmpTargetPage;\n" +
" function validURL(url) {\n" +
" try {\n" +
" url = decodeURIComponent(url);\n" +
" }\n" +
" catch (error) {\n" +
" return false;\n" +
" }\n" +
" var pos = url.indexOf(\".html\");\n" +
" if (pos == -1 || pos != url.length - 5)\n" +
" return false;\n" +
" var allowNumber = false;\n" +
" var allowSep = false;\n" +
" var seenDot = false;\n" +
" for (var i = 0; i < url.length - 5; i++) {\n" +
" var ch = url.charAt(i);\n" +
" if ('a' <= ch && ch <= 'z' ||\n" +
" 'A' <= ch && ch <= 'Z' ||\n" +
" ch == '$' ||\n" +
" ch == '_' ||\n" +
" ch.charCodeAt(0) > 127) {\n" +
" allowNumber = true;\n" +
" allowSep = true;\n" +
" } else if ('0' <= ch && ch <= '9'\n" +
" || ch == '-') {\n" +
" if (!allowNumber)\n" +
" return false;\n" +
" } else if (ch == '/' || ch == '.') {\n" +
" if (!allowSep)\n" +
" return false;\n" +
" allowNumber = false;\n" +
" allowSep = false;\n" +
" if (ch == '.')\n" +
" seenDot = true;\n" +
" if (ch == '/' && seenDot)\n" +
" return false;\n" +
" } else {\n" +
" return false;\n" +
" }\n" +
" }\n" +
" return true;\n" +
" }\n" +
" function loadFrames() {\n" +
" if (targetPage != \"\" && targetPage != \"undefined\")\n" +
" top.classFrame.location = top.targetPage;\n" +
" }\n");
}
}

View File

@ -78,6 +78,7 @@ 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.Script;
import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter;
import jdk.javadoc.internal.doclets.toolkit.ClassWriter;
@ -177,6 +178,13 @@ public class HtmlDocletWriter extends HtmlDocWriter {
final static Pattern IMPROPER_HTML_CHARS = Pattern.compile(".*[&<>].*");
/**
* The window title of this file.
*/
protected String winTitle;
protected Script mainBodyScript;
/**
* Constructor to construct the HtmlStandardWriter object.
*
@ -258,19 +266,18 @@ public class HtmlDocletWriter extends HtmlDocWriter {
* @return a content tree for the script
*/
public Content getAllClassesLinkScript(String id) {
HtmlTree script = HtmlTree.SCRIPT();
String scriptCode = "<!--\n" +
" allClassesLink = document.getElementById(\"" + id + "\");\n" +
Script script = new Script("<!--\n" +
" allClassesLink = document.getElementById(")
.appendStringLiteral(id)
.append(");\n" +
" if(window==top) {\n" +
" allClassesLink.style.display = \"block\";\n" +
" }\n" +
" else {\n" +
" allClassesLink.style.display = \"none\";\n" +
" }\n" +
" //-->\n";
Content scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL));
script.addContent(scriptContent);
Content div = HtmlTree.DIV(script);
" //-->\n");
Content div = HtmlTree.DIV(script.asContent());
Content div_noscript = HtmlTree.DIV(contents.noScriptMessage);
Content noScript = HtmlTree.NOSCRIPT(div_noscript);
div.addContent(noScript);
@ -409,7 +416,7 @@ public class HtmlDocletWriter extends HtmlDocWriter {
Content htmlComment = new Comment(configuration.getText("doclet.New_Page"));
Content head = new HtmlTree(HtmlTag.HEAD);
head.addContent(getGeneratedBy(!configuration.notimestamp));
head.addContent(getTitle());
head.addContent(HtmlTree.TITLE(winTitle));
Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, configuration.charset);
head.addContent(meta);
if (!configuration.notimestamp) {
@ -607,13 +614,11 @@ public class HtmlDocletWriter extends HtmlDocWriter {
tree.addContent(fixedNavDiv);
HtmlTree paddingDiv = HtmlTree.DIV(HtmlStyle.navPadding, Contents.SPACE);
tree.addContent(paddingDiv);
HtmlTree scriptTree = HtmlTree.SCRIPT();
String scriptCode = "<!--\n"
Script script = new Script(
"<!--\n"
+ "$('.navPadding').css('padding-top', $('.fixedNav').css(\"height\"));\n"
+ "//-->\n";
RawHtml scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL));
scriptTree.addContent(scriptContent);
tree.addContent(scriptTree);
+ "//-->\n");
tree.addContent(script.asContent());
} else {
subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_BOTTOM));
tree.addContent(subDiv);
@ -2113,9 +2118,11 @@ public class HtmlDocletWriter extends HtmlDocWriter {
HtmlTree javascript = HtmlTree.SCRIPT(pathToRoot.resolve(DocPaths.JAVASCRIPT).getPath());
head.addContent(javascript);
if (configuration.createindex) {
if (pathToRoot != null && script != null) {
if (pathToRoot != null && mainBodyScript != null) {
String ptrPath = pathToRoot.isEmpty() ? "." : pathToRoot.getPath();
script.addContent(new RawHtml("var pathtoroot = \"" + ptrPath + "/\";loadScripts(document, \'script\');"));
mainBodyScript.append("var pathtoroot = ")
.appendStringLiteral(ptrPath + "/")
.append(";loadScripts(document, \'script\');");
}
addJQueryFile(head, DocPaths.JSZIP_MIN);
addJQueryFile(head, DocPaths.JSZIPUTILS_MIN);
@ -2548,7 +2555,52 @@ public class HtmlDocletWriter extends HtmlDocWriter {
return new TableHeader(contents.packageLabel, contents.descriptionLabel);
}
Content getScript() {
/**
* Returns an HtmlTree for the SCRIPT tag.
*
* @return an HtmlTree for the SCRIPT tag
*/
protected Script getWinTitleScript() {
Script script = new Script();
if (winTitle != null && winTitle.length() > 0) {
script.append("<!--\n" +
" try {\n" +
" if (location.href.indexOf('is-external=true') == -1) {\n" +
" parent.document.title=")
.appendStringLiteral(winTitle)
.append(";\n" +
" }\n" +
" }\n" +
" catch(err) {\n" +
" }\n" +
"//-->\n");
}
return script;
}
/**
* Returns an HtmlTree for the BODY tag.
*
* @param includeScript set true if printing windowtitle script
* @param title title for the window
* @return an HtmlTree for the BODY tag
*/
public HtmlTree getBody(boolean includeScript, String title) {
HtmlTree body = new HtmlTree(HtmlTag.BODY);
// Set window title string which is later printed
this.winTitle = title;
// Don't print windowtitle script for overview-frame, allclasses-frame
// and package-frame
if (includeScript) {
this.mainBodyScript = getWinTitleScript();
body.addContent(mainBodyScript.asContent());
Content noScript = HtmlTree.NOSCRIPT(HtmlTree.DIV(contents.noScriptMessage));
body.addContent(noScript);
}
return body;
}
Script getMainBodyScript() {
return mainBodyScript;
}
}

View File

@ -32,6 +32,7 @@ import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocument;
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.Script;
import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
@ -73,18 +74,18 @@ public class IndexRedirectWriter extends HtmlDocletWriter {
String title = (configuration.windowtitle.length() > 0)
? configuration.windowtitle
: configuration.getText("doclet.Generated_Docs_Untitled");
: resources.getText("doclet.Generated_Docs_Untitled");
Content windowTitle = HtmlTree.TITLE(new StringContent(title));
Content windowTitle = HtmlTree.TITLE(title);
head.addContent(windowTitle);
Content metaContentType = HtmlTree.META("Content", CONTENT_TYPE, configuration.charset);
head.addContent(metaContentType);
String topFilePath = configuration.topFile.getPath();
String javaScriptRefresh = "window.location.replace('" + topFilePath + "')";
HtmlTree scriptTree = HtmlTree.SCRIPT();
scriptTree.addContent(javaScriptRefresh);
head.addContent(scriptTree);
Script script = new Script("window.location.replace(")
.appendStringLiteral(topFilePath, '\'')
.append(")");
head.addContent(script.asContent());
HtmlTree metaRefresh = new HtmlTree(HtmlTag.META);
metaRefresh.addAttr(HtmlAttr.HTTP_EQUIV, "Refresh");
metaRefresh.addAttr(HtmlAttr.CONTENT, "0;" + topFilePath);
@ -98,7 +99,7 @@ public class IndexRedirectWriter extends HtmlDocletWriter {
ContentBuilder bodyContent = new ContentBuilder();
bodyContent.addContent(HtmlTree.NOSCRIPT(
HtmlTree.P(configuration.getContent("doclet.No_Script_Message"))));
HtmlTree.P(contents.getContent("doclet.No_Script_Message"))));
bodyContent.addContent(HtmlTree.P(HtmlTree.A(topFilePath, new StringContent(topFilePath))));

View File

@ -162,7 +162,7 @@ public class ModuleIndexWriter extends AbstractModuleIndexWriter {
}
if (table.needsScript()) {
getScript().addContent(table.getScript());
mainBodyScript.append(table.getScript());
}
}
}

View File

@ -626,7 +626,7 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
addPackageTableRows(table);
li.addContent(table.toContent());
if (table.needsScript()) {
script.addContent(new RawHtml(table.getScript()));
mainBodyScript.append(table.getScript());
}
}

View File

@ -144,7 +144,7 @@ public class PackageIndexWriter extends AbstractPackageIndexWriter {
}
if (table.needsScript()) {
getScript().addContent(table.getScript());
getMainBodyScript().append(table.getScript());
}
}
}

View File

@ -42,6 +42,7 @@ import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.Messages;
import jdk.javadoc.internal.doclets.toolkit.Resources;
import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
@ -77,6 +78,7 @@ public class SourceToHTMLConverter {
private final HtmlConfiguration configuration;
private final Messages messages;
private final Resources resources;
private final Utils utils;
private final DocletEnvironment docEnv;
@ -93,6 +95,7 @@ public class SourceToHTMLConverter {
DocPath outputdir) {
this.configuration = configuration;
this.messages = configuration.getMessages();
this.resources = configuration.resources;
this.utils = configuration.utils;
this.docEnv = rd;
this.outputdir = outputdir;
@ -193,7 +196,7 @@ public class SourceToHTMLConverter {
body.addContent((configuration.allowTag(HtmlTag.MAIN)) ? HtmlTree.MAIN(div) : div);
writeToFile(body, outputdir.resolve(DocPath.forClass(utils, te)));
} catch (IOException e) {
String message = configuration.resources.getText("doclet.exception.read.file", fo.getName());
String message = resources.getText("doclet.exception.read.file", fo.getName());
throw new SimpleDocletException(message, e);
}
}
@ -209,8 +212,7 @@ public class SourceToHTMLConverter {
? DocType.HTML5
: DocType.TRANSITIONAL;
Content head = new HtmlTree(HtmlTag.HEAD);
head.addContent(HtmlTree.TITLE(new StringContent(
configuration.getText("doclet.Window_Source_title"))));
head.addContent(HtmlTree.TITLE(resources.getText("doclet.Window_Source_title")));
addStyleSheetProperties(head);
Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(),
head, body);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2017, 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
@ -39,6 +39,14 @@ import jdk.javadoc.internal.doclets.toolkit.Content;
public class ContentBuilder extends Content {
protected List<Content> contents = Collections.emptyList();
public ContentBuilder() { }
public ContentBuilder(Content... contents) {
for (Content c : contents) {
addContent(c);
}
}
@Override
public void addContent(Content content) {
nullCheck(content);

View File

@ -74,8 +74,8 @@ public class HtmlTree extends Content {
*/
public HtmlTree(HtmlTag tag, Content... contents) {
this(tag);
for (Content content: contents)
addContent(content);
for (Content c: contents)
addContent(c);
}
/**
@ -86,8 +86,8 @@ public class HtmlTree extends Content {
*/
public HtmlTree(HtmlTag tag, List<Content> contents) {
this(tag);
for (Content content: contents)
addContent(content);
for (Content c: contents)
addContent(c);
}
/**
@ -146,8 +146,8 @@ public class HtmlTree extends Content {
@Override
public void addContent(Content tagContent) {
if (tagContent instanceof ContentBuilder) {
for (Content content: ((ContentBuilder)tagContent).contents) {
addContent(content);
for (Content c: ((ContentBuilder)tagContent).contents) {
addContent(c);
}
}
else if (tagContent == HtmlTree.EMPTY || tagContent.isValid()) {
@ -158,9 +158,9 @@ public class HtmlTree extends Content {
}
/**
* This method adds a string content to the htmltree. If the last content member
* Adds String content to the HTML tree. If the last content member
* added is a StringContent, append the string to that StringContent or else
* create a new StringContent and add it to the html tree.
* create a new StringContent and add it to the HTML tree.
*
* @param stringContent string content that needs to be added
*/
@ -177,6 +177,10 @@ public class HtmlTree extends Content {
addContent(new StringContent(stringContent));
}
/**
* {@inheritDoc}
*/
@Override
public int charCount() {
int n = 0;
for (Content c : content)
@ -185,7 +189,7 @@ public class HtmlTree extends Content {
}
/**
* Given a string, escape all special html characters and
* Given a string, escape all special HTML characters and
* return the result.
*
* @param s The string to check.
@ -717,24 +721,13 @@ public class HtmlTree extends Content {
/**
* Generates a SCRIPT tag with the type and src attributes.
*
* @param type type of link
* @param src the path for the script
* @return an HtmlTree object for the SCRIPT tag
*/
public static HtmlTree SCRIPT(String src) {
HtmlTree htmltree = HtmlTree.SCRIPT();
htmltree.addAttr(HtmlAttr.SRC, nullCheck(src));
return htmltree;
}
/**
* Generates a SCRIPT tag with the type attribute.
*
* @return an HtmlTree object for the SCRIPT tag
*/
public static HtmlTree SCRIPT() {
HtmlTree htmltree = new HtmlTree(HtmlTag.SCRIPT);
htmltree.addAttr(HtmlAttr.TYPE, "text/javascript");
htmltree.addAttr(HtmlAttr.SRC, nullCheck(src));
return htmltree;
}
@ -912,8 +905,8 @@ public class HtmlTree extends Content {
* @param body content for the tag
* @return an HtmlTree object for the TITLE tag
*/
public static HtmlTree TITLE(Content body) {
HtmlTree htmltree = new HtmlTree(HtmlTag.TITLE, nullCheck(body));
public static HtmlTree TITLE(String body) {
HtmlTree htmltree = new HtmlTree(HtmlTag.TITLE, new StringContent(body));
return htmltree;
}
@ -949,6 +942,7 @@ public class HtmlTree extends Content {
/**
* {@inheritDoc}
*/
@Override
public boolean isEmpty() {
return (!hasContent() && !hasAttrs());
}
@ -988,6 +982,7 @@ public class HtmlTree extends Content {
*
* @return true if the HTML tree is valid
*/
@Override
public boolean isValid() {
switch (htmlTag) {
case A :

View File

@ -29,15 +29,13 @@ import java.io.*;
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.Resources;
import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
/**
* Class for the Html format code generation.
* Class for the HTML format code generation.
* Initializes PrintWriter with FileWriter, to enable print
* related methods to generate the code to the named File through FileWriter.
*
@ -51,21 +49,8 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
*/
public class HtmlWriter {
/**
* The window title of this file.
*/
protected String winTitle;
/**
* The configuration.
*/
protected HtmlConfiguration configuration;
private final DocFile docFile;
protected Content script;
/**
* Constructor.
*
@ -74,13 +59,7 @@ public class HtmlWriter {
* or null if none to be created
*/
public HtmlWriter(HtmlConfiguration configuration, DocPath path) {
this.configuration = configuration;
docFile = DocFile.createFileForOutput(configuration, path);
// The following should be converted to shared Content objects
// and moved to Contents, but that will require additional
// changes at the use sites.
Resources resources = configuration.getResources();
}
public void write(Content c) throws DocFileIOException {
@ -90,171 +69,4 @@ public class HtmlWriter {
throw new DocFileIOException(docFile, DocFileIOException.Mode.WRITE, e);
}
}
/**
* Returns an HtmlTree for the SCRIPT tag.
*
* @return an HtmlTree for the SCRIPT tag
*/
protected HtmlTree getWinTitleScript(){
HtmlTree scriptTree = HtmlTree.SCRIPT();
if(winTitle != null && winTitle.length() > 0) {
String scriptCode = "<!--\n" +
" try {\n" +
" if (location.href.indexOf('is-external=true') == -1) {\n" +
" parent.document.title=\"" + escapeJavaScriptChars(winTitle) + "\";\n" +
" }\n" +
" }\n" +
" catch(err) {\n" +
" }\n" +
"//-->\n";
RawHtml scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL));
scriptTree.addContent(scriptContent);
}
return scriptTree;
}
/**
* Returns a String with escaped special JavaScript characters.
*
* @param s String that needs to be escaped
* @return a valid escaped JavaScript string
*/
private static String escapeJavaScriptChars(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
switch (ch) {
case '\b':
sb.append("\\b");
break;
case '\t':
sb.append("\\t");
break;
case '\n':
sb.append("\\n");
break;
case '\f':
sb.append("\\f");
break;
case '\r':
sb.append("\\r");
break;
case '"':
sb.append("\\\"");
break;
case '\'':
sb.append("\\\'");
break;
case '\\':
sb.append("\\\\");
break;
default:
if (ch < 32 || ch >= 127) {
sb.append(String.format("\\u%04X", (int)ch));
} else {
sb.append(ch);
}
break;
}
}
return sb.toString();
}
/**
* Returns a content tree for the SCRIPT tag for the main page(index.html).
*
* @return a content for the SCRIPT tag
*/
protected Content getFramesJavaScript() {
HtmlTree scriptTree = HtmlTree.SCRIPT();
String scriptCode = "\n" +
" tmpTargetPage = \"\" + window.location.search;\n" +
" if (tmpTargetPage != \"\" && tmpTargetPage != \"undefined\")\n" +
" tmpTargetPage = tmpTargetPage.substring(1);\n" +
" if (tmpTargetPage.indexOf(\":\") != -1 || (tmpTargetPage != \"\" && !validURL(tmpTargetPage)))\n" +
" tmpTargetPage = \"undefined\";\n" +
" targetPage = tmpTargetPage;\n" +
" function validURL(url) {\n" +
" try {\n" +
" url = decodeURIComponent(url);\n" +
" }\n" +
" catch (error) {\n" +
" return false;\n" +
" }\n" +
" var pos = url.indexOf(\".html\");\n" +
" if (pos == -1 || pos != url.length - 5)\n" +
" return false;\n" +
" var allowNumber = false;\n" +
" var allowSep = false;\n" +
" var seenDot = false;\n" +
" for (var i = 0; i < url.length - 5; i++) {\n" +
" var ch = url.charAt(i);\n" +
" if ('a' <= ch && ch <= 'z' ||\n" +
" 'A' <= ch && ch <= 'Z' ||\n" +
" ch == '$' ||\n" +
" ch == '_' ||\n" +
" ch.charCodeAt(0) > 127) {\n" +
" allowNumber = true;\n" +
" allowSep = true;\n" +
" } else if ('0' <= ch && ch <= '9'\n" +
" || ch == '-') {\n" +
" if (!allowNumber)\n" +
" return false;\n" +
" } else if (ch == '/' || ch == '.') {\n" +
" if (!allowSep)\n" +
" return false;\n" +
" allowNumber = false;\n" +
" allowSep = false;\n" +
" if (ch == '.')\n" +
" seenDot = true;\n" +
" if (ch == '/' && seenDot)\n" +
" return false;\n" +
" } else {\n" +
" return false;\n" +
" }\n" +
" }\n" +
" return true;\n" +
" }\n" +
" function loadFrames() {\n" +
" if (targetPage != \"\" && targetPage != \"undefined\")\n" +
" top.classFrame.location = top.targetPage;\n" +
" }\n";
RawHtml scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL));
scriptTree.addContent(scriptContent);
return scriptTree;
}
/**
* Returns an HtmlTree for the BODY tag.
*
* @param includeScript set true if printing windowtitle script
* @param title title for the window
* @return an HtmlTree for the BODY tag
*/
public HtmlTree getBody(boolean includeScript, String title) {
HtmlTree body = new HtmlTree(HtmlTag.BODY);
// Set window title string which is later printed
this.winTitle = title;
// Don't print windowtitle script for overview-frame, allclasses-frame
// and package-frame
if (includeScript) {
this.script = getWinTitleScript();
body.addContent(script);
Content noScript = HtmlTree.NOSCRIPT(
HtmlTree.DIV(configuration.getContent("doclet.No_Script_Message")));
body.addContent(noScript);
}
return body;
}
/**
* Returns an HtmlTree for the TITLE tag.
*
* @return an HtmlTree for the TITLE tag
*/
public HtmlTree getTitle() {
HtmlTree title = HtmlTree.TITLE(new StringContent(winTitle));
return title;
}
}

View File

@ -0,0 +1,216 @@
/*
* Copyright (c) 2003, 2017, 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.markup;
import java.io.IOException;
import java.io.Writer;
import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
/**
* A builder for HTML script elements.
*
* <p><b>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.</b>
*/
public class Script {
private final StringBuilder sb;
/**
* Creates an empty script.
*/
public Script() {
sb = new StringBuilder();
}
/**
* Creates a new script containing the specified code.
*
* @param code the code
*/
public Script(String code) {
this();
append(code);
}
/**
* Appends the given code to the content.
*
* @param code the code
* @return this object
*/
public Script append(CharSequence code) {
sb.append(code);
return this;
}
/**
* Appends the given text as a string constant to the content.
* Characters within the string will be escaped as needed.
*
* @param text the text
* @return this object
*/
public Script appendStringLiteral(CharSequence text) {
sb.append(stringLiteral(text, '"'));
return this;
}
/**
* Appends the given text as a string constant to the content.
* Characters within the string will be escaped as needed.
*
* @param text the text
* @param quoteChar the quote character to use
* @return this object
*/
// The ability to specify the quote character is for backwards
// compatibility. Ideally, we should simplify the code so that
// the same quote character is always used.
public Script appendStringLiteral(CharSequence text, char quoteChar) {
sb.append(stringLiteral(text, quoteChar));
return this;
}
public Content asContent() {
ScriptContent scriptContent = new ScriptContent(sb);
HtmlTree tree = new HtmlTree(HtmlTag.SCRIPT) {
@Override
public void addContent(CharSequence s) {
throw new UnsupportedOperationException();
}
@Override
public void addContent(Content c) {
if (c != scriptContent) {
throw new IllegalArgumentException();
}
super.addContent(scriptContent);
}
};
tree.addAttr(HtmlAttr.TYPE, "text/javascript");
tree.addContent(scriptContent);
return tree;
}
/**
* Returns a String with escaped special JavaScript characters.
*
* @param s String that needs to be escaped
* @return a valid escaped JavaScript string
*/
public static String stringLiteral(CharSequence s) {
return stringLiteral(s, '"');
}
/**
* Returns a String with escaped special JavaScript characters.
*
* @param s String that needs to be escaped
* @param quoteChar the quote character to use for the literal
* @return a valid escaped JavaScript string
*/
// The ability to specify the quote character is for backwards
// compatibility. Ideally, we should simplify the code so that
// the same quote character is always used.
public static String stringLiteral(CharSequence s, char quoteChar) {
if (quoteChar != '"' && quoteChar != '\'') {
throw new IllegalArgumentException();
}
StringBuilder sb = new StringBuilder();
sb.append(quoteChar);
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
switch (ch) {
case '\b':
sb.append("\\b");
break;
case '\t':
sb.append("\\t");
break;
case '\n':
sb.append("\\n");
break;
case '\f':
sb.append("\\f");
break;
case '\r':
sb.append("\\r");
break;
case '"':
sb.append("\\\"");
break;
case '\'':
sb.append("\\\'");
break;
case '\\':
sb.append("\\\\");
break;
default:
if (ch < 32 || ch >= 127) {
sb.append(String.format("\\u%04X", (int)ch));
} else {
sb.append(ch);
}
break;
}
}
sb.append(quoteChar);
return sb.toString();
}
private static class ScriptContent extends Content {
private final StringBuilder sb;
ScriptContent(StringBuilder sb) {
this.sb = sb;
}
@Override
public void addContent(Content content) {
throw new UnsupportedOperationException();
}
@Override
public void addContent(CharSequence code) {
sb.append(code);
}
@Override
public boolean write(Writer writer, boolean atNewline) throws IOException {
String s = sb.toString();
writer.write(s.replace("\n", DocletConstants.NL));
return s.endsWith("\n");
}
@Override
public boolean isEmpty() {
return false;
}
}
}

View File

@ -570,7 +570,12 @@ public class Table {
}
private void appendTabInfo(StringBuilder sb, int value, String id, String name) {
sb.append(value).append(":[\"").append(id).append("\",").append("\"").append(name).append("\"]");
sb.append(value)
.append(":[")
.append(Script.stringLiteral(id))
.append(",")
.append(Script.stringLiteral(name))
.append("]");
}
private void appendStyleInfo(StringBuilder sb, HtmlStyle... styles) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2017, 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
@ -76,6 +76,10 @@ public abstract class Content {
/**
* Writes content to a writer.
*
* @param writer the writer
* @param atNewline whether the writer has just written a newline
* @return whether the writer has just written a newline
* @throws IOException if an error occurs while writing the output
*/
public abstract boolean write(Writer writer, boolean atNewline) throws IOException ;
@ -107,6 +111,7 @@ public abstract class Content {
/**
* Checks for null values.
*
* @param <T> the type of the item being checked
* @param t reference type to check for null values
* @return the reference type if not null or else throws a null pointer exception
*/

View File

@ -64,10 +64,8 @@ public class TestGroupName extends JavadocTester {
checkExit(Exit.OK);
checkOutput("overview-summary.html", true,
"<span><a href=\"javascript:showGroups(1);\">abc &lt; &amp; &gt; def</a></span>");
checkOutput("overview-summary.html", false,
"abc < & > def");
"<span><a href=\"javascript:showGroups(1);\">abc &lt; &amp; &gt; def</a></span>",
",\"abc < & > def\"],");
}
@Test
@ -99,10 +97,8 @@ public class TestGroupName extends JavadocTester {
checkExit(Exit.OK);
checkOutput("overview-summary.html", true,
"<span><a href=\"javascript:showGroups(1);\">abc &lt; &amp; &gt; def</a></span>");
checkOutput("overview-summary.html", false,
"abc < & > def");
"<span><a href=\"javascript:showGroups(1);\">abc &lt; &amp; &gt; def</a></span>",
",\"abc < & > def\"],");
}
}

View File

@ -1100,7 +1100,7 @@ public class TestModules extends JavadocTester {
+ "&nbsp;</span></span><span id=\"t4\" class=\"tableTab\"><span><a href=\"javascript:showGroups(4);\">"
+ "Other Modules</a></span><span class=\"tabEnd\">&nbsp;</span></span></caption>",
"var groups = {\"i0\":1,\"i1\":2,\"i2\":2,\"i3\":4};\n"
+ "var tabs = {65535:[\"t0\",\"All Modules\"],1:[\"t1\",\"Module Group A\"],2:[\"t2\",\"Module Group B &amp; C\"],4:[\"t4\",\"Other Modules\"]};\n"
+ "var tabs = {65535:[\"t0\",\"All Modules\"],1:[\"t1\",\"Module Group A\"],2:[\"t2\",\"Module Group B & C\"],4:[\"t4\",\"Other Modules\"]};\n"
+ "var altColor = \"altColor\";\n"
+ "var rowColor = \"rowColor\";\n"
+ "var tableTab = \"tableTab\";\n"