Forward ported from 2.3-gae:

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.

Ensure that the configuration settings don't depend on the machine that runs 
the test.


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/755b25a3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/755b25a3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/755b25a3

Branch: refs/heads/3
Commit: 755b25a3e1d6e0c0363609d9a73ba80210df2e81
Parents: 98fd94e
Author: ddekany <[email protected]>
Authored: Tue Mar 14 21:47:29 2017 +0100
Committer: ddekany <[email protected]>
Committed: Tue Mar 14 21:48:17 2017 +0100

----------------------------------------------------------------------
 .../apache/freemarker/core/Configurable.java    |  22 ++--
 .../apache/freemarker/core/Configuration.java   | 118 +++++++++++++++++--
 .../freemarker/core/ConfigurationTest.java      |  65 +++++++++-
 .../apache/freemarker/core/SQLTimeZoneTest.java |   2 +
 .../apache/freemarker/test/TemplateTest.java    |  18 +++
 5 files changed, 204 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/755b25a3/src/main/java/org/apache/freemarker/core/Configurable.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Configurable.java 
b/src/main/java/org/apache/freemarker/core/Configurable.java
index 122a3c0..c2aeb85 100644
--- a/src/main/java/org/apache/freemarker/core/Configurable.java
+++ b/src/main/java/org/apache/freemarker/core/Configurable.java
@@ -91,9 +91,9 @@ import 
org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory;
 public class Configurable {
     static final String C_TRUE_FALSE = "true,false";
     
-    private static final String NULL = "null";
-    private static final String DEFAULT = "default";
-    private static final String JVM_DEFAULT = "JVM default";
+    static final String NULL = "null";
+    static final String DEFAULT = "default";
+    static final String JVM_DEFAULT = "JVM default";
     
     /** Legacy, snake case ({@code like_this}) variation of the setting name. 
@since 2.3.23 */
     public static final String LOCALE_KEY_SNAKE_CASE = "locale";
@@ -361,8 +361,8 @@ public class Configurable {
     protected Configurable(Version incompatibleImprovements) {
         _CoreAPI.checkVersionNotNullAndSupported(incompatibleImprovements);
         parent = null;
-        locale = Locale.getDefault();
-        timeZone = TimeZone.getDefault();
+        locale = Configuration.getDefaultLocale();
+        timeZone = Configuration.getDefaultTimeZone();
         sqlDataAndTimeTimeZone = null;
         numberFormat = "number";
         timeFormat = "";
@@ -1769,7 +1769,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
+     *       "JVM default" (ignoring case) to use the default locale of the 
Java environment.
      *
      *   <li><p>{@code "custom_number_formats"}: See {@link 
#setCustomNumberFormats(Map)}.
      *   <br>String value: Interpreted as an <a href="#fm_obe">object builder 
expression</a>.
@@ -1943,7 +1944,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>
      *       
@@ -2136,7 +2138,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/755b25a3/src/main/java/org/apache/freemarker/core/Configuration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Configuration.java 
b/src/main/java/org/apache/freemarker/core/Configuration.java
index 4bf2eae..73e85f1 100644
--- a/src/main/java/org/apache/freemarker/core/Configuration.java
+++ b/src/main/java/org/apache/freemarker/core/Configuration.java
@@ -354,9 +354,6 @@ public class Configuration extends Configurable implements 
Cloneable, ParserConf
     /** The default of {@link #getIncompatibleImprovements()}, currently 
{@link #VERSION_3_0_0}. */
     public static final Version DEFAULT_INCOMPATIBLE_IMPROVEMENTS = 
Configuration.VERSION_3_0_0;
     
-    private static final String NULL = "null";
-    private static final String DEFAULT = "default";
-    
     private static final Version VERSION;
     static {
         try {
@@ -418,7 +415,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();
 
     /**
@@ -428,7 +428,7 @@ public class Configuration extends Configurable implements 
Cloneable, ParserConf
      */
     private HashMap<String, Object> rewrappableSharedVariables = null;
     
-    private String defaultEncoding = 
_SecurityUtil.getSystemProperty("file.encoding", "utf-8");
+    private String defaultEncoding = getDefaultDefaultEncoding();
     private ConcurrentMap localeToCharsetMap = new ConcurrentHashMap();
     
     /**
@@ -491,11 +491,6 @@ public class Configuration extends Configurable implements 
Cloneable, ParserConf
         loadBuiltInSharedVariables();
     }
 
-    @Override
-    public void setTimeZone(TimeZone timeZone) {
-        super.setTimeZone(timeZone);
-    }
-
     private void createTemplateResolver() {
         templateResolver = new DefaultTemplateResolver(
                 null,
@@ -1123,7 +1118,71 @@ public class Configuration extends Configurable 
implements Cloneable, ParserConf
     public boolean isObjectWrapperExplicitlySet() {
         return objectWrapperExplicitlySet;
     }
-    
+
+    @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);
@@ -2056,6 +2115,7 @@ public class Configuration extends Configurable 
implements Cloneable, ParserConf
      */
     public void setDefaultEncoding(String encoding) {
         defaultEncoding = encoding;
+        defaultEncodingExplicitlySet = true;
     }
 
     /**
@@ -2068,6 +2128,36 @@ public class Configuration extends Configurable 
implements Cloneable, ParserConf
     }
 
     /**
+     * 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 _SecurityUtil.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
      * locale. You can associate encodings with locales using 
@@ -2366,7 +2456,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 (WHITESPACE_STRIPPING_KEY_SNAKE_CASE.equals(name)

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/755b25a3/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/ConfigurationTest.java 
b/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
index c1650ec..0ad4773 100644
--- a/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
+++ b/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
@@ -1460,7 +1460,70 @@ public class ConfigurationTest extends TestCase {
         assertNull(cfg.getLazyAutoImports());
         assertTrue(cfg.isLazyAutoImportsSet());
     }
-    
+
+    public void testLocaleSetting() throws TemplateException, 
ConfigurationException {
+        Configuration cfg = new Configuration(Configuration.VERSION_3_0_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, 
ConfigurationException {
+        Configuration cfg = new Configuration(Configuration.VERSION_3_0_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, 
ConfigurationException {
+        Configuration cfg = new Configuration(Configuration.VERSION_3_0_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_3_0_0);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/755b25a3/src/test/java/org/apache/freemarker/core/SQLTimeZoneTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/SQLTimeZoneTest.java 
b/src/test/java/org/apache/freemarker/core/SQLTimeZoneTest.java
index 9ade361..aae82f6 100644
--- a/src/test/java/org/apache/freemarker/core/SQLTimeZoneTest.java
+++ b/src/test/java/org/apache/freemarker/core/SQLTimeZoneTest.java
@@ -133,6 +133,7 @@ public class SQLTimeZoneTest extends TemplateTest {
         TimeZone.setDefault(GMT_P02);
         try {
             Configuration cfg = getConfiguration();
+            cfg.unsetTimeZone();
             assertNull(cfg.getSQLDateAndTimeTimeZone());
             assertEquals(TimeZone.getDefault(), cfg.getTimeZone());
             
@@ -148,6 +149,7 @@ public class SQLTimeZoneTest extends TemplateTest {
         TimeZone.setDefault(GMT_P02);
         try {
             Configuration cfg = getConfiguration();
+            cfg.unsetTimeZone();
             cfg.setSQLDateAndTimeTimeZone(GMT_P02);
             
             assertOutput(FTL, OUTPUT_BEFORE_SETTING_GMT_CFG_GMT2 + 
OUTPUT_AFTER_SETTING_GMT_CFG_SQL_DIFFERENT);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/755b25a3/src/test/java/org/apache/freemarker/test/TemplateTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/test/TemplateTest.java 
b/src/test/java/org/apache/freemarker/test/TemplateTest.java
index 769ea16..d2eeb72 100644
--- a/src/test/java/org/apache/freemarker/test/TemplateTest.java
+++ b/src/test/java/org/apache/freemarker/test/TemplateTest.java
@@ -28,7 +28,9 @@ import java.io.StringWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
+import java.util.TimeZone;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.freemarker.core.Configuration;
@@ -60,6 +62,7 @@ public abstract class TemplateTest {
             try {
                 configuration = createConfiguration();
                 addCommonTemplates();
+                applyEnvironmentIndependentDefaults();
             } catch (Exception e) {
                 throw new RuntimeException("Failed to set up configuration for 
the test", e);
             }
@@ -67,6 +70,21 @@ public abstract class TemplateTest {
         return configuration;
     }
 
+    /**
+     * Ensure that the configuration settings don't depend on the machine that 
runs the test.
+     */
+    private void applyEnvironmentIndependentDefaults() {
+        if (!configuration.isLocaleExplicitlySet()) {
+            configuration.setLocale(Locale.US);
+        }
+        if (!configuration.isDefaultEncodingExplicitlySet()) {
+            configuration.setDefaultEncoding(StandardCharsets.UTF_8.name());
+        }
+        if (!configuration.isTimeZoneExplicitlySet()) {
+            configuration.setTimeZone(TimeZone.getTimeZone("GMT+1"));
+        }
+    }
+
     protected final void setConfiguration(Configuration configuration) {
         this.configuration = configuration;
     }

Reply via email to