Merge remote-tracking branch 'origin/2.3.24-gae-stabilization' into 2.3.24-stabilization
diff --git a/README b/README
index f6e2c31..dde3126 100644
--- a/README
+++ b/README
@@ -139,10 +139,10 @@
- Install Ant and Ivy, if you haven't yet; see earlier.
- From the command line, run `ant clean javacc ide-dependencies`. (Note that
- now the "ide-dependencies" library was created. Also that
- "build/generated-sources" was created.)
+ now the "ide-dependencies" and "build/generated-sources" was created.)
- Start Eclipse
-- Many developers prefer to start a new workspace (File -> "Switch workspace")
+- You may prefer to start a new workspace (File -> "Switch workspace"), but
+ it's optional.
- Window -> Preferences
- General -> Workspace, set the text file encoding
to "UTF-8". (Or, you can set the same later on project level instead.)
@@ -150,7 +150,7 @@
Select src\ide-settings\Eclipse-Mars\Formatter-profile-FreeMarker.xml
inside the FreeMarker project directory.
This profile uses space-only indentation policy and 120 character line
- width, and formattings rules that are pretty much standard in modern Java.
+ width, and formatting rules that are pretty much standard in modern Java.
- Java -> Installed JRE-s:
Ensure that you have JDK 6 installed, and that it was added to Eclipse.
Note that it's not JRE, but JDK.
@@ -158,16 +158,16 @@
- In the first window popping up:
- Change the "location" to the directory of the FreeMarker project
- Press "Next"
- - In the next window, you see the build path settings. Ensure that:
+ - In the next window, you see the build path settings:
- On "Source" tab, ensure that exactly these are marked as source
- directories:
+ directories (be careful, Eclipse doesn't auto-detect these well):
build/generated-sources/java
src/main/java
src/main/resources
src/test/java
src/test/resources
- On the "Libraries" tab:
- - Delete everyhing from there, except the "JRE System Library [...]".
+ - Delete everyhing from there, except the "JRE System Library [...]"
- Edit "JRE System Library [...]" to "Execution Environment" "JavaSE 1.6"
- Add all jar-s that are directly under the "ide-dependencies" directory
(use the "Add JARs..." and select all those files).
@@ -178,18 +178,19 @@
- Press "Finish"
- Eclipse will indicate many errors at this point; it's expected, read on.
- Project -> Properties -> Java Compiler
- - Set "Compiler Compliance Level" to "1.5"
+ - Set "Compiler Compliance Level" to "1.5" (you will have to uncheck
+ "Use compliance from execution environment" for that)
- In Errors/Warnings, check in "Enable project specific settings", then set
"Forbidden reference (access rules)" from "Error" to "Warning".
-- You will still have errors on these Java files (because different java
+- You will still have errors on these java files (because different java
files depend on different versions of the same library, and Eclipse can't
- handle that). Exclude them from the Build Path (in the Package Explorer,
- right click on the problematic file -> "Build Path" -> "Exclude").
+ handle that). Exclude those java files from the Build Path (in the Package
+ Explorer, right click on the problematic file -> "Build Path" -> "Exclude").
_Jython20*.java
_Jython22*.java
_FreeMarkerPageContext2.java
FreeMarkerJspFactory2.java
- Now you shouldn't have any errors.
+ Also, close these files if they are open. Now you shouldn't have any errors.
- At Project -> Properties -> Java Code Style -> Formatter, check in "Enable
project specific settings", and then select "FreeMarker" as active profile.
- Right click on the project -> Run As -> JUnit Test
diff --git a/src/main/java/freemarker/cache/WebappTemplateLoader.java b/src/main/java/freemarker/cache/WebappTemplateLoader.java
index d4f32dc..44c40df 100644
--- a/src/main/java/freemarker/cache/WebappTemplateLoader.java
+++ b/src/main/java/freemarker/cache/WebappTemplateLoader.java
@@ -51,7 +51,7 @@
private boolean attemptFileAccess = true;
/**
- * Creates a resource template cache that will use the specified servlet context to load the resources. It will use
+ * Creates a template loader that will use the specified servlet context to load the resources. It will use
* the base path of <code>"/"</code> meaning templates will be resolved relative to the servlet context root
* location.
*
diff --git a/src/main/java/freemarker/core/CommonTemplateMarkupOutputModel.java b/src/main/java/freemarker/core/CommonTemplateMarkupOutputModel.java
index 9d04428..3f1a43e 100644
--- a/src/main/java/freemarker/core/CommonTemplateMarkupOutputModel.java
+++ b/src/main/java/freemarker/core/CommonTemplateMarkupOutputModel.java
@@ -45,17 +45,20 @@
public abstract CommonMarkupOutputFormat<MO> getOutputFormat();
- /** Maybe {@code null}, but then the other field isn't {@code null}. */
+ /** Maybe {@code null}, but then {@link #getMarkupContent()} isn't {@code null}. */
final String getPlainTextContent() {
return plainTextContent;
}
- /** Maybe {@code null}, but then the other field isn't {@code null}. */
+ /** Maybe {@code null}, but then {@link #getPlainTextContent()} isn't {@code null}. */
final String getMarkupContent() {
return markupContet;
}
- /** Use only to set {@code null} field to the value calculated from the other field! */
+ /**
+ * Use only to set the value calculated from {@link #getPlainTextContent()}, when {@link #getMarkupContent()} was
+ * still {@code null}!
+ */
final void setMarkupContet(String markupContet) {
this.markupContet = markupContet;
}
diff --git a/src/main/java/freemarker/core/MarkupOutputFormat.java b/src/main/java/freemarker/core/MarkupOutputFormat.java
index 0f1302c..b058591 100644
--- a/src/main/java/freemarker/core/MarkupOutputFormat.java
+++ b/src/main/java/freemarker/core/MarkupOutputFormat.java
@@ -21,13 +21,23 @@
import java.io.IOException;
import java.io.Writer;
+import freemarker.template.Configuration;
import freemarker.template.TemplateModelException;
/**
- * An {@link OutputFormat}-s that represent a "markup", which is any format where certain character sequences have
- * special meaning and thus may need escaping. (Escaping is important for FreeMarker, as typically it has to insert
- * non-markup text from the data-model into the output markup.) This class, among others, defines the operations related
- * to {@link TemplateMarkupOutputModel}-s that belong to the output format.
+ * Superclass of {@link OutputFormat}-s that represent a "markup" format, which is any format where certain character
+ * sequences have special meaning and thus may need escaping. (Escaping is important for FreeMarker, as typically it has
+ * to insert non-markup text from the data-model into the output markup. See also:
+ * {@link Configuration#setOutputFormat(OutputFormat)}.)
+ *
+ * <p>
+ * An {@link OutputFormat} subclass always has a corresponding {@link TemplateMarkupOutputModel} subclass pair (like
+ * {@link HTMLOutputFormat} has {@link TemplateHTMLOutputModel}). The {@link OutputFormat} implements the operations
+ * related to {@link TemplateMarkupOutputModel} objects of that kind, while the {@link TemplateMarkupOutputModel} only
+ * encapsulates the data (the actual markup or text).
+ *
+ * <p>
+ * To implement a custom output format, you may want to extend {@link CommonMarkupOutputFormat}.
*
* @param <MO>
* The {@link TemplateMarkupOutputModel} class this output format can deal with.
@@ -105,15 +115,17 @@
public abstract boolean isEmpty(MO mo) throws TemplateModelException;
/**
- * Tells if a string built-in that can't handle a {@link TemplateMarkupOutputModel} left operand can bypass this
- * object as is. A typical such case would be when a {@link TemplateHTMLOutputModel} of "HTML" format bypasses
+ * Tells if a string built-in that can't handle a {@link TemplateMarkupOutputModel} left hand operand can bypass
+ * this object as is. A typical such case would be when a {@link TemplateHTMLOutputModel} of "HTML" format bypasses
* {@code ?html}.
*/
public abstract boolean isLegacyBuiltInBypassed(String builtInName) throws TemplateModelException;
/**
* Tells if by default auto-escaping should be on for this format. It should be {@code true} if you need to escape
- * on most of the places where you insert values.
+ * on most of the places where you insert values.
+ *
+ * @see Configuration#setAutoEscapingPolicy(int)
*/
public abstract boolean isAutoEscapedByDefault();
diff --git a/src/main/java/freemarker/core/OutputFormat.java b/src/main/java/freemarker/core/OutputFormat.java
index 72ea821..5f4bc60 100644
--- a/src/main/java/freemarker/core/OutputFormat.java
+++ b/src/main/java/freemarker/core/OutputFormat.java
@@ -23,7 +23,7 @@
import freemarker.template.utility.StringUtil;
/**
- * Represents an output format.
+ * Represents an output format. If you need auto-escaping, see its subclass, {@link MarkupOutputFormat}.
*
* @see Configuration#setOutputFormat(OutputFormat)
* @see Configuration#setRegisteredCustomOutputFormats(java.util.Collection)
diff --git a/src/main/java/freemarker/core/TemplateConfiguration.java b/src/main/java/freemarker/core/TemplateConfiguration.java
index 2c89d01..50ebc8f 100644
--- a/src/main/java/freemarker/core/TemplateConfiguration.java
+++ b/src/main/java/freemarker/core/TemplateConfiguration.java
@@ -38,11 +38,11 @@
* <p>
* Note on the {@code locale} setting: When used with the standard template loading/caching mechanism (
* {@link Configuration#getTemplate(String)} and its overloads), localized lookup happens before the {@code locale}
- * specified here could have effect. The {@code locale} will be only set in the template that the localized looks has
+ * specified here could have effect. The {@code locale} will be only set in the template that the localized lookup has
* already found.
*
* <p>
- * Note on encoding setting {@code encoding}: See {@link #setEncoding(String)}.
+ * Note on the encoding setting {@code encoding}: See {@link #setEncoding(String)}.
*
* <p>
* Note that the result value of the reader methods (getter and "is" methods) is usually not useful unless the value of
diff --git a/src/main/java/freemarker/core/TemplateMarkupOutputModel.java b/src/main/java/freemarker/core/TemplateMarkupOutputModel.java
index 3606379..3beead0 100644
--- a/src/main/java/freemarker/core/TemplateMarkupOutputModel.java
+++ b/src/main/java/freemarker/core/TemplateMarkupOutputModel.java
@@ -18,23 +18,23 @@
*/
package freemarker.core;
-import freemarker.template.Configuration;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateScalarModel;
/**
* "markup output" template language data-type; stores markup (some kind of "rich text" / structured format, as opposed
- * to plain text) that meant to be printed as template output. Each implementation of this type has a
- * {@link OutputFormat} subclass pair (like {@link TemplateHTMLOutputModel} has {@link HTMLOutputFormat}). This type is
- * related to the {@link Configuration#setOutputFormat(OutputFormat)} and
- * {@link Configuration#setAutoEscapingPolicy(int)} mechanism; see more there. Values of this type are exempt from
- * automatic escaping with that mechanism.
+ * to plain text) that meant to be printed as template output. This type is related to the {@link OutputFormat}
+ * mechanism. Values of this kind are exempt from {@link OutputFormat}-based automatic escaping.
*
* <p>
- * Note that {@link TemplateMarkupOutputModel}-s are by design not handled like {@link TemplateScalarModel}-s, and so
+ * Each implementation of this type has a {@link OutputFormat} subclass pair, whose singleton instance is returned by
+ * {@link #getOutputFormat()}. See more about how markup output values work at {@link OutputFormat}.
+ *
+ * <p>
+ * Note that {@link TemplateMarkupOutputModel}-s are by design not treated like {@link TemplateScalarModel}-s, and so
* the implementations of this interface usually shouldn't implement {@link TemplateScalarModel}. (Because, operations
- * applicable on plain strings, like converting to upper case, substringing, etc., can corrupt markup.) The template
- * author should make conscious decision of passing in the markup as String by using {@code ?markup_string}.
+ * applicable on plain strings, like converting to upper case, substringing, etc., can corrupt markup.) If the template
+ * author wants to pass in the "source" of the markup as string somewhere, he should use {@code ?markup_string}.
*
* @param <MO>
* Refers to the interface's own type, which is useful in interfaces that extend
@@ -44,6 +44,9 @@
*/
public interface TemplateMarkupOutputModel<MO extends TemplateMarkupOutputModel<MO>> extends TemplateModel {
+ /**
+ * Returns the singleton {@link OutputFormat} object that implements the operations for the "markup output" value.
+ */
MarkupOutputFormat<MO> getOutputFormat();
}
diff --git a/src/main/java/freemarker/template/Configuration.java b/src/main/java/freemarker/template/Configuration.java
index 82c46b3..ec5f270 100644
--- a/src/main/java/freemarker/template/Configuration.java
+++ b/src/main/java/freemarker/template/Configuration.java
@@ -1374,12 +1374,20 @@
}
/**
- * Sets the file system directory from which to load templates.
- * This is equivalent to {@code setTemplateLoader(new FileTemplateLoader(dir))},
- * so see {@link FileTemplateLoader#FileTemplateLoader(File)} for more details.
+ * Sets the file system directory from which to load templates. This is equivalent to
+ * {@code setTemplateLoader(new FileTemplateLoader(dir))}, so see
+ * {@link FileTemplateLoader#FileTemplateLoader(File)} for more details.
*
- * Note that FreeMarker can load templates from non-file-system sources too.
- * See {@link #setTemplateLoader(TemplateLoader)} from more details.
+ * <p>
+ * Note that FreeMarker can load templates from non-file-system sources too. See
+ * {@link #setTemplateLoader(TemplateLoader)} from more details.
+ *
+ * <p>
+ * Note that this shouldn't be used for loading templates that are coming from a WAR; use
+ * {@link #setServletContextForTemplateLoading(Object, String)} then. Servlet containers might not unpack the WAR
+ * file, in which case you clearly can't access the contained files via {@link File}. Even if the WAR is unpacked,
+ * the servlet container might not expose the location as a {@link File}.
+ * {@link #setServletContextForTemplateLoading(Object, String)} on the other hand will work in all these cases.
*/
public void setDirectoryForTemplateLoading(File dir) throws IOException {
TemplateLoader tl = getTemplateLoader();
@@ -1746,8 +1754,8 @@
}
/**
- * Sets when auto-escaping should be enabled depending on the current output format; default is
- * {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}. Note that the default output format,
+ * Sets when auto-escaping should be enabled depending on the current {@linkplain OutputFormat output format};
+ * default is {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}. Note that the default output format,
* {@link UndefinedOutputFormat}, is a non-escaping format, so there auto-escaping will be off.
* Note that the templates can turn auto-escaping on/off locally with directives like {@code <#ftl auto_esc=...>},
* which will ignore the policy.
@@ -1813,7 +1821,7 @@
* Sets the default output format. Usually, you should leave this on its default, which is
* {@link UndefinedOutputFormat#INSTANCE}, and then use standard file extensions like "ftlh" (for HTML) or "ftlx"
* (for XML) and ensure that {@link #setRecognizeStandardFileExtensions(boolean)} is {@code true} (see more there).
- * Where you can't use standard the file extensions, templates still can be associated to output formats with
+ * Where you can't use the standard the extensions, templates still can be associated to output formats with
* patterns matching their name (their path) using {@link #setTemplateConfigurations(TemplateConfigurationFactory)}.
* But if all templates will have the same output format, you may use {@link #setOutputFormat(OutputFormat)} after
* all, to set a value like {@link HTMLOutputFormat#INSTANCE}, {@link XMLOutputFormat#INSTANCE}, etc. Also note
diff --git a/src/main/java/freemarker/template/DefaultObjectWrapper.java b/src/main/java/freemarker/template/DefaultObjectWrapper.java
index bda6f32..8753ebb 100644
--- a/src/main/java/freemarker/template/DefaultObjectWrapper.java
+++ b/src/main/java/freemarker/template/DefaultObjectWrapper.java
@@ -226,7 +226,7 @@
}
/**
- * Called for an object that aren't considered to be of a "basic" Java type, like for an application specific type,
+ * Called for an object that isn't considered to be of a "basic" Java type, like for an application specific type,
* or for a W3C DOM node. In its default implementation, W3C {@link Node}-s will be wrapped as {@link NodeModel}-s
* (allows DOM tree traversal), Jython objects will be delegated to the {@code JythonWrapper}, others will be
* wrapped using {@link BeansWrapper#wrap(Object)}.
diff --git a/src/main/java/freemarker/template/utility/StringUtil.java b/src/main/java/freemarker/template/utility/StringUtil.java
index 58d5ffb..88811f4 100644
--- a/src/main/java/freemarker/template/utility/StringUtil.java
+++ b/src/main/java/freemarker/template/utility/StringUtil.java
@@ -1331,12 +1331,12 @@
* output. Note that for JSON, the quotation marks must be {@code "}, not {@code '}, because JSON doesn't escape
* {@code '}.
*
- * <p>The escaping rules guarantee that if the inside
- * of the literal is from one or more touching sections of strings escaped with this, no character sequence will
- * occur that closes the string literal or has special meaning in HTML/XML that can terminate the script section.
+ * <p>The escaping rules guarantee that if the inside of the JavaScript/JSON string literal is from one or more
+ * touching pieces that were escaped with this, no character sequence can occur that closes the
+ * JavaScript/JSON string literal, or has a meaning in HTML/XML that causes the HTML script section to be closed.
* (If, however, the escaped section is preceded by or followed by strings from other sources, this can't be
* guaranteed in some rare cases. Like <tt>x = "</${a?js_string}"</tt> might closes the "script"
- * element if {@code a} is is {@code "script>"}.)
+ * element if {@code a} is {@code "script>"}.)
*
* The escaped characters are:
*
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 2e6fa99..638c695 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -13328,23 +13328,12 @@
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. It also
- escapes <literal>></literal> as <literal>\></literal> (to
- avoid <literal></script></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>
-
<warning>
<para>When inserting into a JavaScript string literal that's
inside a HTML attribute, you also must escape the value with HTML
- escaping. Thus, of you don't have automatic HTML escaping, this is
- WRONG: <literal><p
+ escaping. Thus, of you don't have <link
+ linkend="pgui_config_outputformatsautoesc">automatic HTML
+ escaping</link>, this is WRONG: <literal><p
onclick="alert('${message?js_string}')"></literal>, and this is
good: <literal><p
onclick="alert('${message?js_string?html}')"></literal>.</para>
@@ -13362,6 +13351,65 @@
<programlisting role="output"><script>
alert("Welcome Big Joe\'s \"right hand\"!");
</script></programlisting>
+
+ <para>The exact escaping rules are:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><literal>"</literal> is escaped as
+ <literal>\"</literal></para>
+ </listitem>
+
+ <listitem>
+ <para><literal>'</literal> is escaped as
+ <literal>\'</literal></para>
+ </listitem>
+
+ <listitem>
+ <para><literal>\</literal> is escaped as
+ <literal>\\</literal></para>
+ </listitem>
+
+ <listitem>
+ <para><literal>/</literal> is escaped as <literal>\/</literal>
+ if the <literal>/</literal> is directly after
+ <literal><</literal> in the escaped string, or if it's at the
+ beginning of the escaped string</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>></literal> is escaped as
+ <literal>\></literal> if the <literal>></literal> is
+ directly after <literal>]]</literal> or <literal>--</literal> in
+ the escaped string, or if it's at the beginning of the escaped
+ string, or if there's only a <literal>]</literal> or
+ <literal>-</literal> before it at the beginning of the escaped
+ string</para>
+ </listitem>
+
+ <listitem>
+ <para><literal><</literal> is escaped as
+ <literal>\u003C</literal> if it's followed by
+ <literal>?</literal> or <literal>!</literal> in the escaped
+ string, or if it's at the end of the escaped string</para>
+ </listitem>
+
+ <listitem>
+ <para>Control characters in <link linkend="gloss.UCS">UCS</link>
+ code point ranges U+0000...U+001f and U+007f...U+009f are
+ escaped as <literal>\r</literal>, <literal>\n</literal>, etc.,
+ or as <literal>\x<replaceable>XX</replaceable></literal> where
+ there's no special escape for them in JavaScript.</para>
+ </listitem>
+
+ <listitem>
+ <para>Control characters with <link
+ linkend="gloss.UCS">UCS</link> code point U+2028 (Line
+ separator) and U+2029 (Paragraph separator) are escaped as
+ <literal>\u<replaceable>XXXX</replaceable></literal>, as they
+ are source code line-breaks in ECMAScript.</para>
+ </listitem>
+ </itemizedlist>
</section>
<section xml:id="ref_builtin_json_string">
@@ -13378,20 +13426,16 @@
<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><</literal>, to avoid <literal></script></literal>
- and such. It will also escape the <literal>></literal> characters
- as <literal>\u003E</literal> where they occur directly after
- <literal>]]</literal>, to avoid exiting an XML
- <literal>CDATA</literal> section.</para>
+ JSON strings must be quoted with <literal>"</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 JSON (like <literal>\n</literal>, <literal>\t</literal>,
- etc.), they will be replaced with a UNICODE escape
- (<literal>\u<replaceable>XXXX</replaceable></literal>).</para>
+ <para>The escaping rules are almost identical to those <link
+ linkend="ref_builtin_j_string">documented for
+ <literal>js_string</literal></link>. The differences are that
+ <literal>'</literal> is not escaped at all, that > is escaped as
+ \u003E (not as \>), and that
+ <literal>\u<replaceable>XXXX</replaceable></literal> escapes are
+ used instead of <literal>\x<replaceable>XX</replaceable></literal>
+ escapes.</para>
</section>
<section xml:id="ref_builtin_keep_after">
@@ -20342,19 +20386,21 @@
</div>
</#list></programlisting>
- <para>As <literal>sep</literal> is just a convenient way of
- writing <literal><#if
- <replaceable>item</replaceable>?has_next>...</#if></literal>,
- it can be used anywhere where there's a <literal>list</literal> or
- <literal>items</literal> loop variable is available, and for
- unlimited times. Also, it can have arbitrary FTL as nested
- content.</para>
+ <para><literal>sep</literal> is just a shorthand for
+ <literal><#if
+ <replaceable>item</replaceable>?has_next>...</#if></literal>.
+ Thus, it can be used anywhere where there's a
+ <literal>list</literal> or <literal>items</literal> loop variable
+ available, it can occur for multiple times, and it can have
+ arbitrary nested content.</para>
- <para>The parser will check that <literal>sep</literal> is used
- inside <literal>list <replaceable>...</replaceable> as
- item</literal> or an <literal>items</literal> directive, so you
- can't move <literal>sep</literal> out from the repeated part into
- a macro or included template.</para>
+ <para>The parser ensures that <literal>sep</literal> is only used
+ on a place where there's a visible loop variable. This happens
+ earlier than the actual execution of the template. Thus, you can't
+ move <literal>sep</literal> from inside the associated
+ <literal>list</literal> or <literal>items</literal> directive into
+ a macro or included template (the parser can't know where those
+ will be called from).</para>
</section>
<section>
diff --git a/src/test/java/freemarker/core/OutputFormatTest.java b/src/test/java/freemarker/core/OutputFormatTest.java
index dc5e7d4..bab9619 100644
--- a/src/test/java/freemarker/core/OutputFormatTest.java
+++ b/src/test/java/freemarker/core/OutputFormatTest.java
@@ -944,7 +944,7 @@
}
@Test
- public void testBannedDirectivesIsWhenAutoEscaping() throws Exception {
+ public void testBannedDirectivesWhenAutoEscaping() throws Exception {
String commonFTL = "<#escape x as x?html>x</#escape>";
assertOutput(commonFTL, "x");
assertErrorContains("<#ftl outputFormat='HTML'>" + commonFTL, "escape", "HTML", "double-escaping");