Added tagSyntax and interpolationSyntax dropdowns. Did cleanup/refactoring of 
old code along the way.


Project: 
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/commit/007c4e13
Tree: 
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/tree/007c4e13
Diff: 
http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/diff/007c4e13

Branch: refs/heads/master
Commit: 007c4e1310c0a1eaca54061cabde6fc7e54837b2
Parents: 5313533
Author: ddekany <[email protected]>
Authored: Thu Mar 22 22:36:11 2018 +0100
Committer: ddekany <[email protected]>
Committed: Fri Mar 23 13:55:32 2018 +0100

----------------------------------------------------------------------
 build.gradle                                    |   3 +
 dependencies.gradle                             |   2 +-
 .../onlinetester/model/ErrorCode.java           |   1 -
 .../onlinetester/model/ExecuteRequest.java      |  56 ++++++++
 .../model/ExecuteResourceField.java             |  57 --------
 .../model/ExecuteResourceProblem.java           |  53 -------
 .../onlinetester/model/ExecuteResponse.java     |   8 +-
 .../model/ExecuteResponseProblem.java           |  53 +++++++
 .../resources/ExecuteApiResource.java           | 138 ++++++++-----------
 .../onlinetester/resources/WebPageResource.java |   4 +-
 .../services/AllowedSettingValues.java          | 123 +++++++++++++++++
 .../services/AllowedSettingValuesMaps.java      | 115 ----------------
 .../services/FreeMarkerService.java             | 125 +++++++++++------
 .../services/FreeMarkerServiceException.java    |   1 -
 .../onlinetester/view/FreeMarkerOnlineView.java | 110 +++++++++++----
 src/main/resources/assets/js/script.js          |   8 +-
 src/main/resources/freemarker-online.yml        |   2 +-
 src/main/resources/view/freemarker-online.ftl   |  12 +-
 .../onlinetester/ApplicationStartsTest.java     |   2 +-
 .../resources/ExecuteApiResourceTest.java       |  44 +++---
 .../resources/WebPageResourceTest.java          |  23 +---
 .../services/FreeMarkerServiceTest.java         | 100 ++++++++++----
 .../view/FreeMarkerOnlineViewTest.java          |   8 +-
 23 files changed, 592 insertions(+), 456 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/build.gradle
