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

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


The following commit(s) were added to refs/heads/2.3-gae by this push:
     new 204d76ab Add fine-grained mixed-content support to markup output 
formats.
     new 836903b6 Merge pull request #83 from nolaviz/nolaviz-devel
204d76ab is described below

commit 204d76ab82a01bce1935029b81a2dfae06dc1a0c
Author: Alon Ziv <[email protected]>
AuthorDate: Wed Aug 24 09:29:00 2022 +0000

    Add fine-grained mixed-content support to markup output formats.
---
 .../freemarker/core/CombinedMarkupOutputFormat.java  |  5 +++++
 src/main/java/freemarker/core/DollarVariable.java    | 10 +++++++---
 .../java/freemarker/core/MarkupOutputFormat.java     |  8 ++++++++
 src/main/java/freemarker/core/OutputFormat.java      | 20 +++++++++++++-------
 src/test/java/freemarker/core/DummyOutputFormat.java | 18 +++++++++++++++++-
 src/test/java/freemarker/core/OutputFormatTest.java  |  9 +++++++++
 6 files changed, 59 insertions(+), 11 deletions(-)

diff --git a/src/main/java/freemarker/core/CombinedMarkupOutputFormat.java 
b/src/main/java/freemarker/core/CombinedMarkupOutputFormat.java
index fd1e8233..a8a6b85c 100644
--- a/src/main/java/freemarker/core/CombinedMarkupOutputFormat.java
+++ b/src/main/java/freemarker/core/CombinedMarkupOutputFormat.java
@@ -69,6 +69,11 @@ public final class CombinedMarkupOutputFormat extends 
CommonMarkupOutputFormat<T
         outer.output(inner.escapePlainText(textToEsc), out);
     }
 
+    @Override
+    public <MO2 extends TemplateMarkupOutputModel<MO2>> void outputForeign(MO2 
mo, Writer out) throws IOException, TemplateModelException {
+        outer.outputForeign(mo, out);
+    }
+
     @Override
     public String escapePlainText(String plainTextContent) throws 
