Added Configuration.isXxxExplictlySet and Configuration.unsetXxx methods for the Configuration settings: locale, time_zone, default_encoding. (This can be utilized in frameworks to detect if the application has missed setting these. The backward compatible default values are often unwanted, as they are the default locale, time zone and file encoding of the Java environment.)
The locale and default_encoding configuration settings now supports the special "JVM default" value when set from Java .properties file, or via Configuration.setSettings(Properties), or via the #setting directive. Earlier only the time_zone setting has supported this value. Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/0fa42996 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/0fa42996 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/0fa42996 Branch: refs/heads/2.3-gae Commit: 0fa4299657306fba0e18d732b122e1ca09d7b54e Parents: 738cef8 Author: ddekany <[email protected]> Authored: Tue Mar 14 20:13:25 2017 +0100 Committer: ddekany <[email protected]> Committed: Tue Mar 14 20:13:25 2017 +0100 ---------------------------------------------------------------------- src/main/java/freemarker/core/Configurable.java | 16 ++- .../java/freemarker/template/Configuration.java | 109 ++++++++++++++++++- .../java/freemarker/template/_TemplateAPI.java | 10 ++ src/manual/en_US/book.xml | 29 ++++- .../freemarker/template/ConfigurationTest.java | 63 +++++++++++ 5 files changed, 218 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/0fa42996/src/main/java/freemarker/core/Configurable.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/Configurable.java b/src/main/java/freemarker/core/Configurable.java index 76a3922..1563a4e 100644 --- a/src/main/java/freemarker/core/Configurable.java +++ b/src/main/java/freemarker/core/Configurable.java @@ -388,10 +388,10 @@ public class Configurable { parent = null; properties = new Properties(); - locale = Locale.getDefault(); + locale = _TemplateAPI.getDefaultLocale(); properties.setProperty(LOCALE_KEY, locale.toString()); - timeZone = TimeZone.getDefault(); + timeZone = _TemplateAPI.getDefaultTimeZone(); properties.setProperty(TIME_ZONE_KEY, timeZone.getID()); sqlDataAndTimeTimeZone = null; @@ -1992,7 +1992,8 @@ public class Configurable { * <ul> * <li><p>{@code "locale"}: * See {@link #setLocale(Locale)}. - * <br>String value: local codes with the usual format in Java, such as {@code "en_US"}. + * <br>String value: local codes with the usual format in Java, such as {@code "en_US"}, or since 2.3.26, + * "JVM default" (ignoring case) to use the default locale of the Java environment. * * <li><p>{@code "classic_compatible"}: * See {@link #setClassicCompatible(boolean)} and {@link Configurable#setClassicCompatibleAsInt(int)}. @@ -2179,7 +2180,8 @@ public class Configurable { * {@code "disable"} for {@link Configuration#DISABLE_AUTO_ESCAPING_POLICY}. * * <li><p>{@code "default_encoding"}: - * See {@link Configuration#setDefaultEncoding(String)}. + * See {@link Configuration#setDefaultEncoding(String)}; since 2.3.26 also accepts value "JVM default" + * (not case sensitive) to set the Java environment default value. * <br>As the default value is the system default, which can change * from one server to another, <b>you should always set this!</b> * @@ -2385,7 +2387,11 @@ public class Configurable { boolean unknown = false; try { if (LOCALE_KEY.equals(name)) { - setLocale(StringUtil.deduceLocale(value)); + if (JVM_DEFAULT.equalsIgnoreCase(value)) { + setLocale(Locale.getDefault()); + } else { + setLocale(StringUtil.deduceLocale(value)); + } } else if (NUMBER_FORMAT_KEY_SNAKE_CASE.equals(name) || NUMBER_FORMAT_KEY_CAMEL_CASE.equals(name)) { setNumberFormat(value); } else if (CUSTOM_NUMBER_FORMATS_KEY_SNAKE_CASE.equals(name) http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/0fa42996/src/main/java/freemarker/template/Configuration.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/template/Configuration.java b/src/main/java/freemarker/template/Configuration.java index a34c605..ca5ee72 100644 --- a/src/main/java/freemarker/template/Configuration.java +++ b/src/main/java/freemarker/template/Configuration.java @@ -38,6 +38,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; +import java.util.TimeZone; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -424,6 +425,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf private static final String NULL = "null"; private static final String DEFAULT = "default"; + private static final String JVM_DEFAULT = "JVM default"; private static final Version VERSION; static { @@ -509,6 +511,10 @@ public class Configuration extends Configurable implements Cloneable, ParserConf private boolean objectWrapperExplicitlySet; private boolean templateExceptionHandlerExplicitlySet; private boolean logTemplateExceptionsExplicitlySet; + private boolean localeExplicitlySet; + private boolean defaultEncodingExplicitlySet; + private boolean timeZoneExplicitlySet; + private HashMap/*<String, TemplateModel>*/ sharedVariables = new HashMap(); @@ -519,7 +525,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf */ private HashMap/*<String, Object>*/ rewrappableSharedVariables = null; - private String defaultEncoding = SecurityUtilities.getSystemProperty("file.encoding", "utf-8"); + private String defaultEncoding = getDefaultDefaultEncoding(); private ConcurrentMap localeToCharsetMap = new ConcurrentHashMap(); /** @@ -1606,6 +1612,70 @@ public class Configuration extends Configurable implements Cloneable, ParserConf } @Override + public void setLocale(Locale locale) { + super.setLocale(locale); + localeExplicitlySet = true; + } + + /** + * Resets the setting to its default, as if it was never set. + * + * @since 2.3.26 + */ + public void unsetLocale() { + if (localeExplicitlySet) { + setLocale(getDefaultLocale()); + localeExplicitlySet = false; + } + } + + /** + * Tells if {@link #setLocale(Locale)} (or equivalent) was already called on this instance, or it just holds the + * default value. + * + * @since 2.3.26 + */ + public boolean isLocaleExplicitlySet() { + return localeExplicitlySet; + } + + static Locale getDefaultLocale() { + return Locale.getDefault(); + } + + @Override + public void setTimeZone(TimeZone timeZone) { + super.setTimeZone(timeZone); + timeZoneExplicitlySet = true; + } + + /** + * Resets the setting to its default, as if it was never set. + * + * @since 2.3.26 + */ + public void unsetTimeZone() { + if (timeZoneExplicitlySet) { + setTimeZone(getDefaultTimeZone()); + timeZoneExplicitlySet = false; + } + } + + /** + * Tells if {@link #setTimeZone(TimeZone)} (or equivalent) was already called on this instance, or it just holds the + * default value. + * + * @since 2.3.26 + */ + public boolean isTimeZoneExplicitlySet() { + return timeZoneExplicitlySet; + } + + static TimeZone getDefaultTimeZone() { + return TimeZone.getDefault(); + } + + @Override public void setTemplateExceptionHandler(TemplateExceptionHandler templateExceptionHandler) { super.setTemplateExceptionHandler(templateExceptionHandler); templateExceptionHandlerExplicitlySet = true; @@ -2534,6 +2604,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf */ public void setDefaultEncoding(String encoding) { defaultEncoding = encoding; + defaultEncodingExplicitlySet = true; } /** @@ -2544,7 +2615,37 @@ public class Configuration extends Configurable implements Cloneable, ParserConf public String getDefaultEncoding() { return defaultEncoding; } + + /** + * Resets the setting to its default, as if it was never set. + * + * @since 2.3.26 + */ + public void unsetDefaultEncoding() { + if (defaultEncodingExplicitlySet) { + setDefaultEncoding(getDefaultDefaultEncoding()); + defaultEncodingExplicitlySet = false; + } + } + + /** + * Tells if {@link #setDefaultEncoding(String)} (or equivalent) was already called on this instance, or it just holds the + * default value. + * + * @since 2.3.26 + */ + public boolean isDefaultEncodingExplicitlySet() { + return defaultEncodingExplicitlySet; + } + + static private String getDefaultDefaultEncoding() { + return getJVMDefaultEncoding(); + } + static private String getJVMDefaultEncoding() { + return SecurityUtilities.getSystemProperty("file.encoding", "utf-8"); + } + /** * Gets the preferred character encoding for the given locale, or the * default encoding if no encoding is set explicitly for the specified @@ -2845,7 +2946,11 @@ public class Configuration extends Configurable implements Cloneable, ParserConf } if (DEFAULT_ENCODING_KEY_SNAKE_CASE.equals(name) || DEFAULT_ENCODING_KEY_CAMEL_CASE.equals(name)) { - setDefaultEncoding(value); + if (JVM_DEFAULT.equalsIgnoreCase(value)) { + setDefaultEncoding(getJVMDefaultEncoding()); + } else { + setDefaultEncoding(value); + } } else if (LOCALIZED_LOOKUP_KEY_SNAKE_CASE.equals(name) || LOCALIZED_LOOKUP_KEY_CAMEL_CASE.equals(name)) { setLocalizedLookup(StringUtil.getYesNo(value)); } else if (STRICT_SYNTAX_KEY_SNAKE_CASE.equals(name) || STRICT_SYNTAX_KEY_CAMEL_CASE.equals(name)) { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/0fa42996/src/main/java/freemarker/template/_TemplateAPI.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/template/_TemplateAPI.java b/src/main/java/freemarker/template/_TemplateAPI.java index 15d5e23..737c528 100644 --- a/src/main/java/freemarker/template/_TemplateAPI.java +++ b/src/main/java/freemarker/template/_TemplateAPI.java @@ -19,7 +19,9 @@ package freemarker.template; +import java.util.Locale; import java.util.Set; +import java.util.TimeZone; import freemarker.cache.CacheStorage; import freemarker.cache.TemplateLoader; @@ -150,4 +152,12 @@ public class _TemplateAPI { return e.getBlamedExpression(); } + public static Locale getDefaultLocale() { + return Configuration.getDefaultLocale(); + } + + public static TimeZone getDefaultTimeZone() { + return Configuration.getDefaultTimeZone(); + } + } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/0fa42996/src/manual/en_US/book.xml ---------------------------------------------------------------------- diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml index 7ba4089..5673870 100644 --- a/src/manual/en_US/book.xml +++ b/src/manual/en_US/book.xml @@ -26862,8 +26862,8 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting> <literal>Configuration</literal> on its default, it's enough to increase the <literal>incompatibleImprovements</literal> setting of the <literal>Configuration</literal> to 2.3.26, as that's - inherited by the default <literal>object_wrapper</literal>. - </para> + inherited by the default + <literal>object_wrapper</literal>.</para> </listitem> <listitem> @@ -26895,6 +26895,31 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting> </listitem> <listitem> + <para>Added + <literal>Configuration.is<replaceable>Xxx</replaceable>ExplictlySet</literal> + and + <literal>Configuration.unset<replaceable>Xxx</replaceable></literal> + methods for the Configuration settings: + <literal>locale</literal>, <literal>time_zone</literal>, + <literal>default_encoding</literal>. (This can be utilized in + frameworks to detect if the application has missed setting + these. The backward compatible default values are often + unwanted, as they are the default locale, time zone and file + encoding of the Java environment.)</para> + </listitem> + + <listitem> + <para>The <literal>locale</literal> and + <literal>default_encoding</literal> configuration settings now + supports the special <literal>"JVM default"</literal> value when + set from Java <literal>.properties</literal> file, or via + <literal>Configuration.setSettings(Properties)</literal>, or via + the <literal>#setting</literal> directive. Earlier only the + <literal>time_zone</literal> setting has supported this + value.</para> + </listitem> + + <listitem> <para>Bug fixed: If <link linkend="pgui_config_incompatible_improvements_how_to_set">the <literal>incompatible_improvements</literal> setting</link> is http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/0fa42996/src/test/java/freemarker/template/ConfigurationTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/freemarker/template/ConfigurationTest.java b/src/test/java/freemarker/template/ConfigurationTest.java index a4dbfcc..15ba915 100644 --- a/src/test/java/freemarker/template/ConfigurationTest.java +++ b/src/test/java/freemarker/template/ConfigurationTest.java @@ -1615,6 +1615,69 @@ public class ConfigurationTest extends TestCase { assertTrue(cfg.isLazyAutoImportsSet()); } + public void testLocaleSetting() throws TemplateException { + Configuration cfg = new Configuration(Configuration.VERSION_2_3_0); + + assertEquals(Locale.getDefault(), cfg.getLocale()); + assertFalse(cfg.isLocaleExplicitlySet()); + + Locale nonDefault = Locale.getDefault().equals(Locale.GERMANY) ? Locale.FRANCE : Locale.GERMANY; + cfg.setLocale(nonDefault); + assertTrue(cfg.isLocaleExplicitlySet()); + assertEquals(nonDefault, cfg.getLocale()); + + cfg.unsetLocale(); + assertEquals(Locale.getDefault(), cfg.getLocale()); + assertFalse(cfg.isLocaleExplicitlySet()); + + cfg.setSetting(Configuration.LOCALE_KEY, "JVM default"); + assertEquals(Locale.getDefault(), cfg.getLocale()); + assertTrue(cfg.isLocaleExplicitlySet()); + } + + public void testDefaultEncodingSetting() throws TemplateException { + Configuration cfg = new Configuration(Configuration.VERSION_2_3_0); + + String defaultFileEncoding = System.getProperty("file.encoding"); + assertNotNull(defaultFileEncoding); + + assertEquals(defaultFileEncoding, cfg.getDefaultEncoding()); + assertFalse(cfg.isDefaultEncodingExplicitlySet()); + + String nonDefault = defaultFileEncoding.equalsIgnoreCase("UTF-8") ? "ISO-8859-1" : "UTF-8"; + cfg.setDefaultEncoding(nonDefault); + assertTrue(cfg.isDefaultEncodingExplicitlySet()); + assertEquals(nonDefault, cfg.getDefaultEncoding()); + + cfg.unsetDefaultEncoding(); + assertEquals(defaultFileEncoding, cfg.getDefaultEncoding()); + assertFalse(cfg.isDefaultEncodingExplicitlySet()); + + cfg.setSetting(Configuration.DEFAULT_ENCODING_KEY, "JVM default"); + assertEquals(defaultFileEncoding, cfg.getDefaultEncoding()); + assertTrue(cfg.isDefaultEncodingExplicitlySet()); + } + + public void testTimeZoneSetting() throws TemplateException { + Configuration cfg = new Configuration(Configuration.VERSION_2_3_0); + + assertEquals(TimeZone.getDefault(), cfg.getTimeZone()); + assertFalse(cfg.isTimeZoneExplicitlySet()); + + TimeZone nonDefault = TimeZone.getDefault().equals(DateUtil.UTC) ? TimeZone.getTimeZone("PST") : DateUtil.UTC; + cfg.setTimeZone(nonDefault); + assertTrue(cfg.isTimeZoneExplicitlySet()); + assertEquals(nonDefault, cfg.getTimeZone()); + + cfg.unsetTimeZone(); + assertEquals(TimeZone.getDefault(), cfg.getTimeZone()); + assertFalse(cfg.isTimeZoneExplicitlySet()); + + cfg.setSetting(Configuration.TIME_ZONE_KEY, "JVM default"); + assertEquals(TimeZone.getDefault(), cfg.getTimeZone()); + assertTrue(cfg.isTimeZoneExplicitlySet()); + } + @Test public void testGetSettingNamesAreSorted() throws Exception { Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
