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 <ddek...@apache.org>
Authored: Fri Mar 16 00:36:31 2018 +0100
Committer: ddekany <ddek...@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}  &lt;#-- GREEN MOUSE 
--&gt;
           <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>&lt;#if x&gt;</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>&lt;</literal> and <literal>&gt;</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>&lt;</literal> and <literal>&gt;</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>
-&lt;p&gt;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">&lt;p&gt;We have these animals:
 &lt;table border=1&gt;
   &lt;tr&gt;&lt;th&gt;Name&lt;th&gt;Price
   <emphasis>[#list animals as animal]</emphasis>
@@ -6273,33 +6291,79 @@ That's all.</programlisting>
   <emphasis>[/#list]</emphasis>
 &lt;/table&gt;</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>&lt;#if <replaceable>...</replaceable>&gt;</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>&lt;#ftl
-        <replaceable>...</replaceable>&gt;</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>&lt;#if
+          <replaceable>...</replaceable>&gt;</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>&lt;#ftl
+          <replaceable>...</replaceable>&gt;</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">&lt;p&gt;We have these animals:
+&lt;table border=1&gt;
+  &lt;tr&gt;&lt;th&gt;Name&lt;th&gt;Price
+  [#list animals as animal]
+  &lt;tr&gt;
+    &lt;td&gt;
+      [#if animal.size == "large"]&lt;b&gt;[/#if]
+      <emphasis>[=animal.name]</emphasis>
+      [#if animal.size == "large"]&lt;/b&gt;[/#if]
+    &lt;td&gt;<emphasis>[=animal.price]</emphasis> Euros
+  [/#list]
+&lt;/table&gt;</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>&lt;</literal> and <literal>&gt;</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>&lt;</literal> and <literal>&gt;</literal>;
+            see more about square bracket tag syntax <link
+            linkend="dgui_misc_alternativesyntax">here.</link> The comparison
+            operators, like <literal>&lt;</literal>, also have an alternative
+            syntax (<literal>lt</literal> and <literal>&amp;lt;</literal> in
+            this case); see more about them <link
+            linkend="dgui_template_exp_comparison">here</link>. Also, the
+            <literal>&amp;&amp;</literal> operator (which is not well-format
+            HTML/XML) can be written as <literal>\and</literal> or
+            <literal>&amp;amp;&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);
 

Reply via email to