You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2022/12/29 01:00:48 UTC

[freemarker] branch 2.3-gae updated: Added "JavaScript or JSON" CFormat, and made that the default. Some CFormat-related JavaDoc improvements/fixes.

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 77adbf2f Added "JavaScript or JSON" CFormat, and made that the default. Some CFormat-related JavaDoc improvements/fixes.
77adbf2f is described below

commit 77adbf2f11119e3b06e8f650d43509ef1fa5fd34
Author: ddekany <dd...@apache.org>
AuthorDate: Thu Dec 29 01:59:28 2022 +0100

    Added "JavaScript or JSON" CFormat, and made that the default. Some CFormat-related JavaDoc improvements/fixes.
---
 src/main/java/freemarker/core/CFormat.java         |  7 +--
 src/main/java/freemarker/core/Configurable.java    | 19 +++++---
 .../java/freemarker/core/Default230CFormat.java    |  6 +--
 .../java/freemarker/core/Default2321CFormat.java   |  6 +--
 src/main/java/freemarker/core/JSONCFormat.java     | 12 ++---
 src/main/java/freemarker/core/JavaCFormat.java     |  2 +-
 .../java/freemarker/core/JavaScriptCFormat.java    |  7 ++-
 ...ONCFormat.java => JavaScriptOrJSONCFormat.java} | 24 ++++++----
 src/main/java/freemarker/core/XSCFormat.java       |  2 +-
 .../java/freemarker/core/_StandardCLanguages.java  | 20 ++++----
 .../java/freemarker/template/Configuration.java    |  4 +-
 src/manual/en_US/book.xml                          | 55 +++++++++++++++-------
 .../java/freemarker/core/CAndCnBuiltInTest.java    | 18 ++++++-
 .../freemarker/template/ConfigurationTest.java     |  7 +--
 14 files changed, 121 insertions(+), 68 deletions(-)

diff --git a/src/main/java/freemarker/core/CFormat.java b/src/main/java/freemarker/core/CFormat.java
index 3c04976c..f5a2c84e 100644
--- a/src/main/java/freemarker/core/CFormat.java
+++ b/src/main/java/freemarker/core/CFormat.java
@@ -25,9 +25,10 @@ import freemarker.template.TemplateException;
 
 /**
  * Defines a format (usually a computer language) that's used by the {@code c}, {@code cn} built-ins, and for the
- * {@code "c"} and {@code "computer"} number formats (see {@link Configurable#setNumberFormat(String)}). A
- * {@link CFormat} currently defines how numbers, booleans, and strings are converted to text that defines a similar
- * value in the computer language that the {@link CFormat} is made for.
+ * {@code "c"} and {@code "computer"} {@link Configurable#setNumberFormat(String)} number_format), and
+ * the {@code "c"} {@link Configurable#setBooleanFormat(String)} boolean_format}.
+ * A {@link CFormat} currently defines how numbers, booleans, and strings are converted to text that defines a similar
+ * value in some computer language (or other computer-parsed syntax) that the {@link CFormat} is made for.
  *
  * <p><b>Experimental class!</b> This class is too new, and might will change over time. Therefore, for now the
  * constructor and most methods are not exposed outside FreeMarker, and so you can't create a custom implementation.
diff --git a/src/main/java/freemarker/core/Configurable.java b/src/main/java/freemarker/core/Configurable.java
index 428ac41d..71339145 100644
--- a/src/main/java/freemarker/core/Configurable.java
+++ b/src/main/java/freemarker/core/Configurable.java
@@ -697,9 +697,13 @@ public class Configurable {
     }
 
     /**
-     * Sets the computer language that's used for the {@code c}, {@code cn} built-ins, and for the {@code "c"}
-     * (and {@code "computer"}) number format ({@link Environment#getCTemplateNumberFormat()}). That is, of the
-     * templates output pieces in a computer language (like JavaScript), you should set what's that here.
+     * Sets the format (usually a computer language) used for the {@code c}, {@code cn} built-ins, and for the
+     * {@code "c"} {@code "computer"} before 2.3.32) {@link #setNumberFormat(String) number_format}, and the
+     * {@code "c"} {@link #setBooleanFormat(String) boolean_format}.
+     *
+     * <p>The default value depends on {@link Configuration#Configuration(Version) incompatible_improvements}.
+     * If that's 2.3.32 or higher, then it's {@code "JavaScript or JSON"}. For lower it's {@code "default 2.3.31"} or
+     * {@code "default 2.3.0"}.
      *
      * @since 2.3.32
      */