----------------------------------------------------------------------
diff --git a/build.gradle b/build.gradle
index f307eb1..8d9c7d5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -36,6 +36,9 @@ project.targetCompatibility = "1.8"
 
 repositories {
     mavenCentral()
+    maven {
+        url "https://repository.apache.org/content/repositories/snapshots/";
+    }
 }
 
 buildscript {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/dependencies.gradle
----------------------------------------------------------------------
diff --git a/dependencies.gradle b/dependencies.gradle
index c7fa0d2..33bcf98 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -30,7 +30,7 @@ ext.libraries = [
         dropwizard_bundles_redirect: 
"io.dropwizard-bundles:dropwizard-redirect-bundle:1.0.5",
 
         // App. specific:
-        freemarker: "org.freemarker:freemarker:2.3.27-incubating",
+        freemarker: "org.freemarker:freemarker:2.3.28-incubating-SNAPSHOT",
         jersey_core: "org.glassfish.jersey.core:jersey-core:$jeresy_version",
         jackson_databind: 
"com.fasterxml.jackson.core:jackson-databind:$jackson_version",
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/java/org/apache/freemarker/onlinetester/model/ErrorCode.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/onlinetester/model/ErrorCode.java 
b/src/main/java/org/apache/freemarker/onlinetester/model/ErrorCode.java
index c983e87..b766edf 100644
--- a/src/main/java/org/apache/freemarker/onlinetester/model/ErrorCode.java
+++ b/src/main/java/org/apache/freemarker/onlinetester/model/ErrorCode.java
@@ -21,5 +21,4 @@ package org.apache.freemarker.onlinetester.model;
 
 public enum ErrorCode {
     FREEMARKER_SERVICE_TIMEOUT
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteRequest.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteRequest.java 
b/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteRequest.java
index a206131..2552fa4 100644
--- a/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteRequest.java
+++ b/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteRequest.java
@@ -18,6 +18,8 @@
  */
  package org.apache.freemarker.onlinetester.model;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
 
 public class ExecuteRequest {
     private String template;
@@ -25,7 +27,45 @@ public class ExecuteRequest {
     private String outputFormat;
     private String locale;
     private String timeZone;
+    private String tagSyntax;
+    private String interpolationSyntax;
 
+    public static enum Field {
+        DATA_MODEL("dataModel"),
+        TEMPLATE("template"),
+        OUTPUT_FORMAT("outputFormat"),
+        LOCALE("locale"),
+        TIME_ZONE("timeZone"),
+       TAG_SYNTAX("tagSyntax"),
+       INTERPOLATION_SYNTAX("interpolationSyntax");
+        
+        private final String fieldName;
+        
+        private Field(String filedName) {
+            this.fieldName = filedName;
+        }
+        
+        public String toString() {
+            return getFieldName();
+        }
+        
+        @JsonValue
+        public String getFieldName() {
+            return fieldName;
+        }
+
+        @JsonCreator
+        public static Field fromEnumString(String val) {
+            for(Field field : values()) {
+                if(field.getFieldName().equals(val)) {
+                    return field;
+                }
+            }
+            throw new IllegalArgumentException("Invalid string value passed: " 
+ val);
+        }
+        
+    }
+    
     public ExecuteRequest() {
     }
 
@@ -74,5 +114,21 @@ public class ExecuteRequest {
     public void setTimeZone(String timeZone) {
         this.timeZone = timeZone;
     }
+
+       public String getTagSyntax() {
+               return tagSyntax;
+       }
+
+       public void setTagSyntax(String tagSyntax) {
+               this.tagSyntax = tagSyntax;
+       }
+
+       public String getInterpolationSyntax() {
+               return interpolationSyntax;
+       }
+
+       public void setInterpolationSyntax(String interpolationSyntax) {
+               this.interpolationSyntax = interpolationSyntax;
+       }
     
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResourceField.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResourceField.java
 
b/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResourceField.java
deleted file mode 100644
index 71ad454..0000000
--- 
a/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResourceField.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.onlinetester.model;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonValue;
-
-public enum ExecuteResourceField {
-    DATA_MODEL("dataModel"),
-    TEMPLATE("template"),
-    OUTPUT_FORMAT("outputFormat"),
-    LOCALE("locale"),
-    TIME_ZONE("timeZone");
-    
-    private final String fieldName;
-    
-    private ExecuteResourceField(String filedName) {
-        this.fieldName = filedName;
-    }
-    
-    public String toString() {
-        return getFieldName();
-    }
-    
-    @JsonValue
-    public String getFieldName() {
-        return fieldName;
-    }
-
-    @JsonCreator
-    public static ExecuteResourceField fromEnumString(String val) {
-        for(ExecuteResourceField field : values()) {
-            if(field.getFieldName().equals(val)) {
-                return field;
-            }
-        }
-        throw new IllegalArgumentException("Invalid string value passed: " + 
val);
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResourceProblem.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResourceProblem.java
 
b/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResourceProblem.java
deleted file mode 100644
index 3c69d43..0000000
--- 
a/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResourceProblem.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.onlinetester.model;
-
-public class ExecuteResourceProblem {
-    
-    private ExecuteResourceField field;
-    private String message;
-    
-    // Needed for JSON unmarshalling
-    public ExecuteResourceProblem() {
-        //
-    }
-    
-    public ExecuteResourceProblem(ExecuteResourceField field, String message) {
-        this.field = field;
-        this.message = message;
-    }
-
-    public ExecuteResourceField getField() {
-        return field;
-    }
-
-    public void setField(ExecuteResourceField field) {
-        this.field = field;
-    }
-
-    public String getMessage() {
-        return message;
-    }
-
-    public void setMessage(String message) {
-        this.message = message;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResponse.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResponse.java 
b/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResponse.java
index 41c33f1..5876d74 100644
--- 
a/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResponse.java
+++ 
b/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResponse.java
@@ -23,10 +23,10 @@ import java.util.List;
 
 public class ExecuteResponse {
     private String result;
-    private List<ExecuteResourceProblem> problems;
+    private List<ExecuteResponseProblem> problems;
     private boolean truncatedResult;
 
-    public ExecuteResponse(String result, List<ExecuteResourceProblem> 
problems, boolean truncatedResult) {
+    public ExecuteResponse(String result, List<ExecuteResponseProblem> 
problems, boolean truncatedResult) {
         this.result = result;
         this.problems = problems;
         this.truncatedResult = truncatedResult;
@@ -36,11 +36,11 @@ public class ExecuteResponse {
 
     }
 
-    public List<ExecuteResourceProblem> getProblems() {
+    public List<ExecuteResponseProblem> getProblems() {
         return problems;
     }
 
-    public void setProblems(List<ExecuteResourceProblem> problems) {
+    public void setProblems(List<ExecuteResponseProblem> problems) {
         this.problems = problems;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResponseProblem.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResponseProblem.java
 
b/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResponseProblem.java
new file mode 100644
index 0000000..d7e02f6
--- /dev/null
+++ 
b/src/main/java/org/apache/freemarker/onlinetester/model/ExecuteResponseProblem.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.onlinetester.model;
+
+public class ExecuteResponseProblem {
+    
+    private ExecuteRequest.Field field;
+    private String message;
+    
+    // Needed for JSON unmarshalling
+    public ExecuteResponseProblem() {
+        //
+    }
+    
+    public ExecuteResponseProblem(ExecuteRequest.Field field, String message) {
+        this.field = field;
+        this.message = message;
+    }
+
+    public ExecuteRequest.Field getField() {
+        return field;
+    }
+
+    public void setField(ExecuteRequest.Field field) {
+        this.field = field;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/java/org/apache/freemarker/onlinetester/resources/ExecuteApiResource.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/onlinetester/resources/ExecuteApiResource.java
 
b/src/main/java/org/apache/freemarker/onlinetester/resources/ExecuteApiResource.java
index 3303e13..3607691 100644
--- 
a/src/main/java/org/apache/freemarker/onlinetester/resources/ExecuteApiResource.java
+++ 
b/src/main/java/org/apache/freemarker/onlinetester/resources/ExecuteApiResource.java
@@ -24,7 +24,6 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.TimeZone;
 import java.util.concurrent.RejectedExecutionException;
 
 import javax.ws.rs.Consumes;
@@ -38,18 +37,22 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.freemarker.onlinetester.model.ErrorCode;
 import org.apache.freemarker.onlinetester.model.ErrorResponse;
 import org.apache.freemarker.onlinetester.model.ExecuteRequest;
-import org.apache.freemarker.onlinetester.model.ExecuteResourceField;
-import org.apache.freemarker.onlinetester.model.ExecuteResourceProblem;
 import org.apache.freemarker.onlinetester.model.ExecuteResponse;
-import org.apache.freemarker.onlinetester.services.AllowedSettingValuesMaps;
+import org.apache.freemarker.onlinetester.model.ExecuteResponseProblem;
+import org.apache.freemarker.onlinetester.services.AllowedSettingValues;
 import org.apache.freemarker.onlinetester.services.FreeMarkerService;
+import 
org.apache.freemarker.onlinetester.services.FreeMarkerService.ExecuteTemplateArgs;
 import org.apache.freemarker.onlinetester.services.FreeMarkerServiceResponse;
 import org.apache.freemarker.onlinetester.util.DataModelParser;
 import org.apache.freemarker.onlinetester.util.DataModelParsingException;
 import org.apache.freemarker.onlinetester.util.ExceptionUtils;
 
-import freemarker.core.OutputFormat;
+import freemarker.template.Configuration;
+import freemarker.template.utility.StringUtil;
 
+/**
+ * AJAX API for executing the template submitted.
+ */
 @Path("/api/execute")
 public class ExecuteApiResource {
     private static final int MAX_TEMPLATE_INPUT_LENGTH = 10000;
@@ -62,17 +65,16 @@ public class ExecuteApiResource {
     private static final String 
MAX_DATA_MODEL_INPUT_LENGTH_EXCEEDED_ERROR_MESSAGE
             = "The data model length has exceeded the {0} character limit set 
for this service.";
 
-    private static final String UNKNOWN_OUTPUT_FORMAT_ERROR_MESSAGE = "Unknown 
output format: {0}";
-    private static final String UNKNOWN_LOCALE_ERROR_MESSAGE = "Unknown 
locale: {0}";
-    private static final String UNKNOWN_TIME_ZONE_ERROR_MESSAGE = "Unknown 
time zone: {0}";
-
-    private static final String SERVICE_OVERBURDEN_ERROR_MESSAGE
+    private static final String SERVICE_TIMEOUT_ERROR_MESSAGE
             = "Sorry, the service is overburden and couldn't handle your 
request now. Try again later.";
 
     static final String DATA_MODEL_ERROR_MESSAGE_HEADING = "Failed to parse 
data model:";
     static final String DATA_MODEL_ERROR_MESSAGE_FOOTER = "Note: This is NOT a 
FreeMarker error message. "
             + "The data model syntax is specific to this online service.";
 
+    public static final int DEFAULT_TAG_SYNTAX = 
Configuration.ANGLE_BRACKET_TAG_SYNTAX;
+    public static final int DEFAULT_INTERPLOATION_SYNTAX = 
Configuration.LEGACY_INTERPOLATION_SYNTAX;
+    
     private final FreeMarkerService freeMarkerService;
 
     public ExecuteApiResource(FreeMarkerService freeMarkerService) {
@@ -80,8 +82,8 @@ public class ExecuteApiResource {
     }
 
     @POST
-    @Produces(MediaType.APPLICATION_JSON)
     @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
     public Response formResult(
             ExecuteRequest req) {
         ExecuteResponse resp = new ExecuteResponse();
@@ -90,118 +92,98 @@ public class ExecuteApiResource {
             return Response.status(400).entity("Empty Template & 
data").build();
         }
 
-        List<ExecuteResourceProblem> problems = new 
ArrayList<ExecuteResourceProblem>();
+        List<ExecuteResponseProblem> problems = new ArrayList<>();
         
-        String template = getTemplate(req, problems);
-        Map<String, Object> dataModel = getDataModel(req, problems);
-        OutputFormat outputFormat = getOutputFormat(req, problems);
-        Locale locale = getLocale(req, problems);
-        TimeZone timeZone = getTimeZone(req, problems);
+        ExecuteTemplateArgs serviceArgs = new ExecuteTemplateArgs()
+                       .templateSourceCode(lengthCheckAndGetTemplate(req, 
problems))
+                       .dataModel(parseDataModel(req, problems))
+                       .outputFormat(parseChoiceField(
+                                       ExecuteRequest.Field.OUTPUT_FORMAT, 
req.getOutputFormat(),
+                                       
AllowedSettingValues.DEFAULT_OUTPUT_FORMAT, 
AllowedSettingValues.OUTPUT_FORMAT_MAP,
+                                       problems))
+                       .locale(parseChoiceField(
+                                       ExecuteRequest.Field.LOCALE, 
req.getLocale(),
+                                       AllowedSettingValues.DEFAULT_LOCALE, 
AllowedSettingValues.LOCALE_MAP,
+                                       problems))
+                       .timeZone(parseChoiceField(
+                                       ExecuteRequest.Field.TIME_ZONE, 
req.getTimeZone(),
+                                       AllowedSettingValues.DEFAULT_TIME_ZONE, 
AllowedSettingValues.TIME_ZONE_MAP,
+                                       problems))
+                       .tagSyntax(parseChoiceField(
+                                       ExecuteRequest.Field.TAG_SYNTAX, 
req.getTagSyntax(),
+                                       
AllowedSettingValues.DEFAULT_TAG_SYNTAX, AllowedSettingValues.TAG_SYNTAX_MAP,
+                                       problems))
+                       .interpolationSyntax(parseChoiceField(
+                                       
ExecuteRequest.Field.INTERPOLATION_SYNTAX, req.getInterpolationSyntax(),
+                                       
AllowedSettingValues.DEFAULT_INTERPOLATION_SYNTAX, 
AllowedSettingValues.INTERPOLATION_SYNTAX_MAP,
+                                       problems));
         
         if (!problems.isEmpty()) {
             resp.setProblems(problems);
             return buildFreeMarkerResponse(resp);
         }
         
-        FreeMarkerServiceResponse freeMarkerServiceResponse;
+        FreeMarkerServiceResponse serviceResponse;
         try {
-            freeMarkerServiceResponse = 
freeMarkerService.calculateTemplateOutput(
-                    template, dataModel,
-                    outputFormat, locale, timeZone);
+            serviceResponse = freeMarkerService.executeTemplate(serviceArgs);
         } catch (RejectedExecutionException e) {
-            String error = SERVICE_OVERBURDEN_ERROR_MESSAGE;
+            String error = SERVICE_TIMEOUT_ERROR_MESSAGE;
             return Response.serverError().entity(new 
ErrorResponse(ErrorCode.FREEMARKER_SERVICE_TIMEOUT, error)).build();
         }
-        if (!freeMarkerServiceResponse.isSuccesful()){
-            Throwable failureReason = 
freeMarkerServiceResponse.getFailureReason();
+        if (!serviceResponse.isSuccesful()){
+            Throwable failureReason = serviceResponse.getFailureReason();
             String error = ExceptionUtils.getMessageWithCauses(failureReason);
-            problems.add(new 
ExecuteResourceProblem(ExecuteResourceField.TEMPLATE, error));
+            problems.add(new 
ExecuteResponseProblem(ExecuteRequest.Field.TEMPLATE, error));
             resp.setProblems(problems);
             return buildFreeMarkerResponse(resp);
         }
 
-        String result = freeMarkerServiceResponse.getTemplateOutput();
+        String result = serviceResponse.getTemplateOutput();
         resp.setResult(result);
-        
resp.setTruncatedResult(freeMarkerServiceResponse.isTemplateOutputTruncated());
+        resp.setTruncatedResult(serviceResponse.isTemplateOutputTruncated());
         return buildFreeMarkerResponse(resp);
     }
 
-    private String getTemplate(ExecuteRequest req, 
List<ExecuteResourceProblem> problems) {
+    private String lengthCheckAndGetTemplate(ExecuteRequest req, 
List<ExecuteResponseProblem> problems) {
         String template = req.getTemplate();
-        
-        if (template.length() > MAX_TEMPLATE_INPUT_LENGTH) {
+        if (template != null && template.length() > MAX_TEMPLATE_INPUT_LENGTH) 
{
             String error = 
formatMessage(MAX_TEMPLATE_INPUT_LENGTH_EXCEEDED_ERROR_MESSAGE, 
MAX_TEMPLATE_INPUT_LENGTH);
-            problems.add(new 
ExecuteResourceProblem(ExecuteResourceField.TEMPLATE, error));
+            problems.add(new 
ExecuteResponseProblem(ExecuteRequest.Field.TEMPLATE, error));
             return null;
         }
-        
         return template;
     }
 
-    private Map<String, Object> getDataModel(ExecuteRequest req, 
List<ExecuteResourceProblem> problems) {
+    private Map<String, Object> parseDataModel(ExecuteRequest req, 
List<ExecuteResponseProblem> problems) {
         String dataModel = req.getDataModel();
 
         if (dataModel.length() > MAX_DATA_MODEL_INPUT_LENGTH) {
             String error = formatMessage(
                     MAX_DATA_MODEL_INPUT_LENGTH_EXCEEDED_ERROR_MESSAGE, 
MAX_DATA_MODEL_INPUT_LENGTH);
-            problems.add(new 
ExecuteResourceProblem(ExecuteResourceField.DATA_MODEL, error));
+            problems.add(new 
ExecuteResponseProblem(ExecuteRequest.Field.DATA_MODEL, error));
             return null;
         }
         
         try {
             return DataModelParser.parse(dataModel, 
freeMarkerService.getFreeMarkerTimeZone());
         } catch (DataModelParsingException e) {
-            problems.add(new 
ExecuteResourceProblem(ExecuteResourceField.DATA_MODEL, 
decorateResultText(e.getMessage())));
+            problems.add(new 
ExecuteResponseProblem(ExecuteRequest.Field.DATA_MODEL, 
decorateResultText(e.getMessage())));
             return null;
         }
     }
 
-    private OutputFormat getOutputFormat(ExecuteRequest req, 
List<ExecuteResourceProblem> problems) {
-        String outputFormatStr = req.getOutputFormat();
-        
-        if (StringUtils.isBlank(outputFormatStr)) {
-            return AllowedSettingValuesMaps.DEFAULT_OUTPUT_FORMAT;
-        }
-    
-        OutputFormat outputFormat = 
AllowedSettingValuesMaps.OUTPUT_FORMAT_MAP.get(outputFormatStr);
-        if (outputFormat == null) {
-            problems.add(new ExecuteResourceProblem(
-                    ExecuteResourceField.OUTPUT_FORMAT,
-                    formatMessage(UNKNOWN_OUTPUT_FORMAT_ERROR_MESSAGE, 
outputFormatStr)));
-        }
-        return outputFormat;
-    }
-
-    private Locale getLocale(ExecuteRequest req, List<ExecuteResourceProblem> 
problems) {
-        String localeStr = req.getLocale();
-        
-        if (StringUtils.isBlank(localeStr)) {
-            return AllowedSettingValuesMaps.DEFAULT_LOCALE;
-        }
-        
-        Locale locale = AllowedSettingValuesMaps.LOCALE_MAP.get(localeStr);
-        if (locale == null) {
-            problems.add(new ExecuteResourceProblem(
-                    ExecuteResourceField.LOCALE,
-                    formatMessage(UNKNOWN_LOCALE_ERROR_MESSAGE, localeStr)));
-        }
-        return locale;
-    }
-
-    private TimeZone getTimeZone(ExecuteRequest req, 
List<ExecuteResourceProblem> problems) {
-        String timeZoneStr = req.getTimeZone();
-        
-        if (StringUtils.isBlank(timeZoneStr)) {
-            return AllowedSettingValuesMaps.DEFAULT_TIME_ZONE;
+    private <T> T parseChoiceField(ExecuteRequest.Field name, String rawValue, 
T defaultValue,
+               Map<String, ? extends T> rawToParsedMap, 
List<ExecuteResponseProblem> problems) {
+        if (StringUtils.isBlank(rawValue)) {
+            return defaultValue;
         }
         
-        TimeZone timeZone = 
AllowedSettingValuesMaps.TIME_ZONE_MAP.get(timeZoneStr);
-        if (timeZone == null) {
-            problems.add(new ExecuteResourceProblem(
-                    ExecuteResourceField.TIME_ZONE,
-                    formatMessage(UNKNOWN_TIME_ZONE_ERROR_MESSAGE, 
timeZoneStr)));
+        T parsedValue = rawToParsedMap.get(rawValue);
+        if (parsedValue == null) {
+            problems.add(new ExecuteResponseProblem(name,
+                       formatMessage("Invalid value for \"{0}\": {1}", name, 
StringUtil.jQuote(rawValue))));
         }
-        return timeZone;
+        return parsedValue;
     }
 
     private Response buildFreeMarkerResponse(ExecuteResponse executeResponse){

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/java/org/apache/freemarker/onlinetester/resources/WebPageResource.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/onlinetester/resources/WebPageResource.java
 
b/src/main/java/org/apache/freemarker/onlinetester/resources/WebPageResource.java
index 1f7a802..89c6da1 100644
--- 
a/src/main/java/org/apache/freemarker/onlinetester/resources/WebPageResource.java
+++ 
b/src/main/java/org/apache/freemarker/onlinetester/resources/WebPageResource.java
@@ -49,7 +49,9 @@ public class WebPageResource {
             @FormParam("dataModel") String dataModel,
             @FormParam("outputFormat") String outputFormat,
             @FormParam("locale") String locale,
-            @FormParam("timeZone") String timeZone) {
+            @FormParam("timeZone") String timeZone,
+            @FormParam("tagSyntax") String tagSyntax,
+            @FormParam("interpolationSyntax") String interpolationSyntax) {
         FreeMarkerOnlineView view = new FreeMarkerOnlineView();
         view.setTemplate(template);
         view.setDataModel(dataModel);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/java/org/apache/freemarker/onlinetester/services/AllowedSettingValues.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/onlinetester/services/AllowedSettingValues.java
 
b/src/main/java/org/apache/freemarker/onlinetester/services/AllowedSettingValues.java
new file mode 100644
index 0000000..6911144
--- /dev/null
+++ 
b/src/main/java/org/apache/freemarker/onlinetester/services/AllowedSettingValues.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.onlinetester.services;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import com.google.common.collect.ImmutableMap;
+
+import freemarker.core.HTMLOutputFormat;
+import freemarker.core.OutputFormat;
+import freemarker.core.PlainTextOutputFormat;
+import freemarker.core.RTFOutputFormat;
+import freemarker.core.UndefinedOutputFormat;
+import freemarker.core.XHTMLOutputFormat;
+import freemarker.core.XMLOutputFormat;
+import freemarker.template.Configuration;
+
+/**
+ * Maps of the FreeMarker configuration setting values the remote caller can
+ * chose from (these are the value shown in a dropdown on the UI). This is
+ * possibly more restricted than what FreeMarker supports, for security 
reasons.
+ */
+public class AllowedSettingValues {
+
+    public static final OutputFormat DEFAULT_OUTPUT_FORMAT = 
UndefinedOutputFormat.INSTANCE;
+    public static final String DEFAULT_OUTPUT_FORMAT_KEY = 
DEFAULT_OUTPUT_FORMAT.getName();
+    public static final Map<String, OutputFormat> OUTPUT_FORMAT_MAP = 
ImmutableMap.<String, OutputFormat>builder()
+               .put(UndefinedOutputFormat.INSTANCE.getName(), 
UndefinedOutputFormat.INSTANCE)
+               .put(HTMLOutputFormat.INSTANCE.getName(), 
HTMLOutputFormat.INSTANCE)
+               .put(XMLOutputFormat.INSTANCE.getName(), 
XMLOutputFormat.INSTANCE)
+               .put(XHTMLOutputFormat.INSTANCE.getName(), 
XHTMLOutputFormat.INSTANCE)
+               .put(RTFOutputFormat.INSTANCE.getName(), 
RTFOutputFormat.INSTANCE)
+               .put(PlainTextOutputFormat.INSTANCE.getName(), 
PlainTextOutputFormat.INSTANCE)
+               .build();
+    
+    public static final Locale DEFAULT_LOCALE = Locale.US;
+    public static final String DEFAULT_LOCALE_KEY = DEFAULT_LOCALE.toString();
+    public static final Map<String, Locale> LOCALE_MAP;
+    static {
+        List<Locale> availableLocales = new 
ArrayList<Locale>(Arrays.asList(Locale.getAvailableLocales()));
+        
+        for (Iterator<Locale> iterator = availableLocales.iterator(); 
iterator.hasNext();) {
+            Locale locale = iterator.next();
+            // Don't bloat the list with "variants"
+            if (!StringUtils.isBlank(locale.getVariant())) {
+                iterator.remove();
+            }
+        }
+        
+        if (!availableLocales.contains(DEFAULT_LOCALE)) {
+            availableLocales.add(DEFAULT_LOCALE);
+        }
+        
+        Map<String, Locale> map = new HashMap<String, Locale>();
+        for (Locale locale : availableLocales) {
+            map.put(locale.toString(), locale);
+        }
+        
+        LOCALE_MAP = Collections.unmodifiableMap(map);
+    }
+
+    public static final TimeZone DEFAULT_TIME_ZONE = 
TimeZone.getTimeZone("America/Los_Angeles");
+    public static final String DEFAULT_TIME_ZONE_KEY;
+    public static final Map<String, TimeZone> TIME_ZONE_MAP;
+    static {
+        String[] availableIDs = TimeZone.getAvailableIDs();
+        
+        DEFAULT_TIME_ZONE_KEY = AllowedSettingValues.DEFAULT_TIME_ZONE.getID();
+        if (!ArrayUtils.contains(availableIDs, DEFAULT_TIME_ZONE_KEY)) {
+            ArrayUtils.add(availableIDs, DEFAULT_TIME_ZONE_KEY);
+        }
+        
+        Map<String, TimeZone> map = new HashMap<String, TimeZone>();
+        for (String timeZoneId : availableIDs) {
+            map.put(timeZoneId, TimeZone.getTimeZone(timeZoneId));
+        }
+        
+        TIME_ZONE_MAP = Collections.unmodifiableMap(map);
+    }
+
+    public static final String DEFAULT_TAG_SYNTAX_KEY = "angleBracket";
+    public static final Map<String, Integer> TAG_SYNTAX_MAP = ImmutableMap.of(
+               "angleBracket", Configuration.ANGLE_BRACKET_TAG_SYNTAX,
+               "squareBracket", Configuration.SQUARE_BRACKET_TAG_SYNTAX,
+               "autoDetect", Configuration.AUTO_DETECT_TAG_SYNTAX);
+    public static final int DEFAULT_TAG_SYNTAX = 
TAG_SYNTAX_MAP.get(DEFAULT_TAG_SYNTAX_KEY);
+    
+    public static final String DEFAULT_INTERPOLATION_SYNTAX_KEY = "legacy";
+    public static final Map<String, Integer> INTERPOLATION_SYNTAX_MAP = 
ImmutableMap.of(
+               "legacy", Configuration.LEGACY_INTERPOLATION_SYNTAX,
+               "dollar", Configuration.DOLLAR_INTERPOLATION_SYNTAX,
+               "squareBracket", 
Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX);
+    public static final int DEFAULT_INTERPOLATION_SYNTAX = 
Configuration.LEGACY_INTERPOLATION_SYNTAX;
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/java/org/apache/freemarker/onlinetester/services/AllowedSettingValuesMaps.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/onlinetester/services/AllowedSettingValuesMaps.java
 
b/src/main/java/org/apache/freemarker/onlinetester/services/AllowedSettingValuesMaps.java
deleted file mode 100644
index e3111aa..0000000
--- 
a/src/main/java/org/apache/freemarker/onlinetester/services/AllowedSettingValuesMaps.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.onlinetester.services;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TimeZone;
-
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.StringUtils;
-
-import freemarker.core.HTMLOutputFormat;
-import freemarker.core.OutputFormat;
-import freemarker.core.PlainTextOutputFormat;
-import freemarker.core.RTFOutputFormat;
-import freemarker.core.UndefinedOutputFormat;
-import freemarker.core.XHTMLOutputFormat;
-import freemarker.core.XMLOutputFormat;
-
-/**
- * Maps of the setting values the caller can chose from (these are the value 
shown in a dropdown on the UI).
- */
-public class AllowedSettingValuesMaps {
-
-    public static final OutputFormat DEFAULT_OUTPUT_FORMAT = 
UndefinedOutputFormat.INSTANCE;
-    public static final String DEFAULT_OUTPUT_FORMAT_KEY = 
DEFAULT_OUTPUT_FORMAT.getName();
-    public static final Map<String, OutputFormat> OUTPUT_FORMAT_MAP;
-    static {
-        Map<String, OutputFormat> map = new HashMap<String, OutputFormat>();
-        
-        addOutputFormatToMap(map, UndefinedOutputFormat.INSTANCE);
-        addOutputFormatToMap(map, HTMLOutputFormat.INSTANCE);
-        addOutputFormatToMap(map, XMLOutputFormat.INSTANCE);
-        addOutputFormatToMap(map, XHTMLOutputFormat.INSTANCE);
-        addOutputFormatToMap(map, RTFOutputFormat.INSTANCE);
-        addOutputFormatToMap(map, PlainTextOutputFormat.INSTANCE);
-        
-        OUTPUT_FORMAT_MAP = Collections.unmodifiableMap(map);
-    }
-    
-    private static void addOutputFormatToMap(Map<String, OutputFormat> map, 
OutputFormat outputFormat) {
-        map.put(outputFormat.getName(), outputFormat);
-    }
-
-    public static final Locale DEFAULT_LOCALE = Locale.US;
-    public static final String DEFAULT_LOCALE_KEY = DEFAULT_LOCALE.toString();
-    public static final Map<String, Locale> LOCALE_MAP;
-    static {
-        List<Locale> availableLocales = new 
ArrayList<Locale>(Arrays.asList(Locale.getAvailableLocales()));
-        
-        for (Iterator<Locale> iterator = availableLocales.iterator(); 
iterator.hasNext();) {
-            Locale locale = iterator.next();
-            // Don't bloat the list with "variants"
-            if (!StringUtils.isBlank(locale.getVariant())) {
-                iterator.remove();
-            }
-        }
-        
-        if (!availableLocales.contains(DEFAULT_LOCALE)) {
-            availableLocales.add(DEFAULT_LOCALE);
-        }
-        
-        Map<String, Locale> map = new HashMap<String, Locale>();
-        for (Locale locale : availableLocales) {
-            map.put(locale.toString(), locale);
-        }
-        
-        LOCALE_MAP = Collections.unmodifiableMap(map);
-    }
-
-    public static final TimeZone DEFAULT_TIME_ZONE = 
TimeZone.getTimeZone("America/Los_Angeles");
-    
-    public static final String DEFAULT_TIME_ZONE_KEY;
-    
-    public static final Map<String, TimeZone> TIME_ZONE_MAP;
-    static {
-        String[] availableIDs = TimeZone.getAvailableIDs();
-        
-        DEFAULT_TIME_ZONE_KEY = 
AllowedSettingValuesMaps.DEFAULT_TIME_ZONE.getID();
-        if (!ArrayUtils.contains(availableIDs, DEFAULT_TIME_ZONE_KEY)) {
-            ArrayUtils.add(availableIDs, DEFAULT_TIME_ZONE_KEY);
-        }
-        
-        Map<String, TimeZone> map = new HashMap<String, TimeZone>();
-        for (String timeZoneId : availableIDs) {
-            map.put(timeZoneId, TimeZone.getTimeZone(timeZoneId));
-        }
-        
-        TIME_ZONE_MAP = Collections.unmodifiableMap(map);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/java/org/apache/freemarker/onlinetester/services/FreeMarkerService.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/onlinetester/services/FreeMarkerService.java
 
b/src/main/java/org/apache/freemarker/onlinetester/services/FreeMarkerService.java
index fee66b7..20f9e78 100644
--- 
a/src/main/java/org/apache/freemarker/onlinetester/services/FreeMarkerService.java
+++ 
b/src/main/java/org/apache/freemarker/onlinetester/services/FreeMarkerService.java
@@ -52,6 +52,7 @@ import freemarker.template.Configuration;
 import freemarker.template.Template;
 import freemarker.template.TemplateException;
 import freemarker.template.TemplateExceptionHandler;
+import freemarker.template.utility.StringUtil;
 
 public class FreeMarkerService {
 
@@ -108,23 +109,13 @@ public class FreeMarkerService {
                                // Suppress it
                        }
         });
-        freeMarkerConfig.setLocale(AllowedSettingValuesMaps.DEFAULT_LOCALE);
-        
freeMarkerConfig.setTimeZone(AllowedSettingValuesMaps.DEFAULT_TIME_ZONE);
-        
freeMarkerConfig.setOutputFormat(AllowedSettingValuesMaps.DEFAULT_OUTPUT_FORMAT);
+        freeMarkerConfig.setLocale(AllowedSettingValues.DEFAULT_LOCALE);
+        freeMarkerConfig.setTimeZone(AllowedSettingValues.DEFAULT_TIME_ZONE);
+        
freeMarkerConfig.setOutputFormat(AllowedSettingValues.DEFAULT_OUTPUT_FORMAT);
         freeMarkerConfig.setOutputEncoding("UTF-8");
     }
     
     /**
-     * @param templateSourceCode
-     *            The FTL to execute; not {@code null}.
-     * @param dataModel
-     *            The FreeMarker data-model to execute the template with; 
maybe {@code null}.
-     * @param outputFormat
-     *            The output format to execute the template with; maybe {@code 
null}.
-     * @param locale
-     *            The locale to execute the template with; maybe {@code null}.
-     * @param timeZone
-     *            The time zone to execute the template with; maybe {@code 
null}.
      * 
      * @return The result of the template parsing and evaluation. The method 
won't throw exception if that fails due to
      *         errors in the template provided, instead it indicates this fact 
in the response object. That's because
@@ -137,13 +128,11 @@ public class FreeMarkerService {
      *             meaning of {@link RejectedExecutionException} either.
      */
     @SuppressWarnings("deprecation") // for Thread.stop()
-       public FreeMarkerServiceResponse calculateTemplateOutput(
-            String templateSourceCode, Object dataModel, OutputFormat 
outputFormat, Locale locale, TimeZone timeZone)
+       public FreeMarkerServiceResponse executeTemplate(ExecuteTemplateArgs 
args)
             throws RejectedExecutionException {
         Objects.requireNonNull(templateExecutor, "templateExecutor was null - 
was postConstruct ever called?");
         
-        final CalculateTemplateOutput task = new CalculateTemplateOutput(
-                templateSourceCode, dataModel, outputFormat, locale, timeZone);
+        final ExecuteTempalteTask task = new ExecuteTempalteTask(args);
         Future<FreeMarkerServiceResponse> future = 
templateExecutor.submit(task);
         
         synchronized (task) {
@@ -203,7 +192,7 @@ public class FreeMarkerService {
                                logger.warn("Calling Thread.stop() on 
unresponsive long template processing, which didn't "
                                                + "respond to 
Template.interrupt() on time. Service state may will be inconsistent; "
                                                + "JVM restart recommended!\n"
-                                               + "Template (quoted): \"" + 
StringEscapeUtils.escapeJava(templateSourceCode) + "\"");
+                                               + "Template (quoted): \"" + 
StringEscapeUtils.escapeJava(args.templateSourceCode) + "\"");
                            }
                            templateExecutorThread.stop();
                        }
@@ -217,7 +206,7 @@ public class FreeMarkerService {
                                    templateExecutionEnded = true;
                                }
                            } // sync
-                               } catch (InterruptedException e1) {
+                               } catch (InterruptedException e2) {
                                        // Just continue...
                                }
             }
@@ -226,14 +215,14 @@ public class FreeMarkerService {
                 logger.debug("Long template processing has ended.");
                 try {
                     return future.get();
-                } catch (InterruptedException | ExecutionException e1) {
-                    throw new FreeMarkerServiceException("Failed to get result 
from template executor task", e1);
+                } catch (InterruptedException | ExecutionException e2) {
+                    throw new FreeMarkerServiceException("Failed to get result 
from template executor task", e2);
                 }
             } else {
                 throw new FreeMarkerServiceException(
                         "Couldn't stop long running template processing within 
" + ABORTION_LOOP_TIME_LIMIT
                         + " ms. It's possibly stuck forever. Such problems can 
exhaust the executor pool. "
-                        + "Template (quoted): \"" + 
StringEscapeUtils.escapeJava(templateSourceCode) + "\"");
+                        + "Template (quoted): " + 
StringUtil.jQuote(args.templateSourceCode));
             }
         }
     }
@@ -265,25 +254,65 @@ public class FreeMarkerService {
         logger.debug("The template had error(s)", e);
         return new FreeMarkerServiceResponse.Builder().buildForFailure(e);
     }
+    
+    /**
+     * Argument to {@link 
FreeMarkerService#executeTemplate(ExecuteTemplateArgs)}; fluent API to deal 
with many parameters,
+     * most of which is optional. Only {@code templateSourceCode} must be set 
to non-{@code null}.
+     */
+       public static class ExecuteTemplateArgs {
+               private String templateSourceCode;
+               private Object dataModel;
+               private OutputFormat outputFormat;
+               private Locale locale;
+               private TimeZone timeZone;
+               private Integer tagSyntax;
+               private Integer interpolationSyntax;
+
+               public ExecuteTemplateArgs templateSourceCode(String 
templateSourceCode) {
+                       this.templateSourceCode = templateSourceCode;
+                       return this;
+               }
+
+               public ExecuteTemplateArgs dataModel(Object dataModel) {
+                       this.dataModel = dataModel;
+                       return this;
+               }
+
+               public ExecuteTemplateArgs outputFormat(OutputFormat 
outputFormat) {
+                       this.outputFormat = outputFormat;
+                       return this;
+               }
+
+               public ExecuteTemplateArgs locale(Locale locale) {
+                       this.locale = locale;
+                       return this;
+               }
 
-    private class CalculateTemplateOutput implements 
Callable<FreeMarkerServiceResponse> {
+               public ExecuteTemplateArgs timeZone(TimeZone timeZone) {
+                       this.timeZone = timeZone;
+                       return this;
+               }
+
+               public ExecuteTemplateArgs tagSyntax(Integer tagSyntax) {
+                       this.tagSyntax = tagSyntax;
+                       return this;
+               }
+
+               public ExecuteTemplateArgs interpolationSyntax(Integer 
interpolationSyntax) {
+                       this.interpolationSyntax = interpolationSyntax;
+                       return this;
+               }
+       }
+
+    private class ExecuteTempalteTask implements 
Callable<FreeMarkerServiceResponse> {
         
+       private final ExecuteTemplateArgs args;
         private boolean templateExecutionStarted;
         private Thread templateExecutorThread;
-        private final String templateSourceCode;
-        private final Object dataModel;
-        private final OutputFormat outputFormat;
-        private final Locale locale;
-        private final TimeZone timeZone;
         private boolean taskEnded;
 
-        private CalculateTemplateOutput(String templateSourceCode, Object 
dataModel,
-                OutputFormat outputFormat, Locale locale, TimeZone timeZone) {
-            this.templateSourceCode = templateSourceCode;
-            this.dataModel = dataModel;
-            this.outputFormat = outputFormat;
-            this.locale = locale;
-            this.timeZone = timeZone;
+        private ExecuteTempalteTask(ExecuteTemplateArgs args) {
+            this.args = args;
         }
         
         @Override
@@ -293,19 +322,25 @@ public class FreeMarkerService {
                 try {
                     TemplateConfiguration tCfg = new TemplateConfiguration();
                     tCfg.setParentConfiguration(freeMarkerConfig);
-                    if (outputFormat != null) {
-                        tCfg.setOutputFormat(outputFormat);
+                    
+                    if (args.outputFormat != null) {
+                        tCfg.setOutputFormat(args.outputFormat);
                     }
-                    if (locale != null) {
-                        tCfg.setLocale(locale);
+                    if (args.locale != null) {
+                        tCfg.setLocale(args.locale);
                     }
-                    if (timeZone != null) {
-                        tCfg.setTimeZone(timeZone);
+                    if (args.timeZone != null) {
+                        tCfg.setTimeZone(args.timeZone);
+                    }
+                    if (args.tagSyntax != null) {
+                       tCfg.setTagSyntax(args.tagSyntax);
+                    }
+                    if (args.interpolationSyntax != null) {
+                       tCfg.setInterpolationSyntax(args.interpolationSyntax);
                     }
                     
                     template = new Template(null, null,
-                            new StringReader(templateSourceCode), 
freeMarkerConfig, tCfg, null);
-                    
+                            new StringReader(args.templateSourceCode), 
freeMarkerConfig, tCfg, null);
                     tCfg.apply(template);
                 } catch (ParseException e) {
                     // Expected (part of normal operation)
@@ -326,7 +361,7 @@ public class FreeMarkerService {
                         notifyAll();
                     }
                     try {
-                        template.process(dataModel, new 
LengthLimitedWriter(writer, maxOutputLength));
+                        template.process(args.dataModel, new 
LengthLimitedWriter(writer, maxOutputLength));
                     } finally {
                         synchronized (this) {
                             templateExecutorThread = null;
@@ -337,7 +372,7 @@ public class FreeMarkerService {
                 } catch (LengthLimitExceededException e) {
                     // Not really an error, we just cut the output here.
                     resultTruncated = true;
-                    writer.write(new 
MessageFormat(MAX_OUTPUT_LENGTH_EXCEEDED_TERMINATION, 
AllowedSettingValuesMaps.DEFAULT_LOCALE)
+                    writer.write(new 
MessageFormat(MAX_OUTPUT_LENGTH_EXCEEDED_TERMINATION, 
AllowedSettingValues.DEFAULT_LOCALE)
                             .format(new Object[] { maxOutputLength }));
                     // Falls through
                 } catch (TemplateException e) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/java/org/apache/freemarker/onlinetester/services/FreeMarkerServiceException.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/onlinetester/services/FreeMarkerServiceException.java
 
b/src/main/java/org/apache/freemarker/onlinetester/services/FreeMarkerServiceException.java
index 2ca7ee4..177b9ce 100644
--- 
a/src/main/java/org/apache/freemarker/onlinetester/services/FreeMarkerServiceException.java
+++ 
b/src/main/java/org/apache/freemarker/onlinetester/services/FreeMarkerServiceException.java
@@ -28,7 +28,6 @@ public class FreeMarkerServiceException extends 
RuntimeException {
 
     public FreeMarkerServiceException(String message) {
         super(message);
-        // TODO Auto-generated constructor stub
     }
 
     public FreeMarkerServiceException(String message, Throwable cause) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/java/org/apache/freemarker/onlinetester/view/FreeMarkerOnlineView.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/onlinetester/view/FreeMarkerOnlineView.java
 
b/src/main/java/org/apache/freemarker/onlinetester/view/FreeMarkerOnlineView.java
index c90e2a5..3932dda 100644
--- 
a/src/main/java/org/apache/freemarker/onlinetester/view/FreeMarkerOnlineView.java
+++ 
b/src/main/java/org/apache/freemarker/onlinetester/view/FreeMarkerOnlineView.java
@@ -25,47 +25,79 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.function.BiFunction;
 
 import io.dropwizard.views.View;
 import org.apache.commons.lang3.StringUtils;
 
 import org.apache.freemarker.onlinetester.model.SelectionOption;
-import org.apache.freemarker.onlinetester.services.AllowedSettingValuesMaps;
+import org.apache.freemarker.onlinetester.services.AllowedSettingValues;
 
 import freemarker.template.Configuration;
 
 public class FreeMarkerOnlineView extends View {
 
-    private static final List<SelectionOption> LOCALE_SELECTION_OPTIONS = 
toLocaleSelectionOptions(AllowedSettingValuesMaps.LOCALE_MAP);
-    private static final List<SelectionOption> TIME_ZONE_SELECTION_OPTIONS = 
toSelectionOptions(AllowedSettingValuesMaps.TIME_ZONE_MAP);
-    private static final List<SelectionOption> OUTPUT_FORMAT_SELECTION_OPTIONS 
= toSelectionOptions(AllowedSettingValuesMaps.OUTPUT_FORMAT_MAP);
+       private static final List<SelectionOption> LOCALE_SELECTION_OPTIONS = 
toSelectionOptions(
+                       AllowedSettingValues.LOCALE_MAP,
+                       (k, v) -> truncate(v.getDisplayName(Locale.US), 18) + 
"; " + v.toString(),
+                       true);
+       private static final List<SelectionOption> TIME_ZONE_SELECTION_OPTIONS 
= toSelectionOptions(
+                       AllowedSettingValues.TIME_ZONE_MAP,
+                       (k, v) -> truncate(k, 25),
+                       true);
+       private static final List<SelectionOption> 
OUTPUT_FORMAT_SELECTION_OPTIONS = toSelectionOptions(
+                       AllowedSettingValues.OUTPUT_FORMAT_MAP,
+                       (k, v) -> k,
+                       true);
+       private static final List<SelectionOption> TAG_SYNTAX_SELECTION_OPTIONS 
= toSelectionOptions(
+                       AllowedSettingValues.TAG_SYNTAX_MAP,
+                       (k, v) -> {
+                               String label = k;
+                               if (v == 
Configuration.ANGLE_BRACKET_TAG_SYNTAX) {
+                                       label += ", like <#...>, <@...>";
+                               } else if (v == 
Configuration.SQUARE_BRACKET_TAG_SYNTAX) {
+                                       label += ", like [#...], [@...]";
+                               }
+                               return label;
+                       },
+                       false);
+       private static final List<SelectionOption> 
INTERPOLATION_SYNTAX_SELECTION_OPTIONS = toSelectionOptions(
+                       AllowedSettingValues.INTERPOLATION_SYNTAX_MAP, (k, v) 
-> {
+                               String label = k;
+                               if (v == 
Configuration.LEGACY_INTERPOLATION_SYNTAX) {
+                                       label += ", like ${...}, #{...}";
+                               } else if (v == 
Configuration.DOLLAR_INTERPOLATION_SYNTAX) {
+                                       label += ", like ${...}";
+                               } else if (v == 
Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX) {
+                                       label += ", like [=...]";
+                               }
+                               return label;
+                       },
+                       false);
     
     private String template = "";
     private String dataModel = "";
-    private String outputFormat = 
AllowedSettingValuesMaps.DEFAULT_OUTPUT_FORMAT_KEY;
-    private String locale = AllowedSettingValuesMaps.DEFAULT_LOCALE_KEY;
-    private String timeZone = AllowedSettingValuesMaps.DEFAULT_TIME_ZONE_KEY;
+    private String outputFormat = 
AllowedSettingValues.DEFAULT_OUTPUT_FORMAT_KEY;
+    private String locale = AllowedSettingValues.DEFAULT_LOCALE_KEY;
+    private String timeZone = AllowedSettingValues.DEFAULT_TIME_ZONE_KEY;
+    private String tagSyntax = 
String.valueOf(AllowedSettingValues.DEFAULT_TAG_SYNTAX_KEY);
+    private String interpolationSyntax = 
String.valueOf(AllowedSettingValues.DEFAULT_INTERPOLATION_SYNTAX_KEY);
     
     private boolean execute;
-    
-    private static List<SelectionOption> toSelectionOptions(Map<String, ?> 
settingValueMap) {
+
+    private static <V> List<SelectionOption> toSelectionOptions(
+               Map<String, ? extends V> settingValueMap,
+               BiFunction<String, ? super V, String> kvpToLabel, boolean 
sortByLabel) {
         ArrayList<SelectionOption> selectionOptions = new 
ArrayList<SelectionOption>(settingValueMap.size());
-        for (String key : settingValueMap.keySet()) {
-            selectionOptions.add(new SelectionOption(key, truncate(key, 25)));
+        for (Map.Entry<String, ? extends V> ent : settingValueMap.entrySet()) {
+               String key = ent.getKey();
+            selectionOptions.add(new SelectionOption(
+                       key,
+                       kvpToLabel.apply(key, ent.getValue())));
         }
-        Collections.sort(selectionOptions);
-        return selectionOptions;
-    }
-    
-    private static List<SelectionOption> toLocaleSelectionOptions(Map<String, 
Locale> localeMap) {
-        ArrayList<SelectionOption> selectionOptions = new 
ArrayList<SelectionOption>(localeMap.size());
-        for (Map.Entry<String, Locale> ent : localeMap.entrySet()) {
-            Locale locale = ent.getValue();
-            selectionOptions.add(
-                    new SelectionOption(ent.getKey(),
-                    truncate(locale.getDisplayName(Locale.US), 18) + "; " + 
locale.toString()));
+        if (sortByLabel) {
+               Collections.sort(selectionOptions);
         }
-        Collections.sort(selectionOptions);
         return selectionOptions;
     }
     
@@ -118,12 +150,20 @@ public class FreeMarkerOnlineView extends View {
         return TIME_ZONE_SELECTION_OPTIONS;
     }
 
+    public List<SelectionOption> getTagSyntaxes() {
+        return TAG_SYNTAX_SELECTION_OPTIONS;
+    }
+
+    public List<SelectionOption> getInterpolationSyntaxes() {
+        return INTERPOLATION_SYNTAX_SELECTION_OPTIONS;
+    }
+    
     public String getOutputFormat() {
         return outputFormat;
     }
 
     public void setOutputFormat(String outputFormat) {
-        this.outputFormat = withDefault(outputFormat, 
AllowedSettingValuesMaps.DEFAULT_OUTPUT_FORMAT_KEY);
+        this.outputFormat = withDefault(outputFormat, 
AllowedSettingValues.DEFAULT_OUTPUT_FORMAT_KEY);
     }
 
     public String getLocale() {
@@ -131,7 +171,7 @@ public class FreeMarkerOnlineView extends View {
     }
 
     public void setLocale(String locale) {
-        this.locale = withDefault(locale, 
AllowedSettingValuesMaps.DEFAULT_LOCALE_KEY);
+        this.locale = withDefault(locale, 
AllowedSettingValues.DEFAULT_LOCALE_KEY);
     }
 
     public String getTimeZone() {
@@ -139,10 +179,26 @@ public class FreeMarkerOnlineView extends View {
     }
 
     public void setTimeZone(String timeZone) {
-        this.timeZone = withDefault(timeZone, 
AllowedSettingValuesMaps.DEFAULT_TIME_ZONE_KEY);
+        this.timeZone = withDefault(timeZone, 
AllowedSettingValues.DEFAULT_TIME_ZONE_KEY);
     }
     
-    public boolean isExecute() {
+    public String getTagSyntax() {
+               return tagSyntax;
+       }
+
+       public void setTagSyntax(String tagSyntax) {
+               this.tagSyntax = withDefault(tagSyntax, 
AllowedSettingValues.DEFAULT_TAG_SYNTAX_KEY);
+       }
+
+       public String getInterpolationSyntax() {
+               return interpolationSyntax;
+       }
+
+       public void setInterpolationSyntax(String interpolationSyntax) {
+               this.interpolationSyntax = withDefault(interpolationSyntax, 
AllowedSettingValues.DEFAULT_INTERPOLATION_SYNTAX_KEY);
+       }
+
+       public boolean isExecute() {
         return execute;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/resources/assets/js/script.js
----------------------------------------------------------------------
diff --git a/src/main/resources/assets/js/script.js 
b/src/main/resources/assets/js/script.js
index 776d90a..9afba14 100644
--- a/src/main/resources/assets/js/script.js
+++ b/src/main/resources/assets/js/script.js
@@ -17,6 +17,8 @@
  * under the License.
  */
 
+// IMPORTANT! If you modify this file, increase the number after "?v=" in the 
FreeMarker template!
+
 $(document).ready(function() {
     $("#eval-btn").click(function() {
         execute();
@@ -42,7 +44,9 @@ function execute() {
         "dataModel": $("#dataModel").val(),
         "outputFormat": $("#outputFormat").val(),
         "locale": $("#locale").val(),
-        "timeZone": $("#timeZone").val()
+        "timeZone": $("#timeZone").val(),
+        "tagSyntax": $("#tagSyntax").val(),
+        "interpolationSyntax": $("#interpolationSyntax").val(),
     }
 
     $.ajax({
@@ -102,3 +106,5 @@ function showResult(result, isError) {
     $(".resultContainer").show();
     autosize.update($("#result"));
 }
+
+//IMPORTANT! If you modify this file, increase the number after "?v=" in the 
FreeMarker template!

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/resources/freemarker-online.yml
----------------------------------------------------------------------
diff --git a/src/main/resources/freemarker-online.yml 
b/src/main/resources/freemarker-online.yml
index a582b87..27070aa 100644
--- a/src/main/resources/freemarker-online.yml
+++ b/src/main/resources/freemarker-online.yml
@@ -36,7 +36,7 @@ server:
     appenders: []
 viewRendererConfiguration:
   .ftl:
-    incompatibleImprovements: 2.3.27
+    incompatibleImprovements: 2.3.28
     locale: en_US
     timeZone: UTC
     outputEncoding: UTF-8
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/main/resources/view/freemarker-online.ftl
----------------------------------------------------------------------
diff --git a/src/main/resources/view/freemarker-online.ftl 
b/src/main/resources/view/freemarker-online.ftl
index 982fcfa..7b13c68 100644
--- a/src/main/resources/view/freemarker-online.ftl
+++ b/src/main/resources/view/freemarker-online.ftl
@@ -34,7 +34,7 @@
     <script src="https://code.jquery.com/jquery-1.11.2.min.js";></script>
     <script 
src="https://cdnjs.cloudflare.com/ajax/libs/jquery.blockUI/2.70/jquery.blockUI.min.js";></script>
     <script 
src="https://cdnjs.cloudflare.com/ajax/libs/autosize.js/3.0.8/autosize.min.js";></script>
-    <script src="assets/js/script.js?v=3"></script>
+    <script src="assets/js/script.js?v=4"></script><!-- Always increase v if 
the script.js is changed! -->
     <script>
         $(function() {
             // Auto-focus on first form input:
@@ -124,6 +124,16 @@ someXML = &lt;example 
x="1"&gt;text&lt;/example&gt;</pre></div>
                        helpHover='Date/time values are shown as seen from this 
time zone' />
                   </div>
                 </div>
+                <div class="formPanel">
+                  <div class="horizontalBox">
+                    <@u.htmlSelect caption="Tag syntax" name="tagSyntax" 
selectionOptions=tagSyntaxes
+                        
helpLink='https://freemarker.apache.org/docs/dgui_misc_alternativesyntax.html#dgui_misc_alternativesyntax_tag'
 />
+                  </div>
+                  <div class="horizontalBox">
+                    <@u.htmlSelect caption="Interpolation syntax" 
name="interpolationSyntax" selectionOptions=interpolationSyntaxes
+                        
helpLink='https://freemarker.apache.org/docs/dgui_misc_alternativesyntax.html#dgui_misc_alternativesyntax_interpolation'
  />
+                  </div>
+                </div>
                 <div class="formBottomButtonsContainer">
                        <input id="eval-btn" type="button" value="Evaluate" 
class="pure-button pure-button-primary"/>
                        &nbsp; <span class="faint">Ctrl+Enter in input fields 
will submit this form too</span>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/test/java/org/apache/freemarker/onlinetester/ApplicationStartsTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/onlinetester/ApplicationStartsTest.java 
b/src/test/java/org/apache/freemarker/onlinetester/ApplicationStartsTest.java
index a422016..7d3aeea 100644
--- 
a/src/test/java/org/apache/freemarker/onlinetester/ApplicationStartsTest.java
+++ 
b/src/test/java/org/apache/freemarker/onlinetester/ApplicationStartsTest.java
@@ -33,7 +33,7 @@ import io.dropwizard.testing.junit.DropwizardAppRule;
 public class ApplicationStartsTest {
 
     @ClassRule
-    public final static DropwizardAppRule RULE = new 
DropwizardAppRule<FreeMarkerOnlineTesterConfiguration>
+    public final static DropwizardAppRule<FreeMarkerOnlineTesterConfiguration> 
RULE = new DropwizardAppRule<>
             (FreeMarkerOnlineTester.class,
             Resources.getResource("freemarker-online.yml").getPath());
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/test/java/org/apache/freemarker/onlinetester/resources/ExecuteApiResourceTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/onlinetester/resources/ExecuteApiResourceTest.java
 
b/src/test/java/org/apache/freemarker/onlinetester/resources/ExecuteApiResourceTest.java
index 7fa3fb4..ef89470 100644
--- 
a/src/test/java/org/apache/freemarker/onlinetester/resources/ExecuteApiResourceTest.java
+++ 
b/src/test/java/org/apache/freemarker/onlinetester/resources/ExecuteApiResourceTest.java
@@ -19,16 +19,19 @@
 
 package org.apache.freemarker.onlinetester.resources;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.core.Response;
 
 import org.apache.freemarker.onlinetester.model.ExecuteRequest;
-import org.apache.freemarker.onlinetester.model.ExecuteResourceField;
-import org.apache.freemarker.onlinetester.model.ExecuteResourceProblem;
 import org.apache.freemarker.onlinetester.model.ExecuteResponse;
+import org.apache.freemarker.onlinetester.model.ExecuteResponseProblem;
 import org.junit.Test;
 
 public class ExecuteApiResourceTest extends ResourceTest {
@@ -53,7 +56,7 @@ public class ExecuteApiResourceTest extends ResourceTest {
         assertEquals(200, resp.getStatus());
         ExecuteResponse response = resp.readEntity(ExecuteResponse.class);
         assertNotNull(response.getProblems());
-        assertTrue(containsProblem(response, ExecuteResourceField.DATA_MODEL));
+        assertTrue(containsProblem(response, ExecuteRequest.Field.DATA_MODEL));
     }
 
     @Test
@@ -63,8 +66,8 @@ public class ExecuteApiResourceTest extends ResourceTest {
         assertEquals(200, resp.getStatus());
         ExecuteResponse response = resp.readEntity(ExecuteResponse.class);
         assertNotNull(response.getProblems());
-        assertTrue(containsProblem(response, ExecuteResourceField.DATA_MODEL));
-        String problemMessage = getProblemMessage(response, 
ExecuteResourceField.DATA_MODEL);
+        assertTrue(containsProblem(response, ExecuteRequest.Field.DATA_MODEL));
+        String problemMessage = getProblemMessage(response, 
ExecuteRequest.Field.DATA_MODEL);
         assertThat(problemMessage, containsString("data model"));
         assertThat(problemMessage, containsString("limit"));
     }
@@ -76,8 +79,8 @@ public class ExecuteApiResourceTest extends ResourceTest {
         assertEquals(200, resp.getStatus());
         ExecuteResponse response = resp.readEntity(ExecuteResponse.class);
         assertNotNull(response.getProblems());
-        assertTrue(containsProblem(response, ExecuteResourceField.TEMPLATE));
-        String problemMessage = getProblemMessage(response, 
ExecuteResourceField.TEMPLATE);
+        assertTrue(containsProblem(response, ExecuteRequest.Field.TEMPLATE));
+        String problemMessage = getProblemMessage(response, 
ExecuteRequest.Field.TEMPLATE);
         assertThat(problemMessage, containsString("template"));
         assertThat(problemMessage, containsString("limit"));
     }
@@ -88,17 +91,22 @@ public class ExecuteApiResourceTest extends ResourceTest {
         req.setOutputFormat("wrongOutputFormat");
         req.setLocale("wrongLocale");
         req.setTimeZone("wrongTimeZone");
+        req.setTagSyntax("wrongTagSyntax");
+        req.setInterpolationSyntax("wrongInterpolationSyntax");
 
         Response resp = postJSON(req);
         
         assertEquals(200, resp.getStatus());
         ExecuteResponse response = resp.readEntity(ExecuteResponse.class);
         assertNotNull(response.getProblems());
-        assertThat(getProblemMessage(response, ExecuteResourceField.TEMPLATE), 
containsString("limit"));
-        assertThat(getProblemMessage(response, 
ExecuteResourceField.DATA_MODEL), containsString("limit"));
-        assertThat(getProblemMessage(response, 
ExecuteResourceField.OUTPUT_FORMAT), containsString("wrongOutputFormat"));
-        assertThat(getProblemMessage(response, ExecuteResourceField.LOCALE), 
containsString("wrongLocale"));
-        assertThat(getProblemMessage(response, 
ExecuteResourceField.TIME_ZONE), containsString("wrongTimeZone"));
+        assertThat(getProblemMessage(response, ExecuteRequest.Field.TEMPLATE), 
containsString("limit"));
+        assertThat(getProblemMessage(response, 
ExecuteRequest.Field.DATA_MODEL), containsString("limit"));
+        assertThat(getProblemMessage(response, 
ExecuteRequest.Field.OUTPUT_FORMAT), containsString("wrongOutputFormat"));
+        assertThat(getProblemMessage(response, ExecuteRequest.Field.LOCALE), 
containsString("wrongLocale"));
+        assertThat(getProblemMessage(response, 
ExecuteRequest.Field.TIME_ZONE), containsString("wrongTimeZone"));
+        assertThat(getProblemMessage(response, 
ExecuteRequest.Field.TAG_SYNTAX), containsString("wrongTagSyntax"));
+        assertThat(getProblemMessage(response, 
ExecuteRequest.Field.INTERPOLATION_SYNTAX), containsString(
+                       "wrongInterpolationSyntax"));
     }
     
     private String create30KString() {
@@ -109,8 +117,8 @@ public class ExecuteApiResourceTest extends ResourceTest {
         return sb.toString();
     }
 
-    private boolean containsProblem(ExecuteResponse response, 
ExecuteResourceField field) {
-        for (ExecuteResourceProblem problem : response.getProblems()) {
+    private boolean containsProblem(ExecuteResponse response, 
ExecuteRequest.Field field) {
+        for (ExecuteResponseProblem problem : response.getProblems()) {
             if (problem.getField() == field) {
                 return true;
             }
@@ -118,8 +126,8 @@ public class ExecuteApiResourceTest extends ResourceTest {
         return false;
     }
 
-    private String getProblemMessage(ExecuteResponse response, 
ExecuteResourceField field) {
-        for (ExecuteResourceProblem problem : response.getProblems()) {
+    private String getProblemMessage(ExecuteResponse response, 
ExecuteRequest.Field field) {
+        for (ExecuteResponseProblem problem : response.getProblems()) {
             if (problem.getField() == field) {
                 return problem.getMessage();
             }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/test/java/org/apache/freemarker/onlinetester/resources/WebPageResourceTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/onlinetester/resources/WebPageResourceTest.java
 
b/src/test/java/org/apache/freemarker/onlinetester/resources/WebPageResourceTest.java
index 739628f..091911a 100644
--- 
a/src/test/java/org/apache/freemarker/onlinetester/resources/WebPageResourceTest.java
+++ 
b/src/test/java/org/apache/freemarker/onlinetester/resources/WebPageResourceTest.java
@@ -21,24 +21,17 @@ package org.apache.freemarker.onlinetester.resources;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyMap;
-import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.when;
 
-import java.util.Locale;
-import java.util.TimeZone;
-
+import org.apache.freemarker.onlinetester.services.FreeMarkerService;
+import 
org.apache.freemarker.onlinetester.services.FreeMarkerService.ExecuteTemplateArgs;
+import org.apache.freemarker.onlinetester.view.FreeMarkerOnlineView;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 
-import org.apache.freemarker.onlinetester.services.FreeMarkerService;
-import org.apache.freemarker.onlinetester.view.FreeMarkerOnlineView;
-
-import freemarker.core.OutputFormat;
-
 @RunWith(MockitoJUnitRunner.class)
 public class WebPageResourceTest {
 
@@ -50,9 +43,7 @@ public class WebPageResourceTest {
 
     @Test
     public void testInitialForm() {
-        when(freeMarkerService.calculateTemplateOutput(
-                anyString(), anyMap(), any(OutputFormat.class), 
any(Locale.class), any(TimeZone.class)))
-                .thenThrow(new AssertionError());
+        
when(freeMarkerService.executeTemplate(any(ExecuteTemplateArgs.class))).thenThrow(new
 AssertionError());
         FreeMarkerOnlineView view = webPageResource.blankForm();
         assertEquals(view.getTemplate(), "");
         assertEquals(view.getDataModel(), "");
@@ -60,10 +51,8 @@ public class WebPageResourceTest {
     
     @Test
     public void testPostedBlankForm() {
-        when(freeMarkerService.calculateTemplateOutput(
-                anyString(), anyMap(), any(OutputFormat.class), 
any(Locale.class), any(TimeZone.class)))
-                .thenThrow(new AssertionError());
-        FreeMarkerOnlineView view = webPageResource.formResult(null, null, 
null, null, null);
+        
when(freeMarkerService.executeTemplate(any(ExecuteTemplateArgs.class))).thenThrow(new
 AssertionError());
+        FreeMarkerOnlineView view = webPageResource.formResult(null, null, 
null, null, null, null, null);
         assertEquals(view.getTemplate(), "");
         assertEquals(view.getDataModel(), "");
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/test/java/org/apache/freemarker/onlinetester/services/FreeMarkerServiceTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/onlinetester/services/FreeMarkerServiceTest.java
 
b/src/test/java/org/apache/freemarker/onlinetester/services/FreeMarkerServiceTest.java
index 37c52c9..3865265 100644
--- 
a/src/test/java/org/apache/freemarker/onlinetester/services/FreeMarkerServiceTest.java
+++ 
b/src/test/java/org/apache/freemarker/onlinetester/services/FreeMarkerServiceTest.java
@@ -36,6 +36,7 @@ import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import 
org.apache.freemarker.onlinetester.services.FreeMarkerService.ExecuteTemplateArgs;
 import org.junit.Before;
 import org.junit.Test;
 import org.slf4j.Logger;
@@ -44,6 +45,7 @@ import org.slf4j.LoggerFactory;
 import freemarker.core.Environment;
 import freemarker.core.HTMLOutputFormat;
 import freemarker.core.ParseException;
+import freemarker.template.Configuration;
 import freemarker.template.TemplateDirectiveBody;
 import freemarker.template.TemplateDirectiveModel;
 import freemarker.template.TemplateException;
@@ -77,8 +79,8 @@ public class FreeMarkerServiceTest {
 
     @Test
     public void testCalculationOfATemplateWithNoDataModel() {
-        FreeMarkerServiceResponse serviceResponse = 
getService().calculateTemplateOutput(
-                "test", Collections.<String, Object>emptyMap(), null, null, 
null);
+        FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       
.templateSourceCode("test").dataModel(Collections.emptyMap()));
         assertThat(serviceResponse.isSuccesful(), is(true));
         assertThat(serviceResponse.getTemplateOutput(), is("test"));
     }
@@ -88,8 +90,8 @@ public class FreeMarkerServiceTest {
         HashMap<String, Object> dataModel = new HashMap<>();
         dataModel.put("var1", "val1");
         String templateSourceCode = "${var1}";
-        FreeMarkerServiceResponse serviceResponse = 
getService().calculateTemplateOutput(
-                templateSourceCode, dataModel, null, null, null);
+        FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       
.templateSourceCode(templateSourceCode).dataModel(dataModel));
         assertThat(serviceResponse.getTemplateOutput(), equalTo("val1"));
     }
 
@@ -99,8 +101,8 @@ public class FreeMarkerServiceTest {
         dataModel.put("var1", "val1");
         dataModel.put("var2", "val2");
         String template = "${var1?capitalize} ${var2?cap_first}";
-        FreeMarkerServiceResponse serviceResponse = 
getService().calculateTemplateOutput(
-                template, dataModel, null, null, null);
+        FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       .templateSourceCode(template).dataModel(dataModel));
         assertThat(serviceResponse.getTemplateOutput(), equalTo("Val1 Val2"));
     }
 
@@ -108,13 +110,13 @@ public class FreeMarkerServiceTest {
     public void testOutputFormatParamterMatters() {
         String template = "${'&'}";
         {
-            FreeMarkerServiceResponse serviceResponse = 
getService().calculateTemplateOutput(
-                    template, null, null, null, null);
+            FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       .templateSourceCode(template));
             assertThat(serviceResponse.getTemplateOutput(), equalTo("&"));
         }
         {
-            FreeMarkerServiceResponse serviceResponse = 
getService().calculateTemplateOutput(
-                    template, null, HTMLOutputFormat.INSTANCE, null, null);
+            FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       
.templateSourceCode(template).outputFormat(HTMLOutputFormat.INSTANCE));
             assertThat(serviceResponse.getTemplateOutput(), equalTo("&amp;"));
         }
     }
@@ -123,13 +125,13 @@ public class FreeMarkerServiceTest {
     public void testLocaleParameterMatters() {
         String template = "${.locale}";
         {
-            FreeMarkerServiceResponse serviceResponse = 
getService().calculateTemplateOutput(
-                    template, null, null, new Locale("en", "US"), null);
+            FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       .templateSourceCode(template).locale(Locale.US));
             assertThat(serviceResponse.getTemplateOutput(), equalTo("en_US"));
         }
         {
-            FreeMarkerServiceResponse serviceResponse = 
getService().calculateTemplateOutput(
-                    template, null, null, new Locale("ru", "RU"), null);
+            FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       .templateSourceCode(template).locale(new Locale("ru", 
"RU")));
             assertThat(serviceResponse.getTemplateOutput(), equalTo("ru_RU"));
         }
     }
@@ -140,33 +142,68 @@ public class FreeMarkerServiceTest {
         
         String gmt1Result;
         {
-            FreeMarkerServiceResponse serviceResponse = 
getService().calculateTemplateOutput(
-                    template, null, null, null, 
TimeZone.getTimeZone("GMT+01"));
+            FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       
.templateSourceCode(template).timeZone(TimeZone.getTimeZone("GMT+01")));
             gmt1Result = serviceResponse.getTemplateOutput();
         }
         
         String gmt2Result;
         {
-            FreeMarkerServiceResponse serviceResponse = 
getService().calculateTemplateOutput(
-                    template, null, null, new Locale("ru", "RU"), null);
+            FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       .templateSourceCode(template).locale(new Locale("ru", 
"RU")));
             gmt2Result = serviceResponse.getTemplateOutput();
         }
         
         assertThat(gmt1Result, not(equalTo(gmt2Result)));
     }
+
+    @Test
+    public void testTagSyntaxParameterMatters() {
+        String template = "[#if true]1[/#if]<#if true>2</#if>";
+        {
+            FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       
.templateSourceCode(template).tagSyntax(Configuration.ANGLE_BRACKET_TAG_SYNTAX));
+            assertThat(serviceResponse.getTemplateOutput(), equalTo("[#if 
true]1[/#if]2"));
+        }
+        for (int tagSyntax : new int[] { 
Configuration.SQUARE_BRACKET_TAG_SYNTAX, Configuration.AUTO_DETECT_TAG_SYNTAX 
}) {
+            FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       .templateSourceCode(template).tagSyntax(tagSyntax));
+            assertThat(serviceResponse.getTemplateOutput(), equalTo("1<#if 
true>2</#if>"));
+        }
+    }
+
+    @Test
+    public void testInterpolationSyntaxParameterMatters() {
+        String template = "${1} #{2} [=3]";
+        {
+            FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       
.templateSourceCode(template).interpolationSyntax(Configuration.LEGACY_INTERPOLATION_SYNTAX));
+            assertThat(serviceResponse.getTemplateOutput(), equalTo("1 2 
[=3]"));
+        }
+        {
+            FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       
.templateSourceCode(template).interpolationSyntax(Configuration.DOLLAR_INTERPOLATION_SYNTAX));
+            assertThat(serviceResponse.getTemplateOutput(), equalTo("1 #{2} 
[=3]"));
+        }
+        {
+            FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       
.templateSourceCode(template).interpolationSyntax(Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX));
+            assertThat(serviceResponse.getTemplateOutput(), equalTo("${1} #{2} 
3"));
+        }
+    }
     
     @Test
     public void testTemplateWithSyntaxError() {
-        FreeMarkerServiceResponse serviceResponse = 
getService().calculateTemplateOutput(
-                "test ${xx", Collections.<String, Object>emptyMap(), null, 
null, null);
+        FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       .templateSourceCode("test 
${xx").dataModel(Collections.emptyMap()));
         assertThat(serviceResponse.isSuccesful(), is(false));
         assertThat(serviceResponse.getFailureReason(), 
instanceOf(ParseException.class));
     }
 
     @Test
     public void testTemplateWithEvaluationError() {
-        FreeMarkerServiceResponse serviceResponse = 
getService().calculateTemplateOutput(
-                "test ${x}", Collections.<String, Object>emptyMap(), null, 
null, null);
+        FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       .templateSourceCode("test 
${x}").dataModel(Collections.emptyMap()));
         assertThat(serviceResponse.isSuccesful(), is(false));
         assertThat(serviceResponse.getFailureReason(), 
instanceOf(TemplateException.class));
     }
@@ -174,8 +211,8 @@ public class FreeMarkerServiceTest {
     @Test
     public void testResultAlmostTruncation() {
         serviceBuilder.setMaxOutputLength(5);
-        FreeMarkerServiceResponse serviceResponse = 
getService().calculateTemplateOutput(
-                TRUNCATION_TEST_TEMPLATE, Collections.<String, 
Object>emptyMap(), null, null, null);
+        FreeMarkerServiceResponse serviceResponse = 
getService().executeTemplate(new ExecuteTemplateArgs()
+                       
.templateSourceCode(TRUNCATION_TEST_TEMPLATE).dataModel(Collections.emptyMap()));
         assertThat(serviceResponse.isSuccesful(), is(true));
         assertThat(serviceResponse.isTemplateOutputTruncated(), is(false));
         assertThat(serviceResponse.getTemplateOutput(), 
equalTo(TRUNCATION_TEST_TEMPLATE));
@@ -185,8 +222,8 @@ public class FreeMarkerServiceTest {
     public void testResultTruncation() {
         serviceBuilder.setMaxOutputLength(4);
         FreeMarkerService service = getService();
-        FreeMarkerServiceResponse serviceResponse = 
service.calculateTemplateOutput(
-                TRUNCATION_TEST_TEMPLATE, Collections.<String, 
Object>emptyMap(), null, null, null);
+        FreeMarkerServiceResponse serviceResponse = 
service.executeTemplate(new ExecuteTemplateArgs()
+                       
.templateSourceCode(TRUNCATION_TEST_TEMPLATE).dataModel(Collections.emptyMap()));
         assertThat(serviceResponse.isSuccesful(), is(true));
         assertThat(serviceResponse.isTemplateOutputTruncated(), is(true));
         assertThat(serviceResponse.getTemplateOutput(),
@@ -205,8 +242,9 @@ public class FreeMarkerServiceTest {
         
                     @Override
                     public FreeMarkerServiceResponse call() throws Exception {
-                        return getService().calculateTemplateOutput(
-                                "<#list 1.. as _></#list>", 
Collections.<String, Object>emptyMap(), null, null, null);
+                        return getService().executeTemplate(new 
ExecuteTemplateArgs()
+                                       .templateSourceCode("<#list 1.. as 
_></#list>")
+                                       .dataModel(Collections.emptyMap()));
                     }
                     
                 });
@@ -231,7 +269,8 @@ public class FreeMarkerServiceTest {
                 new Thread(new Runnable() {
                     @Override
                     public void run() {
-                        service.calculateTemplateOutput("<@blocker/>", 
blockerDataModel, null, null, null);
+                        service.executeTemplate(new ExecuteTemplateArgs()
+                                       
.templateSourceCode("<@blocker/>").dataModel(blockerDataModel));
                     }
                 }).start();
             }
@@ -253,7 +292,8 @@ public class FreeMarkerServiceTest {
             
             // Shouldn't accept on more tasks:
             try {
-                service.calculateTemplateOutput("<@blocker/>", 
blockerDataModel, null, null, null);
+                service.executeTemplate(new ExecuteTemplateArgs()
+                               
.templateSourceCode("<@blocker/>").dataModel(blockerDataModel));
                 fail("Expected RejectedExecutionException, but nothing was 
thrown.");
             } catch (RejectedExecutionException e) {
                 // Expected

http://git-wip-us.apache.org/repos/asf/incubator-freemarker-online-tester/blob/007c4e13/src/test/java/org/apache/freemarker/onlinetester/view/FreeMarkerOnlineViewTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/onlinetester/view/FreeMarkerOnlineViewTest.java
 
b/src/test/java/org/apache/freemarker/onlinetester/view/FreeMarkerOnlineViewTest.java
index b9572ba..e632d77 100644
--- 
a/src/test/java/org/apache/freemarker/onlinetester/view/FreeMarkerOnlineViewTest.java
+++ 
b/src/test/java/org/apache/freemarker/onlinetester/view/FreeMarkerOnlineViewTest.java
@@ -26,7 +26,7 @@ import java.util.TimeZone;
 
 import org.junit.Test;
 
-import org.apache.freemarker.onlinetester.services.AllowedSettingValuesMaps;
+import org.apache.freemarker.onlinetester.services.AllowedSettingValues;
 
 import freemarker.core.HTMLOutputFormat;
 
@@ -41,9 +41,9 @@ public class FreeMarkerOnlineViewTest {
         FreeMarkerOnlineView view = new FreeMarkerOnlineView();
         assertEquals(view.getTemplate(), "");
         assertEquals(view.getDataModel(), "");
-        assertEquals(view.getOutputFormat(), 
AllowedSettingValuesMaps.DEFAULT_OUTPUT_FORMAT_KEY);
-        assertEquals(view.getLocale(), 
AllowedSettingValuesMaps.DEFAULT_LOCALE_KEY);
-        assertEquals(view.getTimeZone(), 
AllowedSettingValuesMaps.DEFAULT_TIME_ZONE_KEY);
+        assertEquals(view.getOutputFormat(), 
AllowedSettingValues.DEFAULT_OUTPUT_FORMAT_KEY);
+        assertEquals(view.getLocale(), 
AllowedSettingValues.DEFAULT_LOCALE_KEY);
+        assertEquals(view.getTimeZone(), 
AllowedSettingValues.DEFAULT_TIME_ZONE_KEY);
     }
 
     @Test


Reply via email to