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(); Table table = getSummaryTable();
if (table.needsScript()) { if (table.needsScript()) {
writer.getScript().addContent(table.getScript()); writer.getMainBodyScript().append(table.getScript());
} }
return table.toContent(); 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.HtmlStyle;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; 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.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.Content; import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; 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); HtmlTree body = new HtmlTree(HtmlTag.BODY);
body.addAttr(HtmlAttr.ONLOAD, "loadFrames()"); body.addAttr(HtmlAttr.ONLOAD, "loadFrames()");
String topFilePath = configuration.topFile.getPath(); String topFilePath = configuration.topFile.getPath();
String javaScriptRefresh = "\nif (targetPage == \"\" || targetPage == \"undefined\")\n" + Script script = new Script(
" window.location.replace('" + topFilePath + "');\n"; "\nif (targetPage == \"\" || targetPage == \"undefined\")\n" +
RawHtml scriptContent = new RawHtml(javaScriptRefresh.replace("\n", DocletConstants.NL)); " window.location.replace(")
HtmlTree scriptTree = HtmlTree.SCRIPT(); .appendStringLiteral(topFilePath, '\'')
scriptTree.addContent(scriptContent); .append(");\n");
body.addContent(scriptTree); body.addContent(script.asContent());
Content noScript = HtmlTree.NOSCRIPT(contents.noScriptMessage); Content noScript = HtmlTree.NOSCRIPT(contents.noScriptMessage);
body.addContent(noScript); body.addContent(noScript);
if (configuration.allowTag(HtmlTag.MAIN)) { if (configuration.allowTag(HtmlTag.MAIN)) {
@ -135,12 +133,12 @@ public class FrameOutputWriter extends HtmlDocletWriter {
Content htmlComment = new Comment(configuration.getText("doclet.New_Page")); Content htmlComment = new Comment(configuration.getText("doclet.New_Page"));
Content head = new HtmlTree(HtmlTag.HEAD); Content head = new HtmlTree(HtmlTag.HEAD);
head.addContent(getGeneratedBy(!configuration.notimestamp)); head.addContent(getGeneratedBy(!configuration.notimestamp));
Content windowTitle = HtmlTree.TITLE(new StringContent(title)); Content windowTitle = HtmlTree.TITLE(title);
head.addContent(windowTitle); head.addContent(windowTitle);
Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, configuration.charset); Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, configuration.charset);
head.addContent(meta); head.addContent(meta);
addStyleSheetProperties(configuration, head); addStyleSheetProperties(configuration, head);
head.addContent(getFramesJavaScript()); head.addContent(getFramesScript().asContent());
Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(), Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(),
head, body); head, body);
Content htmlDocument = new HtmlDocument(htmlDocType, Content htmlDocument = new HtmlDocument(htmlDocType,
@ -217,4 +215,64 @@ public class FrameOutputWriter extends HtmlDocletWriter {
frame.setStyle(HtmlStyle.rightIframe); frame.setStyle(HtmlStyle.rightIframe);
contentTree.addContent(frame); 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.HtmlTag;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; 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.RawHtml;
import jdk.javadoc.internal.doclets.formats.html.markup.Script;
import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter; import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter;
import jdk.javadoc.internal.doclets.toolkit.ClassWriter; import jdk.javadoc.internal.doclets.toolkit.ClassWriter;
@ -177,6 +178,13 @@ public class HtmlDocletWriter extends HtmlDocWriter {
final static Pattern IMPROPER_HTML_CHARS = Pattern.compile(".*[&<>].*"); 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. * Constructor to construct the HtmlStandardWriter object.
* *
@ -258,19 +266,18 @@ public class HtmlDocletWriter extends HtmlDocWriter {
* @return a content tree for the script * @return a content tree for the script
*/ */
public Content getAllClassesLinkScript(String id) { public Content getAllClassesLinkScript(String id) {
HtmlTree script = HtmlTree.SCRIPT(); Script script = new Script("<!--\n" +
String scriptCode = "<!--\n" + " allClassesLink = document.getElementById(")
" allClassesLink = document.getElementById(\"" + id + "\");\n" + .appendStringLiteral(id)
.append(");\n" +
" if(window==top) {\n" + " if(window==top) {\n" +
" allClassesLink.style.display = \"block\";\n" + " allClassesLink.style.display = \"block\";\n" +
" }\n" + " }\n" +
" else {\n" + " else {\n" +
" allClassesLink.style.display = \"none\";\n" + " allClassesLink.style.display = \"none\";\n" +
" }\n" + " }\n" +
" //-->\n"; " //-->\n");
Content scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL)); Content div = HtmlTree.DIV(script.asContent());
script.addContent(scriptContent);
Content div = HtmlTree.DIV(script);
Content div_noscript = HtmlTree.DIV(contents.noScriptMessage); Content div_noscript = HtmlTree.DIV(contents.noScriptMessage);
Content noScript = HtmlTree.NOSCRIPT(div_noscript); Content noScript = HtmlTree.NOSCRIPT(div_noscript);
div.addContent(noScript); div.addContent(noScript);
@ -409,7 +416,7 @@ public class HtmlDocletWriter extends HtmlDocWriter {
Content htmlComment = new Comment(configuration.getText("doclet.New_Page")); Content htmlComment = new Comment(configuration.getText("doclet.New_Page"));
Content head = new HtmlTree(HtmlTag.HEAD); Content head = new HtmlTree(HtmlTag.HEAD);
head.addContent(getGeneratedBy(!configuration.notimestamp)); head.addContent(getGeneratedBy(!configuration.notimestamp));
head.addContent(getTitle()); head.addContent(HtmlTree.TITLE(winTitle));
Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, configuration.charset); Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, configuration.charset);
head.addContent(meta); head.addContent(meta);
if (!configuration.notimestamp) { if (!configuration.notimestamp) {
@ -607,13 +614,11 @@ public class HtmlDocletWriter extends HtmlDocWriter {
tree.addContent(fixedNavDiv); tree.addContent(fixedNavDiv);
HtmlTree paddingDiv = HtmlTree.DIV(HtmlStyle.navPadding, Contents.SPACE); HtmlTree paddingDiv = HtmlTree.DIV(HtmlStyle.navPadding, Contents.SPACE);
tree.addContent(paddingDiv); tree.addContent(paddingDiv);
HtmlTree scriptTree = HtmlTree.SCRIPT(); Script script = new Script(
String scriptCode = "<!--\n" "<!--\n"
+ "$('.navPadding').css('padding-top', $('.fixedNav').css(\"height\"));\n" + "$('.navPadding').css('padding-top', $('.fixedNav').css(\"height\"));\n"
+ "//-->\n"; + "//-->\n");
RawHtml scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL)); tree.addContent(script.asContent());
scriptTree.addContent(scriptContent);
tree.addContent(scriptTree);
} else { } else {
subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_BOTTOM)); subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_BOTTOM));
tree.addContent(subDiv); tree.addContent(subDiv);
@ -2113,9 +2118,11 @@ public class HtmlDocletWriter extends HtmlDocWriter {
HtmlTree javascript = HtmlTree.SCRIPT(pathToRoot.resolve(DocPaths.JAVASCRIPT).getPath()); HtmlTree javascript = HtmlTree.SCRIPT(pathToRoot.resolve(DocPaths.JAVASCRIPT).getPath());
head.addContent(javascript); head.addContent(javascript);
if (configuration.createindex) { if (configuration.createindex) {
if (pathToRoot != null && script != null) { if (pathToRoot != null && mainBodyScript != null) {
String ptrPath = pathToRoot.isEmpty() ? "." : pathToRoot.getPath(); 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.JSZIP_MIN);
addJQueryFile(head, DocPaths.JSZIPUTILS_MIN); addJQueryFile(head, DocPaths.JSZIPUTILS_MIN);
@ -2548,7 +2555,52 @@ public class HtmlDocletWriter extends HtmlDocWriter {
return new TableHeader(contents.packageLabel, contents.descriptionLabel); 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; 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.HtmlDocument;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; 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.HtmlTree;
import jdk.javadoc.internal.doclets.formats.html.markup.Script;
import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
import jdk.javadoc.internal.doclets.toolkit.Content; import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
@ -73,18 +74,18 @@ public class IndexRedirectWriter extends HtmlDocletWriter {
String title = (configuration.windowtitle.length() > 0) String title = (configuration.windowtitle.length() > 0)
? configuration.windowtitle ? 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); head.addContent(windowTitle);
Content metaContentType = HtmlTree.META("Content", CONTENT_TYPE, configuration.charset); Content metaContentType = HtmlTree.META("Content", CONTENT_TYPE, configuration.charset);
head.addContent(metaContentType); head.addContent(metaContentType);
String topFilePath = configuration.topFile.getPath(); String topFilePath = configuration.topFile.getPath();
String javaScriptRefresh = "window.location.replace('" + topFilePath + "')"; Script script = new Script("window.location.replace(")
HtmlTree scriptTree = HtmlTree.SCRIPT(); .appendStringLiteral(topFilePath, '\'')
scriptTree.addContent(javaScriptRefresh); .append(")");
head.addContent(scriptTree); head.addContent(script.asContent());
HtmlTree metaRefresh = new HtmlTree(HtmlTag.META); HtmlTree metaRefresh = new HtmlTree(HtmlTag.META);
metaRefresh.addAttr(HtmlAttr.HTTP_EQUIV, "Refresh"); metaRefresh.addAttr(HtmlAttr.HTTP_EQUIV, "Refresh");
metaRefresh.addAttr(HtmlAttr.CONTENT, "0;" + topFilePath); metaRefresh.addAttr(HtmlAttr.CONTENT, "0;" + topFilePath);
@ -98,7 +99,7 @@ public class IndexRedirectWriter extends HtmlDocletWriter {
ContentBuilder bodyContent = new ContentBuilder(); ContentBuilder bodyContent = new ContentBuilder();
bodyContent.addContent(HtmlTree.NOSCRIPT( 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)))); bodyContent.addContent(HtmlTree.P(HtmlTree.A(topFilePath, new StringContent(topFilePath))));

View File

@ -162,7 +162,7 @@ public class ModuleIndexWriter extends AbstractModuleIndexWriter {
} }
if (table.needsScript()) { 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); addPackageTableRows(table);
li.addContent(table.toContent()); li.addContent(table.toContent());
if (table.needsScript()) { 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()) { 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.formats.html.markup.StringContent;
import jdk.javadoc.internal.doclets.toolkit.Content; import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.Messages; 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.DocFile;
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
@ -77,6 +78,7 @@ public class SourceToHTMLConverter {
private final HtmlConfiguration configuration; private final HtmlConfiguration configuration;
private final Messages messages; private final Messages messages;
private final Resources resources;
private final Utils utils; private final Utils utils;
private final DocletEnvironment docEnv; private final DocletEnvironment docEnv;
@ -93,6 +95,7 @@ public class SourceToHTMLConverter {
DocPath outputdir) { DocPath outputdir) {
this.configuration = configuration; this.configuration = configuration;
this.messages = configuration.getMessages(); this.messages = configuration.getMessages();
this.resources = configuration.resources;
this.utils = configuration.utils; this.utils = configuration.utils;
this.docEnv = rd; this.docEnv = rd;
this.outputdir = outputdir; this.outputdir = outputdir;
@ -193,7 +196,7 @@ public class SourceToHTMLConverter {
body.addContent((configuration.allowTag(HtmlTag.MAIN)) ? HtmlTree.MAIN(div) : div); body.addContent((configuration.allowTag(HtmlTag.MAIN)) ? HtmlTree.MAIN(div) : div);
writeToFile(body, outputdir.resolve(DocPath.forClass(utils, te))); writeToFile(body, outputdir.resolve(DocPath.forClass(utils, te)));
} catch (IOException e) { } 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); throw new SimpleDocletException(message, e);
} }
} }
@ -209,8 +212,7 @@ public class SourceToHTMLConverter {
? DocType.HTML5 ? DocType.HTML5
: DocType.TRANSITIONAL; : DocType.TRANSITIONAL;
Content head = new HtmlTree(HtmlTag.HEAD); Content head = new HtmlTree(HtmlTag.HEAD);
head.addContent(HtmlTree.TITLE(new StringContent( head.addContent(HtmlTree.TITLE(resources.getText("doclet.Window_Source_title")));
configuration.getText("doclet.Window_Source_title"))));
addStyleSheetProperties(head); addStyleSheetProperties(head);
Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(), Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(),
head, body); 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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 { public class ContentBuilder extends Content {
protected List<Content> contents = Collections.emptyList(); protected List<Content> contents = Collections.emptyList();
public ContentBuilder() { }
public ContentBuilder(Content... contents) {
for (Content c : contents) {
addContent(c);
}
}
@Override @Override
public void addContent(Content content) { public void addContent(Content content) {
nullCheck(content); nullCheck(content);

View File

@ -74,8 +74,8 @@ public class HtmlTree extends Content {
*/ */
public HtmlTree(HtmlTag tag, Content... contents) { public HtmlTree(HtmlTag tag, Content... contents) {
this(tag); this(tag);
for (Content content: contents) for (Content c: contents)
addContent(content); addContent(c);
} }
/** /**
@ -86,8 +86,8 @@ public class HtmlTree extends Content {
*/ */
public HtmlTree(HtmlTag tag, List<Content> contents) { public HtmlTree(HtmlTag tag, List<Content> contents) {
this(tag); this(tag);
for (Content content: contents) for (Content c: contents)
addContent(content); addContent(c);
} }
/** /**
@ -146,8 +146,8 @@ public class HtmlTree extends Content {
@Override @Override
public void addContent(Content tagContent) { public void addContent(Content tagContent) {
if (tagContent instanceof ContentBuilder) { if (tagContent instanceof ContentBuilder) {
for (Content content: ((ContentBuilder)tagContent).contents) { for (Content c: ((ContentBuilder)tagContent).contents) {
addContent(content); addContent(c);
} }
} }
else if (tagContent == HtmlTree.EMPTY || tagContent.isValid()) { 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 * 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 * @param stringContent string content that needs to be added
*/ */
@ -177,6 +177,10 @@ public class HtmlTree extends Content {
addContent(new StringContent(stringContent)); addContent(new StringContent(stringContent));
} }
/**
* {@inheritDoc}
*/
@Override
public int charCount() { public int charCount() {
int n = 0; int n = 0;
for (Content c : content) 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. * return the result.
* *
* @param s The string to check. * @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. * Generates a SCRIPT tag with the type and src attributes.
* *
* @param type type of link
* @param src the path for the script * @param src the path for the script
* @return an HtmlTree object for the SCRIPT tag * @return an HtmlTree object for the SCRIPT tag
*/ */
public static HtmlTree SCRIPT(String src) { 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 htmltree = new HtmlTree(HtmlTag.SCRIPT);
htmltree.addAttr(HtmlAttr.TYPE, "text/javascript"); htmltree.addAttr(HtmlAttr.TYPE, "text/javascript");
htmltree.addAttr(HtmlAttr.SRC, nullCheck(src));
return htmltree; return htmltree;
} }
@ -912,8 +905,8 @@ public class HtmlTree extends Content {
* @param body content for the tag * @param body content for the tag
* @return an HtmlTree object for the TITLE tag * @return an HtmlTree object for the TITLE tag
*/ */
public static HtmlTree TITLE(Content body) { public static HtmlTree TITLE(String body) {
HtmlTree htmltree = new HtmlTree(HtmlTag.TITLE, nullCheck(body)); HtmlTree htmltree = new HtmlTree(HtmlTag.TITLE, new StringContent(body));
return htmltree; return htmltree;
} }
@ -949,6 +942,7 @@ public class HtmlTree extends Content {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public boolean isEmpty() { public boolean isEmpty() {
return (!hasContent() && !hasAttrs()); return (!hasContent() && !hasAttrs());
} }
@ -988,6 +982,7 @@ public class HtmlTree extends Content {
* *
* @return true if the HTML tree is valid * @return true if the HTML tree is valid
*/ */
@Override
public boolean isValid() { public boolean isValid() {
switch (htmlTag) { switch (htmlTag) {
case A : case A :

View File

@ -29,15 +29,13 @@ import java.io.*;
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
import jdk.javadoc.internal.doclets.toolkit.Content; 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.DocFile;
import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
import jdk.javadoc.internal.doclets.toolkit.util.DocPath; 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 * Initializes PrintWriter with FileWriter, to enable print
* related methods to generate the code to the named File through FileWriter. * 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 { public class HtmlWriter {
/**
* The window title of this file.
*/
protected String winTitle;
/**
* The configuration.
*/
protected HtmlConfiguration configuration;
private final DocFile docFile; private final DocFile docFile;
protected Content script;
/** /**
* Constructor. * Constructor.
* *
@ -74,13 +59,7 @@ public class HtmlWriter {
* or null if none to be created * or null if none to be created
*/ */
public HtmlWriter(HtmlConfiguration configuration, DocPath path) { public HtmlWriter(HtmlConfiguration configuration, DocPath path) {
this.configuration = configuration;
docFile = DocFile.createFileForOutput(configuration, path); 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 { public void write(Content c) throws DocFileIOException {
@ -90,171 +69,4 @@ public class HtmlWriter {
throw new DocFileIOException(docFile, DocFileIOException.Mode.WRITE, e); 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) { 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) { 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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. * 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 ; public abstract boolean write(Writer writer, boolean atNewline) throws IOException ;
@ -107,6 +111,7 @@ public abstract class Content {
/** /**
* Checks for null values. * Checks for null values.
* *
* @param <T> the type of the item being checked
* @param t reference type to check for null values * @param t reference type to check for null values
* @return the reference type if not null or else throws a null pointer exception * @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); checkExit(Exit.OK);
checkOutput("overview-summary.html", true, checkOutput("overview-summary.html", true,
"<span><a href=\"javascript:showGroups(1);\">abc &lt; &amp; &gt; def</a></span>"); "<span><a href=\"javascript:showGroups(1);\">abc &lt; &amp; &gt; def</a></span>",
",\"abc < & > def\"],");
checkOutput("overview-summary.html", false,
"abc < & > def");
} }
@Test @Test
@ -99,10 +97,8 @@ public class TestGroupName extends JavadocTester {
checkExit(Exit.OK); checkExit(Exit.OK);
checkOutput("overview-summary.html", true, checkOutput("overview-summary.html", true,
"<span><a href=\"javascript:showGroups(1);\">abc &lt; &amp; &gt; def</a></span>"); "<span><a href=\"javascript:showGroups(1);\">abc &lt; &amp; &gt; def</a></span>",
",\"abc < & > def\"],");
checkOutput("overview-summary.html", false,
"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);\">" + "&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>", + "Other Modules</a></span><span class=\"tabEnd\">&nbsp;</span></span></caption>",
"var groups = {\"i0\":1,\"i1\":2,\"i2\":2,\"i3\":4};\n" "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 altColor = \"altColor\";\n"
+ "var rowColor = \"rowColor\";\n" + "var rowColor = \"rowColor\";\n"
+ "var tableTab = \"tableTab\";\n" + "var tableTab = \"tableTab\";\n"