Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

Commit

Permalink
Adding ?json_string, plus some javadoc and Manual improvements for ?j…
Browse files Browse the repository at this point in the history
…s_string and ?java_string.
  • Loading branch information
ddekany committed Feb 29, 2012
1 parent 032faf5 commit 893b240
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 24 deletions.
1 change: 1 addition & 0 deletions src/main/java/freemarker/core/BuiltIn.java
Expand Up @@ -177,6 +177,7 @@ abstract class BuiltIn extends Expression implements Cloneable {
/* showOffset = */ false, DateUtil.ACCURACY_HOURS));
builtins.put("j_string", new j_stringBI());
builtins.put("js_string", new js_stringBI());
builtins.put("json_string", new json_stringBI());
builtins.put("keys", new keysBI());
builtins.put("last_index_of", new last_index_ofBI());
builtins.put("last", new lastBI());
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/freemarker/core/StringBuiltins.java
Expand Up @@ -97,6 +97,12 @@ TemplateModel calculateResult(String s, Environment env) {
}
}

static class json_stringBI extends StringBuiltIn {
TemplateModel calculateResult(String s, Environment env) {
return new SimpleScalar(StringUtil.jsonStringEnc(s));
}
}

static class cap_firstBI extends StringBuiltIn {
TemplateModel calculateResult(String s, Environment env) {
int i = 0;
Expand Down
86 changes: 80 additions & 6 deletions src/main/java/freemarker/template/utility/StringUtil.java
Expand Up @@ -867,9 +867,9 @@ public static String jQuoteNoXSS(String s) {
* string literals, so it is safe to insert the value into a string literal.
* The resulting string will not be quoted.
*
* <p>In additional, all characters under UCS code point 0x20, that has no
* dedicated escape sequence in Java language, will be replaced with UNICODE
* escape (<tt>\<!-- -->u<i>XXXX</i></tt>).
* <p>All characters under UCS code point 0x20 will be escaped.
* Where they have no dedicated escape sequence in Java, they will
* be replaced with hexadecimal escape (<tt>\</tt><tt>u<i>XXXX</i></tt>).
*
* @see #jQuote(String)
*/
Expand Down Expand Up @@ -925,9 +925,11 @@ public static String javaStringEnc(String s) {
*
* <p>It escapes both <tt>'</tt> and <tt>"</tt>.
* In additional it escapes <tt>></tt> as <tt>\></tt> (to avoid
* <tt>&lt;/script></tt>). Furthermore, all characters under UCS code point
* 0x20, that has no dedicated escape sequence in JavaScript language, will
* be replaced with hexadecimal escape (<tt>\x<i>XX</i></tt>).
* <tt>&lt;/script></tt>).
*
* <p>All characters under UCS code point 0x20 will be escaped.
* Where they have no dedicated escape sequence in JavaScript, they will
* be replaced with hexadecimal escape (<tt>\</tt><tt>u<i>XXXX</i></tt>).
*/
public static String javaScriptStringEnc(String s) {
int ln = s.length();
Expand Down Expand Up @@ -979,6 +981,78 @@ public static String javaScriptStringEnc(String s) {
return s;
}

/**
* Escapes a <code>String</code> according the JSON string literal
* escaping rules. The resulting string will <em>not</em> be quoted;
* the caller have to ensure that they are there in the final output.
*
* <p>Beware, it doesn't escape <tt>'</tt>, as JSON string must be delimited with
* <tt>"</tt>, and JSON has no <tt>\'</tt> escape either!
*
* <p>It will escape <tt>/</tt> as <tt>\/</tt> if it's after <tt>&lt;</tt>,
* to avoid <tt>&lt;/script></tt>.
*
* <p>It will escape <tt>></tt> as <tt>\</tt><tt>u003E</tt> if it's after
* <tt>]]</tt>, to avoid closing a CDATA section.
*
* <p>All characters under UCS code point 0x20 will be escaped.
* Where they have no dedicated escape sequence in JSON, they will
* be replaced with hexadecimal escape (<tt>\</tt><tt>u<i>XXXX</i></tt>).
*/
public static String jsonStringEnc(String s) {
int ln = s.length();
for (int i = 0; i < ln; i++) {
char c = s.charAt(i);
if (c == '"' || c == '\\' || c < 0x20
|| (c == '/' && i > 0 && s.charAt(i -1) == '<')
|| (c == '>' && i > 1
&& s.charAt(i - 1) == ']' && s.charAt(i - 2) == ']')) {
StringBuilder b = new StringBuilder(ln + 4);
b.append(s.substring(0, i));
while (true) {
if (c == '"') {
b.append("\\\"");
} else if (c == '\\') {
b.append("\\\\");
} else if (c == '/' && i > 0 && s.charAt(i -1) == '<') {
b.append("\\/");
} else if (c == '>' && i > 1
&& s.charAt(i - 1) == ']' && s.charAt(i - 2) == ']') {
b.append("\\u003E");
} else if (c < 0x20) {
if (c == '\n') {
b.append("\\n");
} else if (c == '\r') {
b.append("\\r");
} else if (c == '\f') {
b.append("\\f");
} else if (c == '\b') {
b.append("\\b");
} else if (c == '\t') {
b.append("\\t");
} else {
b.append("\\u00");
int x = c / 0x10;
b.append((char)
(x < 0xA ? x + '0' : x - 0xA + 'A'));
x = c & 0xF;
b.append((char)
(x < 0xA ? x + '0' : x - 0xA + 'A'));
}
} else {
b.append(c);
}
i++;
if (i >= ln) {
return b.toString();
}
c = s.charAt(i);
}
} // if has to be escaped
} // for each characters
return s;
}

/**
* Parses a name-value pair list, where the pairs are separated with comma,
* and the name and value is separated with colon.
Expand Down
78 changes: 62 additions & 16 deletions src/manual/book.xml
Expand Up @@ -11674,11 +11674,16 @@ ${test3}</programlisting>
</indexterm>

<para>Escapes the string with the escaping rules of Java language
string literals, so it is safe to insert the value into a string
literal. In additional, all characters under <link
linkend="gloss.UCS">UCS</link> code point 0x20, that has no
dedicated escape sequence in Java language, will be replaced with
UNICODE escape
string literals, so it's safe to insert the value into a string
literal. Note that it will <emphasis>not</emphasis> add quotation
marks around the inserted value; you meant to use this
<emphasis>inside</emphasis> the string literal.</para>

<para>All characters under <link linkend="gloss.UCS">UCS</link> code
point 0x20 will be escaped. When they have no dedicated escape
sequence in the Java language (like <literal>\n</literal>,
<literal>\t</literal>, etc.), they will be replaced with a UNICODE
escape
(<literal>\u<replaceable>XXXX</replaceable></literal>).</para>

<para>Example:</para>
Expand All @@ -11699,19 +11704,23 @@ String BEAN_NAME = "${beanName?j_string}";</programlisting>
</indexterm>

<para>Escapes the string with the escaping rules of JavaScript
language string literals, so it is safe to insert the value into a
string literal. Both quotation mark (<literal>"</literal>) and
language string literals, so it's safe to insert the value into a
string literal. Note that it will <emphasis>not</emphasis> add
quotation marks around the inserted value; you meant to use this
<emphasis>inside</emphasis> the string literal.</para>

<para>Both quotation mark (<literal>"</literal>) and
apostrophe-quoate (<literal>'</literal>) are escaped. Starting from
FreeMarker 2.3.1, it also escapes <literal>&gt;</literal> as
<literal>\&gt;</literal> (to avoid
<literal>&lt;/script&gt;</literal>). Furthermore, all characters
under <link linkend="gloss.UCS">UCS</link> code point 0x20, that has
no dedicated escape sequence in JavaScript language, will be
replaced with hexadecimal escape
(<literal>\x<replaceable>XX</replaceable></literal>). (Of course,
according the JavaScript language string literal syntax, backslash
(<literal>\</literal>) will be escaped too, line-feed will be
escaped as <literal>\n</literal>, ...etc.)</para>
<literal>&lt;/script&gt;</literal>).</para>

<para>All characters under <link linkend="gloss.UCS">UCS</link> code
point 0x20 will be escaped. When they have no dedicated escape
sequence in JavaScript (like <literal>\n</literal>,
<literal>\t</literal>, etc.), they will be replaced with a UNICODE
escape
(<literal>\u<replaceable>XXXX</replaceable></literal>).</para>

<para>Example:</para>

Expand All @@ -11727,6 +11736,36 @@ String BEAN_NAME = "${beanName?j_string}";</programlisting>
&lt;/script&gt;</programlisting>
</section>

<section xml:id="ref_builtin_json_string">
<title>json_string</title>

<indexterm>
<primary>json_string built-in</primary>
</indexterm>

<para>Escapes the string with the escaping rules of JSON language
string literals, so it's safe to insert the value into a string
literal. Note that it will <emphasis>not</emphasis> add quotation
marks around the inserted value; you meant to use this
<emphasis>inside</emphasis> the string literal.</para>

<para>This will not escape <literal>'</literal> characters, since
JSON strings must be quoted with <literal>"</literal>. It will,
however escape the <literal>/</literal> (slash) characters as
<literal>\/</literal> where they occur directly after a
<literal>&lt;</literal>, to avoid <literal>&lt;/script&gt;</literal>
and such. It will also escape the <literal>&gt;</literal> characters
as <literal>\u003E</literal> where they occur directly after
<literal>]]</literal>, to avoid exiting an XML
<literal>CDATA</literal> section.</para>

<para>All characters under <link linkend="gloss.UCS">UCS</link> code
point 0x20 will be escaped. When they have no dedicated escape
sequence in JSON (like <literal>\n</literal>, <literal>\t</literal>,
etc.), they will be replaced with a UNICODE escape
(<literal>\u<replaceable>XXXX</replaceable></literal>).</para>
</section>

<section xml:id="ref_builtin_last_index_of">
<title>last_index_of</title>

Expand Down Expand Up @@ -19987,7 +20026,7 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
<section xml:id="versions_2_3_19">
<title>2.3.19</title>

<para>Date of release: FIXME</para>
<para>Date of release: 2012-02-29</para>

<section>
<title>Changes on the FTL side</title>
Expand All @@ -20007,6 +20046,13 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
hence this change.</para>
</listitem>

<listitem>
<para>New built-in for escaping inside JSON string literals:
<link
linkend="ref_builtin_json_string"><literal>json_string</literal></link>.
</para>
</listitem>

<listitem>
<para>Bugfix: Wrong <literal>#</literal> tags were printed as
static text instead of causing parsing error if there was no
Expand Down
Expand Up @@ -67,6 +67,16 @@ FOOBAR
[a] = [a]
[a\\'x'\nb] = [a\\'x'\nb]
[\u0001\u001a ] = [\u0001\u001a ]

[a] = [a]
[a\\\'x\'\nb] = [a\\\'x\'\nb]
[\x01\x1A ] = [\x01\x1A ]
[\x01\x1A ] = [\x01\x1A ]

[a] = [a]
[a\\'x'\nb] = [a\\'x'\nb]
[\u0001\u001A ] = [\u0001\u001A ]
[\n\r\t\f\b\"] = [\n\r\t\f\b\"]
[/] = [/]
[a/b] = [a/b]
[<\/script>] = [<\/script>]
[]]\u003E] = []]\u003E]
Expand Up @@ -65,6 +65,16 @@ ${c?eval}
[${"a"?j_string}] = [a]
[${"a\\'x'\nb"?j_string}] = [a\\'x'\nb]
[${"\x1\x1A\x20"?j_string}] = [\u0001\u001a ]

[${"a"?js_string}] = [a]
[${"a\\'x'\nb"?js_string}] = [a\\\'x\'\nb]
[${"\x1\x1A\x20"?js_string}] = [\x01\x1A ]
[${"\x1\x1A\x20"?js_string}] = [\x01\x1A ]

[${"a"?json_string}] = [a]
[${"a\\'x'\nb"?json_string}] = [a\\'x'\nb]
[${"\x1\x1A\x20"?json_string}] = [\u0001\u001A ]
[${"\n\r\t\f\b\""?json_string}] = [\n\r\t\f\b\"]
[${"/"?json_string}] = [/]
[${"a/b"?json_string}] = [a/b]
[${"</script>"?json_string}] = [<\/script>]
[${"]]>"?json_string}] = []]\u003E]

0 comments on commit 893b240

Please sign in to comment.