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.
diff --git a/src/main/java/freemarker/core/BuiltIn.java b/src/main/java/freemarker/core/BuiltIn.java
index fcea193..1631b76 100644
--- a/src/main/java/freemarker/core/BuiltIn.java
+++ b/src/main/java/freemarker/core/BuiltIn.java
@@ -85,8 +85,8 @@
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 @@
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 @@
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 c97c93e..ce69d42 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 @@
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 @@
}
}
+ 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 a34780e..2198ffc 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -12710,6 +12710,16 @@
<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>
</listitem>
@@ -13306,6 +13316,50 @@
<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 @@
<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 @@
<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 @@
<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">
@@ -29512,6 +29585,23 @@
<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
<literal>?string(<replaceable>format</replaceable>)</literal>),
@@ -29565,6 +29655,19 @@
</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>:
Updated dom4j version used during FreeMarker project compilation
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 6e689ac..adeb39a 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 @@
[<\/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 84c794f..f2f249e 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 @@
[${"</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" />