This is an automated email from the ASF dual-hosted git repository.

ddekany pushed a commit to branch 3
in repository https://gitbox.apache.org/repos/asf/freemarker.git


The following commit(s) were added to refs/heads/3 by this push:
     new f3c7dfbc Forward ported from 2.3-gae: PR #89 TemplateProcessingTracer
f3c7dfbc is described below

commit f3c7dfbc420ac59b184a819a1a8d29ef46868e5f
Author: ddekany <[email protected]>
AuthorDate: Tue Dec 19 21:01:55 2023 +0100

    Forward ported from 2.3-gae: PR #89 TemplateProcessingTracer
---
 .../apache/freemarker/core/OutputFormatTest.java   | 46 ++++++++++++++++++++++
 .../apache/freemarker/core/AutoEscapingPolicy.java |  4 +-
 .../core/BuiltInBannedWhenForcedAutoEscaping.java  | 27 +++++++++++++
 .../core/BuiltInsForOutputFormatRelated.java       |  2 +-
 .../MutableParsingAndProcessingConfiguration.java  | 12 +++---
 .../core/MutableProcessingConfiguration.java       |  4 +-
 .../main/javacc/org/apache/freemarker/core/FTL.jj  | 39 +++++++++++++++++-
 .../org/apache/freemarker/test/TemplateTest.java   | 37 +++++++----------
 8 files changed, 139 insertions(+), 32 deletions(-)

diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/OutputFormatTest.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/OutputFormatTest.java
index 12338661..02dd6bd0 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/OutputFormatTest.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/OutputFormatTest.java
@@ -36,6 +36,7 @@ import java.io.IOException;
 import java.io.StringWriter;
 import java.io.Writer;
 import java.util.Collections;
+import java.util.function.Consumer;
 
 import static org.apache.freemarker.core.AutoEscapingPolicy.*;
 import static org.junit.Assert.assertEquals;
@@ -816,6 +817,51 @@ public class OutputFormatTest extends TemplateTest {
                 .build();
     }
 
+    @Test
+    public void testForcedAutoEsc() throws Exception {
+        String commonFTL = "${'.'} ${.autoEsc?c}";
+        String esced = "\\. true";
+
+        setConfigurationForTestForceAutoEsc(cfgBuilder -> 
cfgBuilder.setOutputFormat(SeldomEscapedOutputFormat.INSTANCE));
+        assertOutput(commonFTL, esced);
+
+        setConfigurationForTestForceAutoEsc(cfgBuilder -> 
cfgBuilder.setOutputFormat(DummyOutputFormat.INSTANCE));
+        assertOutput(commonFTL, esced);
+
+        setConfigurationForTestForceAutoEsc(cfgBuilder -> 
cfgBuilder.setOutputFormat(PlainTextOutputFormat.INSTANCE));
+        assertOutput("<#ftl outputFormat='seldomEscaped'>" + commonFTL, esced);
+
+        setConfigurationForTestForceAutoEsc(cfgBuilder -> 
cfgBuilder.setOutputFormat(HTMLOutputFormat.INSTANCE));
+        assertOutput("<#outputFormat 'seldomEscaped'>" + commonFTL + 
"</#outputFormat>", esced);
+
+        setConfigurationForTestForceAutoEsc(cfgBuilder -> 
cfgBuilder.setOutputFormat(PlainTextOutputFormat.INSTANCE));
+        assertErrorContains("", IllegalArgumentException.class,
+                "plainText", "autoEscapingPolicy", "force");
+
+        setConfigurationForTestForceAutoEsc(cfgBuilder -> 
cfgBuilder.setOutputFormat(DummyOutputFormat.INSTANCE));
+        assertErrorContains("<#ftl autoEsc=false>", ParseException.class,
+                "autoEsc=false", "autoEscapingPolicy", "force");
+        assertErrorContains("<#outputFormat 'plainText'></#outputformat>", 
ParseException.class,
+                "plainText", "autoEscapingPolicy", "force");
+        assertErrorContains("<#noAutoEsc></#noAutoEsc>", ParseException.class,
+                "noAutoEsc", "autoEscapingPolicy", "force");
+        assertErrorContains("<#noAutoEsc></#noautoesc>", ParseException.class,
+                "noAutoEsc", "autoEscapingPolicy", "force");
+        assertErrorContains("<#assign foo='bar'>${foo?noEsc}", 
ParseException.class,
+                "?noEsc", "autoEscapingPolicy", "force");
+        assertErrorContains("<#assign foo='bar'>${foo?noEsc}", 
ParseException.class,
+                "?noEsc", "autoEscapingPolicy", "force");
+    }
+
+    private void 
setConfigurationForTestForceAutoEsc(Consumer<Configuration.ExtendableBuilder<?>>
 cfgBuilderAdjuster) {
+        setConfiguration(cfgBuilder -> {
+            cfgBuilder.setRegisteredCustomOutputFormats(ImmutableList.of(
+                    SeldomEscapedOutputFormat.INSTANCE, 
DummyOutputFormat.INSTANCE));
+            cfgBuilder.setAutoEscapingPolicy(AutoEscapingPolicy.FORCE);
+            cfgBuilderAdjuster.accept(cfgBuilder);
+        });
+    }
+
     @Test
     public void testDynamicParsingBIsInherticContextOutputFormat() throws 
