Repository: incubator-freemarker
Updated Branches:
refs/heads/2.3-gae c630a41ab -> ca1ecf78e
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-gae
Commit: ca1ecf78e7eebdb83c2b147d5fb9aadb1c25f93a
Parents: c630a41
Author: ddekany <[email protected]>
Authored: Fri Mar 16 00:36:31 2018 +0100
Committer: ddekany <[email protected]>
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);