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 2018/03/19 21:29:47 UTC
[01/12] incubator-freemarker git commit: Version history additions
Repository: incubator-freemarker
Updated Branches:
refs/heads/2.3 0c69b1983 -> edefaa2f6
Version history additions
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/12616107
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/12616107
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/12616107
Branch: refs/heads/2.3
Commit: 12616107209fd8d898c25efff8b7d5192a9f3c4e
Parents: 0c77097
Author: ddekany <dd...@apache.org>
Authored: Mon Mar 12 08:04:59 2018 +0100
Committer: ddekany <dd...@apache.org>
Committed: Mon Mar 12 08:04:59 2018 +0100
----------------------------------------------------------------------
src/manual/en_US/book.xml | 13 +++++++++++++
1 file changed, 13 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/12616107/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 4b89a2f..3bd2562 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -27531,6 +27531,14 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
</listitem>
<listitem>
+ <para>Avoided possible performance bottleneck when executing
+ templates on many threads, caused be that
+ <literal>java.beans.PropertyDescriptor.getReadMethod()</literal>
+ is synchronized (<link
+ xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-80">FREEMARKER-80</link>).</para>
+ </listitem>
+
+ <listitem>
<para>Added
<literal>TemplateModelUtils.getKeyValuePairIterator(TemplateHashModelEx)</literal>
static utility class, which can be used to get a
@@ -27550,6 +27558,11 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
it unwraps into a <literal>LinkedHashMap</literal> instead of
into a plain <literal>HashMap</literal>.</para>
</listitem>
+
+ <listitem>
+ <para><literal>freemarker.ext.beans.HashAdapter.size()</literal>
+ was overridden for better performance.</para>
+ </listitem>
</itemizedlist>
</section>
</section>
[04/12] incubator-freemarker git commit: Manual: Noting some common
pitfalls when using extended Java decimal format
Posted by dd...@apache.org.
Manual: Noting some common pitfalls when using extended Java decimal format
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/c630a41a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/c630a41a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/c630a41a
Branch: refs/heads/2.3
Commit: c630a41ab68059ba2e658aaea4281cd512b2f38e
Parents: 54bd25b
Author: ddekany <dd...@apache.org>
Authored: Tue Mar 13 09:48:39 2018 +0100
Committer: ddekany <dd...@apache.org>
Committed: Tue Mar 13 09:48:39 2018 +0100
----------------------------------------------------------------------
src/manual/en_US/book.xml | 15 +++++++++++++++
1 file changed, 15 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c630a41a/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 231b607..52f67af 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -15568,6 +15568,13 @@ German people write: 12.345.678,00</programlisting>
<primary>extended Java decimal format</primary>
</indexterm>
+ <note>
+ <para>You need at least FreeMarker 2.3.24 for these to work.
+ Before that, extended Java decimal format parts are just
+ silently ignored by
+ <literal>java.text.DecimalFormat</literal>.</para>
+ </note>
+
<para>FreeMarker extends the Java decimal format patterns with
extra options. These options are name-value pairs, specified after
two semicolons (<literal>;;</literal>) at the end of the format
@@ -15581,6 +15588,14 @@ Extended decimal format: ${10002.5?string[",000<emphasis>;; roundingMode=halfUp
<programlisting role="output">Standard decimal format: 10,002
Extended decimal format: 10<emphasis>_</emphasis>00<emphasis>3</emphasis></programlisting>
+ <warning>
+ <para>A very easy mistake to make is just using a single
+ semicolon instead of two. It won't even result in an error, as
+ <literal>java.text.DecimalFormat</literal> thinks you have just
+ specified some weird format for negative numbers. So remember to
+ use two semicolons.</para>
+ </warning>
+
<para>Above, in the extended decimal format, we have specified
half-up rounding mode and group separator <literal>"_"</literal>.
The table of all options follows (note that these are defined by
[05/12] incubator-freemarker git commit: Added new
ParserConfiguration setting, interpolation_syntax. It has 3 possible values:
Posted by dd...@apache.org.
Added new ParserConfiguration setting, interpolation_syntax. It has 3 possible values:
- legacy (the default): Interpolations look like ${...} or #{...}. Note that #{...} is deprecated for a long time now.
- dollar: Interpolations look like ${...}. With this syntax, #{...} will be just static text.
- square_bracket: Interpolations look like [=...]. With this syntax ${...} and #{...} will be just static text. So it's useful if you generate output in a format where those (typically ${...}) are already used, such as to generate JSP pages, or to generate FreeMarker templates that use the default syntax.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/ca1ecf78
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/ca1ecf78
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/ca1ecf78
Branch: refs/heads/2.3
Commit: ca1ecf78e7eebdb83c2b147d5fb9aadb1c25f93a
Parents: c630a41
Author: ddekany <dd...@apache.org>
Authored: Fri Mar 16 00:36:31 2018 +0100
Committer: ddekany <dd...@apache.org>
Committed: Fri Mar 16 00:36:31 2018 +0100
----------------------------------------------------------------------
src/main/java/freemarker/core/Configurable.java | 7 +-
.../LegacyConstructorParserConfiguration.java | 9 +-
.../freemarker/core/ParserConfiguration.java | 7 +
.../java/freemarker/core/StringLiteral.java | 14 +-
.../freemarker/core/TemplateConfiguration.java | 27 ++
..._ParserConfigurationWithInheritedFormat.java | 4 +
.../java/freemarker/template/Configuration.java | 59 +++-
.../java/freemarker/template/_TemplateAPI.java | 10 +
src/main/javacc/FTL.jj | 116 +++++---
src/manual/en_US/book.xml | 276 ++++++++++++++-----
.../core/InterpolationSyntaxTest.java | 88 ++++++
.../core/TemplateConfigurationTest.java | 9 +
.../freemarker/template/ConfigurationTest.java | 28 ++
13 files changed, 542 insertions(+), 112 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ca1ecf78/src/main/java/freemarker/core/Configurable.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Configurable.java b/src/main/java/freemarker/core/Configurable.java
index f1a905b..a2b5d29 100644
--- a/src/main/java/freemarker/core/Configurable.java
+++ b/src/main/java/freemarker/core/Configurable.java
@@ -2365,7 +2365,12 @@ public class Configurable {
* <li><p>{@code "tag_syntax"}:
* See {@link Configuration#setTagSyntax(int)}.
* <br>String value: Must be one of
- * {@code "auto_detect"}, {@code "angle_bracket"}, and {@code "square_bracket"}.
+ * {@code "auto_detect"}, {@code "angle_bracket"}, and {@code "square_bracket"}.
+ *
+ * <li><p>{@code "interpolation_syntax"} (since 2.3.28):
+ * See {@link Configuration#setInterpolationSyntax(int)}.
+ * <br>String value: Must be one of
+ * {@code "legacy"}, {@code "dollar"}, and {@code "square_bracket"}.
*
* <li><p>{@code "naming_convention"}:
* See {@link Configuration#setNamingConvention(int)}.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ca1ecf78/src/main/java/freemarker/core/LegacyConstructorParserConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/LegacyConstructorParserConfiguration.java b/src/main/java/freemarker/core/LegacyConstructorParserConfiguration.java
index 479af9a..a9a1e8e 100644
--- a/src/main/java/freemarker/core/LegacyConstructorParserConfiguration.java
+++ b/src/main/java/freemarker/core/LegacyConstructorParserConfiguration.java
@@ -28,6 +28,7 @@ import freemarker.template.Version;
class LegacyConstructorParserConfiguration implements ParserConfiguration {
private final int tagSyntax;
+ private final int interpolationSyntax;
private final int namingConvention;
private final boolean whitespaceStripping;
private final boolean strictSyntaxMode;
@@ -38,11 +39,13 @@ class LegacyConstructorParserConfiguration implements ParserConfiguration {
private Integer tabSize;
private final Version incompatibleImprovements;
- public LegacyConstructorParserConfiguration(boolean strictSyntaxMode, boolean whitespaceStripping, int tagSyntax,
+ LegacyConstructorParserConfiguration(boolean strictSyntaxMode, boolean whitespaceStripping,
+ int tagSyntax, int interpolationSyntax,
int namingConvention, Integer autoEscaping, OutputFormat outputFormat,
Boolean recognizeStandardFileExtensions, Integer tabSize,
Version incompatibleImprovements, ArithmeticEngine arithmeticEngine) {
this.tagSyntax = tagSyntax;
+ this.interpolationSyntax = interpolationSyntax;
this.namingConvention = namingConvention;
this.whitespaceStripping = whitespaceStripping;
this.strictSyntaxMode = strictSyntaxMode;
@@ -57,6 +60,10 @@ class LegacyConstructorParserConfiguration implements ParserConfiguration {
public int getTagSyntax() {
return tagSyntax;
}
+
+ public int getInterpolationSyntax() {
+ return interpolationSyntax;
+ }
public int getNamingConvention() {
return namingConvention;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ca1ecf78/src/main/java/freemarker/core/ParserConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/ParserConfiguration.java b/src/main/java/freemarker/core/ParserConfiguration.java
index 0f097c9..8c125da 100644
--- a/src/main/java/freemarker/core/ParserConfiguration.java
+++ b/src/main/java/freemarker/core/ParserConfiguration.java
@@ -36,6 +36,13 @@ public interface ParserConfiguration {
int getTagSyntax();
/**
+ * See {@link Configuration#getInterpolationSyntax()}.
+ *
+ * @since 2.3.28
+ */
+ int getInterpolationSyntax();
+
+ /**
* See {@link Configuration#getNamingConvention()}.
*/
int getNamingConvention();
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ca1ecf78/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 7c25623..6d53c17 100644
--- a/src/main/java/freemarker/core/StringLiteral.java
+++ b/src/main/java/freemarker/core/StringLiteral.java
@@ -22,6 +22,7 @@ package freemarker.core;
import java.io.StringReader;
import java.util.List;
+import freemarker.template.Configuration;
import freemarker.template.SimpleScalar;
import freemarker.template.Template;
import freemarker.template.TemplateException;
@@ -47,10 +48,15 @@ final class StringLiteral extends Expression implements TemplateScalarModel {
void parseValue(FMParser parentParser, OutputFormat outputFormat) throws ParseException {
// The way this works 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();
- ParserConfiguration pcfg = parentTemplate.getParserConfiguration();
-
+ Template parentTemplate = getTemplate();
+ ParserConfiguration pcfg = parentTemplate.getParserConfiguration();
+ int intSyn = pcfg.getInterpolationSyntax();
+ if (value.length() > 3 && (
+ (intSyn == Configuration.LEGACY_INTERPOLATION_SYNTAX
+ || intSyn == Configuration.DOLLAR_INTERPOLATION_SYNTAX)
+ && (value.indexOf("${") >= 0
+ || intSyn == Configuration.LEGACY_INTERPOLATION_SYNTAX && value.indexOf("#{") >= 0)
+ || intSyn == Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX && value.indexOf("[=") >= 0)) {
try {
SimpleCharStream simpleCharacterStream = new SimpleCharStream(
new StringReader(value),
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ca1ecf78/src/main/java/freemarker/core/TemplateConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/TemplateConfiguration.java b/src/main/java/freemarker/core/TemplateConfiguration.java
index 0835239..ad7b1f8 100644
--- a/src/main/java/freemarker/core/TemplateConfiguration.java
+++ b/src/main/java/freemarker/core/TemplateConfiguration.java
@@ -78,6 +78,7 @@ public final class TemplateConfiguration extends Configurable implements ParserC
private boolean parentConfigurationSet;
private Integer tagSyntax;
+ private Integer interpolationSyntax;
private Integer namingConvention;
private Boolean whitespaceStripping;
private Boolean strictSyntaxMode;
@@ -234,6 +235,9 @@ public final class TemplateConfiguration extends Configurable implements ParserC
if (tc.isTagSyntaxSet()) {
setTagSyntax(tc.getTagSyntax());
}
+ if (tc.isInterpolationSyntaxSet()) {
+ setInterpolationSyntax(tc.getInterpolationSyntax());
+ }
if (tc.isTemplateExceptionHandlerSet()) {
setTemplateExceptionHandler(tc.getTemplateExceptionHandler());
}
@@ -413,6 +417,29 @@ public final class TemplateConfiguration extends Configurable implements ParserC
}
/**
+ * See {@link Configuration#setInterpolationSyntax(int)}.
+ */
+ public void setInterpolationSyntax(int interpolationSyntax) {
+ _TemplateAPI.valideInterpolationSyntaxValue(interpolationSyntax);
+ this.interpolationSyntax = Integer.valueOf(interpolationSyntax);
+ }
+
+ /**
+ * The getter pair of {@link #setInterpolationSyntax(int)}.
+ */
+ public int getInterpolationSyntax() {
+ return interpolationSyntax != null ? interpolationSyntax.intValue()
+ : getNonNullParentConfiguration().getInterpolationSyntax();
+ }
+
+ /**
+ * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
+ */
+ public boolean isInterpolationSyntaxSet() {
+ return interpolationSyntax != null;
+ }
+
+ /**
* See {@link Configuration#setNamingConvention(int)}.
*/
public void setNamingConvention(int namingConvention) {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ca1ecf78/src/main/java/freemarker/core/_ParserConfigurationWithInheritedFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/_ParserConfigurationWithInheritedFormat.java b/src/main/java/freemarker/core/_ParserConfigurationWithInheritedFormat.java
index 8524d62..72152ce 100644
--- a/src/main/java/freemarker/core/_ParserConfigurationWithInheritedFormat.java
+++ b/src/main/java/freemarker/core/_ParserConfigurationWithInheritedFormat.java
@@ -44,6 +44,10 @@ public final class _ParserConfigurationWithInheritedFormat implements ParserConf
return wrappedPCfg.getTagSyntax();
}
+ public int getInterpolationSyntax() {
+ return wrappedPCfg.getInterpolationSyntax();
+ }
+
public boolean getStrictSyntaxMode() {
return wrappedPCfg.getStrictSyntaxMode();
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ca1ecf78/src/main/java/freemarker/template/Configuration.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/Configuration.java b/src/main/java/freemarker/template/Configuration.java
index 5f35435..34b055e 100644
--- a/src/main/java/freemarker/template/Configuration.java
+++ b/src/main/java/freemarker/template/Configuration.java
@@ -252,6 +252,13 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
public static final String TAG_SYNTAX_KEY_CAMEL_CASE = "tagSyntax";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String TAG_SYNTAX_KEY = TAG_SYNTAX_KEY_SNAKE_CASE;
+
+ /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.28 */
+ public static final String INTERPOLATION_SYNTAX_KEY_SNAKE_CASE = "interpolation_syntax";
+ /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.28 */
+ public static final String INTERPOLATION_SYNTAX_KEY_CAMEL_CASE = "interpolationSyntax";
+ /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+ public static final String INTERPOLATION_SYNTAX_KEY = INTERPOLATION_SYNTAX_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String NAMING_CONVENTION_KEY_SNAKE_CASE = "naming_convention";
@@ -315,6 +322,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
CACHE_STORAGE_KEY_SNAKE_CASE,
DEFAULT_ENCODING_KEY_SNAKE_CASE,
INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE,
+ INTERPOLATION_SYNTAX_KEY_SNAKE_CASE,
LOCALIZED_LOOKUP_KEY_SNAKE_CASE,
NAMING_CONVENTION_KEY_SNAKE_CASE,
OUTPUT_FORMAT_KEY_SNAKE_CASE,
@@ -337,6 +345,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
CACHE_STORAGE_KEY_CAMEL_CASE,
DEFAULT_ENCODING_KEY_CAMEL_CASE,
INCOMPATIBLE_IMPROVEMENTS_KEY_CAMEL_CASE,
+ INTERPOLATION_SYNTAX_KEY_CAMEL_CASE,
LOCALIZED_LOOKUP_KEY_CAMEL_CASE,
NAMING_CONVENTION_KEY_CAMEL_CASE,
OUTPUT_FORMAT_KEY_CAMEL_CASE,
@@ -371,6 +380,13 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
public static final int ANGLE_BRACKET_TAG_SYNTAX = 1;
public static final int SQUARE_BRACKET_TAG_SYNTAX = 2;
+ /** <code>${expression}</code> and the deprecated <code>#{expression; numFormat}</code> @since 2.3.28 */
+ public static final int LEGACY_INTERPOLATION_SYNTAX = 0;
+ /** <code>${expression}</code> only (not <code>#{expression; numFormat}</code>) @since 2.3.28 */
+ public static final int DOLLAR_INTERPOLATION_SYNTAX = 1;
+ /** <code>[=expression]</code> @since 2.3.28 */
+ public static final int SQUARE_BRACKET_INTERPOLATION_SYNTAX = 2;
+
public static final int AUTO_DETECT_NAMING_CONVENTION = 10;
public static final int LEGACY_NAMING_CONVENTION = 11;
public static final int CAMEL_CASE_NAMING_CONVENTION = 12;
@@ -494,6 +510,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
private Map<String, ? extends OutputFormat> registeredCustomOutputFormats = Collections.emptyMap();
private Version incompatibleImprovements;
private int tagSyntax = ANGLE_BRACKET_TAG_SYNTAX;
+ private int interpolationSyntax = LEGACY_INTERPOLATION_SYNTAX;
private int namingConvention = AUTO_DETECT_NAMING_CONVENTION;
private int tabSize = 8; // Default from JavaCC 3.x
private boolean preventStrippings;
@@ -2356,9 +2373,8 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
}
/**
- * Determines the syntax of the template files (angle bracket VS square bracket)
- * that has no {@code #ftl} in it. The {@code tagSyntax}
- * parameter must be one of:
+ * Determines the tag syntax (like {@code <#if x>} VS {@code [#if x]}) of the template files
+ * that has no {@code #ftl} header to decide that. The {@code tagSyntax} parameter must be one of:
* <ul>
* <li>{@link Configuration#AUTO_DETECT_TAG_SYNTAX}:
* use the syntax of the first FreeMarker tag (can be anything, like <tt>#list</tt>,
@@ -2377,6 +2393,8 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
* <p>This setting is ignored for the templates that have {@code ftl} directive in
* it. For those templates the syntax used for the {@code ftl} directive determines
* the syntax.
+ *
+ * @see #setInterpolationSyntax(int)
*/
public void setTagSyntax(int tagSyntax) {
_TemplateAPI.valideTagSyntaxValue(tagSyntax);
@@ -2391,6 +2409,30 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
}
/**
+ * Determines the interpolation syntax (like <code>${x}</code> VS <code>[=x]</code>) of the template files in which
+ * there's no {@code #ftl} hader with {@code interpolation_syntax} parameter. The
+ * {@code interpolationSyntax} parameter must be one of {@link Configuration#LEGACY_INTERPOLATION_SYNTAX},
+ * {@link Configuration#DOLLAR_INTERPOLATION_SYNTAX}, and {@link Configuration#SQUARE_BRACKET_INTERPOLATION_SYNTAX}.
+ *
+ * @see #setTagSyntax(int)
+ *
+ * @since 2.3.28
+ */
+ public void setInterpolationSyntax(int interpolationSyntax) {
+ _TemplateAPI.valideInterpolationSyntaxValue(interpolationSyntax);
+ this.interpolationSyntax = interpolationSyntax;
+ }
+
+ /**
+ * The getter pair of {@link #setInterpolationSyntax(int)}.
+ *
+ * @since 2.3.28
+ */
+ public int getInterpolationSyntax() {
+ return interpolationSyntax;
+ }
+
+ /**
* Sets the naming convention used for the identifiers that are part of the template language. The available naming
* conventions are legacy (directive (tag) names are all-lower-case {@code likethis}, others are snake case
* {@code like_this}), and camel case ({@code likeThis}). The default is auto-detect, which detects the naming
@@ -3220,6 +3262,17 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
} else {
throw invalidSettingValueException(name, value);
}
+ } else if (INTERPOLATION_SYNTAX_KEY_SNAKE_CASE.equals(name)
+ || INTERPOLATION_SYNTAX_KEY_CAMEL_CASE.equals(name)) {
+ if ("legacy".equals(value)) {
+ setInterpolationSyntax(LEGACY_INTERPOLATION_SYNTAX);
+ } else if ("dollar".equals(value)) {
+ setInterpolationSyntax(DOLLAR_INTERPOLATION_SYNTAX);
+ } else if ("square_bracket".equals(value) || "squareBracket".equals(value)) {
+ setInterpolationSyntax(SQUARE_BRACKET_INTERPOLATION_SYNTAX);
+ } else {
+ throw invalidSettingValueException(name, value);
+ }
} else if (NAMING_CONVENTION_KEY_SNAKE_CASE.equals(name) || NAMING_CONVENTION_KEY_CAMEL_CASE.equals(name)) {
if ("auto_detect".equals(value) || "autoDetect".equals(value)) {
setNamingConvention(AUTO_DETECT_NAMING_CONVENTION);
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ca1ecf78/src/main/java/freemarker/template/_TemplateAPI.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/_TemplateAPI.java b/src/main/java/freemarker/template/_TemplateAPI.java
index 9a6ee58..9b2c22d 100644
--- a/src/main/java/freemarker/template/_TemplateAPI.java
+++ b/src/main/java/freemarker/template/_TemplateAPI.java
@@ -158,6 +158,16 @@ public class _TemplateAPI {
+ "or Configuration.SQUARE_BRACKET_SYNTAX");
}
}
+
+ public static void valideInterpolationSyntaxValue(int interpolationSyntax) {
+ if (interpolationSyntax != Configuration.LEGACY_INTERPOLATION_SYNTAX
+ && interpolationSyntax != Configuration.DOLLAR_INTERPOLATION_SYNTAX
+ && interpolationSyntax != Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX) {
+ throw new IllegalArgumentException("\"interpolation_syntax\" can only be set to one of these: "
+ + "Configuration.LEGACY_INTERPOLATION_SYNTAX, Configuration.DOLLAR_INTERPOLATION_SYNTAX, "
+ + "or Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX");
+ }
+ }
public static Expression getBlamedExpression(TemplateException e) {
return e.getBlamedExpression();
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ca1ecf78/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/src/main/javacc/FTL.jj b/src/main/javacc/FTL.jj
index 3e87032..f1fa595 100644
--- a/src/main/javacc/FTL.jj
+++ b/src/main/javacc/FTL.jj
@@ -33,6 +33,7 @@ import freemarker.template.*;
import freemarker.template.utility.*;
import java.io.*;
import java.util.*;
+import static freemarker.template.Configuration.*;
/**
* This class is generated by JavaCC from a grammar file.
@@ -165,7 +166,7 @@ public class FMParser {
this(template, reader,
new LegacyConstructorParserConfiguration(
strictSyntaxMode, whitespaceStripping,
- tagSyntax, namingConvention,
+ tagSyntax, LEGACY_INTERPOLATION_SYNTAX, namingConvention,
template != null ? template.getParserConfiguration().getAutoEscapingPolicy()
: Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY,
template != null ? template.getParserConfiguration().getOutputFormat()
@@ -256,6 +257,8 @@ public class FMParser {
throw new IllegalArgumentException("Illegal argument for tagSyntax: " + tagSyntax);
}
+ token_source.interpolationSyntax = pCfg.getInterpolationSyntax();
+
int namingConvention = pCfg.getNamingConvention();
switch (namingConvention) {
case Configuration.AUTO_DETECT_NAMING_CONVENTION:
@@ -612,12 +615,12 @@ TOKEN_MGR_DECLS:
*/
String noparseTag;
+ private FMParser parser;
+ private int postInterpolationLexState = -1;
/**
* Keeps track of how deeply nested we have the hash literals. This is necessary since we need to be able to
* distinguish the } used to close a hash literal and the one used to close a ${
*/
- private FMParser parser;
- private int postInterpolationLexState = -1;
private int hashLiteralNesting;
private int parenthesisNesting;
private int bracketNesting;
@@ -627,6 +630,7 @@ TOKEN_MGR_DECLS:
autodetectTagSyntax,
directiveSyntaxEstablished,
inInvocation;
+ int interpolationSyntax;
int initialNamingConvention;
int namingConvention;
Token namingConventionEstabilisher;
@@ -793,25 +797,25 @@ TOKEN_MGR_DECLS:
}
}
- private void closeBracket(Token tok) {
- if (bracketNesting > 0) {
- --bracketNesting;
- } else {
- tok.kind = DIRECTIVE_END;
- if (inFTLHeader) {
- eatNewline();
- inFTLHeader = false;
- }
- SwitchTo(DEFAULT);
- }
- }
-
private void startInterpolation(Token tok) {
+ if (
+ interpolationSyntax == LEGACY_INTERPOLATION_SYNTAX
+ && tok.kind == SQUARE_BRACKET_INTERPOLATION_OPENING
+ || interpolationSyntax == DOLLAR_INTERPOLATION_SYNTAX
+ && tok.kind != DOLLAR_INTERPOLATION_OPENING
+ || interpolationSyntax == SQUARE_BRACKET_INTERPOLATION_SYNTAX
+ && tok.kind != SQUARE_BRACKET_INTERPOLATION_OPENING) {
+ tok.kind = STATIC_TEXT_NON_WS;
+ return;
+ }
+
if (postInterpolationLexState != -1) {
+ // This certainly never occurs, as starting an interpolation in expression mode fails earlier.
char c = tok.image.charAt(0);
throw new TokenMgrError(
- "You can't start an interpolation (" + c + "{...}) here "
- + "as you are inside another interpolation.)",
+ "You can't start an interpolation (" + tok.image + "..."
+ + (interpolationSyntax == SQUARE_BRACKET_INTERPOLATION_SYNTAX ? "]" : "}")
+ + ") here as you are inside another interpolation.)",
TokenMgrError.LEXICAL_ERROR,
tok.beginLine, tok.beginColumn,
tok.endLine, tok.endColumn);
@@ -1156,6 +1160,8 @@ TOKEN:
<DOLLAR_INTERPOLATION_OPENING : "${"> { startInterpolation(matchedToken); }
|
<HASH_INTERPOLATION_OPENING : "#{"> { startInterpolation(matchedToken); }
+ |
+ <SQUARE_BRACKET_INTERPOLATION_OPENING : "[="> { startInterpolation(matchedToken); }
}
<FM_EXPRESSION, IN_PAREN, NAMED_PARAMETER_EXPRESSION> SKIP :
@@ -1291,7 +1297,20 @@ TOKEN:
|
<CLOSE_BRACKET : "]">
{
- closeBracket(matchedToken);
+ if (bracketNesting > 0) {
+ --bracketNesting;
+ } else if (interpolationSyntax == SQUARE_BRACKET_INTERPOLATION_SYNTAX
+ && (postInterpolationLexState != -1 || !squBracTagSyntax)) {
+ endInterpolation(matchedToken);
+ } else {
+ // Glitch where you can close any tag with `]`, like `<#if x]`. We keep it for backward compatibility.
+ matchedToken.kind = DIRECTIVE_END;
+ if (inFTLHeader) {
+ eatNewline();
+ inFTLHeader = false;
+ }
+ SwitchTo(DEFAULT);
+ }
}
|
<OPEN_PAREN : "(">
@@ -1343,15 +1362,16 @@ TOKEN:
}
}
|
- <OPEN_MISPLACED_INTERPOLATION : "${" | "#{">
+ <OPEN_MISPLACED_INTERPOLATION : "${" | "#{" | "[=">
{
if ("".length() == 0) { // prevents unreachabe "break" compilation error in generated Java
- char c = matchedToken.image.charAt(0);
+ char closerC = matchedToken.image.charAt(0) != '[' ? '}' : ']';
throw new TokenMgrError(
- "You can't use \"" + c + "{\" here as you are already in FreeMarker-expression-mode. Thus, instead "
- + "of " + c + "{myExpression}, just write myExpression. "
- + "(" + c + "{...} is only needed where otherwise static text is expected, i.e, outside "
- + "FreeMarker tags and ${...}-s.)",
+ "You can't use " + matchedToken.image + "..." + closerC + " (an interpolation) here as you are "
+ + "already in FreeMarker-expression-mode. Thus, instead of " + matchedToken.image + "myExpression"
+ + closerC + ", just write myExpression. (" + matchedToken.image + "..." + closerC + " is only "
+ + "used where otherwise static text is expected, i.e., outside FreeMarker tags and "
+ + "interpolations, or inside string literals.)",
TokenMgrError.LEXICAL_ERROR,
matchedToken.beginLine, matchedToken.beginColumn,
matchedToken.endLine, matchedToken.endColumn);
@@ -2306,7 +2326,16 @@ 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(this, outputFormat);
+ int interpolationSyntax = pCfg.getInterpolationSyntax();
+ if ((interpolationSyntax == LEGACY_INTERPOLATION_SYNTAX
+ || interpolationSyntax == DOLLAR_INTERPOLATION_SYNTAX)
+ && t.image.indexOf("${") >= 0
+ || interpolationSyntax == LEGACY_INTERPOLATION_SYNTAX
+ && t.image.indexOf("#{") >= 0
+ || interpolationSyntax == SQUARE_BRACKET_INTERPOLATION_SYNTAX
+ && t.image.indexOf("[=") >= 0) {
+ result.parseValue(this, outputFormat);
+ }
}
return result;
}
@@ -2369,8 +2398,7 @@ HashLiteral HashLiteral() :
}
/**
- * A production representing the ${...}
- * that outputs a variable.
+ * A production representing the ${...} or [=...] that outputs a variable.
*/
DollarVariable StringOutput() :
{
@@ -2378,13 +2406,29 @@ DollarVariable StringOutput() :
Token begin, end;
}
{
- begin = <DOLLAR_INTERPOLATION_OPENING>
- exp = Expression()
- {
- notHashLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
- notListLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
- }
- end = <CLOSING_CURLY_BRACKET>
+ (
+ (
+ begin = <DOLLAR_INTERPOLATION_OPENING>
+ exp = Expression()
+ {
+ notHashLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
+ notListLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
+ }
+
+ end = <CLOSING_CURLY_BRACKET>
+ )
+ |
+ (
+ begin = <SQUARE_BRACKET_INTERPOLATION_OPENING>
+ exp = Expression()
+ {
+ notHashLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
+ notListLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
+ }
+
+ end = <CLOSE_BRACKET>
+ )
+ )
{
DollarVariable result = new DollarVariable(
exp, escapedExpression(exp),
@@ -4449,7 +4493,7 @@ List<Object> StaticTextAndInterpolations() :
}
|
(
- LOOKAHEAD(<DOLLAR_INTERPOLATION_OPENING>)
+ LOOKAHEAD(<DOLLAR_INTERPOLATION_OPENING>|<SQUARE_BRACKET_INTERPOLATION_OPENING>)
(
interpolation = StringOutput()
)
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ca1ecf78/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 52f67af..cbda1a8 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -4199,6 +4199,13 @@ ${("green " + "mouse")?upper_case} <#-- GREEN MOUSE -->
<literal><replaceable>expression</replaceable></literal> can be all
kind of expression (e.g. <literal>${100 + x}</literal>).</para>
+ <note>
+ <para>Actually, FreeMarker can be configured to use
+ <literal>[=<replaceable>expression</replaceable>]</literal> syntax
+ instead. <link linkend="dgui_misc_alternativesyntax">See more
+ about alternative syntaxes...</link></para>
+ </note>
+
<para>The interpolation is used to insert the value of the
<literal><replaceable>expression</replaceable></literal> converted
to text (to string). Interpolations can be used only on two places:
@@ -6221,46 +6228,57 @@ That's all.</programlisting>
<primary>square bracket syntax</primary>
</indexterm>
- <note>
- <para>This feature exists since FreeMarker 2.3.4.</para>
- </note>
+ <para>Both FreeMarker tags (such as <literal><#if x></literal>)
+ and FreeMarker interpolations (such as <literal>${x}</literal>) can be
+ set <emphasis>independently</emphasis> to use so called square bracket
+ syntax (<literal>[#if x]</literal>, and <literal>[=x]</literal>,
+ respectively). This is mostly useful to prevent clashes with the
+ generated content (such as when generating JSP files), or to work
+ around cases where some other tool is confused by the default
+ symbols.</para>
- <para>FreeMarker supports an alternative syntax, where
- <literal>[</literal> and <literal>]</literal> is used instead of
- <literal><</literal> and <literal>></literal> in FreeMarker
- directives and comments, for example:</para>
+ <section xml:id="dgui_misc_alternativesyntax_tag">
+ <title>Square bracket tag syntax</title>
- <itemizedlist spacing="compact">
- <listitem>
- <para>Calling predefined directive: <literal>[#list animals as
- animal]<replaceable>...</replaceable>[/#list]</literal></para>
- </listitem>
+ <note>
+ <para>This feature exists since FreeMarker 2.3.4.</para>
+ </note>
- <listitem>
- <para>Calling user-defined directive: <literal>[@myMacro
- /]</literal></para>
- </listitem>
+ <para>FreeMarker supports an alternative tag syntax, where
+ <literal>[</literal> and <literal>]</literal> is used instead of
+ <literal><</literal> and <literal>></literal> in FreeMarker
+ directives and comments, for example:</para>
- <listitem>
- <para>Comment: <literal>[#-- the comment --]</literal></para>
- </listitem>
- </itemizedlist>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>Calling predefined directive: <literal>[#list animals as
+ animal]<replaceable>...</replaceable>[/#list]</literal></para>
+ </listitem>
+
+ <listitem>
+ <para>Calling user-defined directive: <literal>[@myMacro
+ /]</literal></para>
+ </listitem>
+
+ <listitem>
+ <para>Comment: <literal>[#-- the comment --]</literal></para>
+ </listitem>
+ </itemizedlist>
+
+ <para>To use the alternative tag syntax instead of the default one,
+ the programmers should configure FreeMarker so <phrase
+ role="forProgrammers">(see
+ <literal>Configuraton.setTagSyntax</literal>, or the
+ <literal>tag_syntax</literal> setting)</phrase>. However, the tag
+ syntax can also be enforced in the template, with the <link
+ linkend="ref_directive_ftl"><literal>ftl</literal> directive</link>
+ (see later).</para>
- <para>To use the alternative syntax instead of the default one, start
- the template with the <link
- linkend="ref_directive_ftl"><literal>ftl</literal> directive</link>
- using the alternative syntax. If you don't know what is the
- <literal>ftl</literal> directive, just start the template with
- <literal>[#ftl]</literal>, and remember that it should be the very
- first thing in the file (except that <link
- linkend="gloss.whiteSpace">white-space</link> can precede it). For
- example, this is how the last example of the <link
- linkend="dgui_quickstart_template">Getting Started</link> looks with
- the alternative syntax (assuming it's a complete template, not just a
- fragment):</para>
-
- <programlisting role="template"><emphasis>[#ftl]</emphasis>
-<p>We have these animals:
+ <para>For example, this is how the last example of the <link
+ linkend="dgui_quickstart_template">Getting Started</link> looks with
+ the alternative syntax:</para>
+
+ <programlisting role="template"><p>We have these animals:
<table border=1>
<tr><th>Name<th>Price
<emphasis>[#list animals as animal]</emphasis>
@@ -6273,33 +6291,79 @@ That's all.</programlisting>
<emphasis>[/#list]</emphasis>
</table></programlisting>
- <para>The alternative (square bracket) and the default (angle bracket)
- syntax are mutually exclusive within a template. That is, either the
- whole template uses alternative syntax, or the whole template uses the
- default syntax. If the template uses alternative syntax, things like
- <literal><#if <replaceable>...</replaceable>></literal> are
- count as static text, not as FTL tags. Similarly, if the template uses
- the default syntax, things like <literal>[#if
- <replaceable>...</replaceable>]</literal> count as static text, not as
- FTL tags.</para>
-
- <para>If you start the file with <literal>[#ftl
- <replaceable>...</replaceable>]</literal> (where the
- <literal><replaceable>...</replaceable></literal> stands for the
- optional parameters; of course <literal>[#ftl]</literal> works too)
- the file will surely use the alternative (square bracket) syntax. If
- you start the file with <literal><#ftl
- <replaceable>...</replaceable>></literal> the file will surely use
- the normal (angle bracket) syntax. If there is no
- <literal>ftl</literal> directive in the file, then the programmer
- decides what the syntax will be by configuring FreeMarker <phrase
- role="forProgrammers">(programmers see
- <literal>Configuration.setTagSyntax(int)</literal> in the API
- javadocs)</phrase>. Most probably the programmers use the factory
- default however. The factory default in 2.3.x is using the normal
- syntax. The factory default in 2.4.x will be auto-detection, which
- means that the first FreeMarker tag determines the syntax (it can be
- anything, not just <literal>ftl</literal>).</para>
+ <para>The alternative (square bracket) and the default (angle
+ bracket) syntax are mutually exclusive within a template. That is,
+ either the whole template uses square bracket tag syntax, or the
+ whole template uses the angle bracket tag syntax. If the template
+ uses square bracket tag syntax, then things like <literal><#if
+ <replaceable>...</replaceable>></literal> are count as static
+ text, not as FTL tags. Similarly, if the template uses the angle
+ brackets tag syntax, things like <literal>[#if
+ <replaceable>...</replaceable>]</literal> count as static text, not
+ as FTL tags.</para>
+
+ <para>If you start the file with <literal>[#ftl
+ <replaceable>...</replaceable>]</literal> (where the
+ <literal><replaceable>...</replaceable></literal> stands for the
+ optional parameters; of course <literal>[#ftl]</literal> works too)
+ the file will surely use the square bracket syntax. If you start the
+ file with <literal><#ftl
+ <replaceable>...</replaceable>></literal> the file will surely
+ use the normal (angle bracket) syntax. If there is no
+ <literal>ftl</literal> directive in the file, then the programmer
+ decides what the syntax will be by configuring FreeMarker <phrase
+ role="forProgrammers">(programmers see
+ <literal>Configuration.setTagSyntax(int)</literal> in the API
+ javadocs)</phrase>. Most probably the programmers use the factory
+ default however. The factory default in 2.3.x is using the normal
+ syntax. The factory default in 2.4.x will be auto-detection, which
+ means that the first FreeMarker tag determines the syntax (it can be
+ anything, not just <literal>ftl</literal>).</para>
+ </section>
+
+ <section xml:id="dgui_misc_alternativesyntax_interpolation">
+ <title>Square bracket interpolation syntax</title>
+
+ <note>
+ <para>This feature exists since FreeMarker 2.3.28</para>
+ </note>
+
+ <para>In this case instead of
+ <literal>${<replaceable>expression</replaceable>}</literal> (and
+ <literal>#{<replaceable>expression</replaceable>}</literal>) you
+ write <literal>[=<replaceable>expression</replaceable>]</literal>.
+ This syntax is usually activated by the programmers from the
+ configuration <phrase role="forProgrammers">(see
+ <literal>Configuration.setInterpolationSyntax</literal> in the Java
+ API)</phrase>. It can be used both together with, and without the
+ square bracket <emphasis>tag</emphasis> syntax (see that in the
+ <link linkend="dgui_misc_alternativesyntax_tag">previous
+ section</link>), although it's more likely to be used together with
+ it. Assuming both interpolation and tag syntax was set to square
+ bracket, the earlier example template will look like this:</para>
+
+ <programlisting role="template"><p>We have these animals:
+<table border=1>
+ <tr><th>Name<th>Price
+ [#list animals as animal]
+ <tr>
+ <td>
+ [#if animal.size == "large"]<b>[/#if]
+ <emphasis>[=animal.name]</emphasis>
+ [#if animal.size == "large"]</b>[/#if]
+ <td><emphasis>[=animal.price]</emphasis> Euros
+ [/#list]
+</table></programlisting>
+
+ <para>When square bracket interpolation syntax is used,
+ <literal>${<replaceable>expression</replaceable>}</literal> and
+ <literal>#{<replaceable>expression</replaceable>}</literal> in the
+ template will be just static text, which is printed as is. This is
+ mostly useful if you generate output that should contain those
+ (especially
+ <literal>${<replaceable>expression</replaceable>}</literal> is
+ frequent), such as when generating JSP files.</para>
+ </section>
</section>
</chapter>
</part>
@@ -24325,6 +24389,17 @@ some test
linkend="ref_builtin_c"><literal>c</literal> built-in</link> (like
<literal><replaceable>number</replaceable>?c</literal>).</para>
+ <para>While by default
+ <literal>#{<replaceable>...</replaceable>}</literal> is interpreted,
+ that can be disabled by setting the
+ <literal>interpolation_syntax</literal> configuration setting
+ (<literal>Configuration.setInterpolationSyntax</literal> in the Java
+ API) to <literal>dollar</literal>. Then
+ <literal>#{<replaceable>...</replaceable>}</literal> will be just
+ static text, and only
+ <literal>${<replaceable>...</replaceable>}</literal> will operate as
+ interpolation.</para>
+
<section>
<title>Synopsis</title>
@@ -26513,11 +26588,38 @@ End book</programlisting>
</question>
<answer>
- <para>Starting from FreeMarker 2.3.4 you can use
- <literal>[</literal> and <literal>]</literal> instead of
- <literal><</literal> and <literal>></literal>. For more
- details <link linkend="dgui_misc_alternativesyntax">read
- this...</link></para>
+ <para>You can use <literal>[</literal> and <literal>]</literal>
+ instead of <literal><</literal> and <literal>></literal>;
+ see more about square bracket tag syntax <link
+ linkend="dgui_misc_alternativesyntax">here.</link> The comparison
+ operators, like <literal><</literal>, also have an alternative
+ syntax (<literal>lt</literal> and <literal>&lt;</literal> in
+ this case); see more about them <link
+ linkend="dgui_template_exp_comparison">here</link>. Also, the
+ <literal>&&</literal> operator (which is not well-format
+ HTML/XML) can be written as <literal>\and</literal> or
+ <literal>&amp;&amp;</literal> since 2.3.27.</para>
+ </answer>
+ </qandaentry>
+
+ <qandaentry xml:id="faq_alternative_syntax_interpolation">
+ <question>
+ <para><literal>${<replaceable>...</replaceable>}</literal> and/or
+ <literal>#{<replaceable>...</replaceable>}</literal> is used in
+ the output I have to generate a lot, and FreeMarker tries to
+ resolve them. What to do?</para>
+ </question>
+
+ <answer>
+ <para>You can escape them like
+ <literal>${'$'}{<replaceable>...</replaceable>}</literal>, however
+ that's impractical if you have to do that often. So starting from
+ FreeMarker 2.3.28 you can use
+ <literal>[=<replaceable>...</replaceable></literal><literal>]</literal>
+ instead; see more about the square bracket interpolation syntax
+ <link linkend="dgui_misc_alternativesyntax">here.</link> If you
+ are going to generate JSP files or even FreeMarker templates, this
+ is very useful.</para>
</answer>
</qandaentry>
@@ -27479,6 +27581,46 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
<itemizedlist>
<listitem>
+ <para>Added new <literal>ParserConfiguration</literal> (such as
+ <literal>Configuration</literal> and
+ <literal>TemplateConfiguration</literal>) setting,
+ <literal>interpolation_syntax</literal>. It has 3 possible
+ values:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><literal>legacy</literal> (the default):
+ Interpolations look like
+ <literal>${<replaceable>...</replaceable>}</literal> or
+ <literal>#{<replaceable>...</replaceable>}</literal>. Note
+ that <literal>#{<replaceable>...</replaceable>}</literal> is
+ deprecated for a long time now.</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>dollar</literal>: Interpolations look like
+ <literal>${<replaceable>...</replaceable>}</literal>. With
+ this syntax,
+ <literal>#{<replaceable>...</replaceable>}</literal> will be
+ just static text.</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>square_bracket</literal>: Interpolations look
+ like <literal>[=<replaceable>...</replaceable>]</literal>.
+ With this syntax
+ <literal>${<replaceable>...</replaceable>}</literal> and
+ <literal>#{<replaceable>...</replaceable>}</literal> will be
+ just static text. So it's useful if you generate output in a
+ format where those (typically
+ <literal>${<replaceable>...</replaceable>}</literal>) are
+ already used, such as to generate JSP pages, or to generate
+ FreeMarker templates that use the default syntax.</para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+
+ <listitem>
<para>Added new property to
<literal>BeansWrapper.MethodAppearanceDecision</literal>:
<literal>replaceExistingProperty</literal>. This is useful when
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ca1ecf78/src/test/java/freemarker/core/InterpolationSyntaxTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/InterpolationSyntaxTest.java b/src/test/java/freemarker/core/InterpolationSyntaxTest.java
new file mode 100644
index 0000000..3bd49be
--- /dev/null
+++ b/src/test/java/freemarker/core/InterpolationSyntaxTest.java
@@ -0,0 +1,88 @@
+package freemarker.core;
+
+import static freemarker.template.Configuration.*;
+
+import org.junit.Test;
+
+import freemarker.test.TemplateTest;
+
+public class InterpolationSyntaxTest extends TemplateTest {
+
+ @Test
+ public void legacyInterpolationSyntaxTest() throws Exception {
+ // The default is: getConfiguration().setInterpolationSyntax(Configuration.LEGACY_INTERPOLATION_SYNTAX);
+
+ assertOutput("${1} #{1} [=1]", "1 1 [=1]");
+ assertOutput(
+ "${{'x': 1}['x']} #{{'x': 1}['x']} [={'x': 1}['x']]",
+ "1 1 [={'x': 1}['x']]");
+ }
+
+ @Test
+ public void dollarInterpolationSyntaxTest() throws Exception {
+ getConfiguration().setInterpolationSyntax(DOLLAR_INTERPOLATION_SYNTAX);
+
+ assertOutput("${1} #{1} [=1]", "1 #{1} [=1]");
+ assertOutput(
+ "${{'x': 1}['x']} #{{'x': 1}['x']} [={'x': 1}['x']]",
+ "1 #{{'x': 1}['x']} [={'x': 1}['x']]");
+ }
+
+ @Test
+ public void squareBracketInterpolationSyntaxTest() throws Exception {
+ getConfiguration().setInterpolationSyntax(SQUARE_BRACKET_INTERPOLATION_SYNTAX);
+
+ assertOutput("${1} #{1} [=1]", "${1} #{1} 1");
+ assertOutput(
+ "${{'x': 1}['x']} #{{'x': 1}['x']} [={'x': 1}['x']]",
+ "${{'x': 1}['x']} #{{'x': 1}['x']} 1");
+
+ assertOutput("[=1]][=2]]", "1]2]");
+ assertOutput("[= 1 ][= <#-- c --> 2 <#-- c --> ]", "12");
+ assertOutput("[ =1]", "[ =1]");
+
+ assertErrorContains("<#if [true][0]]></#if>", "\"]\"", "nothing open");
+ assertOutput("[#ftl][#if [true][0]]>[/#if]", ">");
+
+ assertOutput("[='a[=1]b']", "a1b");
+ }
+
+ @Test
+ public void squareBracketTagSyntaxStillWorks() throws Exception {
+ getConfiguration().setTagSyntax(SQUARE_BRACKET_TAG_SYNTAX);
+ for (int syntax : new int[] {
+ LEGACY_INTERPOLATION_SYNTAX, DOLLAR_INTERPOLATION_SYNTAX, SQUARE_BRACKET_INTERPOLATION_SYNTAX }) {
+ assertOutput("[#if [true][0]]t[#else]f[/#if]", "t");
+ }
+ }
+
+ @Test
+ public void legacyTagSyntaxGlitchStillWorksTest() throws Exception {
+ String ftl = "<#if [true][0]]t<#else]f</#if]";
+
+ for (int syntax : new int[] { LEGACY_INTERPOLATION_SYNTAX, DOLLAR_INTERPOLATION_SYNTAX }) {
+ getConfiguration().setInterpolationSyntax(syntax);
+ assertOutput(ftl, "t");
+ }
+
+ // Glitch is not emulated with this:
+ getConfiguration().setInterpolationSyntax(SQUARE_BRACKET_INTERPOLATION_SYNTAX);
+ assertErrorContains(ftl, "\"]\"");
+ }
+
+ @Test
+ public void errorMessagesAreSquareBracketInterpolationSyntaxAwareTest() throws Exception {
+ assertErrorContains("<#if ${x}></#if>", "${...}", "${myExpression}");
+ assertErrorContains("<#if #{x}></#if>", "#{...}", "#{myExpression}");
+ assertErrorContains("<#if [=x]></#if>", "[=...]", "[=myExpression]");
+ }
+
+ @Test
+ public void unclosedSyntaxErrorTest() throws Exception {
+ assertErrorContains("${1", "unclosed \"{\"");
+
+ getConfiguration().setInterpolationSyntax(SQUARE_BRACKET_INTERPOLATION_SYNTAX);
+ assertErrorContains("[=1", "unclosed \"[\"");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ca1ecf78/src/test/java/freemarker/core/TemplateConfigurationTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/TemplateConfigurationTest.java b/src/test/java/freemarker/core/TemplateConfigurationTest.java
index 665f270..76199ad 100644
--- a/src/test/java/freemarker/core/TemplateConfigurationTest.java
+++ b/src/test/java/freemarker/core/TemplateConfigurationTest.java
@@ -183,6 +183,7 @@ public class TemplateConfigurationTest {
// Parser-only settings:
SETTING_ASSIGNMENTS.put("tagSyntax", Configuration.SQUARE_BRACKET_TAG_SYNTAX);
+ SETTING_ASSIGNMENTS.put("interpolationSyntax", Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX);
SETTING_ASSIGNMENTS.put("namingConvention", Configuration.LEGACY_NAMING_CONVENTION);
SETTING_ASSIGNMENTS.put("whitespaceStripping", false);
SETTING_ASSIGNMENTS.put("strictSyntaxMode", false);
@@ -604,6 +605,14 @@ public class TemplateConfigurationTest {
assertOutputWithoutAndWithTC(tc, "[#if true]y[/#if]", "[#if true]y[/#if]", "y");
testedProps.add(Configuration.TAG_SYNTAX_KEY_CAMEL_CASE);
}
+
+ {
+ TemplateConfiguration tc = new TemplateConfiguration();
+ tc.setParentConfiguration(DEFAULT_CFG);
+ tc.setInterpolationSyntax(Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX);
+ assertOutputWithoutAndWithTC(tc, "${1}#{2}[=3]", "12[=3]", "${1}#{2}3");
+ testedProps.add(Configuration.INTERPOLATION_SYNTAX_KEY_CAMEL_CASE);
+ }
{
TemplateConfiguration tc = new TemplateConfiguration();
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ca1ecf78/src/test/java/freemarker/template/ConfigurationTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/template/ConfigurationTest.java b/src/test/java/freemarker/template/ConfigurationTest.java
index cb46a94..fa6b508 100644
--- a/src/test/java/freemarker/template/ConfigurationTest.java
+++ b/src/test/java/freemarker/template/ConfigurationTest.java
@@ -1616,6 +1616,34 @@ public class ConfigurationTest extends TestCase {
assertEquals(Configuration.AUTO_DETECT_NAMING_CONVENTION, cfg.getNamingConvention());
}
+ public void testInterpolationSyntaxSetting() throws TemplateException {
+ Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
+
+ // Default is "legacy":
+ assertEquals(Configuration.LEGACY_INTERPOLATION_SYNTAX, cfg.getInterpolationSyntax());
+
+ cfg.setSetting("interpolation_syntax", "dollar");
+ assertEquals(Configuration.DOLLAR_INTERPOLATION_SYNTAX, cfg.getInterpolationSyntax());
+
+ cfg.setSetting("interpolation_syntax", "square_bracket");
+ assertEquals(Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX, cfg.getInterpolationSyntax());
+
+ cfg.setSetting("interpolation_syntax", "legacy");
+ assertEquals(Configuration.LEGACY_INTERPOLATION_SYNTAX, cfg.getInterpolationSyntax());
+
+ // Camel case:
+ cfg.setSetting("interpolationSyntax", "squareBracket");
+ assertEquals(Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX, cfg.getInterpolationSyntax());
+
+ try {
+ cfg.setSetting("interpolation_syntax", "no_such_syntax");
+ fail();
+ } catch (TemplateException e) {
+ assertThat(e.getMessage(), containsString("no_such_syntax"));
+ }
+
+ }
+
public void testLazyImportsSetSetting() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
[07/12] incubator-freemarker git commit: Added some more
interpolation_syntax tests
Posted by dd...@apache.org.
Added some more interpolation_syntax tests
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/aeaafe51
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/aeaafe51
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/aeaafe51
Branch: refs/heads/2.3
Commit: aeaafe5132fdd8fdde773b29d3584921b2f81d8d
Parents: eb6781b
Author: ddekany <dd...@apache.org>
Authored: Fri Mar 16 18:48:51 2018 +0100
Committer: ddekany <dd...@apache.org>
Committed: Fri Mar 16 18:48:51 2018 +0100
----------------------------------------------------------------------
.../core/InterpolationSyntaxTest.java | 29 +++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/aeaafe51/src/test/java/freemarker/core/InterpolationSyntaxTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/InterpolationSyntaxTest.java b/src/test/java/freemarker/core/InterpolationSyntaxTest.java
index 3bd49be..c1721c8 100644
--- a/src/test/java/freemarker/core/InterpolationSyntaxTest.java
+++ b/src/test/java/freemarker/core/InterpolationSyntaxTest.java
@@ -16,6 +16,13 @@ public class InterpolationSyntaxTest extends TemplateTest {
assertOutput(
"${{'x': 1}['x']} #{{'x': 1}['x']} [={'x': 1}['x']]",
"1 1 [={'x': 1}['x']]");
+
+ assertOutput("${'a[=1]b'}", "a[=1]b");
+ assertOutput("${'a${1}#{2}b'}", "a12b");
+ assertOutput("${'a${1}#{2}b[=3]'}", "a12b[=3]");
+
+ assertOutput("<@r'${1} #{1} [=1]'?interpret />", "1 1 [=1]");
+ assertOutput("${'\"${1} #{1} [=1]\"'?eval}", "1 1 [=1]");
}
@Test
@@ -26,6 +33,13 @@ public class InterpolationSyntaxTest extends TemplateTest {
assertOutput(
"${{'x': 1}['x']} #{{'x': 1}['x']} [={'x': 1}['x']]",
"1 #{{'x': 1}['x']} [={'x': 1}['x']]");
+
+ assertOutput("${'a[=1]b'}", "a[=1]b");
+ assertOutput("${'a${1}#{2}b'}", "a1#{2}b");
+ assertOutput("${'a${1}#{2}b[=3]'}", "a1#{2}b[=3]");
+
+ assertOutput("<@r'${1} #{1} [=1]'?interpret />", "1 #{1} [=1]");
+ assertOutput("${'\"${1} #{1} [=1]\"'?eval}", "1 #{1} [=1]");
}
@Test
@@ -41,10 +55,22 @@ public class InterpolationSyntaxTest extends TemplateTest {
assertOutput("[= 1 ][= <#-- c --> 2 <#-- c --> ]", "12");
assertOutput("[ =1]", "[ =1]");
+ // Legacy tag closing glitch is not emulated with this:
assertErrorContains("<#if [true][0]]></#if>", "\"]\"", "nothing open");
+
+ getConfiguration().setTagSyntax(SQUARE_BRACKET_TAG_SYNTAX);
+ assertOutput("[#if [true][0]]>[/#if]", ">");
+ assertOutput("[=1][=2]${3}", "12${3}");
+ getConfiguration().setTagSyntax(ANGLE_BRACKET_TAG_SYNTAX);
assertOutput("[#ftl][#if [true][0]]>[/#if]", ">");
+ assertOutput("[#ftl][=1][=2]${3}", "12${3}");
assertOutput("[='a[=1]b']", "a1b");
+ assertOutput("[='a${1}#{2}b']", "a${1}#{2}b");
+ assertOutput("[='a${1}#{2}b[=3]']", "a${1}#{2}b3");
+
+ assertOutput("<@r'${1} #{1} [=1]'?interpret />", "${1} #{1} 1");
+ assertOutput("[='\"${1} #{1} [=1]\"'?eval]", "${1} #{1} 1");
}
@Test
@@ -53,6 +79,7 @@ public class InterpolationSyntaxTest extends TemplateTest {
for (int syntax : new int[] {
LEGACY_INTERPOLATION_SYNTAX, DOLLAR_INTERPOLATION_SYNTAX, SQUARE_BRACKET_INTERPOLATION_SYNTAX }) {
assertOutput("[#if [true][0]]t[#else]f[/#if]", "t");
+ assertOutput("[@r'[#if [true][0]]t[#else]f[/#if]'?interpret /]", "t");
}
}
@@ -65,7 +92,7 @@ public class InterpolationSyntaxTest extends TemplateTest {
assertOutput(ftl, "t");
}
- // Glitch is not emulated with this:
+ // Legacy tag closing glitch is not emulated with this:
getConfiguration().setInterpolationSyntax(SQUARE_BRACKET_INTERPOLATION_SYNTAX);
assertErrorContains(ftl, "\"]\"");
}
[12/12] incubator-freemarker git commit: Merge remote-tracking branch
'origin/2.3-gae' into 2.3
Posted by dd...@apache.org.
Merge remote-tracking branch 'origin/2.3-gae' into 2.3
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/edefaa2f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/edefaa2f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/edefaa2f
Branch: refs/heads/2.3
Commit: edefaa2f6cdf00e1f979700b384f0b966873bde4
Parents: 0c69b19 0129453
Author: ddekany <dd...@apache.org>
Authored: Mon Mar 19 22:21:23 2018 +0100
Committer: ddekany <dd...@apache.org>
Committed: Mon Mar 19 22:21:23 2018 +0100
----------------------------------------------------------------------
src/main/java/freemarker/core/Configurable.java | 7 +-
.../java/freemarker/core/DollarVariable.java | 11 +-
.../core/ExtendedDecimalFormatParser.java | 5 +-
.../LegacyConstructorParserConfiguration.java | 9 +-
.../java/freemarker/core/NumericalOutput.java | 5 +-
.../freemarker/core/ParserConfiguration.java | 7 +
.../java/freemarker/core/StringLiteral.java | 14 +-
.../freemarker/core/TemplateConfiguration.java | 27 ++
..._ParserConfigurationWithInheritedFormat.java | 4 +
.../ext/beans/OverloadedNumberUtil.java | 2 +-
.../java/freemarker/template/Configuration.java | 81 +++-
src/main/java/freemarker/template/Template.java | 22 +
.../java/freemarker/template/_TemplateAPI.java | 14 +-
.../freemarker/template/utility/StringUtil.java | 22 +-
src/main/javacc/FTL.jj | 258 +++++++----
src/manual/en_US/book.xml | 440 +++++++++++++++----
.../core/InterpolationSyntaxTest.java | 154 +++++++
.../core/ParsingErrorMessagesTest.java | 66 +--
.../core/TemplateConfigurationTest.java | 9 +
.../freemarker/template/ConfigurationTest.java | 70 +++
.../template/utility/StringUtilTest.java | 24 +
.../templates/string-builtins3.ftl | 4 +-
22 files changed, 1015 insertions(+), 240 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/edefaa2f/src/main/java/freemarker/template/Template.java
----------------------------------------------------------------------
[03/12] incubator-freemarker git commit: Fixed incorrect listing of
valid roundingMode-s in extended Java decimal format parsing error message
Posted by dd...@apache.org.
Fixed incorrect listing of valid roundingMode-s in extended Java decimal format parsing error message
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/54bd25b0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/54bd25b0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/54bd25b0
Branch: refs/heads/2.3
Commit: 54bd25b0ec51f40ba6e231edf21bef56aa6a8330
Parents: 40ced2d
Author: ddekany <dd...@apache.org>
Authored: Tue Mar 13 09:41:35 2018 +0100
Committer: ddekany <dd...@apache.org>
Committed: Tue Mar 13 09:41:35 2018 +0100
----------------------------------------------------------------------
.../java/freemarker/core/ExtendedDecimalFormatParser.java | 5 ++++-
src/manual/en_US/book.xml | 7 +++++++
2 files changed, 11 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/54bd25b0/src/main/java/freemarker/core/ExtendedDecimalFormatParser.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/ExtendedDecimalFormatParser.java b/src/main/java/freemarker/core/ExtendedDecimalFormatParser.java
index c31171b..0c3286a 100644
--- a/src/main/java/freemarker/core/ExtendedDecimalFormatParser.java
+++ b/src/main/java/freemarker/core/ExtendedDecimalFormatParser.java
@@ -80,7 +80,10 @@ class ExtendedDecimalFormatParser {
} else if (value.equals(PARAM_VALUE_RND_UNNECESSARY)) {
parsedValue = RoundingMode.UNNECESSARY;
} else {
- throw new InvalidParameterValueException("Should be one of: u, d, c, f, hd, he, hu, un");
+ throw new InvalidParameterValueException("Should be one of: "
+ + PARAM_VALUE_RND_UP + ", " + PARAM_VALUE_RND_DOWN + ", " + PARAM_VALUE_RND_CEILING + ", "
+ + PARAM_VALUE_RND_FLOOR + ", " + PARAM_VALUE_RND_HALF_DOWN + ", "
+ + PARAM_VALUE_RND_HALF_EVEN + ", " + PARAM_VALUE_RND_UNNECESSARY);
}
if (_JavaVersions.JAVA_6 == null) {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/54bd25b0/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 3d8e321..231b607 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -27578,6 +27578,13 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
<para><literal>freemarker.ext.beans.HashAdapter.size()</literal>
was overridden for better performance.</para>
</listitem>
+
+ <listitem>
+ <para>Fixed incorrect listing of valid
+ <literal>roundingMode</literal>-s in <link
+ linkend="topic.extendedJavaDecimalFormat">extended Java decimal
+ format</link> parsing error message</para>
+ </listitem>
</itemizedlist>
</section>
</section>
[06/12] incubator-freemarker git commit: Changed
Configuration.xxx_INTERPOLATION_SYNTAX int values so that they don't overlap
with ..._TAG_SYNTAX values. Also,
added test case to ConfigurationTest for tag_syntax,
fixing incorrect error message along the
Posted by dd...@apache.org.
Changed Configuration.xxx_INTERPOLATION_SYNTAX int values so that they don't overlap with ..._TAG_SYNTAX values. Also, added test case to ConfigurationTest for tag_syntax, fixing incorrect error message along the way.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/eb6781b4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/eb6781b4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/eb6781b4
Branch: refs/heads/2.3
Commit: eb6781b4444d774c536f3da235972f1c34cd68bd
Parents: ca1ecf7
Author: ddekany <dd...@apache.org>
Authored: Fri Mar 16 18:46:11 2018 +0100
Committer: ddekany <dd...@apache.org>
Committed: Fri Mar 16 18:46:11 2018 +0100
----------------------------------------------------------------------
.../java/freemarker/template/Configuration.java | 6 +--
.../java/freemarker/template/_TemplateAPI.java | 4 +-
.../freemarker/template/ConfigurationTest.java | 44 +++++++++++++++++++-
3 files changed, 48 insertions(+), 6 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eb6781b4/src/main/java/freemarker/template/Configuration.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/Configuration.java b/src/main/java/freemarker/template/Configuration.java
index 34b055e..53fcd49 100644
--- a/src/main/java/freemarker/template/Configuration.java
+++ b/src/main/java/freemarker/template/Configuration.java
@@ -381,11 +381,11 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
public static final int SQUARE_BRACKET_TAG_SYNTAX = 2;
/** <code>${expression}</code> and the deprecated <code>#{expression; numFormat}</code> @since 2.3.28 */
- public static final int LEGACY_INTERPOLATION_SYNTAX = 0;
+ public static final int LEGACY_INTERPOLATION_SYNTAX = 20;
/** <code>${expression}</code> only (not <code>#{expression; numFormat}</code>) @since 2.3.28 */
- public static final int DOLLAR_INTERPOLATION_SYNTAX = 1;
+ public static final int DOLLAR_INTERPOLATION_SYNTAX = 21;
/** <code>[=expression]</code> @since 2.3.28 */
- public static final int SQUARE_BRACKET_INTERPOLATION_SYNTAX = 2;
+ public static final int SQUARE_BRACKET_INTERPOLATION_SYNTAX = 22;
public static final int AUTO_DETECT_NAMING_CONVENTION = 10;
public static final int LEGACY_NAMING_CONVENTION = 11;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eb6781b4/src/main/java/freemarker/template/_TemplateAPI.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/_TemplateAPI.java b/src/main/java/freemarker/template/_TemplateAPI.java
index 9b2c22d..124677a 100644
--- a/src/main/java/freemarker/template/_TemplateAPI.java
+++ b/src/main/java/freemarker/template/_TemplateAPI.java
@@ -154,8 +154,8 @@ public class _TemplateAPI {
&& tagSyntax != Configuration.SQUARE_BRACKET_TAG_SYNTAX
&& tagSyntax != Configuration.ANGLE_BRACKET_TAG_SYNTAX) {
throw new IllegalArgumentException("\"tag_syntax\" can only be set to one of these: "
- + "Configuration.AUTO_DETECT_TAG_SYNTAX, Configuration.ANGLE_BRACKET_SYNTAX, "
- + "or Configuration.SQUARE_BRACKET_SYNTAX");
+ + "Configuration.AUTO_DETECT_TAG_SYNTAX, Configuration.ANGLE_BRACKET_TAG_SYNTAX, "
+ + "or Configuration.SQUARE_BRACKET_TAG_SYNTAX");
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eb6781b4/src/test/java/freemarker/template/ConfigurationTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/template/ConfigurationTest.java b/src/test/java/freemarker/template/ConfigurationTest.java
index fa6b508..72c1291 100644
--- a/src/test/java/freemarker/template/ConfigurationTest.java
+++ b/src/test/java/freemarker/template/ConfigurationTest.java
@@ -1616,6 +1616,41 @@ public class ConfigurationTest extends TestCase {
assertEquals(Configuration.AUTO_DETECT_NAMING_CONVENTION, cfg.getNamingConvention());
}
+ public void testTagSyntaxSetting() throws TemplateException {
+ Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
+
+ // Default is "angle brackets":
+ assertEquals(Configuration.ANGLE_BRACKET_TAG_SYNTAX, cfg.getTagSyntax());
+
+ cfg.setSetting("tag_syntax", "angle_bracket");
+ assertEquals(Configuration.ANGLE_BRACKET_TAG_SYNTAX, cfg.getTagSyntax());
+
+ cfg.setSetting("tag_syntax", "square_bracket");
+ assertEquals(Configuration.SQUARE_BRACKET_TAG_SYNTAX, cfg.getTagSyntax());
+
+ cfg.setSetting("tag_syntax", "auto_detect");
+ assertEquals(Configuration.AUTO_DETECT_TAG_SYNTAX, cfg.getTagSyntax());
+
+ // Camel case:
+ cfg.setSetting("tagSyntax", "squareBracket");
+ assertEquals(Configuration.SQUARE_BRACKET_TAG_SYNTAX, cfg.getTagSyntax());
+
+ try {
+ cfg.setSetting("tag_syntax", "no_such_syntax");
+ fail();
+ } catch (TemplateException e) {
+ assertThat(e.getMessage(), containsString("no_such_syntax"));
+ }
+
+ // Catch an oversight that's easy to do:
+ try {
+ cfg.setTagSyntax(Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertThat(e.getMessage(), containsString("SQUARE_BRACKET_TAG_SYNTAX"));
+ }
+ }
+
public void testInterpolationSyntaxSetting() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
@@ -1641,7 +1676,14 @@ public class ConfigurationTest extends TestCase {
} catch (TemplateException e) {
assertThat(e.getMessage(), containsString("no_such_syntax"));
}
-
+
+ // Catch an oversight that's easy to do:
+ try {
+ cfg.setInterpolationSyntax(Configuration.SQUARE_BRACKET_TAG_SYNTAX);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertThat(e.getMessage(), containsString("SQUARE_BRACKET_INTERPOLATION_SYNTAX"));
+ }
}
public void testLazyImportsSetSetting() throws TemplateException {
[11/12] incubator-freemarker git commit: Cleaned up more lexer/parser
logic related to the handling of tag-closer delimiters ('>' and ']'). This
has yielded the following two change log entries (and some improvements in
error message quality, but that's
Posted by dd...@apache.org.
Cleaned up more lexer/parser logic related to the handling of tag-closer delimiters ('>' and ']'). This has yielded the following two change log entries (and some improvements in error message quality, but that's hardly noticeable):
1. When the incompatible_improvements setting is set to 2.3.28 (or greater), fixed legacy parser glitch where a tag can be closed with an illegal ] (when it's not part of an expression) despite that the tag syntax is set to angle brackets. For example <#if x] worked just like <#if x>. Note that it doesn't affect the legal usage of ], like <#if x[0]> works correctly without this fix as well.
2. Fixed parser bug that disallowed using > at the top-level inside an interpolation (${...}). It had the same reason why <#if x > y> doesn't work as naively expected, but there's no real ambiguity in ${x > y}, so now it's allowed. Note that ${(x > y)?c} and ${(x > y)?string('y', 'n')}, which are how booleans are commonly printed, have always worked, as the > operation is not on the top-level inside the interpolation.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/01294537
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/01294537
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/01294537
Branch: refs/heads/2.3
Commit: 01294537b882d06e2ac7df5b5fff590bf45f5227
Parents: f55f9d8
Author: ddekany <dd...@apache.org>
Authored: Mon Mar 19 22:19:37 2018 +0100
Committer: ddekany <dd...@apache.org>
Committed: Mon Mar 19 22:19:37 2018 +0100
----------------------------------------------------------------------
.../java/freemarker/template/Configuration.java | 4 +
src/main/javacc/FTL.jj | 77 ++++++++++++++++----
src/manual/en_US/book.xml | 27 +++++++
.../core/InterpolationSyntaxTest.java | 33 +++++++--
.../core/ParsingErrorMessagesTest.java | 66 ++++++-----------
.../templates/string-builtins3.ftl | 4 +-
6 files changed, 147 insertions(+), 64 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/01294537/src/main/java/freemarker/template/Configuration.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/Configuration.java b/src/main/java/freemarker/template/Configuration.java
index 3114747..5376dda 100644
--- a/src/main/java/freemarker/template/Configuration.java
+++ b/src/main/java/freemarker/template/Configuration.java
@@ -885,6 +885,10 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
* (Of course, the parameter default value expression is still evaluated in the context of the called
* macro or function.) Similarly, {@code .macro_caller_template_name} (which itself was added in 2.3.28),
* when used in a macro call argument, won't be incorrectly evaluated in the context of the called macro.
+ * <li><p>Fixed legacy parser glitch where a tag can be closed with an illegal {@code ]} (when it's not part
+ * of an expression) despite that the tag syntax is set to angle brackets. For example {@code <#if x]}
+ * worked just like {@code <#if x>}. Note that it doesn't affect the legal usage of {@code ]}, like
+ * {@code <#if x[0]>} works correctly without this fix as well.
* </ul>
* </li>
* </ul>
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/01294537/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/src/main/javacc/FTL.jj b/src/main/javacc/FTL.jj
index 26f3368..630ef39 100644
--- a/src/main/javacc/FTL.jj
+++ b/src/main/javacc/FTL.jj
@@ -667,7 +667,7 @@ TOKEN_MGR_DECLS:
if (!strictSyntaxMode) {
// Legacy feature (or bug?): Tag syntax never gets estabilished in non-strict mode.
- // We do establilish the naming convention though.
+ // We do establish the naming convention though.
checkNamingConvention(tok, tokenNamingConvention);
SwitchTo(newLexState);
return;
@@ -684,6 +684,24 @@ TOKEN_MGR_DECLS:
// We only get here if this is a strict FTL tag.
directiveSyntaxEstablished = true;
+ if (incompatibleImprovements >= _TemplateAPI.VERSION_INT_2_3_28
+ || interpolationSyntax == SQUARE_BRACKET_INTERPOLATION_SYNTAX) {
+ // For END_xxx tags, as they can't contain expressions, the whole tag is a single token. So this is the only
+ // chance to check if we got something inconsistent like `</#if]`. (We can't do this at the #CLOSE_TAG1 or
+ // such, as at that point it's possible that the tag syntax is not yet established.)
+ char lastChar = image.charAt(image.length() - 1);
+ // Is it an end tag?
+ if (lastChar == ']' || lastChar == '>') {
+ if (!squBracTagSyntax && lastChar != '>' || squBracTagSyntax && lastChar != ']') {
+ throw new TokenMgrError(
+ "The tag shouldn't end with \""+ lastChar + "\".",
+ TokenMgrError.LEXICAL_ERROR,
+ tok.beginLine, tok.beginColumn,
+ tok.endLine, tok.endColumn);
+ }
+ } // if end-tag
+ }
+
checkNamingConvention(tok, tokenNamingConvention);
SwitchTo(newLexState);
@@ -825,9 +843,6 @@ TOKEN_MGR_DECLS:
}
private void endInterpolation(Token closingTk) {
- if (postInterpolationLexState == -1) {
- throw newUnexpectedClosingTokenException(closingTk);
- }
SwitchTo(postInterpolationLexState);
postInterpolationLexState = -1;
}
@@ -1298,11 +1313,19 @@ TOKEN:
{
if (bracketNesting > 0) {
--bracketNesting;
- } else if (interpolationSyntax == SQUARE_BRACKET_INTERPOLATION_SYNTAX
- && (postInterpolationLexState != -1 || !squBracTagSyntax)) {
+ } else if (interpolationSyntax == SQUARE_BRACKET_INTERPOLATION_SYNTAX && postInterpolationLexState != -1) {
endInterpolation(matchedToken);
} else {
- // Glitch where you can close any tag with `]`, like `<#if x]`. We keep it for backward compatibility.
+ // There's a legacy glitch where you can always close a tag with `]`, like `<#if x]`. We have to keep that
+ // working for backward compatibility, hence we don't always throw at !squBracTagSyntax:
+ if (!squBracTagSyntax
+ && (incompatibleImprovements >= _TemplateAPI.VERSION_INT_2_3_28
+ || interpolationSyntax == SQUARE_BRACKET_INTERPOLATION_SYNTAX)
+ || postInterpolationLexState != -1 /* We're in an interpolation => We aren't in a tag */) {
+ throw newUnexpectedClosingTokenException(matchedToken);
+ }
+
+ // Close tag, either legally or to emulate legacy glitch:
matchedToken.kind = DIRECTIVE_END;
if (inFTLHeader) {
eatNewline();
@@ -1336,7 +1359,7 @@ TOKEN:
{
if (curlyBracketNesting > 0) {
--curlyBracketNesting;
- } else if (interpolationSyntax != SQUARE_BRACKET_INTERPOLATION_SYNTAX) {
+ } else if (interpolationSyntax != SQUARE_BRACKET_INTERPOLATION_SYNTAX && postInterpolationLexState != -1) {
endInterpolation(matchedToken);
} else {
throw newUnexpectedClosingTokenException(matchedToken);
@@ -1529,19 +1552,47 @@ TOKEN:
{
<DIRECTIVE_END : ">">
{
- if (inFTLHeader) eatNewline();
- inFTLHeader = false;
- if (squBracTagSyntax) {
+ if (inFTLHeader) {
+ eatNewline();
+ inFTLHeader = false;
+ }
+ if (squBracTagSyntax || postInterpolationLexState != -1 /* We are in an interpolation */) {
matchedToken.kind = NATURAL_GT;
} else {
+ if (directiveSyntaxEstablished && ( incompatibleImprovements >= _TemplateAPI.VERSION_INT_2_3_28
+ || interpolationSyntax == SQUARE_BRACKET_INTERPOLATION_SYNTAX)) {
+ if (squBracTagSyntax) {
+ throw new TokenMgrError(
+ "The tag shouldn't end with \"" + image.charAt(image.length() - 1) + "\".",
+ TokenMgrError.LEXICAL_ERROR,
+ matchedToken.beginLine, matchedToken.beginColumn,
+ matchedToken.endLine, matchedToken.endColumn);
+ }
+ }
+
SwitchTo(DEFAULT);
}
}
|
<EMPTY_DIRECTIVE_END : "/>" | "/]">
{
- if (inFTLHeader) eatNewline();
- inFTLHeader = false;
+ if (directiveSyntaxEstablished && (incompatibleImprovements >= _TemplateAPI.VERSION_INT_2_3_28
+ || interpolationSyntax == SQUARE_BRACKET_INTERPOLATION_SYNTAX)) {
+ String image = matchedToken.image;
+ char lastChar = image.charAt(image.length() - 1);
+ if (!squBracTagSyntax && lastChar != '>' || squBracTagSyntax && lastChar != ']') {
+ throw new TokenMgrError(
+ "The tag shouldn't end with \""+ lastChar + "\".",
+ TokenMgrError.LEXICAL_ERROR,
+ matchedToken.beginLine, matchedToken.beginColumn,
+ matchedToken.endLine, matchedToken.endColumn);
+ }
+ }
+
+ if (inFTLHeader) {
+ eatNewline();
+ inFTLHeader = false;
+ }
SwitchTo(DEFAULT);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/01294537/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index ac58a2a..f06b759 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -27788,6 +27788,33 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
</listitem>
<listitem>
+ <para>When the <link
+ linkend="pgui_config_incompatible_improvements_how_to_set"><literal>incompatible_improvements</literal>
+ setting</link> is set to 2.3.28 (or greater), fixed legacy
+ parser glitch where a tag can be closed with an illegal
+ <literal>]</literal> (when it's not part of an expression)
+ despite that the tag syntax is set to angle brackets. For
+ example <literal><#if x]</literal> worked just like
+ <literal><#if x></literal>. Note that it doesn't affect
+ the legal usage of <literal>]</literal>, like <literal><#if
+ x[0]></literal> works correctly without this fix as
+ well.</para>
+ </listitem>
+
+ <listitem>
+ <para>Fixed parser bug that disallowed using
+ <literal>></literal> at the top-level inside an interpolation
+ (<literal>${...}</literal>). It had the same reason why
+ <literal><#if x > y></literal> doesn't work as naively
+ expected, but there's no real ambiguity in <literal>${x >
+ y}</literal>, so now it's allowed. Note that <literal>${(x >
+ y)?c}</literal> and <literal>${(x > y)?string('y',
+ 'n')}</literal>, which are how booleans are commonly printed,
+ have always worked, as the <literal>></literal> operation is
+ not on the top-level inside the interpolation.</para>
+ </listitem>
+
+ <listitem>
<para>Fixed incorrect listing of valid
<literal>roundingMode</literal>-s in <link
linkend="topic.extendedJavaDecimalFormat">extended Java decimal
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/01294537/src/test/java/freemarker/core/InterpolationSyntaxTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/InterpolationSyntaxTest.java b/src/test/java/freemarker/core/InterpolationSyntaxTest.java
index 2322d99..9a56f13 100644
--- a/src/test/java/freemarker/core/InterpolationSyntaxTest.java
+++ b/src/test/java/freemarker/core/InterpolationSyntaxTest.java
@@ -7,6 +7,7 @@ import java.io.StringWriter;
import org.junit.Test;
+import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.test.TemplateTest;
@@ -27,6 +28,9 @@ public class InterpolationSyntaxTest extends TemplateTest {
assertOutput("<@r'${1} #{1} [=1]'?interpret />", "1 1 [=1]");
assertOutput("${'\"${1} #{1} [=1]\"'?eval}", "1 1 [=1]");
+
+ assertOutput("<#setting booleanFormat='y,n'>${2>1}", "y"); // Not an error since 2.3.28
+ assertOutput("[#ftl][#setting booleanFormat='y,n']${2>1}", "y"); // Not an error since 2.3.28
}
@Test
@@ -78,7 +82,9 @@ public class InterpolationSyntaxTest extends TemplateTest {
assertErrorContains("[=", "end of file");
assertErrorContains("[=1", "unclosed \"[\"");
- assertErrorContains("[=1}", "\"}\"", "open");
+
+ assertOutput("<#setting booleanFormat='y,n'>[=2>1]", "y");
+ assertOutput("[#ftl][#setting booleanFormat='y,n'][=2>1]", "y");
assertOutput("[='[\\=1]']", "[=1]");
assertOutput("[='[\\=1][=2]']", "12"); // Usual legacy interpolation escaping glitch...
@@ -101,18 +107,35 @@ public class InterpolationSyntaxTest extends TemplateTest {
@Test
public void legacyTagSyntaxGlitchStillWorksTest() throws Exception {
- String ftl = "<#if [true][0]]t<#else]f</#if]";
+ String badFtl1 = "<#if [true][0]]OK</#if>";
+ String badFtl2 = "<#if true>OK</#if]";
+ String badFtl3 = "<#assign x = 'OK'/]${x}";
+ String badFtl4 = " <#t/]OK\n";
+ getConfiguration().setIncompatibleImprovements(Configuration.VERSION_2_3_27);
for (int syntax : new int[] { LEGACY_INTERPOLATION_SYNTAX, DOLLAR_INTERPOLATION_SYNTAX }) {
getConfiguration().setInterpolationSyntax(syntax);
- assertOutput(ftl, "t");
+ assertOutput(badFtl1, "OK");
+ assertOutput(badFtl2, "OK");
+ assertOutput(badFtl3, "OK");
+ assertOutput(badFtl4, "OK");
}
// Legacy tag closing glitch is not emulated with this:
getConfiguration().setInterpolationSyntax(SQUARE_BRACKET_INTERPOLATION_SYNTAX);
- assertErrorContains(ftl, "\"]\"");
+ assertErrorContains(badFtl1, "\"]\"");
+ assertErrorContains(badFtl2, "\"]\"");
+ assertErrorContains(badFtl3, "\"]\"");
+ assertErrorContains(badFtl4, "\"]\"");
+
+ getConfiguration().setInterpolationSyntax(LEGACY_INTERPOLATION_SYNTAX);
+ getConfiguration().setIncompatibleImprovements(Configuration.VERSION_2_3_28);
+ assertErrorContains(badFtl1, "\"]\"");
+ assertErrorContains(badFtl2, "\"]\"");
+ assertErrorContains(badFtl3, "\"]\"");
+ assertErrorContains(badFtl4, "\"]\"");
}
-
+
@Test
public void errorMessagesAreSquareBracketInterpolationSyntaxAwareTest() throws Exception {
assertErrorContains("<#if ${x}></#if>", "${...}", "${myExpression}");
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/01294537/src/test/java/freemarker/core/ParsingErrorMessagesTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/ParsingErrorMessagesTest.java b/src/test/java/freemarker/core/ParsingErrorMessagesTest.java
index e471a31..0081662 100644
--- a/src/test/java/freemarker/core/ParsingErrorMessagesTest.java
+++ b/src/test/java/freemarker/core/ParsingErrorMessagesTest.java
@@ -19,23 +19,23 @@
package freemarker.core;
-import static org.junit.Assert.*;
-
-import java.io.IOException;
+import static freemarker.template.Configuration.*;
import org.junit.Test;
import freemarker.template.Configuration;
-import freemarker.template.Template;
-import freemarker.template.utility.StringUtil;
+import freemarker.test.TemplateTest;
-public class ParsingErrorMessagesTest {
+public class ParsingErrorMessagesTest extends TemplateTest {
- private Configuration cfg = new Configuration(Configuration.VERSION_2_3_21);
- {
+ @Override
+ protected Configuration createConfiguration() throws Exception {
+ Configuration cfg = super.createConfiguration();
+ cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_21);
cfg.setTagSyntax(Configuration.AUTO_DETECT_TAG_SYNTAX);
+ return cfg;
}
-
+
@Test
public void testNeedlessInterpolation() {
assertErrorContains("<#if ${x} == 3></#if>", "instead of ${");
@@ -77,45 +77,23 @@ public class ParsingErrorMessagesTest {
}
@Test
- public void testInterpolatingClosingsErrors() {
- assertErrorContains("${x", "unclosed");
+ public void testInterpolatingClosingsErrors() throws Exception {
+ assertErrorContains("<#ftl>${x", "unclosed");
assertErrorContains("<#assign x = x}>", "\"}\"", "open");
- // TODO assertErrorContains("<#assign x = '${x'>", "unclosed");
- }
-
- private void assertErrorContains(String ftl, String... expectedSubstrings) {
- assertErrorContains(false, ftl, expectedSubstrings);
- assertErrorContains(true, ftl, expectedSubstrings);
- }
-
- private void assertErrorContains(boolean squareTags, String ftl, String... expectedSubstrings) {
- try {
- if (squareTags) {
- ftl = ftl.replace('<', '[').replace('>', ']');
- }
- new Template("adhoc", ftl, cfg);
- fail("The tempalte had to fail");
- } catch (ParseException e) {
- String msg = e.getMessage();
- for (String needle: expectedSubstrings) {
- if (needle.startsWith("\\!")) {
- String netNeedle = needle.substring(2);
- if (msg.contains(netNeedle)) {
- fail("The message shouldn't contain substring " + StringUtil.jQuote(netNeedle) + ":\n" + msg);
- }
- } else if (!msg.contains(needle)) {
- fail("The message didn't contain substring " + StringUtil.jQuote(needle) + ":\n" + msg);
- }
- }
- showError(e);
- } catch (IOException e) {
- // Won't happen
- throw new RuntimeException(e);
+ assertOutput("<#assign x = '${x'>", ""); // Legacy glitch... should fail in theory.
+
+ for (int syntax : new int[] { LEGACY_INTERPOLATION_SYNTAX, DOLLAR_INTERPOLATION_SYNTAX }) {
+ getConfiguration().setInterpolationSyntax(syntax);
+ assertErrorContains("<#ftl>${'x']", "\"]\"", "open");
+ super.assertErrorContains("<#ftl>${'x'>", "end of file");
+ super.assertErrorContains("[#ftl]${'x'>", "end of file");
}
}
- private void showError(Throwable e) {
- //System.out.println(e);
+ protected Throwable assertErrorContains(String ftl, String... expectedSubstrings) {
+ super.assertErrorContains(ftl, expectedSubstrings);
+ ftl = ftl.replace('<', '[').replace('>', ']');
+ return super.assertErrorContains(ftl, expectedSubstrings);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/01294537/src/test/resources/freemarker/test/templatesuite/templates/string-builtins3.ftl
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/test/templatesuite/templates/string-builtins3.ftl b/src/test/resources/freemarker/test/templatesuite/templates/string-builtins3.ftl
index 77389fa..06e7dd7 100644
--- a/src/test/resources/freemarker/test/templatesuite/templates/string-builtins3.ftl
+++ b/src/test/resources/freemarker/test/templatesuite/templates/string-builtins3.ftl
@@ -198,7 +198,7 @@
<@assertEquals expected='aa' actual=27?lower_abc />
<@assertEquals expected='ab' actual=28?lower_abc />
<@assertEquals expected='cv' actual=100?lower_abc />
-<@assertFails messageRegexp='0|at least 1']>
+<@assertFails messageRegexp='0|at least 1'>
${0?lower_abc}
</...@assertFails>
<@assertFails messageRegexp='0|at least 1'>
@@ -214,7 +214,7 @@
<@assertEquals expected='AA' actual=27?upper_abc />
<@assertEquals expected='AB' actual=28?upper_abc />
<@assertEquals expected='CV' actual=100?upper_abc />
-<@assertFails messageRegexp='0|at least 1']>
+<@assertFails messageRegexp='0|at least 1'>
${0?upper_abc}
</...@assertFails>
<@assertFails messageRegexp='0|at least 1'>
[10/12] incubator-freemarker git commit: (Some more cleanup in
FTL.jj... as far as BC allows us)
Posted by dd...@apache.org.
(Some more cleanup in FTL.jj... as far as BC allows us)
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/f55f9d89
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/f55f9d89
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/f55f9d89
Branch: refs/heads/2.3
Commit: f55f9d8919aa7fce3b88af37ac55ea04eb701033
Parents: eacd516
Author: ddekany <dd...@apache.org>
Authored: Mon Mar 19 00:24:15 2018 +0100
Committer: ddekany <dd...@apache.org>
Committed: Mon Mar 19 00:24:15 2018 +0100
----------------------------------------------------------------------
src/main/javacc/FTL.jj | 64 +++++++++++---------
.../core/InterpolationSyntaxTest.java | 1 +
2 files changed, 35 insertions(+), 30 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f55f9d89/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/src/main/javacc/FTL.jj b/src/main/javacc/FTL.jj
index d3be697..26f3368 100644
--- a/src/main/javacc/FTL.jj
+++ b/src/main/javacc/FTL.jj
@@ -116,29 +116,29 @@ public class FMParser {
* The template associated with this parser.
* @param reader
* The character stream to use as input
- * @param strictEscapeSyntax
+ * @param strictSyntaxMode
* Whether FreeMarker directives must start with a #
*
* @Deprecated This is an internal API of FreeMarker; will be removed in 2.4.
*/
- public FMParser(Template template, Reader reader, boolean strictEscapeSyntax, boolean stripWhitespace) {
- this(template, reader, strictEscapeSyntax, stripWhitespace, Configuration.AUTO_DETECT_TAG_SYNTAX);
+ public FMParser(Template template, Reader reader, boolean strictSyntaxMode, boolean stripWhitespace) {
+ this(template, reader, strictSyntaxMode, stripWhitespace, Configuration.AUTO_DETECT_TAG_SYNTAX);
}
/**
* @Deprecated This is an internal API of FreeMarker; will be changed in 2.4.
*/
- public FMParser(Template template, Reader reader, boolean strictEscapeSyntax, boolean stripWhitespace, int tagSyntax) {
- this(template, reader, strictEscapeSyntax, stripWhitespace, tagSyntax,
+ public FMParser(Template template, Reader reader, boolean strictSyntaxMode, boolean stripWhitespace, int tagSyntax) {
+ this(template, reader, strictSyntaxMode, stripWhitespace, tagSyntax,
Configuration.PARSED_DEFAULT_INCOMPATIBLE_ENHANCEMENTS);
}
/**
* @Deprecated This is an internal API of FreeMarker; will be changed in 2.4.
*/
- public FMParser(Template template, Reader reader, boolean strictEscapeSyntax, boolean stripWhitespace,
+ public FMParser(Template template, Reader reader, boolean strictSyntaxMode, boolean stripWhitespace,
int tagSyntax, int incompatibleImprovements) {
- this(template, reader, strictEscapeSyntax, stripWhitespace,
+ this(template, reader, strictSyntaxMode, stripWhitespace,
tagSyntax, Configuration.AUTO_DETECT_NAMING_CONVENTION, incompatibleImprovements);
}
@@ -240,7 +240,7 @@ public class FMParser {
token_source.setParser(this);
- token_source.strictEscapeSyntax = pCfg.getStrictSyntaxMode();
+ token_source.strictSyntaxMode = pCfg.getStrictSyntaxMode();
int tagSyntax = pCfg.getTagSyntax();
switch (tagSyntax) {
@@ -287,7 +287,7 @@ public class FMParser {
token_source.initialNamingConvention = parentTokenSource.initialNamingConvention;
token_source.namingConvention = parentTokenSource.namingConvention;
token_source.namingConventionEstabilisher = parentTokenSource.namingConventionEstabilisher;
- token_source.SwitchTo(NODIRECTIVE);
+ token_source.SwitchTo(NO_DIRECTIVE);
this.outputFormat = outputFormat;
recalculateAutoEscapingField();
@@ -621,11 +621,11 @@ TOKEN_MGR_DECLS:
* Keeps track of how deeply nested we have the hash literals. This is necessary since we need to be able to
* distinguish the } used to close a hash literal and the one used to close a ${
*/
- private int hashLiteralNesting;
+ private int curlyBracketNesting;
private int parenthesisNesting;
private int bracketNesting;
private boolean inFTLHeader;
- boolean strictEscapeSyntax,
+ boolean strictSyntaxMode,
squBracTagSyntax,
autodetectTagSyntax,
directiveSyntaxEstablished,
@@ -649,7 +649,7 @@ TOKEN_MGR_DECLS:
// Non-strict syntax (deprecated) only supports legacy naming convention.
// We didn't push this on the tokenizer because it made it slow, so we filter here.
- if (!strictEscapeSyntax
+ if (!strictSyntaxMode
&& (tokenNamingConvention == Configuration.CAMEL_CASE_NAMING_CONVENTION)
&& !isStrictTag(image)) {
tok.kind = STATIC_TEXT_NON_WS;
@@ -665,7 +665,7 @@ TOKEN_MGR_DECLS:
return;
}
- if (!strictEscapeSyntax) {
+ if (!strictSyntaxMode) {
// Legacy feature (or bug?): Tag syntax never gets estabilished in non-strict mode.
// We do establilish the naming convention though.
checkNamingConvention(tok, tokenNamingConvention);
@@ -824,22 +824,21 @@ TOKEN_MGR_DECLS:
SwitchTo(FM_EXPRESSION);
}
- /**
- * @param tok
- * Assumed to be an '}', or something that is the closing pair of another "mirror image" character.
- */
- private void endInterpolation(Token tok) {
+ private void endInterpolation(Token closingTk) {
if (postInterpolationLexState == -1) {
- char c = tok.image.charAt(0);
- throw new TokenMgrError(
- "You can't have an \"" + c + "\" here, as there's nothing open that it could close.",
- TokenMgrError.LEXICAL_ERROR,
- tok.beginLine, tok.beginColumn,
- tok.endLine, tok.endColumn);
+ throw newUnexpectedClosingTokenException(closingTk);
}
SwitchTo(postInterpolationLexState);
postInterpolationLexState = -1;
}
+
+ private TokenMgrError newUnexpectedClosingTokenException(Token closingTk) {
+ return new TokenMgrError(
+ "You can't have an \"" + closingTk.image + "\" here, as there's nothing open that it could close.",
+ TokenMgrError.LEXICAL_ERROR,
+ closingTk.beginLine, closingTk.beginColumn,
+ closingTk.endLine, closingTk.endColumn);
+ }
private void eatNewline() {
int charsRead = 0;
@@ -1095,7 +1094,7 @@ TOKEN:
matchedToken.kind = STATIC_TEXT_NON_WS;
} else if (firstChar == '[' && !squBracTagSyntax) {
matchedToken.kind = STATIC_TEXT_NON_WS;
- } else if (strictEscapeSyntax) {
+ } else if (strictSyntaxMode) {
String dn = matchedToken.image;
int index = dn.indexOf('#');
dn = dn.substring(index + 1);
@@ -1149,7 +1148,7 @@ TOKEN:
}
}
-<DEFAULT, NODIRECTIVE> TOKEN :
+<DEFAULT, NO_DIRECTIVE> TOKEN :
{
<STATIC_TEXT_WS : ("\n" | "\r" | "\t" | " ")+>
|
@@ -1330,13 +1329,18 @@ TOKEN:
|
<OPENING_CURLY_BRACKET : "{">
{
- ++hashLiteralNesting;
+ ++curlyBracketNesting;
}
|
<CLOSING_CURLY_BRACKET : "}">
{
- if (hashLiteralNesting == 0) endInterpolation(matchedToken);
- else --hashLiteralNesting;
+ if (curlyBracketNesting > 0) {
+ --curlyBracketNesting;
+ } else if (interpolationSyntax != SQUARE_BRACKET_INTERPOLATION_SYNTAX) {
+ endInterpolation(matchedToken);
+ } else {
+ throw newUnexpectedClosingTokenException(matchedToken);
+ }
}
|
<IN : "in">
@@ -4341,7 +4345,7 @@ void HeaderElement() :
} else if (ks.equalsIgnoreCase("STRIP_TEXT") || ks.equals("stripText")) {
this.stripText = getBoolean(exp, true);
} else if (ks.equalsIgnoreCase("STRICT_SYNTAX") || ks.equals("strictSyntax")) {
- this.token_source.strictEscapeSyntax = getBoolean(exp, true);
+ this.token_source.strictSyntaxMode = getBoolean(exp, true);
} else if (ks.equalsIgnoreCase("auto_esc") || ks.equals("autoEsc")) {
if (getBoolean(exp, false)) {
autoEscRequester = key;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f55f9d89/src/test/java/freemarker/core/InterpolationSyntaxTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/InterpolationSyntaxTest.java b/src/test/java/freemarker/core/InterpolationSyntaxTest.java
index 8c57d36..2322d99 100644
--- a/src/test/java/freemarker/core/InterpolationSyntaxTest.java
+++ b/src/test/java/freemarker/core/InterpolationSyntaxTest.java
@@ -78,6 +78,7 @@ public class InterpolationSyntaxTest extends TemplateTest {
assertErrorContains("[=", "end of file");
assertErrorContains("[=1", "unclosed \"[\"");
+ assertErrorContains("[=1}", "\"}\"", "open");
assertOutput("[='[\\=1]']", "[=1]");
assertOutput("[='[\\=1][=2]']", "12"); // Usual legacy interpolation escaping glitch...
[09/12] incubator-freemarker git commit: Cleanup related to [=...],
also some missing functionality added.
Posted by dd...@apache.org.
Cleanup related to [=...], also some missing functionality added.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/eacd5168
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/eacd5168
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/eacd5168
Branch: refs/heads/2.3
Commit: eacd51689fb301d4d128bc26dd9e96781474fd89
Parents: df4dc52
Author: ddekany <dd...@apache.org>
Authored: Sun Mar 18 18:37:23 2018 +0100
Committer: ddekany <dd...@apache.org>
Committed: Sun Mar 18 18:37:23 2018 +0100
----------------------------------------------------------------------
.../java/freemarker/core/DollarVariable.java | 11 ++++--
.../java/freemarker/core/NumericalOutput.java | 5 ++-
.../java/freemarker/core/StringLiteral.java | 6 +--
.../ext/beans/OverloadedNumberUtil.java | 2 +-
.../java/freemarker/template/Configuration.java | 3 +-
src/main/java/freemarker/template/Template.java | 22 +++++++++++
src/main/javacc/FTL.jj | 41 ++++++++------------
.../core/InterpolationSyntaxTest.java | 8 ++++
8 files changed, 64 insertions(+), 34 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eacd5168/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 7e7bd1f..12fa02a 100644
--- a/src/main/java/freemarker/core/DollarVariable.java
+++ b/src/main/java/freemarker/core/DollarVariable.java
@@ -22,11 +22,15 @@ package freemarker.core;
import java.io.IOException;
import java.io.Writer;
+import freemarker.template.Configuration;
import freemarker.template.TemplateException;
import freemarker.template.utility.StringUtil;
/**
- * An instruction that outputs the value of an <tt>Expression</tt>.
+ * An interpolation like <code>${exp}</code> or {@code [=exp]}. The class name is the remnant of old times, but as
+ * some users are using the package-visible AST API, it wasn't renamed.
+ *
+ * @see NumericalOutput
*/
final class DollarVariable extends Interpolation {
@@ -99,10 +103,11 @@ final class DollarVariable extends Interpolation {
@Override
protected String dump(boolean canonical, boolean inStringLiteral) {
StringBuilder sb = new StringBuilder();
- sb.append("${");
+ int syntax = getTemplate().getInterpolationSyntax();
+ sb.append(syntax != Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX ? "${" : "[=");
final String exprCF = expression.getCanonicalForm();
sb.append(inStringLiteral ? StringUtil.FTLStringLiteralEnc(exprCF, '"') : exprCF);
- sb.append("}");
+ sb.append(syntax != Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX ? "}" : "]");
if (!canonical && expression != escapedExpression) {
sb.append(" auto-escaped");
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eacd5168/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 08e8301..c897bd1 100644
--- a/src/main/java/freemarker/core/NumericalOutput.java
+++ b/src/main/java/freemarker/core/NumericalOutput.java
@@ -28,7 +28,10 @@ import freemarker.template.TemplateException;
import freemarker.template.utility.StringUtil;
/**
- * An instruction that outputs the value of a numerical expression.
+ * An interpolation like <code>#{numericalExp; format}</code>; it's deprecated, but still supported. The class name is
+ * the remnant of old times, but as some users are using the package-visible AST API, it wasn't renamed.
+ *
+ * @see DollarVariable
*/
final class NumericalOutput extends Interpolation {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eacd5168/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 6d53c17..c116498 100644
--- a/src/main/java/freemarker/core/StringLiteral.java
+++ b/src/main/java/freemarker/core/StringLiteral.java
@@ -54,9 +54,9 @@ final class StringLiteral extends Expression implements TemplateScalarModel {
if (value.length() > 3 && (
(intSyn == Configuration.LEGACY_INTERPOLATION_SYNTAX
|| intSyn == Configuration.DOLLAR_INTERPOLATION_SYNTAX)
- && (value.indexOf("${") >= 0
- || intSyn == Configuration.LEGACY_INTERPOLATION_SYNTAX && value.indexOf("#{") >= 0)
- || intSyn == Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX && value.indexOf("[=") >= 0)) {
+ && (value.indexOf("${") != -1
+ || intSyn == Configuration.LEGACY_INTERPOLATION_SYNTAX && value.indexOf("#{") != -1)
+ || intSyn == Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX && value.indexOf("[=") != -1)) {
try {
SimpleCharStream simpleCharacterStream = new SimpleCharStream(
new StringReader(value),
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eacd5168/src/main/java/freemarker/ext/beans/OverloadedNumberUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/beans/OverloadedNumberUtil.java b/src/main/java/freemarker/ext/beans/OverloadedNumberUtil.java
index 352d1de..fbb2fc0 100644
--- a/src/main/java/freemarker/ext/beans/OverloadedNumberUtil.java
+++ b/src/main/java/freemarker/ext/beans/OverloadedNumberUtil.java
@@ -89,7 +89,7 @@ class OverloadedNumberUtil {
* @param num the number to coerce
* @param typeFlags the type flags of the target parameter position; see {@link TypeFlags}
*
- * @returns The original number or a {@link NumberWithFallbackType}, depending on the actual value and the types
+ * @return The original number or a {@link NumberWithFallbackType}, depending on the actual value and the types
* indicated in the {@code targetNumTypes} parameter.
*/
static Number addFallbackType(final Number num, final int typeFlags) {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eacd5168/src/main/java/freemarker/template/Configuration.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/Configuration.java b/src/main/java/freemarker/template/Configuration.java
index 53fcd49..3114747 100644
--- a/src/main/java/freemarker/template/Configuration.java
+++ b/src/main/java/freemarker/template/Configuration.java
@@ -2409,8 +2409,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
}
/**
- * Determines the interpolation syntax (like <code>${x}</code> VS <code>[=x]</code>) of the template files in which
- * there's no {@code #ftl} hader with {@code interpolation_syntax} parameter. The
+ * Determines the interpolation syntax (like <code>${x}</code> VS <code>[=x]</code>) of the template files. The
* {@code interpolationSyntax} parameter must be one of {@link Configuration#LEGACY_INTERPOLATION_SYNTAX},
* {@link Configuration#DOLLAR_INTERPOLATION_SYNTAX}, and {@link Configuration#SQUARE_BRACKET_INTERPOLATION_SYNTAX}.
*
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eacd5168/src/main/java/freemarker/template/Template.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/Template.java b/src/main/java/freemarker/template/Template.java
index c95be5e..f21fffc 100644
--- a/src/main/java/freemarker/template/Template.java
+++ b/src/main/java/freemarker/template/Template.java
@@ -87,6 +87,7 @@ public class Template extends Configurable {
private TemplateElement rootElement;
private String encoding, defaultNS;
private Object customLookupCondition;
+ private int interpolationSyntax;
private int actualTagSyntax;
private int actualNamingConvention;
private boolean autoEscaping;
@@ -258,6 +259,7 @@ public class Template extends Configurable {
rootElement = null;
}
this.actualTagSyntax = parser._getLastTagSyntax();
+ this.interpolationSyntax = actualParserConfiguration.getInterpolationSyntax();
this.actualNamingConvention = parser._getLastNamingConvention();
} catch (TokenMgrError exc) {
// TokenMgrError VS ParseException is not an interesting difference for the user, so we just convert it
@@ -640,6 +642,8 @@ public class Template extends Configurable {
* returns whatever the default is in the current configuration, so it's maybe
* {@link Configuration#AUTO_DETECT_TAG_SYNTAX}.
*
+ * @see Configuration#setTagSyntax(int)
+ *
* @since 2.3.20
*/
public int getActualTagSyntax() {
@@ -647,12 +651,30 @@ public class Template extends Configurable {
}
/**
+ * Returns the interpolation syntax the parser has used for this template. Because the interpolation syntax is
+ * never auto-detected, it's not called "getActualInterpolationSyntax" (unlike {@link #getActualTagSyntax()}).
+ *
+ * @return A constant like {@link Configuration#LEGACY_INTERPOLATION_SYNTAX},
+ * {@link Configuration#DOLLAR_INTERPOLATION_SYNTAX}, or
+ * {@link Configuration#SQUARE_BRACKET_INTERPOLATION_SYNTAX}.
+ *
+ * @see Configuration#setInterpolationSyntax(int)
+ *
+ * @since 2.3.28
+ */
+ public int getInterpolationSyntax() {
+ return interpolationSyntax;
+ }
+
+ /**
* Returns the naming convention the parser has chosen for this template. If it could be determined, it's
* {@link Configuration#LEGACY_NAMING_CONVENTION} or {@link Configuration#CAMEL_CASE_NAMING_CONVENTION}. If it
* couldn't be determined (like because there no identifier that's part of the template language was used where
* the naming convention matters), this returns whatever the default is in the current configuration, so it's maybe
* {@link Configuration#AUTO_DETECT_TAG_SYNTAX}.
*
+ * @see Configuration#setNamingConvention(int)
+ *
* @since 2.3.23
*/
public int getActualNamingConvention() {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eacd5168/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/src/main/javacc/FTL.jj b/src/main/javacc/FTL.jj
index 4134b70..d3be697 100644
--- a/src/main/javacc/FTL.jj
+++ b/src/main/javacc/FTL.jj
@@ -1194,7 +1194,7 @@ TOKEN:
<#ESCAPED_CHAR :
"\\"
(
- ("n" | "t" | "r" | "f" | "b" | "g" | "l" | "a" | "\\" | "'" | "\"" | "$" | "{" | "=")
+ ("n" | "t" | "r" | "f" | "b" | "g" | "l" | "a" | "\\" | "'" | "\"" | "{" | "=")
|
("x" ["0"-"9", "A"-"F", "a"-"f"])
)
@@ -2329,11 +2329,11 @@ StringLiteral StringLiteral(boolean interpolate) :
int interpolationSyntax = pCfg.getInterpolationSyntax();
if ((interpolationSyntax == LEGACY_INTERPOLATION_SYNTAX
|| interpolationSyntax == DOLLAR_INTERPOLATION_SYNTAX)
- && t.image.indexOf("${") >= 0
+ && t.image.indexOf("${") != -1
|| interpolationSyntax == LEGACY_INTERPOLATION_SYNTAX
- && t.image.indexOf("#{") >= 0
+ && t.image.indexOf("#{") != -1
|| interpolationSyntax == SQUARE_BRACKET_INTERPOLATION_SYNTAX
- && t.image.indexOf("[=") >= 0) {
+ && t.image.indexOf("[=") != -1) {
result.parseValue(this, outputFormat);
}
}
@@ -2400,7 +2400,7 @@ HashLiteral HashLiteral() :
/**
* A production representing the ${...} or [=...] that outputs a variable.
*/
-DollarVariable StringOutput() :
+DollarVariable NormalInterpolation() :
{
Expression exp;
Token begin, end;
@@ -2410,26 +2410,19 @@ DollarVariable StringOutput() :
(
begin = <DOLLAR_INTERPOLATION_OPENING>
exp = Expression()
- {
- notHashLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
- notListLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
- }
-
end = <CLOSING_CURLY_BRACKET>
)
|
(
begin = <SQUARE_BRACKET_INTERPOLATION_OPENING>
exp = Expression()
- {
- notHashLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
- notListLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
- }
-
end = <CLOSE_BRACKET>
)
)
{
+ notHashLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
+ notListLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
+
DollarVariable result = new DollarVariable(
exp, escapedExpression(exp),
outputFormat,
@@ -2439,7 +2432,7 @@ DollarVariable StringOutput() :
}
}
-NumericalOutput NumericalOutput() :
+NumericalOutput NumericalInterpolation() :
{
Expression exp;
Token fmt = null, begin, end;
@@ -4140,9 +4133,9 @@ TemplateElements MixedContentElements() :
(
elem = PCData()
|
- elem = StringOutput()
+ elem = NormalInterpolation()
|
- elem = NumericalOutput()
+ elem = NumericalInterpolation()
|
elem = FreemarkerDirective()
)
@@ -4186,9 +4179,9 @@ MixedContent MixedContent() :
(
elem = PCData()
|
- elem = StringOutput()
+ elem = NormalInterpolation()
|
- elem = NumericalOutput()
+ elem = NumericalInterpolation()
|
elem = FreemarkerDirective()
)
@@ -4243,9 +4236,9 @@ TemplateElement FreeMarkerText() :
(
elem = PCData()
|
- elem = StringOutput()
+ elem = NormalInterpolation()
|
- elem = NumericalOutput()
+ elem = NumericalInterpolation()
)
{
if (begin == null) {
@@ -4495,12 +4488,12 @@ List<Object> StaticTextAndInterpolations() :
(
LOOKAHEAD(<DOLLAR_INTERPOLATION_OPENING>|<SQUARE_BRACKET_INTERPOLATION_OPENING>)
(
- interpolation = StringOutput()
+ interpolation = NormalInterpolation()
)
|
LOOKAHEAD(<HASH_INTERPOLATION_OPENING>)
(
- interpolation = NumericalOutput()
+ interpolation = NumericalInterpolation()
)
)
{
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eacd5168/src/test/java/freemarker/core/InterpolationSyntaxTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/InterpolationSyntaxTest.java b/src/test/java/freemarker/core/InterpolationSyntaxTest.java
index adea7c9..8c57d36 100644
--- a/src/test/java/freemarker/core/InterpolationSyntaxTest.java
+++ b/src/test/java/freemarker/core/InterpolationSyntaxTest.java
@@ -1,9 +1,13 @@
package freemarker.core;
import static freemarker.template.Configuration.*;
+import static org.junit.Assert.*;
+
+import java.io.StringWriter;
import org.junit.Test;
+import freemarker.template.Template;
import freemarker.test.TemplateTest;
public class InterpolationSyntaxTest extends TemplateTest {
@@ -78,6 +82,10 @@ public class InterpolationSyntaxTest extends TemplateTest {
assertOutput("[='[\\=1]']", "[=1]");
assertOutput("[='[\\=1][=2]']", "12"); // Usual legacy interpolation escaping glitch...
assertOutput("[=r'[=1]']", "[=1]");
+
+ StringWriter sw = new StringWriter();
+ new Template(null, "[= 1 + '[= 2 ]' ]", getConfiguration()).dump(sw);
+ assertEquals("[=1 + \"[=2]\"]", sw.toString());
}
@Test
[02/12] incubator-freemarker git commit: Made it more clear that
using Configuration.getVersion() for the value of the
incompatibleImprovements setting is bad idea.
Posted by dd...@apache.org.
Made it more clear that using Configuration.getVersion() for the value of the incompatibleImprovements setting is bad idea.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/40ced2d4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/40ced2d4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/40ced2d4
Branch: refs/heads/2.3
Commit: 40ced2d45fcf92723e6db386e13df9161d5a4608
Parents: 1261610
Author: ddekany <dd...@apache.org>
Authored: Tue Mar 13 08:11:25 2018 +0100
Committer: ddekany <dd...@apache.org>
Committed: Tue Mar 13 08:11:25 2018 +0100
----------------------------------------------------------------------
.../java/freemarker/template/Configuration.java | 19 +++++++---
src/manual/en_US/book.xml | 37 ++++++++++++++------
2 files changed, 41 insertions(+), 15 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/40ced2d4/src/main/java/freemarker/template/Configuration.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/Configuration.java b/src/main/java/freemarker/template/Configuration.java
index 3673272..5f35435 100644
--- a/src/main/java/freemarker/template/Configuration.java
+++ b/src/main/java/freemarker/template/Configuration.java
@@ -547,8 +547,8 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
* <p><b>About the "incompatible improvements" setting</b>
*
* <p>This setting value is the FreeMarker version number where the not 100% backward compatible bug fixes and
- * improvements that you want to enable were already implemented. In new projects you should set this to the
- * version of FreeMarker that you start the development with. In older projects it's also usually better to keep
+ * improvements that you want to enable were already implemented. In new projects you should set this to the fixed
+ * FreeMarker version that you start the development with. In older projects it's also usually better to keep
* this high, however you should check the changes activated (find them below), especially if not only the 3rd
* version number (the micro version) of {@code incompatibleImprovements} is increased. Generally, as far as you
* only increase the last version number of this setting, the changes are low risk. The default value is 2.3.0 to
@@ -557,6 +557,9 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
* <p>Bugfixes and improvements that are fully backward compatible, also those that are important security fixes,
* are enabled regardless of the incompatible improvements setting.
*
+ * <p>Do NOT ever use {@link #getVersion()} to set the "incompatible improvements". Always use a fixed value, like
+ * {@link #VERSION_2_3_28}. Otherwise your application can break as you upgrade FreeMarker.
+ *
* <p>An important consequence of setting this setting is that now your application will check if the stated minimum
* FreeMarker version requirement is met. Like if you set this setting to 2.3.22, but accidentally the application
* is deployed with FreeMarker 2.3.21, then FreeMarker will fail, telling that a higher version is required. After
@@ -1865,7 +1868,11 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
/**
* Use {@link #Configuration(Version)} instead if possible; see the meaning of the parameter there.
- * If the default value of a setting depends on the {@code incompatibleImprovements} and the value of that setting
+ *
+ * <p>Do NOT ever use {@link #getVersion()} to set the "incompatible improvements". Always use a fixed value, like
+ * {@link #VERSION_2_3_28}. Otherwise your application can break as you upgrade FreeMarker.
+ *
+ * <p>If the default value of a setting depends on the {@code incompatibleImprovements} and the value of that setting
* was never set in this {@link Configuration} object through the public API, its value will be set to the default
* value appropriate for the new {@code incompatibleImprovements}. (This adjustment of a setting value doesn't
* count as setting that setting, so setting {@code incompatibleImprovements} for multiple times also works as
@@ -3393,7 +3400,11 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
}
/**
- * Returns the FreeMarker version information, most importantly the major.minor.micro version numbers.
+ * Returns FreeMarker version information, most importantly the major.minor.micro version numbers;
+ * do NOT use this as the value of the {@code incompatible_improvements} setting (as the parameter to
+ * {@link Configuration#Configuration(Version)}), as then your application can break when you upgrade FreeMarker!
+ * Use a constant value, like {@link #VERSION_2_3_28}, to protect your application from fixes/changes that aren't
+ * entirely backward compatible. Fixes and features that are backward compatible are always enabled.
*
* On FreeMarker version numbering rules:
* <ul>
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/40ced2d4/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 3bd2562..3d8e321 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -10043,17 +10043,21 @@ cfg.setNumberFormat("@ua 0.####;; roundingMode=halfUp");</programlisting>
implemented. Usually, it's a bad idea to left it on its default,
which is 2.3.0 (maximum backward compatibility).</para>
- <para>In new projects you should set this to the FreeMarker version
- that you are actually using. In older projects it's also usually
- better to keep this high, however you better check the changes
- activated (find them in <link
+ <para>In new projects you should set this to the fixed FreeMarker
+ version (like <literal>Configuration.VERSION_2_3_28</literal>) that
+ you are actually using when starting the project. In older projects
+ it's also usually better to keep this high , however you should
+ check the changes activated (find them in <link
xlink:href="https://freemarker.apache.org/docs/api/freemarker/template/Configuration.html#Configuration-freemarker.template.Version-">the
API JavaDoc of the <literal>Configuration(Version)</literal>
- constructor</link>), at least if not only the 3rd version number
+ constructor</link>), especially if not only the 3rd version number
(the micro version) of <quote>incompatible improvements</quote>
setting is increased. Generally, as far as you only increase the
- last version number of this setting, the changes are low
- risk.</para>
+ last (3rd) version number of this setting, the changes are low risk,
+ and whether you can afford that risk depends on the concrete
+ application. Never use a dynamic value like
+ <literal>Configuration.getVersion()</literal> though, as that way
+ upgrading FreeMarker can break your application!</para>
<para>Bug fixes and improvements that are fully backward compatible,
also those that are important security fixes, are enabled regardless
@@ -10081,14 +10085,14 @@ cfg.setNumberFormat("@ua 0.####;; roundingMode=halfUp");</programlisting>
<literal>freemarker.template.Configuration</literal> object
like:</para>
- <programlisting role="unspecified">... = new Configuration(Configuration.VERSION_2_3_27)</programlisting>
+ <programlisting role="unspecified">... = new Configuration(Configuration.VERSION_2_3_28)</programlisting>
</listitem>
<listitem>
<para>Or, alter the <literal>Configuration</literal> singleton
where you initialize its other settings like:</para>
- <programlisting role="unspecified">cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_27)</programlisting>
+ <programlisting role="unspecified">cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_28)</programlisting>
</listitem>
<listitem>
@@ -10096,7 +10100,7 @@ cfg.setNumberFormat("@ua 0.####;; roundingMode=halfUp");</programlisting>
(<literal>*.properties</literal> file or
<literal>java.util.Properties</literal> object), add:</para>
- <programlisting role="unspecified">incompatible_improvements=2.3.27</programlisting>
+ <programlisting role="unspecified">incompatible_improvements=2.3.28</programlisting>
</listitem>
<listitem>
@@ -10107,7 +10111,7 @@ cfg.setNumberFormat("@ua 0.####;; roundingMode=halfUp");</programlisting>
<programlisting role="unspecified"><init-param>
<param-name>incompatible_improvements</param-name>
- <param-value>2.3.27</param-value>
+ <param-value>2.3.28</param-value>
</init-param></programlisting>
</listitem>
</itemizedlist>
@@ -10139,6 +10143,17 @@ cfg.setNumberFormat("@ua 0.####;; roundingMode=halfUp");</programlisting>
<literal>DefaultObjectWrapper</literal> (for
<literal>BeansWrapper</literal> it's the same, only with different
class name of course).</para>
+
+ <warning>
+ <para>Do not ever use
+ <literal>Configuration.getVersion()</literal> to set the
+ <quote>incompatible improvements</quote> setting. Always use a
+ fixed value, like <literal>Configuration.VERSION_2_3_28</literal>.
+ Otherwise your application can break as you upgrade FreeMarker.
+ The whole point of <quote>incompatible improvements</quote> is to
+ protect you from that, while you still always get the
+ fixes/improvements that are backward compatible.</para>
+ </warning>
</section>
</section>
</chapter>
[08/12] incubator-freemarker git commit: In string literals,
\= is now a valid escape sequence,
resulting in a =. This is useful when you are using the new [=exp]
interpolation syntax,
which can be escaped in a string literal like "Literal [\=x]".
Posted by dd...@apache.org.
In string literals, \= is now a valid escape sequence, resulting in a =. This is useful when you are using the new [=exp] interpolation syntax, which can be escaped in a string literal like "Literal [\=x]".
(Also improved [=...] related documentation and test a bit.)
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/df4dc52f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/df4dc52f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/df4dc52f
Branch: refs/heads/2.3
Commit: df4dc52f2f1799299a9455ac016dfecaecf9004b
Parents: aeaafe5
Author: ddekany <dd...@apache.org>
Authored: Fri Mar 16 23:58:29 2018 +0100
Committer: ddekany <dd...@apache.org>
Committed: Fri Mar 16 23:58:29 2018 +0100
----------------------------------------------------------------------
.../freemarker/template/utility/StringUtil.java | 22 +++++--
src/main/javacc/FTL.jj | 2 +-
src/manual/en_US/book.xml | 67 +++++++++++++++++---
.../core/InterpolationSyntaxTest.java | 7 ++
.../template/utility/StringUtilTest.java | 24 +++++++
5 files changed, 108 insertions(+), 14 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/df4dc52f/src/main/java/freemarker/template/utility/StringUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/utility/StringUtil.java b/src/main/java/freemarker/template/utility/StringUtil.java
index 33fe03b..a5156aa 100644
--- a/src/main/java/freemarker/template/utility/StringUtil.java
+++ b/src/main/java/freemarker/template/utility/StringUtil.java
@@ -38,6 +38,10 @@ import freemarker.template.Version;
*/
public class StringUtil {
+ /**
+ * Used to look up if the chars with low code needs to be escaped, but note that it gives bad result for '=', as
+ * there the it matters if it's after '['.
+ */
private static final char[] ESCAPES = createEscapes();
private static final char[] LT = new char[] { '&', 'l', 't', ';' };
@@ -426,6 +430,7 @@ public class StringUtil {
escapes['\''] = '\'';
escapes['"'] = '"';
escapes['<'] = 'l';
+ // As '=' is only escaped if it's after '[', we can't handle it here
escapes['>'] = 'g';
escapes['&'] = 'a';
escapes['\b'] = 'b';
@@ -480,10 +485,16 @@ public class StringUtil {
StringBuilder buf = null;
for (int i = 0; i < ln; i++) {
char c = s.charAt(i);
- char escape =
- c < escLn ? ESCAPES[c] :
- c == '{' && i > 0 && isInterpolationStart(s.charAt(i - 1)) ? '{' :
- 0;
+ char escape;
+ if (c == '=') {
+ escape = i > 0 && s.charAt(i - 1) == '[' ? '=' : 0;
+ } else if (c < escLn) {
+ escape = ESCAPES[c]; //
+ } else if (c == '{' && i > 0 && isInterpolationStart(s.charAt(i - 1))) {
+ escape = '{';
+ } else {
+ escape = 0;
+ }
if (escape == 0 || escape == otherQuotation) {
if (buf != null) {
buf.append(c);
@@ -605,7 +616,8 @@ public class StringUtil {
bidx = idx + 2;
break;
case '{':
- buf.append('{');
+ case '=':
+ buf.append(c);
bidx = idx + 2;
break;
case 'x': {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/df4dc52f/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/src/main/javacc/FTL.jj b/src/main/javacc/FTL.jj
index f1fa595..4134b70 100644
--- a/src/main/javacc/FTL.jj
+++ b/src/main/javacc/FTL.jj
@@ -1194,7 +1194,7 @@ TOKEN:
<#ESCAPED_CHAR :
"\\"
(
- ("n" | "t" | "r" | "f" | "b" | "g" | "l" | "a" | "\\" | "'" | "\"" | "$" | "{")
+ ("n" | "t" | "r" | "f" | "b" | "g" | "l" | "a" | "\\" | "'" | "\"" | "$" | "{" | "=")
|
("x" ["0"-"9", "A"-"F", "a"-"f"])
)
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/df4dc52f/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index cbda1a8..ac58a2a 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -2276,6 +2276,13 @@ this is a backslash: \</programlisting>
</tr>
<tr>
+ <td><literal>\=</literal></td>
+
+ <td>Equals character: <literal>=</literal> (Supported since
+ FreeMarker 2.3.28.)</td>
+ </tr>
+
+ <tr>
<td><literal>\\</literal></td>
<td>Back slash (u005C)</td>
@@ -2349,15 +2356,20 @@ this is a backslash: \</programlisting>
digit, you must use all 4 digits or else FreeMarker will
misunderstand you.</para>
- <para>Note that the character sequence <literal>${</literal> (and
- <literal>#{</literal>) has special meaning. It's used to insert
- the value of expressions (typically: the value of variables, as in
- <literal>"Hello ${user}!"</literal>). This will be explained <link
+ <para>Note that the character sequence <literal>${</literal> and
+ <literal>#{</literal> (and rarely <literal>[=</literal> instead,
+ depending on <link linkend="dgui_misc_alternativesyntax">the
+ configured syntax</link>) has special meaning. They are used to
+ insert the value of expressions (typically: the value of
+ variables, as in <literal>"Hello ${user}!"</literal>). This will
+ be explained <link
linkend="dgui_template_exp_stringop_interpolation">later</link>.
If you want to print <literal>${</literal> or
- <literal>#{</literal>, you should either use raw string literals
- as explained below, or escape the <literal>{</literal> like in
- <literal>"foo $\{bar}"</literal>.</para>
+ <literal>#{</literal> (or <literal>[=</literal>), you should
+ either use raw string literals as explained below, or escape the
+ <literal>{</literal> like in <literal>"foo $\{bar}"</literal> (or
+ the <literal>=</literal> like in <literal>"foo
+ [\=bar]"</literal>).</para>
<indexterm>
<primary>raw string literal</primary>
@@ -2839,6 +2851,14 @@ baz
sections</link> (so it goes through the same <emphasis>locale
sensitive</emphasis> number and date/time formatting).</para>
+ <note>
+ <para>It's possible to configure FreeMarker's interpolation
+ syntax to use
+ <literal>[=<replaceable>...</replaceable>]</literal> instead;
+ <link linkend="dgui_misc_alternativesyntax_interpolation">see
+ here</link>.</para>
+ </note>
+
<para>Example (assume that user is <quote>Big Joe</quote>):</para>
<programlisting role="template"><#assign s = "Hello ${user}!">
@@ -4200,7 +4220,7 @@ ${("green " + "mouse")?upper_case} <#-- GREEN MOUSE -->
kind of expression (e.g. <literal>${100 + x}</literal>).</para>
<note>
- <para>Actually, FreeMarker can be configured to use
+ <para>FreeMarker can be configured to use
<literal>[=<replaceable>expression</replaceable>]</literal> syntax
instead. <link linkend="dgui_misc_alternativesyntax">See more
about alternative syntaxes...</link></para>
@@ -27559,6 +27579,37 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
</listitem>
<listitem>
+ <para>The template language can now be configured to use
+ <literal>[=<replaceable>expression</replaceable>]</literal>
+ instead of
+ <literal>${<replaceable>expression</replaceable>}</literal> and
+ <literal>#{<replaceable>expression</replaceable>}</literal>,
+ which is very useful if you have a lot of
+ <literal>${<replaceable>...</replaceable>}</literal> or
+ <literal>#{<replaceable>...</replaceable>}</literal> in the text
+ that you are generating, and so they should be static text. See
+ <link linkend="dgui_misc_alternativesyntax_interpolation">more
+ about the square bracket interpolation syntax here.</link> The
+ template language can also be configured to only use
+ <literal>${<replaceable>expression</replaceable>}</literal>, and
+ treat
+ <literal>#{<replaceable>expression</replaceable>}</literal> as
+ static text. (See the <literal>interpolation_syntax</literal>
+ configuration setting, or the
+ <literal>Configuration.setInterpolationSyntax(int)</literal>
+ method.)</para>
+ </listitem>
+
+ <listitem>
+ <para>In string literals, <literal>\=</literal> is now a valid
+ escape sequence, resulting in a <literal>=</literal>. This is
+ useful when you are using the new
+ <literal>[=<replaceable>exp</replaceable>]</literal>
+ interpolation syntax, which can be escaped in a string literal
+ like <literal>"Literal [\=x]"</literal>.</para>
+ </listitem>
+
+ <listitem>
<para>Bug fixed (<link
xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-83">FREEMARKER-83</link>);
this fix is only active when <link
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/df4dc52f/src/test/java/freemarker/core/InterpolationSyntaxTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/InterpolationSyntaxTest.java b/src/test/java/freemarker/core/InterpolationSyntaxTest.java
index c1721c8..adea7c9 100644
--- a/src/test/java/freemarker/core/InterpolationSyntaxTest.java
+++ b/src/test/java/freemarker/core/InterpolationSyntaxTest.java
@@ -71,6 +71,13 @@ public class InterpolationSyntaxTest extends TemplateTest {
assertOutput("<@r'${1} #{1} [=1]'?interpret />", "${1} #{1} 1");
assertOutput("[='\"${1} #{1} [=1]\"'?eval]", "${1} #{1} 1");
+
+ assertErrorContains("[=", "end of file");
+ assertErrorContains("[=1", "unclosed \"[\"");
+
+ assertOutput("[='[\\=1]']", "[=1]");
+ assertOutput("[='[\\=1][=2]']", "12"); // Usual legacy interpolation escaping glitch...
+ assertOutput("[=r'[=1]']", "[=1]");
}
@Test
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/df4dc52f/src/test/java/freemarker/template/utility/StringUtilTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/template/utility/StringUtilTest.java b/src/test/java/freemarker/template/utility/StringUtilTest.java
index 49556e2..557c5dc 100644
--- a/src/test/java/freemarker/template/utility/StringUtilTest.java
+++ b/src/test/java/freemarker/template/utility/StringUtilTest.java
@@ -19,6 +19,7 @@
package freemarker.template.utility;
+import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.IOException;
@@ -28,6 +29,8 @@ import java.util.regex.Pattern;
import org.hamcrest.Matchers;
import org.junit.Test;
+import freemarker.core.ParseException;
+
public class StringUtilTest {
@Test
@@ -200,6 +203,27 @@ public class StringUtilTest {
assertEquals("a\\'c\"d", StringUtil.FTLStringLiteralEnc("a'c\"d", '\''));
assertEquals("\\n\\r\\t\\f\\x0002\\\\", StringUtil.FTLStringLiteralEnc("\n\r\t\f\u0002\\"));
assertEquals("\\l\\g\\a", StringUtil.FTLStringLiteralEnc("<>&"));
+ assertEquals("=[\\=]=", StringUtil.FTLStringLiteralEnc("=[=]="));
+ assertEquals("[\\=", StringUtil.FTLStringLiteralEnc("[="));
+ }
+
+ @Test
+ public void testFTLStringLiteralDec() throws ParseException {
+ assertEquals("", StringUtil.FTLStringLiteralDec(""));
+ assertEquals("x", StringUtil.FTLStringLiteralDec("x"));
+ assertEquals("\nq", StringUtil.FTLStringLiteralDec("\\x0Aq"));
+ assertEquals("\n\r1", StringUtil.FTLStringLiteralDec("\\x0A\\x000D1"));
+ assertEquals("\n\r\t", StringUtil.FTLStringLiteralDec("\\n\\r\\t"));
+ assertEquals("${1}#{2}[=3]", StringUtil.FTLStringLiteralDec("$\\{1}#\\{2}[\\=3]"));
+ assertEquals("{=", StringUtil.FTLStringLiteralDec("\\{\\="));
+ assertEquals("\\=", StringUtil.FTLStringLiteralDec("\\\\="));
+
+ try {
+ StringUtil.FTLStringLiteralDec("\\[");
+ fail();
+ } catch (ParseException e) {
+ assertThat(e.getMessage(), containsString("\\["));
+ }
}
@Test