@@ -2185,10 +2189,11 @@ public class Configurable {
      *   <li><p>{@code "c_format"}:
      *       See {@link Configuration#setCFormat(CFormat)}.
      *       <br>String value: {@code "default"} (case insensitive) for the default (on {@link Configuration} only), or
-     *       one of the predefined values {@value JSONCFormat#NAME}, {@value JavaScriptCFormat#NAME},
-     *       {@value JavaCFormat#NAME}, {@value XSCFormat#NAME}, {@value Default230CFormat#NAME},
-     *       {@value Default2321CFormat#NAME}, or an <a href="#fm_obe">object builder expression</a> that gives a
-     *       {@link CFormat} object.
+     *       one of the predefined values {@code "JavaScript or JSON"}, {@code "JSON"},
+     *       {@code "JavaScript"}, {@code "Java"}, {@code "XS"},
+     *       {@code "default 2.3.0"}, {@code "default 2.3.21"}, or
+     *       {@code "default"} (only allowed for {@link Configuration} instances) for the default value,
+     *       or an <a href="#fm_obe">object builder expression</a> that gives a {@link CFormat} object.
      *
      *   <li><p>{@code "template_exception_handler"}:
      *       See {@link #setTemplateExceptionHandler(TemplateExceptionHandler)}.
diff --git a/src/main/java/freemarker/core/Default230CFormat.java b/src/main/java/freemarker/core/Default230CFormat.java
index b1ba1b4e..2a247844 100644
--- a/src/main/java/freemarker/core/Default230CFormat.java
+++ b/src/main/java/freemarker/core/Default230CFormat.java
@@ -29,8 +29,8 @@ import freemarker.template.Version;
 
 /**
  * Corresponds to the behavior of {@code ?c} if
- * {@linkplain Configuration#setIncompatibleImprovements(Version) Incompatible Improvements} is less than
- * {@link Configuration#VERSION_2_3_21}.
+ * {@link Configuration#Configuration(Version) incompatible_improvements} is less than
+ * {@linkplain Configuration#VERSION_2_3_21 2.3.21}.
  * The only good reason for using this is strict backward-compatibility.
  *
  * <p><b>Experimental class!</b> This class is too new, and might will change over time. Therefore, for now the
@@ -42,7 +42,7 @@ import freemarker.template.Version;
  *
  * @since 2.3.32
  */
-public class Default230CFormat extends AbstractLegacyCFormat {
+public final class Default230CFormat extends AbstractLegacyCFormat {
     public static final Default230CFormat INSTANCE = new Default230CFormat();
     public static final String NAME = "default 2.3.0";
 
diff --git a/src/main/java/freemarker/core/Default2321CFormat.java b/src/main/java/freemarker/core/Default2321CFormat.java
index d9a6463a..34258892 100644
--- a/src/main/java/freemarker/core/Default2321CFormat.java
+++ b/src/main/java/freemarker/core/Default2321CFormat.java
@@ -28,8 +28,8 @@ import freemarker.template.Version;
 
 /**
  * Corresponds to the behavior of {@code ?c} if
- * {@linkplain Configuration#setIncompatibleImprovements(Version) Incompatible Improvements} is between
- * {@link Configuration#VERSION_2_3_21} and {@link Configuration#VERSION_2_3_31}.
+ * {@link Configuration#Configuration(Version) incompatible_improvements} is between
+ * {@linkplain Configuration#VERSION_2_3_21 2.3.21} and {@linkplain Configuration#VERSION_2_3_31 2.3.31}.
  * The only good reason for using this is strict backward-compatibility.
  *
  * <p><b>Experimental class!</b> This class is too new, and might will change over time. Therefore, for now the
@@ -41,7 +41,7 @@ import freemarker.template.Version;
  *
  * @since 2.3.32
  */
-public class Default2321CFormat extends AbstractLegacyCFormat {
+public final class Default2321CFormat extends AbstractLegacyCFormat {
     public static final Default2321CFormat INSTANCE = new Default2321CFormat();
     public static final String NAME = "default 2.3.21";
 
diff --git a/src/main/java/freemarker/core/JSONCFormat.java b/src/main/java/freemarker/core/JSONCFormat.java
index ee469941..de102676 100644
--- a/src/main/java/freemarker/core/JSONCFormat.java
+++ b/src/main/java/freemarker/core/JSONCFormat.java
@@ -19,24 +19,22 @@
 
 package freemarker.core;
 
-import freemarker.template.Configuration;
 import freemarker.template.TemplateException;
-import freemarker.template.Version;
 import freemarker.template.utility.StringUtil;
 import freemarker.template.utility.StringUtil.JsStringEncCompatibility;
 import freemarker.template.utility.StringUtil.JsStringEncQuotation;
 
 /**
- * JSON {@link CFormat}; when this is used, values output by {@code ?c} are valid JSON values, and therefore also
- * valid JavaScript values.
- * This is the default of {@link Configurable#getCFormat()} starting from
- * {@linkplain Configuration#setIncompatibleImprovements(Version) Incompatible Improvements}
- * {@link Configuration#VERSION_2_3_32}.
+ * {@value #NAME} {@link CFormat}; to be used when generating JSON (and not JavaScript), except, in most cases
+ * {@link JavaScriptOrJSONCFormat} is recommended over this.
  *
  * <p><b>Experimental class!</b> This class is too new, and might will change over time. Therefore, for now the
  * most methods are not exposed outside FreeMarker. The class itself and some members are exposed as they are needed for
  * configuring FreeMarker.
  *
+ * @see JavaScriptCFormat
+ * @see JavaScriptOrJSONCFormat
+ *
  * @since 2.3.32
  */
 public final class JSONCFormat extends AbstractJSONLikeFormat {
diff --git a/src/main/java/freemarker/core/JavaCFormat.java b/src/main/java/freemarker/core/JavaCFormat.java
index 6a1d0d61..023044df 100644
--- a/src/main/java/freemarker/core/JavaCFormat.java
+++ b/src/main/java/freemarker/core/JavaCFormat.java
@@ -27,7 +27,7 @@ import freemarker.template.TemplateException;
 import freemarker.template.utility.StringUtil;
 
 /**
- * Java {@link CFormat}.
+ * {@value #NAME} {@link CFormat}.
  *
  * @since 2.3.32
  */
diff --git a/src/main/java/freemarker/core/JavaScriptCFormat.java b/src/main/java/freemarker/core/JavaScriptCFormat.java
index 1cb44adf..ce5daab8 100644
--- a/src/main/java/freemarker/core/JavaScriptCFormat.java
+++ b/src/main/java/freemarker/core/JavaScriptCFormat.java
@@ -25,13 +25,16 @@ import freemarker.template.utility.StringUtil.JsStringEncCompatibility;
 import freemarker.template.utility.StringUtil.JsStringEncQuotation;
 
 /**
- * JavaScript {@link CFormat}. This is almost the same as {@link JSONCFormat}, but it uses shorter forms where
- * the additional JavaScript features make that possible.
+ * {@value #NAME} {@link CFormat}, to be used when generating JavaScript (and not JSON), except, in most cases
+ * {@link JavaScriptOrJSONCFormat} is recommended over this.
  *
  * <p><b>Experimental class!</b> This class is too new, and might will change over time. Therefore, for now the
  * most methods are not exposed outside FreeMarker. The class itself and some members are exposed as they are needed for
  * configuring FreeMarker.
  *
+ * @see JSONCFormat
+ * @see JavaScriptOrJSONCFormat
+ *
  * @since 2.3.32
  */
 public final class JavaScriptCFormat extends AbstractJSONLikeFormat {
diff --git a/src/main/java/freemarker/core/JSONCFormat.java b/src/main/java/freemarker/core/JavaScriptOrJSONCFormat.java
similarity index 62%
copy from src/main/java/freemarker/core/JSONCFormat.java
copy to src/main/java/freemarker/core/JavaScriptOrJSONCFormat.java
index ee469941..039db400 100644
--- a/src/main/java/freemarker/core/JSONCFormat.java
+++ b/src/main/java/freemarker/core/JavaScriptOrJSONCFormat.java
@@ -27,28 +27,34 @@ import freemarker.template.utility.StringUtil.JsStringEncCompatibility;
 import freemarker.template.utility.StringUtil.JsStringEncQuotation;
 
 /**
- * JSON {@link CFormat}; when this is used, values output by {@code ?c} are valid JSON values, and therefore also
- * valid JavaScript values.
+ * {@value #NAME} {@link CFormat}; for generating output that's compatible with both JSON and JavaScript. This format is
+ * therefore resilient against configuration mistakes, where we generate output in one language, but use the
+ * {@link CFormat} for the other. The small price to pay is that we can't utilize some language-specific opportunities
+ * to make the output a bit shorter, but that hardly matters in practice.
  * This is the default of {@link Configurable#getCFormat()} starting from
- * {@linkplain Configuration#setIncompatibleImprovements(Version) Incompatible Improvements}
- * {@link Configuration#VERSION_2_3_32}.
+ * {@link Configuration#Configuration(Version) incompatible_improvements}
+ * {@linkplain Configuration#VERSION_2_3_32 2.3.32}.
  *
  * <p><b>Experimental class!</b> This class is too new, and might will change over time. Therefore, for now the
  * most methods are not exposed outside FreeMarker. The class itself and some members are exposed as they are needed for
  * configuring FreeMarker.
  *
+ * @see JavaScriptCFormat
+ * @see JSONCFormat
+ *
  * @since 2.3.32
  */
-public final class JSONCFormat extends AbstractJSONLikeFormat {
-    public static final String NAME = "JSON";
-    public static final JSONCFormat INSTANCE = new JSONCFormat();
+public final class JavaScriptOrJSONCFormat extends AbstractJSONLikeFormat {
+    public static final String NAME = "JavaScript or JSON";
+    public static final JavaScriptOrJSONCFormat INSTANCE = new JavaScriptOrJSONCFormat();
 
-    private JSONCFormat() {
+    private JavaScriptOrJSONCFormat() {
     }
 
     @Override
     String formatString(String s, Environment env) throws TemplateException {
-        return StringUtil.jsStringEnc(s, JsStringEncCompatibility.JSON, JsStringEncQuotation.QUOTATION_MARK);
+        return StringUtil.jsStringEnc(
+                s, JsStringEncCompatibility.JAVA_SCRIPT_OR_JSON, JsStringEncQuotation.QUOTATION_MARK);
     }
 
     @Override
diff --git a/src/main/java/freemarker/core/XSCFormat.java b/src/main/java/freemarker/core/XSCFormat.java
index c3ee5774..67da2d8f 100644
--- a/src/main/java/freemarker/core/XSCFormat.java
+++ b/src/main/java/freemarker/core/XSCFormat.java
@@ -26,7 +26,7 @@ import java.text.NumberFormat;
 import freemarker.template.TemplateException;
 
 /**
- * {@link CFormat} for outputting XML that follows the conventions of XML Schema.
+ * {@value #NAME} {@link CFormat}, for outputting XML that follows the conventions of XML Schema.
  *
  * <p><b>Experimental class!</b> This class is too new, and might will change over time. Therefore, for now the
  * most methods are not exposed outside FreeMarker. The class itself and some members are exposed as they are needed for
diff --git a/src/main/java/freemarker/core/_StandardCLanguages.java b/src/main/java/freemarker/core/_StandardCLanguages.java
index 37da6381..9dec5a1b 100644
--- a/src/main/java/freemarker/core/_StandardCLanguages.java
+++ b/src/main/java/freemarker/core/_StandardCLanguages.java
@@ -26,15 +26,19 @@ final class StandardCFormats {
     private StandardCFormats() {
     }
 
-    static final Map<String, CFormat> STANDARD_C_FORMATS;
+    static final Map<String, CFormat> STANDARD_C_FORMATS = new LinkedHashMap<>();
     static {
-            STANDARD_C_FORMATS = new LinkedHashMap<>();
-            STANDARD_C_FORMATS.put(JSONCFormat.INSTANCE.getName(), JSONCFormat.INSTANCE);
-            STANDARD_C_FORMATS.put(JavaScriptCFormat.INSTANCE.getName(), JavaScriptCFormat.INSTANCE);
-            STANDARD_C_FORMATS.put(JavaCFormat.INSTANCE.getName(), JavaCFormat.INSTANCE);
-            STANDARD_C_FORMATS.put(XSCFormat.INSTANCE.getName(), XSCFormat.INSTANCE);
-            STANDARD_C_FORMATS.put(Default230CFormat.INSTANCE.getName(), Default230CFormat.INSTANCE);
-            STANDARD_C_FORMATS.put(Default2321CFormat.INSTANCE.getName(), Default2321CFormat.INSTANCE);
+        addStandardCFormat(JavaScriptOrJSONCFormat.INSTANCE);
+        addStandardCFormat(JSONCFormat.INSTANCE);
+        addStandardCFormat(JavaScriptCFormat.INSTANCE);
+        addStandardCFormat(JavaCFormat.INSTANCE);
+        addStandardCFormat(XSCFormat.INSTANCE);
+        addStandardCFormat(Default230CFormat.INSTANCE);
+        addStandardCFormat(Default2321CFormat.INSTANCE);
+    }
+
+    private static void addStandardCFormat(CFormat cFormat) {
+        STANDARD_C_FORMATS.put(cFormat.getName(), cFormat);
     }
 
 }
diff --git a/src/main/java/freemarker/template/Configuration.java b/src/main/java/freemarker/template/Configuration.java
index 1ffa86f6..b2d34a21 100644
--- a/src/main/java/freemarker/template/Configuration.java
+++ b/src/main/java/freemarker/template/Configuration.java
@@ -67,8 +67,8 @@ import freemarker.core.Default230CFormat;
 import freemarker.core.Default2321CFormat;
 import freemarker.core.Environment;
 import freemarker.core.HTMLOutputFormat;
-import freemarker.core.JSONCFormat;
 import freemarker.core.JSONOutputFormat;
+import freemarker.core.JavaScriptOrJSONCFormat;
 import freemarker.core.JavaScriptOutputFormat;
 import freemarker.core.MarkupOutputFormat;
 import freemarker.core.OutputFormat;
@@ -2499,7 +2499,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
 
     static CFormat getDefaultCFormat(Version incompatibleImprovements) {
         if (incompatibleImprovements.intValue() >= _VersionInts.V_2_3_32) {
-            return JSONCFormat.INSTANCE;
+            return JavaScriptOrJSONCFormat.INSTANCE;
         }
         if (incompatibleImprovements.intValue() >= _VersionInts.V_2_3_21) {
             return Default2321CFormat.INSTANCE;
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index b4288ced..33079432 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -6041,7 +6041,7 @@ To prove that "s" didn't contain the value in escaped form:
 
         <para>The <literal>c</literal> built-in will format string values to
         string literals. Like if the <literal>c_format</literal> setting is
-        <quote>JSON</quote>, then <literal>{"fullName":
+        <literal>"JSON"</literal>, then <literal>{"fullName":
         ${fullName?c}}</literal> will output something like
         <literal>{"fullName": "John Doe"}</literal>, where the quotation marks
         (and <literal>\</literal> escaping if needed) were added by
@@ -6085,31 +6085,49 @@ To prove that "s" didn't contain the value in escaped form:
 
         <itemizedlist>
           <listitem>
-            <para><literal>JSON</literal>: JSON generation</para>
+            <para><literal>"JSON"</literal>: JSON generation. Generally,
+            <literal>"JavaScript or JSON"</literal> (see later) is recommended
+            over this.</para>
           </listitem>
 
           <listitem>
-            <para><literal>JavaScript</literal>: JavaScript generation</para>
+            <para><literal>"JavaScript"</literal>: JavaScript generation.
+            Generally, <literal>"JavaScript or JSON"</literal> (see later) is
+            recommended over this.</para>
           </listitem>
 
           <listitem>
-            <para><literal>Java</literal>: Java source code generation</para>
+            <para><literal>"Java"</literal>: Java source code
+            generation</para>
           </listitem>
 
           <listitem>
-            <para><literal>XS</literal>: XML Schema compliant XML
+            <para><literal>"XS"</literal>: XML Schema compliant XML
             generation</para>
           </listitem>
 
           <listitem>
-            <para><literal>default 2.3.0</literal>: Default for backward
+            <para><literal>"JavaScript or JSON"</literal>: For generating
+            output that's compatible with both JSON and JavaScript. This setup
+            is therefore resilient against configuration mistakes, where we
+            generate output in one language, but use the
+            <literal>c_format</literal> for the other. The small price to pay
+            is that we can't utilize some language-specific opportunities to
+            make the output a bit shorter, but that hardly matters in
+            practice. This is the default if if the <link
+            linkend="pgui_config_incompatible_improvements"><literal>incompatible_improvements</literal>
+            setting</link> is at least 2.3.32.</para>
+          </listitem>
+
+          <listitem>
+            <para><literal>"default 2.3.0"</literal>: Default for backward
             compatibility if the <link
             linkend="pgui_config_incompatible_improvements"><literal>incompatible_improvements</literal>
             setting</link> is less than 2.3.21. Avoid!</para>
           </listitem>
 
           <listitem>
-            <para><literal>default 2.3.21</literal>: Default for backward
+            <para><literal>"default 2.3.21"</literal>: Default for backward
             compatibility if the <link
             linkend="pgui_config_incompatible_improvements"><literal>incompatible_improvements</literal>
             setting</link> is equal or greater than 2.3.21. Avoid!</para>
@@ -16041,7 +16059,8 @@ rif: foo XYr baar</programlisting>
               <itemizedlist>
                 <listitem>
                   <para>For <literal>c_format</literal>-s <quote>JSON</quote>,
-                  and <quote>JavaScript</quote>: <literal>Infinity</literal>,
+                  and <quote>JavaScript</quote>, <quote>JavaScript or
+                  JSON</quote>: <literal>Infinity</literal>,
                   <literal>-Infinity</literal>, <literal>NaN</literal></para>
                 </listitem>
 
@@ -16152,9 +16171,9 @@ rif: foo XYr baar</programlisting>
           the same output as
           <literal>${<replaceable>aNumber</replaceable>?c}</literal>. (In this
           case you should use a <literal>c_format</literal> like
-          <literal>"JSON"</literal>, and not some of the strictly backward
-          compatible defaults, as those are emulating some confusing old
-          glitches.)</para>
+          <literal>"JavaScript or JSON"</literal>, and not some of the
+          strictly backward compatible defaults, as those are emulating some
+          confusing old glitches.)</para>
 
           <para>If the value the <literal>c</literal> built-in is applied on
           is <literal>null</literal>/missing, it will stop the template
@@ -24761,8 +24780,8 @@ ${"'{}"}
                   <primary>c_format</primary>
                 </indexterm><literal>c_format</literal> (since FreeMarker
               2.3.32): Sets what format to use when formatting for computer
-              consumption, like <literal>"JSON"</literal>. Mostly prominently
-              this affects the <link
+              consumption, like <literal>"JavaScript or JSON"</literal>.
+              Mostly prominently this affects the <link
               linkend="ref_builtin_c"><literal>c</literal> built-in</link>,
               hence the name. See valid values and their meaning here: <xref
               linkend="dgui_misc_computer_vs_human_format"/>.</para>
@@ -30133,12 +30152,14 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
                   linkend="ref_builtin_c"><literal>c</literal>
                   built-in</link>.</para>
 
-                  <para>Because setting the <link
+                  <para>Setting the <link
                   linkend="pgui_config_incompatible_improvements_how_to_set"><literal>incompatible_improvements</literal>
                   setting</link> to 2.3.32 will change the default of
-                  <literal>c_format</literal> to <literal>JSON</literal>, it
-                  will consequently also activate these changes. But this only
-                  affects number formatting done with <literal>?c</literal>,
+                  <literal>c_format</literal> to <literal>"JavaScript or
+                  JSON"</literal>, which has these number formatting changes
+                  (just like all <literal>c_format</literal>-s that aren't
+                  just to emulate old behavior have). But this only affects
+                  number formatting done with <literal>?c</literal>,
                   <literal>?cn</literal>, and with <quote>c</quote>
                   <literal>number_format</literal>, and not number formatting
                   in general.</para>
diff --git a/src/test/java/freemarker/core/CAndCnBuiltInTest.java b/src/test/java/freemarker/core/CAndCnBuiltInTest.java
index af7d4622..9e73e8a8 100644
--- a/src/test/java/freemarker/core/CAndCnBuiltInTest.java
+++ b/src/test/java/freemarker/core/CAndCnBuiltInTest.java
@@ -49,7 +49,7 @@ public class CAndCnBuiltInTest extends TemplateTest {
         addToDataModel("floatInf", Float.POSITIVE_INFINITY);
         addToDataModel("floatNegativeInf", Float.NEGATIVE_INFINITY);
         addToDataModel("floatNaN", Float.NaN);
-        addToDataModel("string", "a\nb");
+        addToDataModel("string", "a\nb\u0000c");
         addToDataModel("long", Long.MAX_VALUE);
         addToDataModel("int", Integer.MAX_VALUE);
         addToDataModel("bigInteger", new BigInteger("123456789123456789123456789123456789"));
@@ -126,13 +126,27 @@ public class CAndCnBuiltInTest extends TemplateTest {
     }
 
     private void testWithNonNumber(String builtInName, Version ici) throws TemplateException, IOException {
-        assertOutput("${string?" + builtInName + "}", "\"a\\nb\"");
+        getConfiguration().setIncompatibleImprovements(ici);
+        assertOutput("${string?" + builtInName + "}", "\"a\\nb\\u0000c\"");
         assertOutput("${booleanTrue?" + builtInName + "}", "true");
         assertOutput("${booleanFalse?" + builtInName + "}", "false");
         assertErrorContains("${dateTime?" + builtInName + "}",
                 "Expected a number, boolean, or string");
     }
 
+    @Test
+    public void testCFormatsWithString() throws TemplateException, IOException {
+        Configuration conf = getConfiguration();
+        conf.setCFormat(JavaScriptCFormat.INSTANCE);
+        assertOutput("${string?c}", "\"a\\nb\\x00c\"");
+        conf.setCFormat(JSONCFormat.INSTANCE);
+        assertOutput("${string?c}", "\"a\\nb\\u0000c\"");
+        conf.setCFormat(JavaScriptOrJSONCFormat.INSTANCE);
+        assertOutput("${string?c}", "\"a\\nb\\u0000c\"");
+        conf.setCFormat(XSCFormat.INSTANCE);
+        assertOutput("${string?c}", "a\nb\u0000c");
+    }
+
     @Test
     public void testWithNull() throws TemplateException, IOException {
         assertOutput("${noSuchVar?cn}", "null");
diff --git a/src/test/java/freemarker/template/ConfigurationTest.java b/src/test/java/freemarker/template/ConfigurationTest.java
index 7e11be74..3a6dea5b 100644
--- a/src/test/java/freemarker/template/ConfigurationTest.java
+++ b/src/test/java/freemarker/template/ConfigurationTest.java
@@ -73,6 +73,7 @@ import freemarker.core.HexTemplateNumberFormatFactory;
 import freemarker.core.JSONCFormat;
 import freemarker.core.JavaCFormat;
 import freemarker.core.JavaScriptCFormat;
+import freemarker.core.JavaScriptOrJSONCFormat;
 import freemarker.core.MarkupOutputFormat;
 import freemarker.core.OptInTemplateClassResolver;
 import freemarker.core.OutputFormat;
@@ -197,10 +198,10 @@ public class ConfigurationTest extends TestCase {
         cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_31);
         assertSame(Default2321CFormat.INSTANCE, cfg.getCFormat());
         cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_32);
-        assertSame(JSONCFormat.INSTANCE, cfg.getCFormat());
-        cfg.setCFormat(JSONCFormat.INSTANCE); // Same as default, but explicitly set now
+        assertSame(JavaScriptOrJSONCFormat.INSTANCE, cfg.getCFormat());
+        cfg.setCFormat(JavaScriptOrJSONCFormat.INSTANCE); // Same as default, but explicitly set now
         cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_31);
-        assertSame(JSONCFormat.INSTANCE, cfg.getCFormat());
+        assertSame(JavaScriptOrJSONCFormat.INSTANCE, cfg.getCFormat());
     }
 
     private void assertUses2322ObjectWrapper(Configuration cfg) {