Merge pull request #88 from nolaviz/nolaviz-devel-forceautoesc
Support "forced" auto-escaping policy.
diff --git a/src/main/java/freemarker/core/BuiltInBannedWhenForcedAutoEscaping.java b/src/main/java/freemarker/core/BuiltInBannedWhenForcedAutoEscaping.java
new file mode 100644
index 0000000..c877fa8
--- /dev/null
+++ b/src/main/java/freemarker/core/BuiltInBannedWhenForcedAutoEscaping.java
@@ -0,0 +1,25 @@
+/*
+ * 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 freemarker.core;
+
+/**
+ * A built-in whose usage is banned when auto-escaping is in the "forced" state.
+ * This is just a marker; the actual checking is in {@code FTL.jj}.
+ */
+interface BuiltInBannedWhenForcedAutoEscaping {}
diff --git a/src/main/java/freemarker/core/BuiltInsForOutputFormatRelated.java b/src/main/java/freemarker/core/BuiltInsForOutputFormatRelated.java
index b4b9fe8..7397924 100644
--- a/src/main/java/freemarker/core/BuiltInsForOutputFormatRelated.java
+++ b/src/main/java/freemarker/core/BuiltInsForOutputFormatRelated.java
@@ -23,7 +23,7 @@
class BuiltInsForOutputFormatRelated {
- static class no_escBI extends AbstractConverterBI {
+ static class no_escBI extends AbstractConverterBI implements BuiltInBannedWhenForcedAutoEscaping {
@Override
protected TemplateModel calculateResult(String lho, MarkupOutputFormat outputFormat, Environment env)
diff --git a/src/main/java/freemarker/core/Configurable.java b/src/main/java/freemarker/core/Configurable.java
index 1056b26..4471349 100644
--- a/src/main/java/freemarker/core/Configurable.java
+++ b/src/main/java/freemarker/core/Configurable.java
@@ -2394,7 +2394,8 @@
* <br>String value: {@code "enable_if_default"} or {@code "enableIfDefault"} for
* {@link Configuration#ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY},
* {@code "enable_if_supported"} or {@code "enableIfSupported"} for
- * {@link Configuration#ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY}
+ * {@link Configuration#ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY},
+ * {@code "force" for {@link Configuration#FORCE_AUTO_ESCAPING_POLICY}, or
* {@code "disable"} for {@link Configuration#DISABLE_AUTO_ESCAPING_POLICY}.
*
* <li><p>{@code "default_encoding"}:
diff --git a/src/main/java/freemarker/template/Configuration.java b/src/main/java/freemarker/template/Configuration.java
index a89bb59..c92fbf7 100644
--- a/src/main/java/freemarker/template/Configuration.java
+++ b/src/main/java/freemarker/template/Configuration.java
@@ -437,7 +437,9 @@
public static final int ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY = 21;
/** Enable auto-escaping if the {@link OutputFormat} supports it. */
public static final int ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY = 22;
-
+ /** Require auto-escaping always. */
+ public static final int FORCE_AUTO_ESCAPING_POLICY = 23;
+
/** FreeMarker version 2.3.0 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_0 = new Version(2, 3, 0);
@@ -2174,7 +2176,8 @@
*
* @param autoEscapingPolicy
* One of the {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY},
- * {@link #ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY}, and {@link #DISABLE_AUTO_ESCAPING_POLICY} constants.
+ * {@link #ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY}, {@link #DISABLE_AUTO_ESCAPING_POLICY}, and
+ * {@link #FORCE_AUTO_ESCAPING_POLICY} constants.
*
* @see TemplateConfiguration#setAutoEscapingPolicy(int)
* @see Configuration#setOutputFormat(OutputFormat)
@@ -3405,6 +3408,8 @@
setAutoEscapingPolicy(ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY);
} else if ("enable_if_supported".equals(value) || "enableIfSupported".equals(value)) {
setAutoEscapingPolicy(ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY);
+ } else if ("force".equals(value)) {
+ setAutoEscapingPolicy(FORCE_AUTO_ESCAPING_POLICY);
} else if ("disable".equals(value)) {
setAutoEscapingPolicy(DISABLE_AUTO_ESCAPING_POLICY);
} else {
diff --git a/src/main/java/freemarker/template/_TemplateAPI.java b/src/main/java/freemarker/template/_TemplateAPI.java
index 8fa3250..d1f61fb 100644
--- a/src/main/java/freemarker/template/_TemplateAPI.java
+++ b/src/main/java/freemarker/template/_TemplateAPI.java
@@ -140,10 +140,12 @@
public static void validateAutoEscapingPolicyValue(int autoEscaping) {
if (autoEscaping != Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY
&& autoEscaping != Configuration.ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY
+ && autoEscaping != Configuration.FORCE_AUTO_ESCAPING_POLICY
&& autoEscaping != Configuration.DISABLE_AUTO_ESCAPING_POLICY) {
throw new IllegalArgumentException("\"auto_escaping\" can only be set to one of these: "
+ "Configuration.ENABLE_AUTO_ESCAPING_IF_DEFAULT, "
+ "or Configuration.ENABLE_AUTO_ESCAPING_IF_SUPPORTED"
+ + "or Configuration.FORCE_AUTO_ESCAPING_POLICY"
+ "or Configuration.DISABLE_AUTO_ESCAPING");
}
}
diff --git a/src/main/javacc/FTL.jj b/src/main/javacc/FTL.jj
index b13069f..a744727 100644
--- a/src/main/javacc/FTL.jj
+++ b/src/main/javacc/FTL.jj
@@ -237,6 +237,11 @@
outputFormat = outputFormatFromExt;
}
}
+ if (!(outputFormat instanceof MarkupOutputFormat)
+ && autoEscapingPolicy == Configuration.FORCE_AUTO_ESCAPING_POLICY) {
+ throw new IllegalArgumentException(
+ "Non-markup output format cannot be used when auto_escaping_policy is FORCE_AUTO_ESCAPING_POLICY.");
+ }
recalculateAutoEscapingField();
token_source.setParser(this);
@@ -358,7 +363,8 @@
if (outputFormat instanceof MarkupOutputFormat) {
if (autoEscapingPolicy == Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY) {
autoEscaping = ((MarkupOutputFormat) outputFormat).isAutoEscapedByDefault();
- } else if (autoEscapingPolicy == Configuration.ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY) {
+ } else if (autoEscapingPolicy == Configuration.ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY
+ || autoEscapingPolicy == Configuration.FORCE_AUTO_ESCAPING_POLICY) {
autoEscaping = true;
} else if (autoEscapingPolicy == Configuration.DISABLE_AUTO_ESCAPING_POLICY) {
autoEscaping = false;
@@ -2214,16 +2220,24 @@
}
if (result instanceof BuiltInBannedWhenAutoEscaping) {
- if (outputFormat instanceof MarkupOutputFormat && autoEscaping) {
- throw new ParseException(
- "Using ?" + t.image + " (legacy escaping) is not allowed when auto-escaping is on with "
- + "a markup output format (" + outputFormat.getName() + "), to avoid double-escaping mistakes.",
- template, t);
- }
+ if (outputFormat instanceof MarkupOutputFormat && autoEscaping) {
+ throw new ParseException(
+ "Using ?" + t.image + " (legacy escaping) is not allowed when auto-escaping is on with "
+ + "a markup output format (" + outputFormat.getName() + "), to avoid double-escaping mistakes.",
+ template, t);
+ }
return result;
}
+ if (result instanceof BuiltInBannedWhenForcedAutoEscaping) {
+ if (autoEscapingPolicy == Configuration.FORCE_AUTO_ESCAPING_POLICY) {
+ throw new ParseException(
+ "Using ?" + t.image + " is not allowed while auto_escaping_policy is FORCE_AUTO_ESCAPING_POLICY.",
+ template, t);
+ }
+ }
+
if (result instanceof MarkupOutputFormatBoundBuiltIn) {
if (!(outputFormat instanceof MarkupOutputFormat)) {
throw new ParseException(
@@ -4046,6 +4060,12 @@
} else {
outputFormat = template.getConfiguration().getOutputFormat(paramStr);
}
+ if (!(outputFormat instanceof MarkupOutputFormat)
+ && autoEscapingPolicy == Configuration.FORCE_AUTO_ESCAPING_POLICY) {
+ throw new ParseException(
+ "Non-markup output format cannot be used when auto_escaping_policy is FORCE_AUTO_ESCAPING_POLICY.",
+ template, start);
+ }
recalculateAutoEscapingField();
} catch (IllegalArgumentException e) {
throw new ParseException("Invalid format name: " + e.getMessage(), template, start, e.getCause());
@@ -4100,6 +4120,11 @@
{
start = <NOAUTOESC>
{
+ if (autoEscapingPolicy == Configuration.FORCE_AUTO_ESCAPING_POLICY) {
+ throw new ParseException(
+ "<#noautoesc> cannot be used when auto_escaping_policy is FORCE_AUTO_ESCAPING_POLICY.",
+ template, start);
+ }
lastAutoEscapingPolicy = autoEscapingPolicy;
autoEscapingPolicy = Configuration.DISABLE_AUTO_ESCAPING_POLICY;
recalculateAutoEscapingField();
@@ -4539,6 +4564,11 @@
autoEscRequester = key;
autoEscapingPolicy = Configuration.ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY;
} else {
+ if (autoEscapingPolicy == Configuration.FORCE_AUTO_ESCAPING_POLICY) {
+ throw new ParseException(
+ "auto_esc setting cannot be used when auto_escaping_policy is FORCE_AUTO_ESCAPING_POLICY.",
+ exp);
+ }
autoEscapingPolicy = Configuration.DISABLE_AUTO_ESCAPING_POLICY;
}
recalculateAutoEscapingField();
diff --git a/src/test/java/freemarker/core/OutputFormatTest.java b/src/test/java/freemarker/core/OutputFormatTest.java
index 3e3207d..14d3ecf 100644
--- a/src/test/java/freemarker/core/OutputFormatTest.java
+++ b/src/test/java/freemarker/core/OutputFormatTest.java
@@ -851,7 +851,29 @@
+ dExpted);
}
}
-
+
+ @Test
+ public void testForcedAutoEsc() throws Exception {
+ Configuration cfg = getConfiguration();
+ cfg.setRegisteredCustomOutputFormats(ImmutableList.of(
+ SeldomEscapedOutputFormat.INSTANCE, DummyOutputFormat.INSTANCE));
+ cfg.setAutoEscapingPolicy(Configuration.FORCE_AUTO_ESCAPING_POLICY);
+
+ String commonFTL = "${'.'} ${.autoEsc?c}";
+ String esced = "\\. true";
+
+ cfg.setOutputFormat(SeldomEscapedOutputFormat.INSTANCE);
+ assertOutput(commonFTL, esced);
+
+ cfg.setOutputFormat(DummyOutputFormat.INSTANCE);
+ assertOutput(commonFTL, esced);
+
+ cfg.setOutputFormat(DummyOutputFormat.INSTANCE);
+ assertErrorContains("<#outputformat 'plainText'></#outputformat>", "auto_escaping_policy is FORCE_AUTO_ESCAPING_POLICY");
+ assertErrorContains("<#noAutoEsc></#noAutoEsc>", "auto_escaping_policy is FORCE_AUTO_ESCAPING_POLICY");
+ assertErrorContains("<#assign foo='bar'>${foo?noEsc}", "auto_escaping_policy is FORCE_AUTO_ESCAPING_POLICY");
+ }
+
@Test
public void testDynamicParsingBIsInherticContextOutputFormat() throws Exception {
// Dynamic parser BI-s are supposed to use the parserConfiguration of the calling template, and ignore anything