Exception {
         // Dynamic parser BI-s are supposed to use the ParsingConfiguration of 
the calling template, and ignore anything
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/AutoEscapingPolicy.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/AutoEscapingPolicy.java
index c221559a..4958308a 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/AutoEscapingPolicy.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/AutoEscapingPolicy.java
@@ -40,5 +40,7 @@ public enum AutoEscapingPolicy {
     ENABLE_IF_DEFAULT,
 
     /** Enable auto-escaping if the {@link OutputFormat} supports it. */
-    ENABLE_IF_SUPPORTED
+    ENABLE_IF_SUPPORTED,
+
+    FORCE
 }
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInBannedWhenForcedAutoEscaping.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInBannedWhenForcedAutoEscaping.java
new file mode 100644
index 00000000..d87c0a3e
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInBannedWhenForcedAutoEscaping.java
@@ -0,0 +1,27 @@
+/*
+ * 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.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/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForOutputFormatRelated.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForOutputFormatRelated.java
index 5fa65582..8f44653c 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForOutputFormatRelated.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForOutputFormatRelated.java
@@ -24,7 +24,7 @@ import 
org.apache.freemarker.core.outputformat.MarkupOutputFormat;
 
 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/freemarker-core/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
index 1c05c5da..284abb06 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
@@ -19,10 +19,6 @@
 
 package org.apache.freemarker.core;
 
-import java.io.InputStream;
-import java.nio.charset.Charset;
-import java.util.Set;
-
 import org.apache.freemarker.core.outputformat.OutputFormat;
 import org.apache.freemarker.core.outputformat.impl.UndefinedOutputFormat;
 import org.apache.freemarker.core.templateresolver.TemplateLoader;
@@ -31,6 +27,10 @@ import org.apache.freemarker.core.util._SortedArraySet;
 import org.apache.freemarker.core.util._StringUtils;
 import org.apache.freemarker.core.util._UnmodifiableCompositeSet;
 
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.Set;
+
 public abstract class MutableParsingAndProcessingConfiguration<
         SelfT extends MutableParsingAndProcessingConfiguration<SelfT>>
         extends MutableProcessingConfiguration<SelfT>
@@ -98,6 +98,8 @@ public abstract class 
MutableParsingAndProcessingConfiguration<
                     
setAutoEscapingPolicy(AutoEscapingPolicy.ENABLE_IF_DEFAULT);
                 } else if ("enableIfSupported".equals(value)) {
                     
setAutoEscapingPolicy(AutoEscapingPolicy.ENABLE_IF_SUPPORTED);
+                } else if ("force".equals(value)) {
+                    setAutoEscapingPolicy(AutoEscapingPolicy.FORCE);
                 } else if ("disable".equals(value)) {
                     setAutoEscapingPolicy(AutoEscapingPolicy.DISABLE);
                 } else {
@@ -237,7 +239,7 @@ public abstract class 
MutableParsingAndProcessingConfiguration<
     }
 
     /**
-     * * Setter pair of {@link #getAutoEscapingPolicy()}.
+     * Setter pair of {@link #getAutoEscapingPolicy()}.
      */
     public void setAutoEscapingPolicy(AutoEscapingPolicy autoEscapingPolicy) {
         _NullArgumentException.check("autoEscapingPolicy", autoEscapingPolicy);
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
index 740f17ff..580d34eb 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
@@ -1532,7 +1532,9 @@ public abstract class 
MutableProcessingConfiguration<SelfT extends MutableProces
      *       <br>String value: {@code "enableIfDefault"} or {@code 
"enableIfDefault"} for
      *       {@link AutoEscapingPolicy#ENABLE_IF_DEFAULT},
      *       {@code "enableIfDefault"} or {@code "enableIfSupported"} for
-     *       {@link AutoEscapingPolicy#ENABLE_IF_SUPPORTED}
+     *       {@link AutoEscapingPolicy#ENABLE_IF_SUPPORTED},
+     *       {@code "enableIfDefault"} or {@code "force"} for
+     *       {@link AutoEscapingPolicy#FORCE}
      *       {@code "disable"} for {@link AutoEscapingPolicy#DISABLE}.
      *       
      *   <li><p>{@code "sourceEncoding"}:
diff --git a/freemarker-core/src/main/javacc/org/apache/freemarker/core/FTL.jj 
b/freemarker-core/src/main/javacc/org/apache/freemarker/core/FTL.jj
index 69bd6559..404ca5c1 100644
--- a/freemarker-core/src/main/javacc/org/apache/freemarker/core/FTL.jj
+++ b/freemarker-core/src/main/javacc/org/apache/freemarker/core/FTL.jj
@@ -191,7 +191,8 @@ public class FMParser {
         if (outputFormat instanceof MarkupOutputFormat) {
             if (autoEscapingPolicy == AutoEscapingPolicy.ENABLE_IF_DEFAULT) {
                 autoEscaping = ((MarkupOutputFormat) 
outputFormat).isAutoEscapedByDefault();
-            } else if (autoEscapingPolicy == 
AutoEscapingPolicy.ENABLE_IF_SUPPORTED) {
+            } else if (autoEscapingPolicy == 
AutoEscapingPolicy.ENABLE_IF_SUPPORTED
+                    || autoEscapingPolicy == AutoEscapingPolicy.FORCE) {
                 autoEscaping = true;
             } else if (autoEscapingPolicy == AutoEscapingPolicy.DISABLE) {
                 autoEscaping = false;
@@ -354,7 +355,16 @@ public class FMParser {
                     template, start);
         }
     }    
-    
+
+    private static String 
forcedAutoEscapingPolicyExceptionMessage(OutputFormat outputFormat) {
+        return forcedAutoEscapingPolicyExceptionMessage("Non-markup output 
format " + outputFormat);
+    }
+
+    private static String forcedAutoEscapingPolicyExceptionMessage(String 
whatCanNotBeUsed) {
+        return whatCanNotBeUsed + " can't be used when the \"" + 
MutableParsingAndProcessingConfiguration.AUTO_ESCAPING_POLICY_KEY + "\" "
+                + "configuration setting was set to \"force\" 
(AutoEscapingPolicy.FORCE).";
+    }
+
     private ParserIteratorBlockContext pushIteratorBlockContext() {
         if (iteratorBlockContexts == null) {
             iteratorBlockContexts = new ArrayList(4);
@@ -1953,6 +1963,14 @@ ASTExpression BuiltIn(ASTExpression lhoExp) :
             return result;
         }
 
+        if (result instanceof BuiltInBannedWhenForcedAutoEscaping) {
+            if (autoEscapingPolicy == AutoEscapingPolicy.FORCE) {
+                throw new ParseException(
+                        forcedAutoEscapingPolicyExceptionMessage("The ?" + 
t.image + " expression"),
+                        template, t);
+            }
+        }
+
         if (result instanceof MarkupOutputFormatBoundBuiltIn) {
             if (!(outputFormat instanceof MarkupOutputFormat)) {
                 throw new ParseException(
@@ -3742,6 +3760,9 @@ ASTDirOutputFormat OutputFormat() :
             } else {
                 outputFormat = 
template.getConfiguration().getOutputFormat(paramStr);
             }
+            if (!(outputFormat instanceof MarkupOutputFormat) && 
autoEscapingPolicy == AutoEscapingPolicy.FORCE) {
+                throw new 
ParseException(forcedAutoEscapingPolicyExceptionMessage(outputFormat), 
template, start);
+            }
             recalculateAutoEscapingField();
         } catch (IllegalArgumentException e) {
             throw new ParseException("Invalid format name: " + e.getMessage(), 
template, start, e.getCause());
@@ -3796,6 +3817,11 @@ ASTDirNoAutoEsc NoAutoEsc() :
 {
     start = <NOAUTOESC>
     {
+        if (autoEscapingPolicy == AutoEscapingPolicy.FORCE) {
+            throw new ParseException(
+                    forcedAutoEscapingPolicyExceptionMessage("<#noAutoEsc>"),
+                    template, start);
+        }
         lastAutoEscapingPolicy = autoEscapingPolicy;
         autoEscapingPolicy = AutoEscapingPolicy.DISABLE;
         recalculateAutoEscapingField();
@@ -4146,6 +4172,11 @@ void HeaderElement() :
                                 autoEscRequester = key;
                                 autoEscapingPolicy = 
AutoEscapingPolicy.ENABLE_IF_SUPPORTED;
                             } else {
+                                if (autoEscapingPolicy == 
AutoEscapingPolicy.FORCE) {
+                                    throw new ParseException(
+                                            
forcedAutoEscapingPolicyExceptionMessage(ks + "=false"),
+                                            exp);
+                                }
                                 autoEscapingPolicy = 
AutoEscapingPolicy.DISABLE;
                             }
                             recalculateAutoEscapingField();
@@ -4364,6 +4395,10 @@ ASTElement Root() :
         HeaderElement()
     ]
     {
+        if (!(outputFormat instanceof MarkupOutputFormat) && 
autoEscapingPolicy == AutoEscapingPolicy.FORCE) {
+            throw new 
IllegalArgumentException(forcedAutoEscapingPolicyExceptionMessage(outputFormat));
+        }
+
         // There will be no WrongEncodingException exception, release mark 
buffer:
         if (streamToUnmarkWhenEncEstabd != null) {
             streamToUnmarkWhenEncEstabd.mark(0);
diff --git 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTest.java
 
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTest.java
index 747fa606..7c1f9525 100644
--- 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTest.java
+++ 
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTest.java
@@ -19,29 +19,10 @@
 
 package org.apache.freemarker.test;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.io.FileNotFoundException;
-import java.io.FilterWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.Properties;
-
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import org.apache.commons.io.IOUtils;
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.ParseException;
-import org.apache.freemarker.core.Template;
-import org.apache.freemarker.core.TemplateConfiguration;
+import org.apache.freemarker.core.*;
 import org.apache.freemarker.core.TemplateConfiguration.Builder;
-import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core.Version;
 import org.apache.freemarker.core.templateresolver.TemplateLoader;
 import 
org.apache.freemarker.core.templateresolver.impl.ByteArrayTemplateLoader;
 import org.apache.freemarker.core.templateresolver.impl.MultiTemplateLoader;
@@ -49,7 +30,13 @@ import 
org.apache.freemarker.core.util._NullArgumentException;
 import org.apache.freemarker.core.util._StringUtils;
 import org.junit.Ignore;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.function.Consumer;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.*;
 
 /**
  * Superclass of JUnit tests that process templates.
@@ -104,6 +91,12 @@ public abstract class TemplateTest {
         setConfiguration(configurationBuilder.build());
     }
 
+    protected final void 
setConfiguration(Consumer<Configuration.ExtendableBuilder<?>> 
cfgBuilderAdjuster) {
+        Configuration.ExtendableBuilder<?> configBuilder = 
newConfigurationBuilder();
+        cfgBuilderAdjuster.accept(configBuilder);
+        setConfiguration(configBuilder.build());
+    }
+
     protected void assertOutput(String ftl, String expectedOut) throws 
IOException, TemplateException {
         assertOutput(createTemplate(ftl), expectedOut, false);
     }

Reply via email to