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/18 19:18:26 UTC

[freemarker] branch 2.3-gae updated: FREEMARKER-208: Added ?c_lower_case, and ?c_upper_case, which are the non-localized (computer language) variants of ?lower_case, and ?upper_case. The primary problem people run into with the localized versions is that with Turkish locale the letter i, and I has different conversions as in most languages, which causes problem if the conversion was for computer consumption (for technical purposes), and not for humans.

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 f6f5207a FREEMARKER-208: Added ?c_lower_case, and ?c_upper_case, which are the non-localized (computer language) variants of ?lower_case, and ?upper_case. The primary problem people run into with the localized versions is that with Turkish locale the letter i, and I has different conversions as in most languages, which causes problem if the conversion was for computer consumption (for technical purposes), and not for humans.
f6f5207a is described below

commit f6f5207a3a0cdd3442deb704269fd2e34bb02ed6
Author: ddekany <dd...@apache.org>
AuthorDate: Sun Dec 18 20:17:24 2022 +0100

    FREEMARKER-208: Added ?c_lower_case, and ?c_upper_case, which are the non-localized (computer language) variants of ?lower_case, and ?upper_case. The primary problem people run into with the localized versions is that with Turkish locale the letter i, and I has different conversions as in most languages, which causes problem if the conversion was for computer consumption (for technical purposes), and not for humans.
---
 src/main/java/freemarker/core/BuiltIn.java         |   6 +-
 .../freemarker/core/BuiltInsForStringsBasic.java   |  17 ++-
 src/manual/en_US/book.xml                          | 119 +++++++++++++++++++--
 .../templatesuite/expected/string-builtins1.txt    |   1 +
 .../templatesuite/templates/string-builtins1.ftl   |   7 ++
 5 files changed, 139 insertions(+), 11 deletions(-)