TemplateModelException {
         return outer.escapePlainText(inner.escapePlainText(plainTextContent));
diff --git a/src/main/java/freemarker/core/DollarVariable.java 
b/src/main/java/freemarker/core/DollarVariable.java
index 12fa02a4..6823a5ad 100644
--- a/src/main/java/freemarker/core/DollarVariable.java
+++ b/src/main/java/freemarker/core/DollarVariable.java
@@ -73,7 +73,9 @@ final class DollarVariable extends Interpolation {
             final TemplateMarkupOutputModel mo = (TemplateMarkupOutputModel) 
moOrStr;
             final MarkupOutputFormat moOF = mo.getOutputFormat();
             // ATTENTION: Keep this logic in sync. ?esc/?noEsc's logic!
-            if (moOF != outputFormat && 
!outputFormat.isOutputFormatMixingAllowed()) {
+            if (moOF == outputFormat) {
+                moOF.output(mo, out);
+            } else if (!outputFormat.isOutputFormatMixingAllowed()) {
                 final String srcPlainText;
                 // ATTENTION: Keep this logic in sync. ?esc/?noEsc's logic!
                 srcPlainText = moOF.getSourcePlainText(mo);
@@ -83,11 +85,13 @@ final class DollarVariable extends Interpolation {
                             " format, which differs from the current output 
format, ",
                             new _DelayedToString(outputFormat), ". Format 
conversion wasn't possible.");
                 }
-                if (outputFormat instanceof MarkupOutputFormat) {
-                    ((MarkupOutputFormat) outputFormat).output(srcPlainText, 
out);
+                if (markupOutputFormat != null) {
+                    markupOutputFormat.output(srcPlainText, out);
                 } else {
                     out.write(srcPlainText);
                 }
+            } else if (markupOutputFormat != null) {
+                markupOutputFormat.outputForeign(mo, out);
             } else {
                 moOF.output(mo, out);
             }
diff --git a/src/main/java/freemarker/core/MarkupOutputFormat.java 
b/src/main/java/freemarker/core/MarkupOutputFormat.java
index 81ffe8b2..352a1228 100644
--- a/src/main/java/freemarker/core/MarkupOutputFormat.java
+++ b/src/main/java/freemarker/core/MarkupOutputFormat.java
@@ -81,6 +81,14 @@ public abstract class MarkupOutputFormat<MO extends 
TemplateMarkupOutputModel> e
      */
     public abstract void output(String textToEsc, Writer out) throws 
IOException, TemplateModelException;
     
+    /**
+     * Outputs a value from a foreign output format; only used if {@link 
#isOutputFormatMixingAllowed()} is true.
+     * By default will just let the other output format handle the value, but 
can be overridden to support more nuanced conversions.
+     */
+    public <MO2 extends TemplateMarkupOutputModel<MO2>> void outputForeign(MO2 
mo, Writer out) throws IOException, TemplateModelException {
+        mo.getOutputFormat().output(mo, out);
+    }
+
     /**
      * If this {@link TemplateMarkupOutputModel} was created with {@link 
#fromPlainTextByEscaping(String)}, it returns
      * the original plain text, otherwise it returns {@code null}. Useful for 
converting between different types
diff --git a/src/main/java/freemarker/core/OutputFormat.java 
b/src/main/java/freemarker/core/OutputFormat.java
index 8ab52da0..eb7d6486 100644
--- a/src/main/java/freemarker/core/OutputFormat.java
+++ b/src/main/java/freemarker/core/OutputFormat.java
@@ -46,13 +46,19 @@ public abstract class OutputFormat {
 
     /**
      * Tells if this output format allows inserting {@link 
TemplateMarkupOutputModel}-s of another output formats into
-     * it. If {@code true}, the foreign {@link TemplateMarkupOutputModel} will 
be inserted into the output as is (like
-     * if the surrounding output format was the same). This is usually a bad 
idea to allow, as such an event could
-     * indicate application bugs. If this method returns {@code false} 
(recommended), then FreeMarker will try to
-     * assimilate the inserted value by converting its format to this format, 
which will currently (2.3.24) cause
-     * exception, unless the inserted value is made by escaping plain text and 
the target format is non-escaping, in
-     * which case format conversion is trivially possible. (It's not 
impossible that conversions will be extended beyond
-     * this, if there will be demand for that.)
+     * it.
+     *
+     * <p>If {@code true}, the foreign {@link TemplateMarkupOutputModel} will 
be inserted into the output. If the current
+     * output format is a {@link MarkupOutputFormat} this is done using the
+     * {@link MarkupOutputFormat#outputForeign(TemplateMarkupOutputModel, 
Writer)} method, which can implement smart
+     * conversions. The default behavior (and the only behavior for non-markup 
outputs) is to behave as if the surrounding
+     * output format was the same; this is usually a bad idea to allow, as 
such an event could
+     * indicate application bugs.
+     *
+     * <p>If this method returns {@code false} (recommended), then FreeMarker 
will try to assimilate the inserted value by
+     * converting its format to this format, which will currently (2.3.24) 
cause exception, unless the inserted value is
+     * made by escaping plain text and the target format is non-escaping, in 
which case format conversion is trivially
+     * possible. (It's not impossible that conversions will be extended beyond 
this, if there will be demand for that.)
      * 
      * <p>
      * {@code true} value is used by {@link UndefinedOutputFormat}.
diff --git a/src/test/java/freemarker/core/DummyOutputFormat.java 
b/src/test/java/freemarker/core/DummyOutputFormat.java
index c37f45e5..78186dae 100644
--- a/src/test/java/freemarker/core/DummyOutputFormat.java
+++ b/src/test/java/freemarker/core/DummyOutputFormat.java
@@ -22,6 +22,8 @@ import java.io.IOException;
 import java.io.Writer;
 
 import freemarker.template.TemplateModelException;
+import freemarker.core._TemplateModelException;
+import freemarker.core._DelayedToString;
 
 public class DummyOutputFormat extends 
CommonMarkupOutputFormat<TemplateDummyOutputModel> {
     
@@ -46,6 +48,20 @@ public class DummyOutputFormat extends 
CommonMarkupOutputFormat<TemplateDummyOut
         out.write(escapePlainText(textToEsc));
     }
 
+    @Override
+    public boolean isOutputFormatMixingAllowed() {
+        return true;
+    }
+
+    @Override
+    public <MO extends TemplateMarkupOutputModel<MO>> void outputForeign(MO 
mo, Writer out) throws IOException, TemplateModelException {
+        if (mo.getOutputFormat().getMimeType().equals("text/html")) {
+            mo.getOutputFormat().output(mo, out);
+        } else {
+            throw new _TemplateModelException("DummyOutputFormat is 
incompatible with ", new _DelayedToString(mo.getOutputFormat()));
+        }
+    }
+
     @Override
     public String escapePlainText(String plainTextContent) {
         return plainTextContent.replaceAll("(\\.|\\\\)", "\\\\$1");
@@ -61,4 +77,4 @@ public class DummyOutputFormat extends 
CommonMarkupOutputFormat<TemplateDummyOut
         return new TemplateDummyOutputModel(plainTextContent, markupContent);
     }
     
-}
\ No newline at end of file
+}
diff --git a/src/test/java/freemarker/core/OutputFormatTest.java 
b/src/test/java/freemarker/core/OutputFormatTest.java
index 0c72faa6..1856166c 100644
--- a/src/test/java/freemarker/core/OutputFormatTest.java
+++ b/src/test/java/freemarker/core/OutputFormatTest.java
@@ -744,6 +744,15 @@ public class OutputFormatTest extends TemplateTest {
                 "convention", "#noAutoEsc", "#noautoesc");
     }
     
+    @Test
+    public void testMixedContent() throws Exception {
+        
getConfiguration().setRegisteredCustomOutputFormats(Collections.singleton(DummyOutputFormat.INSTANCE));
+        addToDataModel("m1", HTMLOutputFormat.INSTANCE.fromMarkup("x"));
+        addToDataModel("m2", XMLOutputFormat.INSTANCE.fromMarkup("y"));
+        assertOutput("<#ftl outputFormat='dummy'>${m1}", "x");
+        assertErrorContains("<#ftl outputFormat='dummy'>${m2}", "is 
incompatible with");
+    }
+
     @Test
     public void testExplicitAutoEscBannedForNonMarkup() throws Exception {
         // While this restriction is technically unnecessary, we can catch a 
dangerous and probably common user

Reply via email to