You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2015/10/04 16:49:31 UTC
[06/14] incubator-freemarker git commit: `${...}` inside string
literals is equivalent to using the `+` operator again. This rule was broken
by `+` supporting markup operands,
while `${...}` inside string literals didn't. Now similarly as `"foo " + someM
`${...}` inside string literals is equivalent to using the `+` operator again. This rule was broken by `+` supporting markup operands, while `${...}` inside string literals didn't. Now similarly as `"foo " + someMarkup` works and gives a markup result, `"foo ${someMarkup}"` does too.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/fa6ac0ee
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/fa6ac0ee
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/fa6ac0ee
Branch: refs/heads/master
Commit: fa6ac0eea88c38c816298b518c5d249d4599f93e
Parents: d8487ff
Author: ddekany <dd...@apache.org>
Authored: Sun Oct 4 01:42:47 2015 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sun Oct 4 01:43:45 2015 +0200
----------------------------------------------------------------------
.../freemarker/core/AddConcatExpression.java | 32 +-----
.../java/freemarker/core/DollarVariable.java | 10 +-
src/main/java/freemarker/core/EvalUtil.java | 28 ++++-
.../java/freemarker/core/Interpolation.java | 9 ++
.../java/freemarker/core/NumericalOutput.java | 18 ++-
.../java/freemarker/core/ParameterRole.java | 1 +
.../java/freemarker/core/StringLiteral.java | 105 +++++++++++------
src/main/javacc/FTL.jj | 66 ++++++++++-
src/manual/book.xml | 114 ++++++++-----------
.../java/freemarker/core/NumberFormatTest.java | 4 +-
.../java/freemarker/core/OutputFormatTest.java | 23 +++-
.../core/StringLiteralInterpolationTest.java | 23 ++++
src/test/java/freemarker/test/TemplateTest.java | 6 +-
src/test/resources/freemarker/core/ast-1.ast | 19 ++--
.../resources/freemarker/core/ast-builtins.ast | 3 -
.../resources/freemarker/core/ast-range.ast | 2 -
.../freemarker/core/ast-strlitinterpolation.ast | 72 ++++++++++--
.../freemarker/core/ast-strlitinterpolation.ftl | 8 +-
.../AutoEscapingExample-stringLiteral2.ftlh | 2 +-
.../AutoEscapingExample-stringLiteral2.ftlh.out | 2 +-
20 files changed, 364 insertions(+), 183 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/main/java/freemarker/core/AddConcatExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/AddConcatExpression.java b/src/main/java/freemarker/core/AddConcatExpression.java
index 7c1eb75..4dbfd78 100644
--- a/src/main/java/freemarker/core/AddConcatExpression.java
+++ b/src/main/java/freemarker/core/AddConcatExpression.java
@@ -85,18 +85,18 @@ final class AddConcatExpression extends Expression {
return new SimpleScalar(((String) leftOMOrStr).concat((String) rightOMOrStr));
} else { // rightOMOrStr instanceof TemplateMarkupOutputModel
TemplateMarkupOutputModel<?> rightMO = (TemplateMarkupOutputModel<?>) rightOMOrStr;
- return concatMarkupOutputs(parent,
+ return EvalUtil.concatMarkupOutputs(parent,
rightMO.getOutputFormat().fromPlainTextByEscaping((String) leftOMOrStr),
rightMO);
}
} else { // leftOMOrStr instanceof TemplateMarkupOutputModel
TemplateMarkupOutputModel<?> leftMO = (TemplateMarkupOutputModel<?>) leftOMOrStr;
if (rightOMOrStr instanceof String) { // markup output
- return concatMarkupOutputs(parent,
+ return EvalUtil.concatMarkupOutputs(parent,
leftMO,
leftMO.getOutputFormat().fromPlainTextByEscaping((String) rightOMOrStr));
} else { // rightOMOrStr instanceof TemplateMarkupOutputModel
- return concatMarkupOutputs(parent,
+ return EvalUtil.concatMarkupOutputs(parent,
leftMO,
(TemplateMarkupOutputModel) rightOMOrStr);
}
@@ -124,32 +124,6 @@ final class AddConcatExpression extends Expression {
}
}
- private static TemplateModel concatMarkupOutputs(TemplateObject parent, TemplateMarkupOutputModel leftMO,
- TemplateMarkupOutputModel rightMO) throws TemplateModelException {
- MarkupOutputFormat leftOF = leftMO.getOutputFormat();
- MarkupOutputFormat rightOF = rightMO.getOutputFormat();
- if (rightOF != leftOF) {
- String rightPT;
- String leftPT;
- if ((rightPT = rightOF.getSourcePlainText(rightMO)) != null) {
- return leftOF.concat(leftMO, leftOF.fromPlainTextByEscaping(rightPT));
- } else if ((leftPT = leftOF.getSourcePlainText(leftMO)) != null) {
- return rightOF.concat(rightOF.fromPlainTextByEscaping(leftPT), rightMO);
- } else {
- Object[] message = { "Concatenation left hand operand is in ", new _DelayedToString(leftOF),
- " format, while the right hand operand is in ", new _DelayedToString(rightOF),
- ". Conversion to common format wasn't possible." };
- if (parent instanceof Expression) {
- throw new _TemplateModelException((Expression) parent, message);
- } else {
- throw new _TemplateModelException(message);
- }
- }
- } else {
- return leftOF.concat(leftMO, rightMO);
- }
- }
-
static TemplateModel _evalOnNumbers(Environment env, TemplateObject parent, Number first, Number second)
throws TemplateException {
ArithmeticEngine ae = EvalUtil.getArithmeticEngine(env, parent);
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/main/java/freemarker/core/DollarVariable.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/DollarVariable.java b/src/main/java/freemarker/core/DollarVariable.java
index a3abc31..2ecab8c 100644
--- a/src/main/java/freemarker/core/DollarVariable.java
+++ b/src/main/java/freemarker/core/DollarVariable.java
@@ -23,7 +23,6 @@ import java.io.IOException;
import java.io.Writer;
import freemarker.template.TemplateException;
-import freemarker.template.TemplateModel;
import freemarker.template.utility.StringUtil;
/**
@@ -57,10 +56,8 @@ final class DollarVariable extends Interpolation {
*/
@Override
void accept(Environment env) throws TemplateException, IOException {
- final TemplateModel tm = escapedExpression.eval(env);
+ final Object moOrStr = calculateInterpolatedStringOrMarkup(env);
final Writer out = env.getOut();
- final Object moOrStr = EvalUtil.coerceModelToStringOrMarkup(
- tm, escapedExpression, null, env);
if (moOrStr instanceof String) {
final String s = (String) moOrStr;
if (autoEscape) {
@@ -94,6 +91,11 @@ final class DollarVariable extends Interpolation {
}
@Override
+ protected Object calculateInterpolatedStringOrMarkup(Environment env) throws TemplateException {
+ return EvalUtil.coerceModelToStringOrMarkup(escapedExpression.eval(env), escapedExpression, null, env);
+ }
+
+ @Override
protected String dump(boolean canonical, boolean inStringLiteral) {
StringBuilder sb = new StringBuilder();
sb.append("${");
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/main/java/freemarker/core/EvalUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/EvalUtil.java b/src/main/java/freemarker/core/EvalUtil.java
index ca21ac5..886308c 100644
--- a/src/main/java/freemarker/core/EvalUtil.java
+++ b/src/main/java/freemarker/core/EvalUtil.java
@@ -528,10 +528,36 @@ class EvalUtil {
throw new NullPointerException("TemplateValueFormatter result can't be null");
}
+ static TemplateMarkupOutputModel concatMarkupOutputs(TemplateObject parent, TemplateMarkupOutputModel leftMO,
+ TemplateMarkupOutputModel rightMO) throws TemplateException {
+ MarkupOutputFormat leftOF = leftMO.getOutputFormat();
+ MarkupOutputFormat rightOF = rightMO.getOutputFormat();
+ if (rightOF != leftOF) {
+ String rightPT;
+ String leftPT;
+ if ((rightPT = rightOF.getSourcePlainText(rightMO)) != null) {
+ return leftOF.concat(leftMO, leftOF.fromPlainTextByEscaping(rightPT));
+ } else if ((leftPT = leftOF.getSourcePlainText(leftMO)) != null) {
+ return rightOF.concat(rightOF.fromPlainTextByEscaping(leftPT), rightMO);
+ } else {
+ Object[] message = { "Concatenation left hand operand is in ", new _DelayedToString(leftOF),
+ " format, while the right hand operand is in ", new _DelayedToString(rightOF),
+ ". Conversion to common format wasn't possible." };
+ if (parent instanceof Expression) {
+ throw new _MiscTemplateException((Expression) parent, message);
+ } else {
+ throw new _MiscTemplateException(message);
+ }
+ }
+ } else {
+ return leftOF.concat(leftMO, rightMO);
+ }
+ }
+
/**
* Returns an {@link ArithmeticEngine} even if {@code env} is {@code null}, because we are in parsing phase.
*/
- public static ArithmeticEngine getArithmeticEngine(Environment env, TemplateObject tObj) {
+ static ArithmeticEngine getArithmeticEngine(Environment env, TemplateObject tObj) {
return env != null
? env.getArithmeticEngine()
: tObj.getTemplate().getParserConfiguration().getArithmeticEngine();
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/main/java/freemarker/core/Interpolation.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Interpolation.java b/src/main/java/freemarker/core/Interpolation.java
index 733fafa..2dcb11a 100644
--- a/src/main/java/freemarker/core/Interpolation.java
+++ b/src/main/java/freemarker/core/Interpolation.java
@@ -18,6 +18,8 @@
*/
package freemarker.core;
+import freemarker.template.TemplateException;
+
abstract class Interpolation extends TemplateElement {
protected abstract String dump(boolean canonical, boolean inStringLiteral);
@@ -31,4 +33,11 @@ abstract class Interpolation extends TemplateElement {
return dump(true, true);
}
+ /**
+ * Returns the already type-converted value that this interpolation will insert into the output.
+ *
+ * @return A {@link String} or {@link TemplateMarkupOutputModel}. Not {@code null}.
+ */
+ protected abstract Object calculateInterpolatedStringOrMarkup(Environment env) throws TemplateException;
+
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/main/java/freemarker/core/NumericalOutput.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/NumericalOutput.java b/src/main/java/freemarker/core/NumericalOutput.java
index 0ded83f..c9028c6 100644
--- a/src/main/java/freemarker/core/NumericalOutput.java
+++ b/src/main/java/freemarker/core/NumericalOutput.java
@@ -60,6 +60,17 @@ final class NumericalOutput extends Interpolation {
@Override
void accept(Environment env) throws TemplateException, IOException {
+ String s = calculateInterpolatedStringOrMarkup(env);
+ Writer out = env.getOut();
+ if (autoEscapeOutputFormat != null) {
+ autoEscapeOutputFormat.output(s, out);
+ } else {
+ out.write(s);
+ }
+ }
+
+ @Override
+ protected String calculateInterpolatedStringOrMarkup(Environment env) throws TemplateException {
Number num = expression.evalToNumber(env);
FormatHolder fmth = formatCache; // atomic sampling
@@ -85,12 +96,7 @@ final class NumericalOutput extends Interpolation {
// Some locales may use non-Arabic digits, thus replacing the
// decimal separator in the result of toString() is not enough.
String s = fmth.format.format(num);
- Writer out = env.getOut();
- if (autoEscapeOutputFormat != null) {
- autoEscapeOutputFormat.output(s, out);
- } else {
- out.write(s);
- }
+ return s;
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/main/java/freemarker/core/ParameterRole.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/ParameterRole.java b/src/main/java/freemarker/core/ParameterRole.java
index d178881..1293d5c 100644
--- a/src/main/java/freemarker/core/ParameterRole.java
+++ b/src/main/java/freemarker/core/ParameterRole.java
@@ -62,6 +62,7 @@ final class ParameterRole {
static final ParameterRole ARGUMENT_VALUE = new ParameterRole("argument value");
static final ParameterRole CONTENT = new ParameterRole("content");
static final ParameterRole EMBEDDED_TEMPLATE = new ParameterRole("embedded template");
+ static final ParameterRole VALUE_PART = new ParameterRole("value part");
static final ParameterRole MINIMUM_DECIMALS = new ParameterRole("minimum decimals");
static final ParameterRole MAXIMUM_DECIMALS = new ParameterRole("maximum decimals");
static final ParameterRole NODE = new ParameterRole("node");
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/main/java/freemarker/core/StringLiteral.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/StringLiteral.java b/src/main/java/freemarker/core/StringLiteral.java
index 051c0aa..aa32d3e 100644
--- a/src/main/java/freemarker/core/StringLiteral.java
+++ b/src/main/java/freemarker/core/StringLiteral.java
@@ -19,14 +19,12 @@
package freemarker.core;
-import java.io.IOException;
import java.io.StringReader;
-import java.util.Enumeration;
+import java.util.List;
import freemarker.template.SimpleScalar;
import freemarker.template.Template;
import freemarker.template.TemplateException;
-import freemarker.template.TemplateExceptionHandler;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateScalarModel;
import freemarker.template.utility.StringUtil;
@@ -34,7 +32,9 @@ import freemarker.template.utility.StringUtil;
final class StringLiteral extends Expression implements TemplateScalarModel {
private final String value;
- private TemplateElement dynamicValue;
+
+ /** {@link List} of {@link String}-s and {@link Interpolation}-s. */
+ private List<Object> dynamicValue;
StringLiteral(String value) {
this.value = value;
@@ -45,8 +45,9 @@ final class StringLiteral extends Expression implements TemplateScalarModel {
* The token source of the template that contains this string literal. As of this writing, we only need
* this to share the {@code namingConvetion} with that.
*/
- // TODO This should be the part of the "parent" parsing; now it contains hacks like those with namingConvention.
- void parseValue(FMParserTokenManager parentTkMan) throws ParseException {
+ void parseValue(FMParserTokenManager parentTkMan, OutputFormat outputFormat) throws ParseException {
+ // The way this work is incorrect (the literal should be parsed without un-escaping),
+ // but we can't fix this backward compatibly.
if (value.length() > 3 && (value.indexOf("${") >= 0 || value.indexOf("#{") >= 0)) {
Template parentTemplate = getTemplate();
@@ -60,9 +61,9 @@ final class StringLiteral extends Expression implements TemplateScalarModel {
FMParser parser = new FMParser(parentTemplate, false, tkMan, parentTemplate.getParserConfiguration());
// We continue from the parent parser's current state:
- parser.setupStringLiteralMode(parentTkMan);
+ parser.setupStringLiteralMode(parentTkMan, outputFormat);
try {
- dynamicValue = parser.FreeMarkerText();
+ dynamicValue = parser.StaticTextAndInterpolations();
} finally {
// The parent parser continues from this parser's current state:
parser.tearDownStringLiteralMode(parentTkMan);
@@ -77,7 +78,51 @@ final class StringLiteral extends Expression implements TemplateScalarModel {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
- return new SimpleScalar(evalAndCoerceToPlainText(env));
+ if (dynamicValue == null) {
+ return new SimpleScalar(value);
+ } else {
+ // This should behave like concatenating the values with `+`. Thus, an interpolated expression that
+ // returns markup promotes the result of the whole expression to markup.
+
+ // Exactly one of these is non-null, depending on if the result will be plain text or markup, which can
+ // change during evaluation, depending on the result of the interpolations:
+ StringBuilder plainTextResult = null;
+ TemplateMarkupOutputModel<?> markupResult = null;
+
+ for (Object part : dynamicValue) {
+ Object calcedPart =
+ part instanceof String ? part
+ : ((Interpolation) part).calculateInterpolatedStringOrMarkup(env);
+ if (markupResult != null) {
+ TemplateMarkupOutputModel<?> partMO = calcedPart instanceof String
+ ? markupResult.getOutputFormat().fromPlainTextByEscaping((String) calcedPart)
+ : (TemplateMarkupOutputModel<?>) calcedPart;
+ markupResult = EvalUtil.concatMarkupOutputs(this, markupResult, partMO);
+ } else { // We are using `plainTextOutput` (or nothing yet)
+ if (calcedPart instanceof String) {
+ String partStr = (String) calcedPart;
+ if (plainTextResult == null) {
+ plainTextResult = new StringBuilder(partStr);
+ } else {
+ plainTextResult.append(partStr);
+ }
+ } else { // `calcedPart` is TemplateMarkupOutputModel
+ TemplateMarkupOutputModel<?> moPart = (TemplateMarkupOutputModel<?>) calcedPart;
+ if (plainTextResult != null) {
+ TemplateMarkupOutputModel<?> leftHandMO = moPart.getOutputFormat()
+ .fromPlainTextByEscaping(plainTextResult.toString());
+ markupResult = EvalUtil.concatMarkupOutputs(this, leftHandMO, moPart);
+ plainTextResult = null;
+ } else {
+ markupResult = moPart;
+ }
+ }
+ }
+ } // for each part
+ return markupResult != null ? markupResult
+ : plainTextResult != null ? new SimpleScalar(plainTextResult.toString())
+ : SimpleScalar.EMPTY_STRING;
+ }
}
public String getAsString() {
@@ -88,40 +133,22 @@ final class StringLiteral extends Expression implements TemplateScalarModel {
* Tells if this is something like <tt>"${foo}"</tt>, which is usually a user mistake.
*/
boolean isSingleInterpolationLiteral() {
- return dynamicValue != null && dynamicValue.getChildCount() == 1
- && dynamicValue.getChildAt(0) instanceof DollarVariable;
+ return dynamicValue != null && dynamicValue.size() == 1
+ && dynamicValue.get(0) instanceof Interpolation;
}
@Override
- String evalAndCoerceToPlainText(Environment env) throws TemplateException {
- if (dynamicValue == null) {
- return value;
- } else {
- TemplateExceptionHandler teh = env.getTemplateExceptionHandler();
- env.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
- try {
- return env.renderElementToString(dynamicValue);
- } catch (IOException ioe) {
- throw new _MiscTemplateException(ioe, env);
- } finally {
- env.setTemplateExceptionHandler(teh);
- }
- }
- }
-
- @Override
public String getCanonicalForm() {
if (dynamicValue == null) {
return StringUtil.ftlQuote(value);
} else {
StringBuilder sb = new StringBuilder();
sb.append('"');
- for (Enumeration childrenEnum = dynamicValue.children(); childrenEnum.hasMoreElements(); ) {
- TemplateElement child = (TemplateElement) childrenEnum.nextElement();
+ for (Object child : dynamicValue) {
if (child instanceof Interpolation) {
sb.append(((Interpolation) child).getCanonicalFormInStringLiteral());
} else {
- sb.append(StringUtil.FTLStringLiteralEnc(child.getCanonicalForm(), '"'));
+ sb.append(StringUtil.FTLStringLiteralEnc((String) child, '"'));
}
}
sb.append('"');
@@ -150,19 +177,25 @@ final class StringLiteral extends Expression implements TemplateScalarModel {
@Override
int getParameterCount() {
- return 1;
+ return dynamicValue == null ? 0 : dynamicValue.size();
}
@Override
Object getParameterValue(int idx) {
- if (idx != 0) throw new IndexOutOfBoundsException();
- return dynamicValue;
+ checkIndex(idx);
+ return dynamicValue.get(idx);
+ }
+
+ private void checkIndex(int idx) {
+ if (dynamicValue == null || idx >= dynamicValue.size()) {
+ throw new IndexOutOfBoundsException();
+ }
}
@Override
ParameterRole getParameterRole(int idx) {
- if (idx != 0) throw new IndexOutOfBoundsException();
- return ParameterRole.EMBEDDED_TEMPLATE;
+ checkIndex(idx);
+ return ParameterRole.VALUE_PART;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/src/main/javacc/FTL.jj b/src/main/javacc/FTL.jj
index 9c6bae1..34f737b 100644
--- a/src/main/javacc/FTL.jj
+++ b/src/main/javacc/FTL.jj
@@ -254,13 +254,13 @@ public class FMParser {
}
}
- void setupStringLiteralMode(FMParserTokenManager parentTokenSource) {
+ void setupStringLiteralMode(FMParserTokenManager parentTokenSource, OutputFormat outputFormat) {
token_source.initialNamingConvention = parentTokenSource.initialNamingConvention;
token_source.namingConvention = parentTokenSource.namingConvention;
token_source.namingConventionEstabilisher = parentTokenSource.namingConventionEstabilisher;
token_source.SwitchTo(NODIRECTIVE);
- outputFormat = PlainTextOutputFormat.INSTANCE;
+ this.outputFormat = outputFormat;
recalculateAutoEscapingField();
if (incompatibleImprovements < _TemplateAPI.VERSION_INT_2_3_24) {
// Emulate bug, where the string literal parser haven't inherited the IcI:
@@ -2271,7 +2271,7 @@ StringLiteral StringLiteral(boolean interpolate) :
result.setLocation(template, t, t);
if (interpolate && !raw) {
// TODO: This logic is broken. It can't handle literals that contains both ${...} and $\{...}.
- if (t.image.indexOf("${") >= 0 || t.image.indexOf("#{") >= 0) result.parseValue(token_source);
+ if (t.image.indexOf("${") >= 0 || t.image.indexOf("#{") >= 0) result.parseValue(token_source, outputFormat);
}
return result;
}
@@ -4205,6 +4205,66 @@ Map ParamList() :
}
/**
+ * Parses the already un-escaped content of a string literal (input must not include the quotation marks).
+ *
+ * @return A {@link List} of {@link String}-s and {@link Interpolation}-s.
+ */
+List<Object> StaticTextAndInterpolations() :
+{
+ Token t;
+ Interpolation interpolation;
+ StringBuilder staticTextCollector = null;
+ ArrayList<Object> parts = new ArrayList<Object>();
+}
+{
+ (
+ (
+ t = <STATIC_TEXT_WS>
+ |
+ t = <STATIC_TEXT_NON_WS>
+ |
+ t = <STATIC_TEXT_FALSE_ALARM>
+ )
+ {
+ String s = t.image;
+ if (!s.isEmpty()) {
+ if (staticTextCollector == null) {
+ staticTextCollector = new StringBuilder(t.image);
+ } else {
+ staticTextCollector.append(t.image);
+ }
+ }
+ }
+ |
+ (
+ LOOKAHEAD(<DOLLAR_INTERPOLATION_OPENING>)
+ (
+ interpolation = StringOutput()
+ )
+ |
+ LOOKAHEAD(<HASH_INTERPOLATION_OPENING>)
+ (
+ interpolation = NumericalOutput()
+ )
+ )
+ {
+ if (staticTextCollector != null) {
+ parts.add(staticTextCollector.toString());
+ staticTextCollector.setLength(0);
+ }
+ parts.add(interpolation);
+ }
+ )*
+ {
+ if (staticTextCollector != null && staticTextCollector.length() != 0) {
+ parts.add(staticTextCollector.toString());
+ }
+ parts.trimToSize();
+ return parts;
+ }
+}
+
+/**
* Root production to be used when parsing
* an entire file.
*/
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/manual/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/book.xml b/src/manual/book.xml
index 6decbd9..3546923 100644
--- a/src/manual/book.xml
+++ b/src/manual/book.xml
@@ -2798,14 +2798,7 @@ baz
literals <link linkend="dgui_template_valueinsertion">behaves
similarly as in <phrase role="markedText">text</phrase>
sections</link> (so it goes through the same <emphasis>locale
- sensitive</emphasis> number and date/time formatting), except that
- no automatic escaping will happen (the deprecated <link
- linkend="ref.directive.escape">escaping by
- <literal>escape</literal> directive</link> has no effect there,
- nor <link linkend="dgui_misc_autoescaping">auto-escaping</link>
- happens as <link
- linkend="dgui_misc_autoescaping_stringliteral">explained
- here</link>).</para>
+ sensitive</emphasis> number and date/time formatting).</para>
<para>Example (assume that user is <quote>Big Joe</quote>):</para>
@@ -2865,17 +2858,6 @@ ${s} <#-- Just to see what the value of s is -->
something like <literal>"someUrl?id=1234"</literal>, regardless
of locale and format settings.</para>
</warning>
-
- <para>Note for advanced users that <literal>+</literal> is more
- flexible than <literal>${<replaceable>...</replaceable>}</literal>
- when it comes to handling <link
- linkend="dgui_misc_autoescaping_movalues">markup output
- values</link>, because <link
- linkend="dgui_misc_autoescaping_concatenation"><literal>+</literal>
- can have markup output result</link>, while <link
- linkend="dgui_misc_autoescaping_stringliteral">string literal
- interpolation can't</link>, as the literal must yield a
- string.</para>
</section>
<section xml:id="dgui_template_exp_get_character">
@@ -5690,51 +5672,6 @@ XML: <p>Test</p>
RTF: \par Test</programlisting>
</section>
- <section xml:id="dgui_misc_autoescaping_stringliteral">
- <title>Auto-escaping and ${...} inside string literals</title>
-
- <para>Inside string literals (quoted text expressions), the <link
- linkend="dgui_misc_autoescaping_outputformat">output format</link>
- is <literal>plainText</literal>. Thus no auto-escaping will occur
- there:</para>
-
- <programlisting role="template"><#-- We assume that we have "HTML" output format by default. -->
-<#assign s = "Foo & bar">
-${s}
-${"${s} & baz"}</programlisting>
-
- <programlisting role="output">Foo &amp; bar
-Foo &amp; bar &amp; baz</programlisting>
-
- <para>Above, because inside the string literal
- <literal>${s}</literal> did no auto-escaping, when we print the
- whole string we don't end up with double-escaping.</para>
-
- <para>The <literal>plainText</literal> output format only allows
- the inserting of markup output values that were created by
- escaping plain text
- (<literal><replaceable>plainText</replaceable>?esc</literal>), as
- those are trivially convertible back to plain text. Thus only such
- markup output values can be insert into string literals with
- <literal>${<replaceable>...</replaceable>}</literal> (the
- <literal>+</literal> operator is more flexible, but see that
- later):</para>
-
- <programlisting role="template"><#-- We assume that we have "HTML" output format by default. -->
-
-<#-- Markup output value created by escaping plain text: -->
-<#assign mo1 = "Foo & bar"?esc>
-
-<#-- Markup output value created outherwise: -->
-<#assign mo2 = "<p>Foo"?no_esc>
-
-${"${mo1} baz"}
-<#attempt>${"${mo2} baz"}<#recover>Failed</#attempt></programlisting>
-
- <programlisting role="output">Foo &amp; bar baz
-Failed</programlisting>
- </section>
-
<section xml:id="dgui_misc_autoescaping_concatenation">
<title>Markup output values and the <quote>+</quote>
operator</title>
@@ -5764,6 +5701,32 @@ ${"<h1>"?no_esc + "Foo & bar" + "</h1>"?no_esc}</programlisting>
of conversions here</link>.)</para>
</section>
+ <section xml:id="dgui_misc_autoescaping_stringliteral">
+ <title>Auto-escaping and ${...} inside string literals</title>
+
+ <para>A string <emphasis>expression</emphasis> like
+ <literal>"Hello ${name}!"</literal> is just a shorthand for
+ <literal>"Hello" + name + "!"</literal>, so that
+ <literal>${<replaceable>...</replaceable>}</literal> doesn't
+ auto-escape.</para>
+
+ <programlisting role="template"><#-- We assume that we have "HTML" output format by default. -->
+<#assign name = "Foo & Bar">
+
+<#assign s = "<p>Hello ${name}!">
+${s}
+<p>Hello ${name}!
+
+To prove that s didn't contain the value escaped:
+${s?replace('&'), 'and'}</programlisting>
+
+ <programlisting role="output">&lt;p&gt;Hello Foo &amp; Bar!
+<p>Hello Foo &amp; Bar!
+
+To prove that s didn't contain the value escaped:
+&lt;p&gt;Hello Foo and Bar!</programlisting>
+ </section>
+
<section>
<title>Combined output formats</title>
@@ -26158,6 +26121,18 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
which can't be loaded due to zip format errors in the error
message.</para>
</listitem>
+
+ <listitem>
+ <para>The non-public AST API of
+ <literal>freemarker.core.StringLiteral</literal>-s has been
+ changed. In principle it doesn't mater as it isn't a public API,
+ but some might used these regardless to introspect templates.
+ Earlier it had an <quote>embedded template</quote> parameter
+ inside, now it has 0 (for purely static string literals), one or
+ multiple <quote>value part</quote>-ts, which are
+ <literal>String</literal>-s and
+ <literal>Interpolation</literal>-s.</para>
+ </listitem>
</itemizedlist>
</section>
@@ -26267,6 +26242,17 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
</listitem>
<listitem>
+ <para><literal>${<replaceable>...</replaceable>}</literal>
+ inside string literals is equivalent to using the
+ <literal>+</literal> operator again. This rule was broken by
+ <literal>+</literal> supporting markup operands, while
+ <literal>${<replaceable>...</replaceable>}</literal> inside
+ string literals didn't. Now similarly as <literal>"foo " +
+ someMarkup</literal> works and gives a markup result,
+ <literal>"foo ${someMarkup}"</literal> does too.</para>
+ </listitem>
+
+ <listitem>
<para>Added <literal>XHTMLOutputFormat</literal> and
<literal>TemplateXHTMLOutputModel</literal>.</para>
</listitem>
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/test/java/freemarker/core/NumberFormatTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/NumberFormatTest.java b/src/test/java/freemarker/core/NumberFormatTest.java
index ddfbf28..19d2cba 100644
--- a/src/test/java/freemarker/core/NumberFormatTest.java
+++ b/src/test/java/freemarker/core/NumberFormatTest.java
@@ -297,9 +297,7 @@ public class NumberFormatTest extends TemplateTest {
assertOutput("<#escape x as x?html>" + commonFTL + "</#escape>", commonOutput);
assertOutput("<#escape x as x?xhtml>" + commonFTL + "</#escape>", commonOutput);
assertOutput("<#escape x as x?xml>" + commonFTL + "</#escape>", commonOutput);
- // TODO: Should give markup, but currently does interpolation in plain text:
- // assertOutput("${\"" + commonFTL + "\"}",
- // "1.23*10<sup>6</sup> cat:1.23*10<sup>6</sup> 1.23*10<sup>-5</sup>");
+ assertOutput("${\"" + commonFTL + "\"}", "1.23*10<sup>6</sup> cat:1.23*10<sup>6</sup> 1.23*10<sup>-5</sup>");
assertErrorContains("<#ftl outputFormat='plainText'>" + commonFTL, "HTML", "plainText", "conversion");
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/test/java/freemarker/core/OutputFormatTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/OutputFormatTest.java b/src/test/java/freemarker/core/OutputFormatTest.java
index f7614c3..b36761e 100644
--- a/src/test/java/freemarker/core/OutputFormatTest.java
+++ b/src/test/java/freemarker/core/OutputFormatTest.java
@@ -436,12 +436,21 @@ public class OutputFormatTest extends TemplateTest {
}
@Test
- public void testStringLiteralTemplateModificationBug() throws IOException, TemplateException {
+ public void testStringLiteralInterpolation() throws IOException, TemplateException {
Template t = new Template(null, "<#ftl outputFormat='XML'>${'&'} ${\"(${'&'})\"?noEsc}", getConfiguration());
assertEquals(XMLOutputFormat.INSTANCE, t.getOutputFormat());
- assertOutput("${.outputFormat} ${'${.outputFormat}'} ${.outputFormat}", "undefined plainText undefined");
- assertOutput("${'foo ${xmlPlain}'}", "foo a < {x'}");
- assertErrorContains("${'${xmlMarkup}'}", "plainText", "XML", "conversion");
+
+ assertOutput("${.outputFormat} ${'${.outputFormat}'} ${.outputFormat}",
+ "undefined undefined undefined");
+ assertOutput("<#ftl outputFormat='HTML'>${.outputFormat} ${'${.outputFormat}'} ${.outputFormat}",
+ "HTML HTML HTML");
+ assertOutput("${.outputFormat} <#outputFormat 'XML'>${'${.outputFormat}'}</#outputFormat> ${.outputFormat}",
+ "undefined XML undefined");
+ assertOutput("${'foo ${xmlPlain}'}", "foo a < {x'}");
+ assertOutput("${'${xmlMarkup}'}", "<p>c</p>");
+ assertErrorContains("${'${\"x\"?esc}'}", "?esc", "undefined");
+ assertOutput("<#ftl outputFormat='XML'>${'${xmlMarkup?esc} ${\"<\"?esc} ${\">\"} ${\"&\"?noEsc}'}",
+ "<p>c</p> < > &");
}
@Test
@@ -875,11 +884,13 @@ public class OutputFormatTest extends TemplateTest {
assertOutput(commonFTL, "x");
assertErrorContains("<#ftl outputFormat='HTML'>" + commonFTL,
"?" + biName, "HTML", "double-escaping");
+ assertErrorContains("<#ftl outputFormat='HTML'>${'${\"x\"?" + biName + "}'}",
+ "?" + biName, "HTML", "double-escaping");
assertOutput("<#ftl outputFormat='plainText'>" + commonFTL, "x");
assertOutput("<#ftl outputFormat='HTML' autoEsc=false>" + commonFTL, "x");
assertOutput("<#ftl outputFormat='HTML'><#noAutoEsc>" + commonFTL + "</#noAutoEsc>", "x");
- assertOutput("<#ftl outputFormat='HTML'><#outputFormat 'plainText'>" + commonFTL + "</#outputFormat>", "x");
- assertOutput("<#ftl outputFormat='HTML'>${'${\"x\"?" + biName + "}'}", "x");
+ assertOutput("<#ftl outputFormat='HTML'><#outputFormat 'plainText'>" + commonFTL + "</#outputFormat>",
+ "x");
}
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/test/java/freemarker/core/StringLiteralInterpolationTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/StringLiteralInterpolationTest.java b/src/test/java/freemarker/core/StringLiteralInterpolationTest.java
index fd6fb14..b7bfeee 100644
--- a/src/test/java/freemarker/core/StringLiteralInterpolationTest.java
+++ b/src/test/java/freemarker/core/StringLiteralInterpolationTest.java
@@ -19,6 +19,7 @@
package freemarker.core;
import java.io.IOException;
+import java.util.Collections;
import org.junit.Test;
@@ -36,6 +37,7 @@ public class StringLiteralInterpolationTest extends TemplateTest {
assertOutput("${'#{x}'}", "1");
assertOutput("${'a${x}b${x*2}c'}", "a1b2c");
assertOutput("${'a#{x}b#{x*2}c'}", "a1b2c");
+ assertOutput("${'a#{x; m2}'}", "a1.00");
assertOutput("${'${x} ${x}'}", "1 1");
assertOutput("${'$\\{x}'}", "${x}");
assertOutput("${'$\\{x} $\\{x}'}", "${x} ${x}");
@@ -73,6 +75,13 @@ public class StringLiteralInterpolationTest extends TemplateTest {
assertOutput("${'${1}'}", "1");
assertErrorContains("${'${ '}", "");
}
+
+ @Test
+ public void testErrors() {
+ addToDataModel("x", 1);
+ assertErrorContains("${'${noSuchVar}'}", InvalidReferenceException.class, "missing", "noSuchVar");
+ assertErrorContains("${'${x/0}'}", ArithmeticException.class, "zero");
+ }
@Test
public void escaping() throws IOException, TemplateException {
@@ -90,4 +99,18 @@ public class StringLiteralInterpolationTest extends TemplateTest {
assertOutput("${'&\\''?html} ${\"${'&\\\\\\''?html}\"}", "&' &'");
}
+ @Test
+ public void markup() throws IOException, TemplateException {
+ Configuration cfg = getConfiguration();
+ cfg.setCustomNumberFormats(Collections.singletonMap("G", PrintfGTemplateNumberFormatFactory.INSTANCE));
+ cfg.setNumberFormat("@G 3");
+
+ assertOutput("${\"${1000}\"}", "1.00*10<sup>3</sup>");
+ assertOutput("${\"&_${1000}\"}", "&_1.00*10<sup>3</sup>");
+ assertOutput("${\"${1000}_&\"}", "1.00*10<sup>3</sup>_&");
+ assertOutput("${\"${1000}, ${2000}\"}", "1.00*10<sup>3</sup>, 2.00*10<sup>3</sup>");
+ assertOutput("${\"& ${'x'}, ${2000}\"}", "& x, 2.00*10<sup>3</sup>");
+ assertOutput("${\"& ${'x'}, #{2000}\"}", "& x, 2000");
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/test/java/freemarker/test/TemplateTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/test/TemplateTest.java b/src/test/java/freemarker/test/TemplateTest.java
index 0b1e03d..6047e2e 100644
--- a/src/test/java/freemarker/test/TemplateTest.java
+++ b/src/test/java/freemarker/test/TemplateTest.java
@@ -229,11 +229,13 @@ public abstract class TemplateTest {
}
assertContainsAll(e.getEditorMessage(), expectedSubstrings);
return e;
- } catch (IOException e) {
+ } catch (Exception e) {
if (exceptionClass != null) {
assertThat(e, instanceOf(exceptionClass));
+ return e;
+ } else {
+ throw new RuntimeException("Unexpected exception class: " + e.getClass().getName(), e);
}
- throw new RuntimeException("Unexpected exception class: " + e.getClass().getName(), e);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/test/resources/freemarker/core/ast-1.ast
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/core/ast-1.ast b/src/test/resources/freemarker/core/ast-1.ast
index 6c5a753..9c3b8d1 100644
--- a/src/test/resources/freemarker/core/ast-1.ast
+++ b/src/test/resources/freemarker/core/ast-1.ast
@@ -64,19 +64,15 @@
- AST-node subtype: "1" // Integer
${...} // f.c.DollarVariable
- content: "static" // f.c.StringLiteral
- - embedded template: null // Null
${...} // f.c.DollarVariable
- content: dynamic "..." // f.c.StringLiteral
- - embedded template: #mixed_content // f.c.MixedContent
- #text // f.c.TextBlock
- - content: "x" // String
- ${...} // f.c.DollarVariable
- - content: * // f.c.ArithmeticExpression
- - left-hand operand: baaz // f.c.Identifier
- - right-hand operand: 10 // f.c.NumberLiteral
- - AST-node subtype: "1" // Integer
- #text // f.c.TextBlock
- - content: "y" // String
+ - value part: "x" // String
+ - value part: ${...} // f.c.DollarVariable
+ - content: * // f.c.ArithmeticExpression
+ - left-hand operand: baaz // f.c.Identifier
+ - right-hand operand: 10 // f.c.NumberLiteral
+ - AST-node subtype: "1" // Integer
+ - value part: "y" // String
#text // f.c.TextBlock
- content: "\n5 " // String
#switch // f.c.SwitchBlock
@@ -163,7 +159,6 @@
- content: "\n11 " // String
#outputformat // f.c.OutputFormatBlock
- value: "XML" // f.c.StringLiteral
- - embedded template: null // Null
#noautoesc // f.c.NoAutoEscBlock
${...} // f.c.DollarVariable
- content: a // f.c.Identifier
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/test/resources/freemarker/core/ast-builtins.ast
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/core/ast-builtins.ast b/src/test/resources/freemarker/core/ast-builtins.ast
index 164c790..9278d8b 100644
--- a/src/test/resources/freemarker/core/ast-builtins.ast
+++ b/src/test/resources/freemarker/core/ast-builtins.ast
@@ -20,7 +20,6 @@
- right-hand operand: "left_pad" // String
- argument value: 5 // f.c.NumberLiteral
- argument value: "-" // f.c.StringLiteral
- - embedded template: null // Null
#text // f.c.TextBlock
- content: "\n" // String
${...} // f.c.DollarVariable
@@ -28,9 +27,7 @@
- left-hand operand: x // f.c.Identifier
- right-hand operand: "then" // String
- argument value: "y" // f.c.StringLiteral
- - embedded template: null // Null
- argument value: "n" // f.c.StringLiteral
- - embedded template: null // Null
#text // f.c.TextBlock
- content: "\n" // String
${...} // f.c.DollarVariable
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/test/resources/freemarker/core/ast-range.ast
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/core/ast-range.ast b/src/test/resources/freemarker/core/ast-range.ast
index eb862f4..3ddae45 100644
--- a/src/test/resources/freemarker/core/ast-range.ast
+++ b/src/test/resources/freemarker/core/ast-range.ast
@@ -102,13 +102,11 @@
- left-hand operand: n // f.c.Identifier
- right-hand operand: "index_of" // String
- argument value: "x" // f.c.StringLiteral
- - embedded template: null // Null
- right-hand operand: ...(...) // f.c.MethodCall
- callee: ?index_of // f.c.BuiltInsForStringsBasic$index_ofBI
- left-hand operand: m // f.c.Identifier
- right-hand operand: "index_of" // String
- argument value: "y" // f.c.StringLiteral
- - embedded template: null // Null
- variable scope: "1" // Integer
- namespace: null // Null
#assign // f.c.Assignment
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/test/resources/freemarker/core/ast-strlitinterpolation.ast
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/core/ast-strlitinterpolation.ast b/src/test/resources/freemarker/core/ast-strlitinterpolation.ast
index f8f57bd..c55a320 100644
--- a/src/test/resources/freemarker/core/ast-strlitinterpolation.ast
+++ b/src/test/resources/freemarker/core/ast-strlitinterpolation.ast
@@ -1,10 +1,64 @@
-@ // f.c.UnifiedCall
- - callee: m // f.c.Identifier
- - argument name: "x" // String
- - argument value: dynamic "..." // f.c.StringLiteral
- - embedded template: #mixed_content // f.c.MixedContent
- ${...} // f.c.DollarVariable
+#mixed_content // f.c.MixedContent
+ #text // f.c.TextBlock
+ - content: "1. " // String
+ @ // f.c.UnifiedCall
+ - callee: m // f.c.Identifier
+ - argument name: "x" // String
+ - argument value: dynamic "..." // f.c.StringLiteral
+ - value part: ${...} // f.c.DollarVariable
- content: e1 // f.c.Identifier
- - argument name: "y" // String
- - argument value: "$\\{e2}" // f.c.StringLiteral
- - embedded template: null // Null
\ No newline at end of file
+ - argument name: "y" // String
+ - argument value: "$\\{e2}" // f.c.StringLiteral
+ #text // f.c.TextBlock
+ - content: "\n2. " // String
+ ${...} // f.c.DollarVariable
+ - content: dynamic "..." // f.c.StringLiteral
+ - value part: "a" // String
+ - value part: ${...} // f.c.DollarVariable
+ - content: x // f.c.Identifier
+ - value part: "b" // String
+ - value part: ${...} // f.c.DollarVariable
+ - content: x // f.c.Identifier
+ - value part: "c" // String
+ #text // f.c.TextBlock
+ - content: "\n3. " // String
+ ${...} // f.c.DollarVariable
+ - content: dynamic "..." // f.c.StringLiteral
+ - value part: ${...} // f.c.DollarVariable
+ - content: x // f.c.Identifier
+ - value part: "b" // String
+ #text // f.c.TextBlock
+ - content: "\n4. " // String
+ ${...} // f.c.DollarVariable
+ - content: dynamic "..." // f.c.StringLiteral
+ - value part: "a" // String
+ - value part: ${...} // f.c.DollarVariable
+ - content: x // f.c.Identifier
+ #text // f.c.TextBlock
+ - content: "\n5. " // String
+ ${...} // f.c.DollarVariable
+ - content: dynamic "..." // f.c.StringLiteral
+ - value part: ${...} // f.c.DollarVariable
+ - content: x // f.c.Identifier
+ - value part: #{...} // f.c.NumericalOutput
+ - content: y // f.c.Identifier
+ - minimum decimals: "0" // Integer
+ - maximum decimals: "0" // Integer
+ #text // f.c.TextBlock
+ - content: "\n6. " // String
+ ${...} // f.c.DollarVariable
+ - content: dynamic "..." // f.c.StringLiteral
+ - value part: "a b " // String
+ - value part: ${...} // f.c.DollarVariable
+ - content: x // f.c.Identifier
+ - value part: " c d" // String
+ #text // f.c.TextBlock
+ - content: "\n7. " // String
+ ${...} // f.c.DollarVariable
+ - content: dynamic "..." // f.c.StringLiteral
+ - value part: ${...} // f.c.DollarVariable
+ - content: x // f.c.Identifier
+ - value part: " a b " // String
+ - value part: ${...} // f.c.DollarVariable
+ - content: y // f.c.Identifier
+ - value part: " c$d" // String
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/test/resources/freemarker/core/ast-strlitinterpolation.ftl
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/core/ast-strlitinterpolation.ftl b/src/test/resources/freemarker/core/ast-strlitinterpolation.ftl
index efaced8..f67ba55 100644
--- a/src/test/resources/freemarker/core/ast-strlitinterpolation.ftl
+++ b/src/test/resources/freemarker/core/ast-strlitinterpolation.ftl
@@ -1 +1,7 @@
-<@m x='${e1}' y='$\\{e2}' />
\ No newline at end of file
+1. <@m x='${e1}' y='$\\{e2}' />
+2. ${'a${x}b${x}c'}
+3. ${'${x}b'}
+4. ${'a${x}'}
+5. ${'${x}#{y}'}
+6. ${'a b ${x} c d'}
+7. ${'${x} a b ${y} c$d'}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/test/resources/freemarker/manual/AutoEscapingExample-stringLiteral2.ftlh
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/AutoEscapingExample-stringLiteral2.ftlh b/src/test/resources/freemarker/manual/AutoEscapingExample-stringLiteral2.ftlh
index 6d951a6..060b000 100644
--- a/src/test/resources/freemarker/manual/AutoEscapingExample-stringLiteral2.ftlh
+++ b/src/test/resources/freemarker/manual/AutoEscapingExample-stringLiteral2.ftlh
@@ -6,4 +6,4 @@
<#assign mo2 = "<p>Foo"?no_esc>
${"${mo1} baz"}
-<#attempt>${"${mo2} baz"}<#recover>Failed</#attempt>
\ No newline at end of file
+${"${mo2} baz"}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fa6ac0ee/src/test/resources/freemarker/manual/AutoEscapingExample-stringLiteral2.ftlh.out
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/AutoEscapingExample-stringLiteral2.ftlh.out b/src/test/resources/freemarker/manual/AutoEscapingExample-stringLiteral2.ftlh.out
index bc280c6..b8e929b 100644
--- a/src/test/resources/freemarker/manual/AutoEscapingExample-stringLiteral2.ftlh.out
+++ b/src/test/resources/freemarker/manual/AutoEscapingExample-stringLiteral2.ftlh.out
@@ -1,3 +1,3 @@
Foo & bar baz
-Failed
\ No newline at end of file
+<p>Foo baz
\ No newline at end of file