diff --git a/src/main/java/freemarker/core/BuiltIn.java b/src/main/java/freemarker/core/BuiltIn.java
index fcea193a..1631b76b 100644
--- a/src/main/java/freemarker/core/BuiltIn.java
+++ b/src/main/java/freemarker/core/BuiltIn.java
@@ -85,8 +85,8 @@ abstract class BuiltIn extends Expression implements Cloneable {
 
     static final Set<String> CAMEL_CASE_NAMES = new TreeSet<>();
     static final Set<String> SNAKE_CASE_NAMES = new TreeSet<>();
-    static final int NUMBER_OF_BIS = 291;
-    static final HashMap<String, BuiltIn> BUILT_INS_BY_NAME = new HashMap(NUMBER_OF_BIS * 3 / 2 + 1, 1f);
+    static final int NUMBER_OF_BIS = 295;
+    static final HashMap<String, BuiltIn> BUILT_INS_BY_NAME = new HashMap<>(NUMBER_OF_BIS * 3 / 2 + 1, 1f);
 
     static final String BI_NAME_SNAKE_CASE_WITH_ARGS = "with_args";
     static final String BI_NAME_CAMEL_CASE_WITH_ARGS = "withArgs";
@@ -247,6 +247,7 @@ abstract class BuiltIn extends Expression implements Cloneable {
         putBI("long", new longBI());
         putBI("lower_abc", "lowerAbc", new BuiltInsForNumbers.lower_abcBI());
         putBI("lower_case", "lowerCase", new BuiltInsForStringsBasic.lower_caseBI());
+        putBI("c_lower_case", "cLowerCase", new BuiltInsForStringsBasic.c_lower_caseBI());
         putBI("map", new BuiltInsForSequences.mapBI());
         putBI("namespace", new BuiltInsForMultipleTypes.namespaceBI());
         putBI("new", new NewBI());
@@ -300,6 +301,7 @@ abstract class BuiltIn extends Expression implements Cloneable {
         putBI("uncap_first", "uncapFirst", new BuiltInsForStringsBasic.uncap_firstBI());
         putBI("upper_abc", "upperAbc", new BuiltInsForNumbers.upper_abcBI());
         putBI("upper_case", "upperCase", new BuiltInsForStringsBasic.upper_caseBI());
+        putBI("c_upper_case", "cUpperCase", new BuiltInsForStringsBasic.c_upper_caseBI());
         putBI("url", new BuiltInsForStringsEncoding.urlBI());
         putBI("url_path", "urlPath", new BuiltInsForStringsEncoding.urlPathBI());
         putBI("values", new BuiltInsForHashes.valuesBI());
diff --git a/src/main/java/freemarker/core/BuiltInsForStringsBasic.java b/src/main/java/freemarker/core/BuiltInsForStringsBasic.java
index c97c93ed..ce69d426 100644
--- a/src/main/java/freemarker/core/BuiltInsForStringsBasic.java
+++ b/src/main/java/freemarker/core/BuiltInsForStringsBasic.java
@@ -20,6 +20,7 @@
 package freemarker.core;
 
 import java.util.List;
+import java.util.Locale;
 import java.util.StringTokenizer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -435,7 +436,14 @@ class BuiltInsForStringsBasic {
         TemplateModel calculateResult(String s, Environment env) {
             return new SimpleScalar(s.toLowerCase(env.getLocale()));
         }
-    }    
+    }
+
+    static class c_lower_caseBI extends BuiltInForString {
+        @Override
+        TemplateModel calculateResult(String s, Environment env) {
+            return new SimpleScalar(s.toLowerCase(Locale.ROOT));
+        }
+    }
 
     static class padBI extends BuiltInForString {
         
@@ -830,6 +838,13 @@ class BuiltInsForStringsBasic {
         }
     }
 
+    static class c_upper_caseBI extends BuiltInForString {
+        @Override
+        TemplateModel calculateResult(String s, Environment env) {
+            return new SimpleScalar(s.toUpperCase(Locale.ROOT));
+        }
+    }
+
     static class word_listBI extends BuiltInForString {
         @Override
         TemplateModel calculateResult(String s, Environment env) {
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index a34780ed..2198ffc1 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -12708,6 +12708,16 @@ grant codeBase "file:/path/to/freemarker.jar"
             linkend="ref_builtin_c_boolean">for booleans</link></para>
           </listitem>
 
+          <listitem>
+            <para><link
+            linkend="ref_builtin_c_lower_case">c_lower_case</link></para>
+          </listitem>
+
+          <listitem>
+            <para><link
+            linkend="ref_builtin_c_upper_case">c_upper_case</link></para>
+          </listitem>
+
           <listitem>
             <para><link
             linkend="ref_builtin_cap_first">cap_first</link></para>
@@ -13306,6 +13316,50 @@ GreEN mouse
 
           <para>In the case of <literal>"- green mouse"</literal>, the first
           word is the <literal>-</literal>.</para>
+
+          <para>Note that this uses locale-aware conversion, that is, the
+          result can be different depending on the current
+          <literal>locale</literal> (language, country).</para>
+        </section>
+
+        <section xml:id="ref_builtin_c_lower_case">
+          <title>c_lower_case</title>
+
+          <indexterm>
+            <primary>c_lower_case built-in</primary>
+          </indexterm>
+
+          <para>The string converted to lower case, for computer consumption
+          (<quote>c</quote> as in the <link
+          linkend="ref_builtin_c"><literal>c</literal> built-in</link>). Put
+          simply, it will be converted to lower case as in English, regardless
+          of the current <literal>locale</literal> (language, country). For
+          example <literal>"ITEM list"?c_lower_case</literal> will be
+          <literal>"item list"</literal>, always.</para>
+
+          <para>For lower case conversion for human consumption use the <link
+          linkend="ref_builtin_lower_case"><literal>lower_case</literal>
+          built-in</link> instead!</para>
+        </section>
+
+        <section xml:id="ref_builtin_c_upper_case">
+          <title>c_upper_case</title>
+
+          <indexterm>
+            <primary>c_upper_case built-in</primary>
+          </indexterm>
+
+          <para>The string converted to upper case, for computer consumption
+          (<quote>c</quote> as in the <link
+          linkend="ref_builtin_c"><literal>c</literal> built-in</link>). Put
+          simply, it will be converted to upper case as in English, regardless
+          of the current <literal>locale</literal> (language, country). For
+          example <literal>"ITEM list"?c_upper_case</literal> will be
+          <literal>"ITEM LIST"</literal>, always.</para>
+
+          <para>For upper case conversion for human consumption use the <link
+          linkend="ref_builtin_upper_case"><literal>upper_case</literal>
+          built-in</link> instead!</para>
         </section>
 
         <section xml:id="ref_builtin_capitalize">
@@ -14150,9 +14204,18 @@ String BEAN_NAME = "${beanName?j_string}";</programlisting>
             <primary>lower_case built-in</primary>
           </indexterm>
 
-          <para>The lower case version of the string. For example
-          <literal>"GrEeN MoUsE"?lower_case</literal> will be <literal>"green
-          mouse"</literal>.</para>
+          <para>The lower case version of the string, using rules that depend
+          on the current <literal>locale</literal> (language, country). For
+          example <literal>"KARIŞIK işaretler"?lower_case</literal> will be
+          <literal>"karişik işaretler"</literal> in most locales, but will be
+          <literal>"karışık işaretler"</literal> in Turkish
+          (<literal>tr_TR</literal>) locale (note the missing dot above some
+          of the <quote>i</quote>-s).</para>
+
+          <para>To convert to lower case for computer consumption (as opposed
+          to human consumption), use the <link
+          linkend="ref_builtin_c_lower_case"><literal>c_lower_case</literal>
+          built-in</link> instead!</para>
         </section>
 
         <section xml:id="ref_builtin_matches">
@@ -15005,8 +15068,10 @@ This is auto-escaped: &amp;lt;span&amp;gt;, but not &lt;span class='truncateTerm
 
           <para>The opposite of <link
           linkend="ref_builtin_cap_first"><literal>cap_first</literal></link>.
-          The string with the very first word of the string
-          un-capitalized.</para>
+          The string with the very first word of the string un-capitalized.
+          Note that this uses locale-aware conversion, that is, the result can
+          be different depending on the current <literal>locale</literal>
+          (language, country).</para>
         </section>
 
         <section xml:id="ref_builtin_upper_case">
@@ -15016,9 +15081,17 @@ This is auto-escaped: &amp;lt;span&amp;gt;, but not &lt;span class='truncateTerm
             <primary>upper_case built-in</primary>
           </indexterm>
 
-          <para>The upper case version of the string. For example
-          <literal>"GrEeN MoUsE"</literal> will be <literal>"GREEN
-          MOUSE"</literal>.</para>
+          <para>The upper case version of the string, using rules that depend
+          on the current <literal>locale</literal> (language, country). For
+          example <literal>"KARIŞIK işaretler"?upper_case</literal> will be
+          <literal>"KARIŞIK IŞARETLER"</literal> in most locales, but with
+          Turkish locale it will be <literal>"KARIŞIK İŞARETLER"</literal>
+          (note the dot above the 2nd <quote>I</quote>).</para>
+
+          <para>To convert to upper case for computer consumption (as opposed
+          to human consumption), use the <link
+          linkend="ref_builtin_c_upper_case"><literal>c_upper_case</literal>
+          built-in</link> instead!</para>
         </section>
 
         <section xml:id="ref_builtin_url">
@@ -29511,6 +29584,23 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
           <title>Changes on the FTL side</title>
 
           <itemizedlist>
+            <listitem>
+              <para><link
+              xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-208">FREEMARKER-208</link>:
+              Added <link
+              linkend="ref_builtin_c_lower_case"><literal>?c_lower_case</literal></link>,
+              and <link
+              linkend="ref_builtin_c_upper_case"><literal>?c_upper_case</literal></link>,
+              which are the non-localized (computer language) variants of
+              <literal>?lower_case</literal>, and
+              <literal>?upper_case</literal>. The primary problem people run
+              into with the localized versions is that with Turkish locale the
+              letter <literal>i</literal>, and <literal>I</literal> has
+              different conversions as in most languages, which causes problem
+              if the conversion was for computer consumption (for technical
+              purposes), and not for humans.</para>
+            </listitem>
+
             <listitem>
               <para>When setting the <literal>number_format</literal> setting
               (also when formatting with
@@ -29564,6 +29654,19 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
               processing managed to start.)</para>
             </listitem>
 
+            <listitem>
+              <para>Added
+              <literal>Environment.getCTemplateNumberFormat()</literal> that
+              returns a
+              <literal>freemarker.core.TemplateNumberFormat</literal>, and
+              deprecated <literal>getCNumberFormat()</literal> that returns a
+              <literal>java.text.NumberFormat</literal>. The new
+              <literal>?c</literal> behavior (see earlier) is only reflected
+              by the new method, and what the deprecated methods returns is
+              stuck at 2.3.31 <literal>incompatible_improvements</literal>
+              mode.</para>
+            </listitem>
+
             <listitem>
               <para><link
               xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-190">FREEMARKER-190</link>:
diff --git a/src/test/resources/freemarker/test/templatesuite/expected/string-builtins1.txt b/src/test/resources/freemarker/test/templatesuite/expected/string-builtins1.txt
index 6e689ac4..adeb39a9 100644
--- a/src/test/resources/freemarker/test/templatesuite/expected/string-builtins1.txt
+++ b/src/test/resources/freemarker/test/templatesuite/expected/string-builtins1.txt
@@ -110,3 +110,4 @@ FOOBAR
 [<\/script>] = [<\/script>]
 [\u003C![CDATA[] = [\u003C![CDATA[]
 []]\u003E] = []]\u003E]
+
diff --git a/src/test/resources/freemarker/test/templatesuite/templates/string-builtins1.ftl b/src/test/resources/freemarker/test/templatesuite/templates/string-builtins1.ftl
index 84c794fd..f2f249ef 100644
--- a/src/test/resources/freemarker/test/templatesuite/templates/string-builtins1.ftl
+++ b/src/test/resources/freemarker/test/templatesuite/templates/string-builtins1.ftl
@@ -127,3 +127,10 @@ ${c?eval}
 [${"</script>"?json_string}] = [<\/script>]
 [${"<![CDATA["?json_string}] = [\u003C![CDATA[]
 [${"]]>"?json_string}] = []]\u003E]
+
+<#-- ?c_...: -->
+<#setting locale="tr_TR">
+<@assertEquals actual="i"?upper_case expected="\x0130" />
+<@assertEquals actual="i"?c_upper_case expected="I" />
+<@assertEquals actual="I"?lower_case expected="\x0131" />
+<@assertEquals actual="I"?c_lower_case expected="i" />