Revision: 5168
Author: [email protected]
Date: Thu Nov 29 12:27:35 2012
Log: Refactor HtmlDefinitions JS emission.
https://codereview.appspot.com/6782129
* Build the quoted-key Closure-compatibility exports into the definition
generators, so that there is no list at the end of the file to forget
to update.
* Merge duplicate code in the two entry points HtmlDefinitions$Builder
and HtmlDefinitions.main; the latter now produces output identical to
the former (it is not used from code, so I assume it is intended for
debugging).
[email protected]
http://code.google.com/p/google-caja/source/detail?r=5168
Modified:
/trunk/src/com/google/caja/lang/html/HtmlDefinitions.java
=======================================
--- /trunk/src/com/google/caja/lang/html/HtmlDefinitions.java Tue Nov 13
10:21:28 2012
+++ /trunk/src/com/google/caja/lang/html/HtmlDefinitions.java Thu Nov 29
12:27:35 2012
@@ -107,7 +107,17 @@
private static final AttribKey SCRIPT_SRC = AttribKey.forHtmlAttrib(
ElKey.forHtmlElement("script"), "src");
- private static <U> ExpressionStmt mapFromEnum(
+ private static Statement export(String key, Expression e) {
+ FilePosition unk = FilePosition.UNKNOWN;
+ // The html4[@k] assignment is an explicit export for Closure Compiler
+ return (Statement) QuasiBuilder.substV(
+ "{ html4.@i = @e; html4[@k] = html4.@i; }",
+ "i", new Reference(new Identifier(unk, key)),
+ "k", new StringLiteral(unk, key),
+ "e", e);
+ }
+
+ private static <U> Statement mapFromEnum(
Iterable<U> entries, String key,
Function<U, String> keyMaker, Function<U, Integer> valueMaker) {
FilePosition unk = FilePosition.UNKNOWN;
@@ -120,12 +130,11 @@
keys.add(new StringLiteral(unk, quoted));
values.add(new IntegerLiteral(unk, valueMaker.apply(e)));
}
- return new ExpressionStmt(unk,
- (Expression) QuasiBuilder.substV(
- "html4.@i = { @k*: @v* };",
- "i", new Reference(new Identifier(unk, key)),
- "k", new ParseTreeNodeContainer(keys),
- "v", new ParseTreeNodeContainer(values)));
+ return export(key, (Expression) QuasiBuilder.substV(
+ "({ @k*: @v* })",
+ "i", new Reference(new Identifier(unk, key)),
+ "k", new ParseTreeNodeContainer(keys),
+ "v", new ParseTreeNodeContainer(values)));
}
/**
@@ -133,7 +142,7 @@
* should stringify to the JS keys and whose values are mapped by the
function
* {@code codegen}.
*/
- private static <T> ExpressionStmt mapFromMap(
+ private static <T> Statement mapFromMap(
Map<?, T> entries, String key, Function<T, Expression> codegen) {
final FilePosition unk = FilePosition.UNKNOWN;
List<StringLiteral> keys = new ArrayList<StringLiteral>();
@@ -142,13 +151,10 @@
keys.add(StringLiteral.valueOf(unk, e.getKey().toString()));
values.add(codegen.apply(e.getValue()));
}
- ExpressionStmt def = new ExpressionStmt(unk, (Expression)
- QuasiBuilder.substV(
- "html4.@i = { @k*: @v* };",
- "i", new Reference(new Identifier(unk, key)),
- "k", new ParseTreeNodeContainer(keys),
- "v", new ParseTreeNodeContainer(values)));
- return def;
+ return export(key, (Expression) QuasiBuilder.substV(
+ "({ @k*: @v* })",
+ "k", new ParseTreeNodeContainer(keys),
+ "v", new ParseTreeNodeContainer(values)));
}
public static Block generateJavascriptDefinitions(HtmlSchema schema) {
@@ -190,9 +196,9 @@
values.add(new IntegerLiteral(unk,
A_TYPE_MAP.get(e.getValue())));
}
}
- definitions.appendChild(new ExpressionStmt(unk, (Expression)
- QuasiBuilder.substV(
- "html4.ATTRIBS = { @k*: @v* };",
+ definitions.appendChild(export("ATTRIBS",
+ (Expression) QuasiBuilder.substV(
+ "({ @k*: @v* })",
"k", new ParseTreeNodeContainer(keys),
"v", new ParseTreeNodeContainer(values))));
}
@@ -469,6 +475,45 @@
}
return data;
}
+
+ private static void generateSourceText(HtmlSchema schema, Writer out)
+ throws IOException {
+ String currentDate = "" + new Date();
+ if (currentDate.indexOf("\n") >= 0) {
+ throw new SomethingWidgyHappenedError("Date should not contain
newline");
+ }
+ out.write("// Copyright Google Inc.\n");
+ out.write("// Licensed under the Apache Licence Version 2.0\n");
+ out.write("// Autogenerated at " + currentDate + "\n");
+ out.write("// @overrides window\n");
+ out.write("// @provides html4\n");
+ Block node = generateJavascriptDefinitions(schema);
+ RenderContext rc = new RenderContext(node.makeRenderer(out, null))
+ .withPropertyNameQuotingMode(
+ PropertyNameQuotingMode.PRESERVE_QUOTES);
+ renderFlattenedBlocks(node, rc);
+ rc.getOut().noMoreTokens();
+ out.write("\n");
+ out.write("// export for Closure Compiler\n");
+ out.write("if (typeof window !== 'undefined') {\n");
+ out.write(" window['html4'] = html4;\n");
+ out.write("}\n");
+ }
+
+ /**
+ * The Blocks in the tree are used just as bundles of statements, so
+ * flatten them out.
+ */
+ private static void renderFlattenedBlocks(Statement node, RenderContext
rc) {
+ if (node instanceof Block) {
+ for (Statement s : ((Block) node).children()) {
+ renderFlattenedBlocks(s, rc);
+ if (!s.isTerminal()) { rc.getOut().consume(";"); }
+ }
+ } else {
+ node.render(rc);
+ }
+ }
public static class Builder implements BuildCommand {
public boolean build(List<File> inputs, List<File> deps, Map<String,
Object> options,
@@ -526,39 +571,7 @@
Writer out = new OutputStreamWriter(
new FileOutputStream(output), Charsets.UTF_8.name());
try {
- String currentDate = "" + new Date();
- if (currentDate.indexOf("*/") >= 0) {
- throw new SomethingWidgyHappenedError("Date should not
contain '*/'");
- }
- out.write("// Copyright Google Inc.\n");
- out.write("// Licensed under the Apache Licence Version 2.0\n");
- out.write("// Autogenerated at " + currentDate + "\n");
- out.write("// @overrides window\n");
- out.write("// @provides html4\n");
- Block node = generateJavascriptDefinitions(schema);
- RenderContext rc = new RenderContext(node.makeRenderer(out, null))
- .withPropertyNameQuotingMode(
- PropertyNameQuotingMode.PRESERVE_QUOTES);
- for (Statement s : node.children()) {
- s.render(rc);
- if (!s.isTerminal()) { rc.getOut().consume(";"); }
- }
- rc.getOut().noMoreTokens();
- out.write("\n");
- out.write("// exports for Closure Compiler\n");
- out.write("html4['ATTRIBS'] = html4.ATTRIBS;\n");
- out.write("html4['ELEMENTS'] = html4.ELEMENTS;\n");
- out.write("html4['ELEMENT_DOM_INTERFACES'] = " +
- "html4.ELEMENT_DOM_INTERFACES;\n");
- out.write("html4['URIEFFECTS'] = html4.URIEFFECTS;\n");
- out.write("html4['LOADERTYPES'] = html4.LOADERTYPES;\n");
- out.write("html4['atype'] = html4.atype;\n");
- out.write("html4['eflags'] = html4.eflags;\n");
- out.write("html4['ltypes'] = html4.ltypes;\n");
- out.write("html4['ueffects'] = html4.ueffects;\n");
- out.write("if (typeof window !== 'undefined') {\n");
- out.write(" window['html4'] = html4;\n");
- out.write("}\n");
+ generateSourceText(schema, out);
} finally {
out.close();
}
@@ -566,14 +579,13 @@
}
}
- public static void main(String[] args) {
+ public static void main(String[] args) throws IOException {
HtmlSchema schema = HtmlSchema.getDefault(new SimpleMessageQueue());
- Block node = generateJavascriptDefinitions(schema);
- RenderContext rc = new RenderContext(node.makeRenderer(System.out,
null));
- for (Statement s : node.children()) {
- s.render(rc);
- if (!s.isTerminal()) { rc.getOut().consume(";"); }
+ Writer out = new OutputStreamWriter(System.out);
+ try {
+ generateSourceText(schema, out);
+ } finally {
+ out.close();
}
- rc.getOut().noMoreTokens();
}
}