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 2019/01/26 17:29:29 UTC
[freemarker] branch 3 updated: 1. Forward ported from 2.3-gae: The
"booleanFormat" configuration setting now can be set to "c". Then
${aBoolean} will behave as ${aBoolean?c}. This should be only used if you
are generating output for non-human (computer) consumption only. If your
output has pieces for human audience too,
it's still recommended to use ${aBoolean?c} where true/false is needed,
and either not set the "booleanFormat" at all,
or set it to something that's appropriate for everyday uses (like "yes,
no").
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 4ed5053 1. Forward ported from 2.3-gae: The "booleanFormat" configuration setting now can be set to "c". Then ${aBoolean} will behave as ${aBoolean?c}. This should be only used if you are generating output for non-human (computer) consumption only. If your output has pieces for human audience too, it's still recommended to use ${aBoolean?c} where true/false is needed, and either not set the "booleanFormat" at all, or set it to something that's appropriate for everyday uses (like [...]
4ed5053 is described below
commit 4ed50534ad80e2ede479136f328e4511b093fd1c
Author: ddekany <dd...@apache.org>
AuthorDate: Sat Jan 26 17:41:19 2019 +0100
1. Forward ported from 2.3-gae: The "booleanFormat" configuration setting now can be set to "c". Then ${aBoolean} will behave as ${aBoolean?c}. This should be only used if you are generating output for non-human (computer) consumption only. If your output has pieces for human audience too, it's still recommended to use ${aBoolean?c} where true/false is needed, and either not set the "booleanFormat" at all, or set it to something that's appropriate for everyday uses (like "yes,no").
2. "" (empty string) is now a valid booleanFormat (which will give error if you try to format with it), and "true,false" is not special anymore. The default was changed to "" from "true/false".
---
FM3-CHANGE-LOG.txt | 6 ++
.../core/FM2ASTToFM3SourceConverter.java | 3 +
...nfigurationWithDefaultTemplateResolverTest.java | 3 +-
.../templates/boolean-formatting.f3ac | 25 ++++----
.../templatesuite/templates/sequence-builtins.f3ac | 37 ++++++------
.../templates/string-builtins-regexps.f3ac | 51 ++++++++--------
.../templatesuite/templates/string-builtins2.f3ac | 23 ++++----
.../templatesuite/templates/stringbimethods.f3ac | 4 +-
.../freemarker/core/BuiltInsForMultipleTypes.java | 4 +-
.../freemarker/core/BuiltInsForStringsMisc.java | 31 ++++++----
.../org/apache/freemarker/core/Configuration.java | 4 +-
.../org/apache/freemarker/core/Environment.java | 68 +++++++++-------------
.../core/MutableProcessingConfiguration.java | 11 +---
.../freemarker/core/ProcessingConfiguration.java | 16 ++---
.../freemarker/core/TemplateBooleanFormat.java | 56 +++++++++++-------
.../org/apache/freemarker/core/_EvalUtils.java | 2 +-
.../dom/templatesuite/templates/default-xmlns.f3ac | 2 +-
17 files changed, 182 insertions(+), 164 deletions(-)
diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt
index 258b739..21f2d08 100644
--- a/FM3-CHANGE-LOG.txt
+++ b/FM3-CHANGE-LOG.txt
@@ -120,6 +120,8 @@ Node: Changes already mentioned above aren't repeated here!
- Inside a #switch, #case can't be after #default anymore (this is actually a bug in FM2)
(The template converter can't fix this automatically, but reports it as an error.)
- #else and #elseIf tags must be ended without "/", for example, <#else/> is illegal now, <#else> is legal.
+- ${someBoolean?string} with the default booleanFormat setting value will not convert the boolean to true/false anymore,
+ instead it will fail just like ${someBoolean} would. Use ${someBoolean?c} instead - it's preferable in FM2 as well.
- Renamed `?datetime` and `?datetimeIfUnknown` and the `datetimeFormat` setting to
`?dateTime` and `?dateTimeIfUnknown` and `dateTimeFormat`. (In general, it's `dateTime`, not `datetime` everywhere.)
assertConverted("${.outputFormat}","${.output_format}");
@@ -620,6 +622,10 @@ Core / Miscellaneous
- Removed `Template.createPlainTextTemplate`, as this convenience methods was hardly ever used.
- The extended BigDecimal format parser doesn't accept the "multipier" parameter name anymore. It has to be written with
correct spelling, as "multiplier".
+- The booleanFormat setting now defaults to "", just like numberFormat, etc. It's not possible to format a boolean with
+ that setting value, it will give an error. On the other hand, "true,false" is now a legit setting value (although the
+ better practice is to set the format to "c", if you generate computer language output), but it's hardly a good practice
+ to use.
Build / development process changes
-----------------------------------
diff --git a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
index d816a04..ca7c1f2 100644
--- a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
+++ b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
@@ -79,6 +79,9 @@ import freemarker.template.utility.StringUtil;
* that as the FM3 template language evolves, we will have to change the output for some pieces.
* </ul>
*/
+// TODO:
+// - Log warn for myBoolean?string, recommend using myBoolean?c, as myBoolean?string won't work with the default
+// booleanFormat.
@SuppressWarnings("deprecation")
public class FM2ASTToFM3SourceConverter {
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateConfigurationWithDefaultTemplateResolverTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateConfigurationWithDefaultTemplateResolverTest.java
index 48b830e..46593d7 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateConfigurationWithDefaultTemplateResolverTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateConfigurationWithDefaultTemplateResolverTest.java
@@ -137,7 +137,7 @@ public class TemplateConfigurationWithDefaultTemplateResolverTest {
@Test
public void testConfigurableSettings() throws Exception {
- String commonFTL = "${.locale} ${true?string} ${1.2}";
+ String commonFTL = "${.locale} ${true} ${1.2}";
StringTemplateLoader loader = new StringTemplateLoader();
loader.putTemplate("default", commonFTL);
loader.putTemplate("(fr)", commonFTL);
@@ -145,6 +145,7 @@ public class TemplateConfigurationWithDefaultTemplateResolverTest {
loader.putTemplate("(00)(fr)", commonFTL);
Configuration cfg = new TestConfigurationBuilder()
+ .booleanFormat("c")
.templateConfigurations(
new MergingTemplateConfigurationFactory(
new ConditionalTemplateConfigurationFactory(
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/boolean-formatting.f3ac b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/boolean-formatting.f3ac
index 95bee38..874dc6b 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/boolean-formatting.f3ac
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/boolean-formatting.f3ac
@@ -16,21 +16,22 @@
specific language governing permissions and limitations
under the License.
-->
-<#assign suppress>
-<@assertFails message="true,false">${true}</@>
-<@assertFails message="true,false">${false}</@>
-<@assertFails message="true,false">${"" + true}</@>
-<@assertFails message="true,false">${"" + false}</@>
-<@assertFails message="true,false">${true?upperCase}</@>
-<@assertFails message="true,false">${false?upperCase}</@>
-<@assertEquals expected="true" actual=true?string />
-<@assertEquals expected="false" actual=false?string />
+<@noOutput>
+<@assertFails message="setting was not set">${true}</@>
+<@assertFails message="setting was not set">${false}</@>
+<@assertFails message="setting was not set">${true?string}</@>
+<@assertFails message="setting was not set">${false?string}</@>
+<@assertFails message="setting was not set">${"" + true}</@>
+<@assertFails message="setting was not set">${"" + false}</@>
+<@assertFails message="setting was not set">${true?upperCase}</@>
+<@assertFails message="setting was not set">${false?upperCase}</@>
<@assertEquals expected="true" actual=true?c />
<@assertEquals expected="false" actual=false?c />
<@assertEquals expected="t" actual=true?string('t', 'f') />
<@assertEquals expected="f" actual=false?string('t', 'f') />
-<#setting booleanFormat = 'true,false'>
-<@assertFails message="true,false">${true}</@>
+<#setting booleanFormat='true,false'>
+<@assertEquals expected="true" actual=true?string />
+<@assertEquals expected="true" actual=""+true />
<#setting booleanFormat = 'ja,nein'>
<@assertEquals expected="ja" actual="" + true />
@@ -58,7 +59,7 @@
<@assertEquals actual=123?upperCase expected="123" />
<@assertEquals actual=true?upperCase expected="Y" />
-</#assign>
+</...@noOutput>
<#escape x as x?upperCase>
<#assign x = true>${x} ${true} ${true?string}
<#assign x = false>${x} ${false} ${false?string}
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.f3ac b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.f3ac
index b8cb070..b8f8dcb 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.f3ac
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.f3ac
@@ -18,6 +18,7 @@
-->
<@noOutput>
<#setting locale="en_US">
+<#setting booleanFormat="c">
<#setting numberFormat="0.#########">
<#assign ls = []?sort>
@@ -67,7 +68,7 @@ Boolean order:
false,
true]>
<#list x?sort as i>
-- ${i?string}
+- ${i?c}
</#list>
@@ -135,25 +136,25 @@ Contains:
<#macro test></#macro>
<#assign x = [1, "2", true, [1,2,3], {"a":1}, test, '1992-02-21'?date('yyyy-MM-dd')]>
True:
-${x?seqContains(1.0)?string}
-${x?seqContains("2")?string}
-${x?seqContains(true)?string}
-${x?seqContains('1992-02-21'?date('yyyy-MM-dd'))?string}
-${abcSet?seqContains("a")?string}
-${abcSet?seqContains("b")?string}
-${abcSet?seqContains("c")?string}
+${x?seqContains(1.0)}
+${x?seqContains("2")}
+${x?seqContains(true)}
+${x?seqContains('1992-02-21'?date('yyyy-MM-dd'))}
+${abcSet?seqContains("a")}
+${abcSet?seqContains("b")}
+${abcSet?seqContains("c")}
False:
-${x?seqContains("1")?string}
-${x?seqContains(2)?string}
-${x?seqContains(false)?string}
-${x?seqContains('1992-02-22'?date('yyyy-MM-dd'))?string}
-${abcSet?seqContains("A")?string}
-${abcSet?seqContains(1)?string}
-${abcSet?seqContains(true)?string}
+${x?seqContains("1")}
+${x?seqContains(2)}
+${x?seqContains(false)}
+${x?seqContains('1992-02-22'?date('yyyy-MM-dd'))}
+${abcSet?seqContains("A")}
+${abcSet?seqContains(1)}
+${abcSet?seqContains(true)}
<#assign x = []>
-False: ${x?seqContains(1)?string}
+False: ${x?seqContains(1)}
Index_of:
---------
@@ -285,12 +286,12 @@ seq_last_index_of "c":
Sequence builtins ignoring nulls
--------------------------------
-true = ${listWithNull?seqContains('c')?string}
+true = ${listWithNull?seqContains('c')}
2 = ${listWithNull?seqIndexOf('c')}
0 = ${listWithNull?seqLastIndexOf('a')}
These should throw exception, but for BC they don't:
-false = ${listWithNull?seqContains(noSuchVar)?string}
+false = ${listWithNull?seqContains(noSuchVar)}
-1 = ${listWithNull?seqIndexOf(noSuchVar)}
-1 = ${listWithNull?seqLastIndexOf(noSuchVar)}
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps.f3ac b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps.f3ac
index f3a4a9f..60bac30 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps.f3ac
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps.f3ac
@@ -16,31 +16,32 @@
specific language governing permissions and limitations
under the License.
-->
-${"test"?matches('test')?string} == true
-${"test"?matches('test', '')?string} == true
-
-${"TEST"?matches('test')?string} == false
-${"TEST"?matches('test', 'i')?string} == true
-
-${"test\nfoo"?matches('.*^foo')?string} == false
-${"test\nfoo"?matches(r'.*\n^foo', 'm')?string} == true
-
-${"test\nfoo"?matches('test.foo')?string} == false
-${"test\nfoo"?matches('test.foo', 's')?string} == true
-
-${"test\nFoo"?matches('.*foo', 's')?string} == false
-${"test\nFoo"?matches('.*foo', 'i')?string} == false
-${"test\nFoo"?matches('.*foo', 'im')?string} == false
-${"test\nFoo"?matches('.*foo', 'si')?string} == true
-${"test\nFoo"?matches('.*foo', 'is')?string} == true
-${"test\nFoo"?matches('.*foo', 'mis')?string} == true
-
-${"test\nFoo"?matches('.*\n^foo', 'm')?string} == false
-${"test\nFoo"?matches('.*\n^foo', 'i')?string} == false
-${"test\nFoo"?matches('.*\n^foo', 'im')?string} == true
-${"test\nFoo"?matches('.*\n^foo', 'mi')?string} == true
-${"test\nFoo"?matches('.*^foo', 'ism')?string} == true
-${"test\nFoo"?matches('.*^foo', 'smi')?string} == true
+<#setting booleanFormat="c">
+${"test"?matches('test')} == true
+${"test"?matches('test', '')} == true
+
+${"TEST"?matches('test')} == false
+${"TEST"?matches('test', 'i')} == true
+
+${"test\nfoo"?matches('.*^foo')} == false
+${"test\nfoo"?matches(r'.*\n^foo', 'm')} == true
+
+${"test\nfoo"?matches('test.foo')} == false
+${"test\nfoo"?matches('test.foo', 's')} == true
+
+${"test\nFoo"?matches('.*foo', 's')} == false
+${"test\nFoo"?matches('.*foo', 'i')} == false
+${"test\nFoo"?matches('.*foo', 'im')} == false
+${"test\nFoo"?matches('.*foo', 'si')} == true
+${"test\nFoo"?matches('.*foo', 'is')} == true
+${"test\nFoo"?matches('.*foo', 'mis')} == true
+
+${"test\nFoo"?matches('.*\n^foo', 'm')} == false
+${"test\nFoo"?matches('.*\n^foo', 'i')} == false
+${"test\nFoo"?matches('.*\n^foo', 'im')} == true
+${"test\nFoo"?matches('.*\n^foo', 'mi')} == true
+${"test\nFoo"?matches('.*^foo', 'ism')} == true
+${"test\nFoo"?matches('.*^foo', 'smi')} == true
<#setting booleanFormat="True,False">
<@assert false?matches('[eslaF]+') />
<@assert 'False'?matches('[eslaF]+') />
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins2.f3ac b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins2.f3ac
index b12d149..60344db 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins2.f3ac
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins2.f3ac
@@ -16,6 +16,7 @@
specific language governing permissions and limitations
under the License.
-->
+<#setting booleanFormat="c">
--
<#assign s = "abbcdbb">
${s?indexOf("bb")} = 1
@@ -26,19 +27,19 @@ ${s?lastIndexOf("bb")} = 5
${s?lastIndexOf("bb", 4)} = 1
${s?lastIndexOf("")} = ${s?length}
--
-${s?startsWith("abb")?string} = true
-${s?startsWith("bb")?string} = false
-${s?startsWith("")?string} = true
+${s?startsWith("abb")} = true
+${s?startsWith("bb")} = false
+${s?startsWith("")} = true
--
-${s?endsWith("dbb")?string} = true
-${s?endsWith("cbb")?string} = false
-${s?endsWith("")?string} = true
+${s?endsWith("dbb")} = true
+${s?endsWith("cbb")} = false
+${s?endsWith("")} = true
--
-${s?contains("abb")?string} = true
-${s?contains("bcd")?string} = true
-${s?contains("dbb")?string} = true
-${s?contains("bbx")?string} = false
-${s?contains("")?string} = true
+${s?contains("abb")} = true
+${s?contains("bcd")} = true
+${s?contains("dbb")} = true
+${s?contains("bbx")} = false
+${s?contains("")} = true
--
[${s?chopLinebreak}] = [abbcdbb]
[${"qwe\n"?chopLinebreak}] = [qwe]
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/stringbimethods.f3ac b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/stringbimethods.f3ac
index c3bd460..1723f6a 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/stringbimethods.f3ac
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/stringbimethods.f3ac
@@ -26,7 +26,7 @@ ${'01:02:03'?time.iso?string("iso ms nz")}
${multi}
<#assign a = true>
<#assign b = false>
-${a?string} ${b?string}
+${a?c} ${b?c}
${a?string("yes", "no")} ${b?string("yes", "no")}
<#setting booleanFormat="igen,nem"/>
-${a?string} ${b?string}
+${a?string} ${b}
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
index cf31218..8723e7d 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
@@ -458,11 +458,11 @@ class BuiltInsForMultipleTypes {
@Override
public String getAsString() throws TemplateException {
- // Boolean should have come first... but that change would be non-BC.
+ // TODO [FM3] Boolean should have come first... but that change would be non-BC.
if (bool instanceof TemplateStringModel) {
return ((TemplateStringModel) bool).getAsString();
} else {
- return env.formatBoolean(bool.getAsBoolean(), true);
+ return env.formatBoolean(bool.getAsBoolean());
}
}
}
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
index e956859..d5bb4bf 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
@@ -47,20 +47,27 @@ class BuiltInsForStringsMisc {
static class booleanBI extends BuiltInForString {
@Override
TemplateModel calculateResult(String s, Environment env) throws TemplateException {
- final boolean b;
+ return toBool(s, env) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
+ }
+
+ private boolean toBool(String s, Environment env) throws TemplateException {
if (s.equals(TemplateBooleanFormat.C_TRUE)) {
- b = true;
- } else if (s.equals(TemplateBooleanFormat.C_FALSE)) {
- b = false;
- } else if (s.equals(env.getTemplateBooleanFormat().getTrueStringValue())) {
- b = true;
- } else if (s.equals(env.getTemplateBooleanFormat().getFalseStringValue())) {
- b = false;
- } else {
- throw new TemplateException(this, env,
- "Can't convert this string to boolean: ", new _DelayedJQuote(s));
+ return true;
+ }
+ if (s.equals(TemplateBooleanFormat.C_FALSE)) {
+ return false;
+ }
+ TemplateBooleanFormat templateBooleanFormat = env.getTemplateBooleanFormat();
+ if (templateBooleanFormat != null) {
+ if (s.equals(templateBooleanFormat.getTrueStringValue())) {
+ return true;
+ }
+ if (s.equals(templateBooleanFormat.getFalseStringValue())) {
+ return false;
+ }
}
- return b ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
+ throw new TemplateException(this, env,
+ "Can't convert this string to boolean: ", new _DelayedJQuote(s));
}
}
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java
index cf77d8a..cb90d19 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java
@@ -389,9 +389,9 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc
timeFormat = builder.getTimeFormat();
dateFormat = builder.getDateFormat();
dateTimeFormat = builder.getDateTimeFormat();
+ booleanFormat = builder.getBooleanFormat();
timeZone = builder.getTimeZone();
sqlDateAndTimeTimeZone = builder.getSQLDateAndTimeTimeZone();
- booleanFormat = builder.getBooleanFormat();
templateExceptionHandler = builder.getTemplateExceptionHandler();
attemptExceptionReporter = builder.getAttemptExceptionReporter();
arithmeticEngine = builder.getArithmeticEngine();
@@ -2546,7 +2546,7 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc
@Override
protected String getDefaultBooleanFormat() {
- return TemplateBooleanFormat.C_TRUE_FALSE;
+ return "";
}
@Override
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
index e6a91b6..5b2c8f8 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
@@ -1131,38 +1131,41 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
}
}
- String formatBoolean(boolean value, boolean fallbackToTrueFalse) throws TemplateException {
+ String formatBoolean(boolean value) throws TemplateException {
TemplateBooleanFormat templateBooleanFormat = getTemplateBooleanFormat();
- if (value) {
- String s = templateBooleanFormat.getTrueStringValue();
- if (s == null) {
- if (fallbackToTrueFalse) {
- return TemplateBooleanFormat.C_TRUE;
- } else {
- throw new TemplateException(getNullBooleanFormatErrorDescription());
- }
- } else {
- return s;
- }
- } else {
- String s = templateBooleanFormat.getFalseStringValue();
- if (s == null) {
- if (fallbackToTrueFalse) {
- return TemplateBooleanFormat.C_FALSE;
- } else {
- throw new TemplateException(getNullBooleanFormatErrorDescription());
- }
- } else {
- return s;
- }
+ if (templateBooleanFormat == null) {
+ throw new TemplateException(new _ErrorDescriptionBuilder(
+ "Can't convert boolean to string automatically, because the "
+ + "\"", BOOLEAN_FORMAT_KEY ,"\" setting was not set. This is the default configuration; "
+ + "you should provide the format explicitly for each place where you print a boolean."
+ ).tips(
+ "Write something like myBool?string('yes', 'no') to specify boolean formatting in place.",
+ new Object[]{
+ "If you want \"true\"/\"false\" result as you are generating computer-language output "
+ + "(not for direct human consumption), then use \"?c\", like ${myBool?c}. (If you "
+ + "always generate computer-language output, then it's might be reasonable to set "
+ + "the \"", BOOLEAN_FORMAT_KEY, "\" setting to \"c\" instead.)",
+ },
+ new Object[] {
+ "If you need the same two values on most places, the programmers can set the \"",
+ BOOLEAN_FORMAT_KEY ,"\" setting to something like \"yes,no\". However, then it will be "
+ + "easy to unwillingly format booleans like that."
+ }
+ ));
}
+ return value ? templateBooleanFormat.getTrueStringValue() : templateBooleanFormat.getFalseStringValue();
}
+ /**
+ * @return {@code null} if the boolean format string was an empty string.
+ */
TemplateBooleanFormat getTemplateBooleanFormat() {
TemplateBooleanFormat format = cachedTemplateBooleanFormat;
if (format == null) {
format = TemplateBooleanFormat.getInstance(getBooleanFormat());
- cachedTemplateBooleanFormat = format;
+ if (format != null) {
+ cachedTemplateBooleanFormat = format;
+ }
}
return format;
}
@@ -1176,23 +1179,6 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
}
}
- private _ErrorDescriptionBuilder getNullBooleanFormatErrorDescription() {
- return new _ErrorDescriptionBuilder(
- "Can't convert boolean to string automatically, because the \"", BOOLEAN_FORMAT_KEY ,"\" setting was ",
- new _DelayedJQuote(getBooleanFormat()),
- (getBooleanFormat().equals(TemplateBooleanFormat.C_TRUE_FALSE)
- ? ", which is the legacy default computer-language format, and hence isn't accepted."
- : ".")
- ).tips(
- "If you just want \"true\"/\"false\" result as you are generting computer-language output, "
- + "use \"?c\", like ${myBool?c}.",
- "You can write myBool?string('yes', 'no') and like to specify boolean formatting in place.",
- new Object[] {
- "If you need the same two values on most places, the programmers should set the \"",
- BOOLEAN_FORMAT_KEY ,"\" setting to something like \"yes,no\"." }
- );
- }
-
/**
* Format number with the default number format.
*
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 d959251..936cce3 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
@@ -447,15 +447,8 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
*/
public void setBooleanFormat(String booleanFormat) {
_NullArgumentException.check("booleanFormat", booleanFormat);
-
- int commaIdx = booleanFormat.indexOf(',');
- if (commaIdx == -1) {
- throw new IllegalArgumentException(
- "Setting value must be a string that contains two comma-separated values for true and false, " +
- "respectively.");
- }
-
- this.booleanFormat = booleanFormat;
+ TemplateBooleanFormat.validateFormatString(booleanFormat);
+ this.booleanFormat = booleanFormat;
}
/**
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java
index add8387..230dfed 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java
@@ -219,13 +219,15 @@ public interface ProcessingConfiguration {
boolean isCustomNumberFormatsSet();
/**
- * The string value for the boolean {@code true} and {@code false} values, intended for human audience (not for a
- * computer language), separated with comma. For example, {@code "yes,no"}. Note that white-space is significant,
- * so {@code "yes, no"} is WRONG (unless you want that leading space before "no").
- *
- * <p>For backward compatibility the default is {@code "true,false"}, but using that value is denied for automatic
- * boolean-to-string conversion (like <code>${myBoolean}</code> will fail with it), only {@code myBool?string} will
- * allow it, which is deprecated since FreeMarker 2.3.20.
+ * The string value for the boolean {@code true} and {@code false} values, usually intended for human consumption
+ * (not for a computer language), separated with comma. For example, {@code "yes,no"}. Note that white-space is
+ * significant, so {@code "yes, no"} is WRONG (unless you want that leading space before "no"). Because the proper
+ * way of formatting booleans depends on the context too much, it's probably the best to leave this setting on its
+ * default, {@code ""}, which will enforce explicit formatting, like <code>${aBoolean?string('on', 'off')}</code>.
+ *
+ * <p>If you generate the piece of output for "computer audience" as opposed to "human audience", then you should
+ * write <code>${myBoolean?c}</code>, which will print {@code true} or {@code false}. If you really want to always
+ * format for computer audience, then it's might be reasonable to set this setting to {@code c}.
*/
String getBooleanFormat();
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateBooleanFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateBooleanFormat.java
index b2de905..86575d1 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateBooleanFormat.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateBooleanFormat.java
@@ -25,39 +25,55 @@ import org.apache.freemarker.core.valueformat.TemplateValueFormat;
// TODO Should be public and moved over to core.valueformat?
final class TemplateBooleanFormat extends TemplateValueFormat {
+ static final String C_FORMAT_STRING = "c";
static final String C_FALSE = "false";
static final String C_TRUE = "true";
- static final String C_TRUE_FALSE = C_TRUE + "," + C_FALSE;
- static final TemplateBooleanFormat C_TRUE_FALSE_FORMAT = new TemplateBooleanFormat();
-
- static TemplateBooleanFormat getInstance(String format) {
- return format.equals(C_TRUE_FALSE) ? C_TRUE_FALSE_FORMAT : new TemplateBooleanFormat(format);
- }
-
- private final String formatString;
- private final String trueStringValue; // deduced from booleanFormat
- private final String falseStringValue; // deduced from booleanFormat
+ static final TemplateBooleanFormat C_INSTANCE = new TemplateBooleanFormat(C_FORMAT_STRING, C_TRUE, C_FALSE);
/**
- * Use for {@link #C_TRUE_FALSE} only!
+ * @return {@code null} if the format string was an empty string.
*/
- private TemplateBooleanFormat() {
- formatString = C_TRUE_FALSE;
- trueStringValue = null;
- falseStringValue = null;
+ static TemplateBooleanFormat getInstance(String formatString) {
+ return getInstanceOrValidate(formatString, false);
}
- private TemplateBooleanFormat(String formatString) {
+ static void validateFormatString(String formatString) {
+ getInstanceOrValidate(formatString, true);
+ }
+
+ private static TemplateBooleanFormat getInstanceOrValidate(String formatString, boolean validationOnly) {
+ if (formatString.length() == 0) {
+ return null;
+ }
+ if (formatString.equals(C_FORMAT_STRING)) {
+ return C_INSTANCE;
+ }
+
int commaIdx = formatString.indexOf(',');
if (commaIdx == -1) {
throw new IllegalArgumentException(
- "Setting value must be string that contains two comma-separated values for true and false, " +
- "respectively.");
+ "Setting value must be a string that contains two comma-separated values for true and false, "
+ + "or it must be \"" + TemplateBooleanFormat.C_FORMAT_STRING + "\", "
+ + "or it must be an empty string, but it was "
+ + _StringUtils.jQuote(formatString) + ".");
}
+ if (validationOnly) {
+ return null;
+ }
+
+ String trueStringValue = formatString.substring(0, commaIdx);
+ String falseStringValue = formatString.substring(commaIdx + 1);
+ return new TemplateBooleanFormat(formatString, trueStringValue, falseStringValue);
+ }
+
+ private final String formatString;
+ private final String trueStringValue; // Usually deduced from booleanFormat
+ private final String falseStringValue; // Usually deduced from booleanFormat
+ private TemplateBooleanFormat(String formatString, String trueStringValue, String falseStringValue) {
this.formatString = formatString;
- trueStringValue = formatString.substring(0, commaIdx);
- falseStringValue = formatString.substring(commaIdx + 1);
+ this.trueStringValue = trueStringValue;
+ this.falseStringValue = falseStringValue;
}
public String getFormatString() {
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java
index eda1ebe..c5602ce 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java
@@ -468,7 +468,7 @@ public class _EvalUtils {
// `${missing!}` wouldn't print `""` anymore if we reorder these. But, if and when `null` handling is
// reworked ("checked nulls"), this problem should go away, and so we should move this.
boolean booleanValue = ((TemplateBooleanModel) tm).getAsBoolean();
- return env.formatBoolean(booleanValue, false);
+ return env.formatBoolean(booleanValue);
} else {
if (returnNullOnNonCoercableType) {
return null;
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/default-xmlns.f3ac b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/default-xmlns.f3ac
index 306ddfb..36393f7 100644
--- a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/default-xmlns.f3ac
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/default-xmlns.f3ac
@@ -24,5 +24,5 @@ ${r["y:t3"]!'-'} = y NS
${r["./D:t4"]!'-'} = x NS
<#assign bool = doc["true()"]>
-${bool?string}
+${bool?c}