You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2020/12/21 17:56:28 UTC

[commons-lang] 02/02: Add and use LocaleUtils.toLocale(Locale) to avoid NPEs and use the default locale when an input locale is null.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git

commit c9e825e823e30c5b1e3ddc9de5e8fd0094d52ee5
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Mon Dec 21 12:56:19 2020 -0500

    Add and use LocaleUtils.toLocale(Locale) to avoid NPEs and use the
    default locale when an input locale is null.
---
 src/changes/changes.xml                            |  1 +
 .../java/org/apache/commons/lang3/LocaleUtils.java | 12 +++++++-
 .../java/org/apache/commons/lang3/StringUtils.java |  4 +--
 .../commons/lang3/text/ExtendedMessageFormat.java  |  9 +++---
 .../org/apache/commons/lang3/time/DateUtils.java   |  3 +-
 .../apache/commons/lang3/time/FastDateParser.java  | 16 ++++++-----
 .../apache/commons/lang3/time/FastDatePrinter.java |  7 +++--
 .../org/apache/commons/lang3/time/FormatCache.java | 21 ++++++--------
 .../org/apache/commons/lang3/LocaleUtilsTest.java  | 33 +++++++++++++++++-----
 9 files changed, 69 insertions(+), 37 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 25a2839..5804a9c 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -81,6 +81,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action issue="LANG-1596" type="update" dev="aherbert" due-to="Richard Eckart de Castilho">ArrayUtils.toPrimitive(Object) does not support boolean and other types #607.</action>
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add fluent-style ArrayUtils.sort(Object[]).</action>
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add fluent-style ArrayUtils.sort(Object[], Comparable).</action>
+    <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add and use LocaleUtils.toLocale(Locale) to avoid NPEs.</action>
     <!--  UPDATES -->
     <action                   type="update" dev="ggregory" due-to="Gary Gregory">Enable Dependabot #587.</action>
     <action                   type="update" dev="chtompki">Bump junit-jupiter from 5.6.2 to 5.7.0.</action>
diff --git a/src/main/java/org/apache/commons/lang3/LocaleUtils.java b/src/main/java/org/apache/commons/lang3/LocaleUtils.java
index 24b7f3e..b73856e 100644
--- a/src/main/java/org/apache/commons/lang3/LocaleUtils.java
+++ b/src/main/java/org/apache/commons/lang3/LocaleUtils.java
@@ -276,7 +276,17 @@ public class LocaleUtils {
         throw new IllegalArgumentException("Invalid locale format: " + str);
     }
 
-    //-----------------------------------------------------------------------
+    /**
+     * Returns the given locale if non-{@code null}, otherwise {@link Locale#getDefault()}.
+     *
+     * @param locale a locale or {@code null}.
+     * @return the given locale if non-{@code null}, otherwise {@link Locale#getDefault()}.
+     * @since 3.12
+     */
+    public static Locale toLocale(final Locale locale) {
+        return locale != null ? locale : Locale.getDefault();
+    }
+
     /**
      * <p>Converts a String to a Locale.</p>
      *
diff --git a/src/main/java/org/apache/commons/lang3/StringUtils.java b/src/main/java/org/apache/commons/lang3/StringUtils.java
index ab9d952..a35b002 100644
--- a/src/main/java/org/apache/commons/lang3/StringUtils.java
+++ b/src/main/java/org/apache/commons/lang3/StringUtils.java
@@ -5406,7 +5406,7 @@ public class StringUtils {
         if (str == null) {
             return null;
         }
-        return str.toLowerCase(locale);
+        return str.toLowerCase(LocaleUtils.toLocale(locale));
     }
 
     private static int[] matches(final CharSequence first, final CharSequence second) {
@@ -9414,7 +9414,7 @@ public class StringUtils {
         if (str == null) {
             return null;
         }
-        return str.toUpperCase(locale);
+        return str.toUpperCase(LocaleUtils.toLocale(locale));
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/lang3/text/ExtendedMessageFormat.java b/src/main/java/org/apache/commons/lang3/text/ExtendedMessageFormat.java
index 6b921b8..c0cd9b3 100644
--- a/src/main/java/org/apache/commons/lang3/text/ExtendedMessageFormat.java
+++ b/src/main/java/org/apache/commons/lang3/text/ExtendedMessageFormat.java
@@ -26,6 +26,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 
+import org.apache.commons.lang3.LocaleUtils;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.Validate;
 
@@ -118,14 +119,14 @@ public class ExtendedMessageFormat extends MessageFormat {
     /**
      * Create a new ExtendedMessageFormat.
      *
-     * @param pattern  the pattern to use, not null
-     * @param locale  the locale to use, not null
-     * @param registry  the registry of format factories, may be null
+     * @param pattern  the pattern to use, not null.
+     * @param locale  the locale to use.
+     * @param registry  the registry of format factories, may be null.
      * @throws IllegalArgumentException in case of a bad pattern.
      */
     public ExtendedMessageFormat(final String pattern, final Locale locale, final Map<String, ? extends FormatFactory> registry) {
         super(DUMMY_PATTERN);
-        setLocale(locale);
+        setLocale(LocaleUtils.toLocale(locale));
         this.registry = registry;
         applyPattern(pattern);
     }
diff --git a/src/main/java/org/apache/commons/lang3/time/DateUtils.java b/src/main/java/org/apache/commons/lang3/time/DateUtils.java
index fddfcbb..4b20c54 100644
--- a/src/main/java/org/apache/commons/lang3/time/DateUtils.java
+++ b/src/main/java/org/apache/commons/lang3/time/DateUtils.java
@@ -26,6 +26,7 @@ import java.util.NoSuchElementException;
 import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.commons.lang3.LocaleUtils;
 import org.apache.commons.lang3.Validate;
 
 /**
@@ -367,7 +368,7 @@ public class DateUtils {
         }
 
         final TimeZone tz = TimeZone.getDefault();
-        final Locale lcl = locale == null ? Locale.getDefault() : locale;
+        final Locale lcl = LocaleUtils.toLocale(locale);
         final ParsePosition pos = new ParsePosition(0);
         final Calendar calendar = Calendar.getInstance(tz, lcl);
         calendar.setLenient(lenient);
diff --git a/src/main/java/org/apache/commons/lang3/time/FastDateParser.java b/src/main/java/org/apache/commons/lang3/time/FastDateParser.java
index e53a246..9ae683b 100644
--- a/src/main/java/org/apache/commons/lang3/time/FastDateParser.java
+++ b/src/main/java/org/apache/commons/lang3/time/FastDateParser.java
@@ -39,6 +39,8 @@ import java.util.concurrent.ConcurrentMap;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.commons.lang3.LocaleUtils;
+
 /**
  * <p>FastDateParser is a fast and thread-safe version of
  * {@link java.text.SimpleDateFormat}.</p>
@@ -125,15 +127,15 @@ public class FastDateParser implements DateParser, Serializable {
     protected FastDateParser(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) {
         this.pattern = pattern;
         this.timeZone = timeZone;
-        this.locale = locale;
+        this.locale = LocaleUtils.toLocale(locale);
 
-        final Calendar definingCalendar = Calendar.getInstance(timeZone, locale);
+        final Calendar definingCalendar = Calendar.getInstance(timeZone, this.locale);
 
         int centuryStartYear;
         if (centuryStart!=null) {
             definingCalendar.setTime(centuryStart);
             centuryStartYear = definingCalendar.get(Calendar.YEAR);
-        } else if (locale.equals(JAPANESE_IMPERIAL)) {
+        } else if (this.locale.equals(JAPANESE_IMPERIAL)) {
             centuryStartYear = 0;
         } else {
             // from 80 years ago to 20 years from now
@@ -458,9 +460,9 @@ public class FastDateParser implements DateParser, Serializable {
      * @param regex The regular expression to build
      * @return The map of string display names to field values
      */
-    private static Map<String, Integer> appendDisplayNames(final Calendar cal, final Locale locale, final int field, final StringBuilder regex) {
+    private static Map<String, Integer> appendDisplayNames(final Calendar cal, Locale locale, final int field, final StringBuilder regex) {
         final Map<String, Integer> values = new HashMap<>();
-
+        locale = LocaleUtils.toLocale(locale);
         final Map<String, Integer> displayNames = cal.getDisplayNames(field, Calendar.ALL_STYLES, locale);
         final TreeSet<String> sorted = new TreeSet<>(LONGER_FIRST_LOWERCASE);
         for (final Map.Entry<String, Integer> displayName : displayNames.entrySet()) {
@@ -697,7 +699,7 @@ public class FastDateParser implements DateParser, Serializable {
          */
         CaseInsensitiveTextStrategy(final int field, final Calendar definingCalendar, final Locale locale) {
             this.field = field;
-            this.locale = locale;
+            this.locale = LocaleUtils.toLocale(locale);
 
             final StringBuilder regex = new StringBuilder();
             regex.append("((?iu)");
@@ -837,7 +839,7 @@ public class FastDateParser implements DateParser, Serializable {
          * @param locale The Locale
          */
         TimeZoneStrategy(final Locale locale) {
-            this.locale = locale;
+            this.locale = LocaleUtils.toLocale(locale);
 
             final StringBuilder sb = new StringBuilder();
             sb.append("((?iu)" + RFC_822_TIME_ZONE + "|" + GMT_OPTION );
diff --git a/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java b/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java
index 3402409..bc5cda5 100644
--- a/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java
+++ b/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java
@@ -31,6 +31,7 @@ import java.util.TimeZone;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
+import org.apache.commons.lang3.LocaleUtils;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 
 /**
@@ -150,7 +151,7 @@ public class FastDatePrinter implements DatePrinter, Serializable {
     protected FastDatePrinter(final String pattern, final TimeZone timeZone, final Locale locale) {
         mPattern = pattern;
         mTimeZone = timeZone;
-        mLocale = locale;
+        mLocale = LocaleUtils.toLocale(locale);
 
         init();
     }
@@ -1342,7 +1343,7 @@ public class FastDatePrinter implements DatePrinter, Serializable {
          * @param style the style
          */
         TimeZoneNameRule(final TimeZone timeZone, final Locale locale, final int style) {
-            mLocale = locale;
+            mLocale = LocaleUtils.toLocale(locale);
             mStyle = style;
 
             mStandard = getTimeZoneDisplay(timeZone, false, style, locale);
@@ -1539,7 +1540,7 @@ public class FastDatePrinter implements DatePrinter, Serializable {
             } else {
                 mStyle = style;
             }
-            mLocale = locale;
+            mLocale = LocaleUtils.toLocale(locale);
         }
 
         /**
diff --git a/src/main/java/org/apache/commons/lang3/time/FormatCache.java b/src/main/java/org/apache/commons/lang3/time/FormatCache.java
index e1e3a01..4786cd9 100644
--- a/src/main/java/org/apache/commons/lang3/time/FormatCache.java
+++ b/src/main/java/org/apache/commons/lang3/time/FormatCache.java
@@ -25,6 +25,7 @@ import java.util.TimeZone;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
+import org.apache.commons.lang3.LocaleUtils;
 import org.apache.commons.lang3.Validate;
 
 /**
@@ -73,9 +74,7 @@ abstract class FormatCache<F extends Format> {
         if (timeZone == null) {
             timeZone = TimeZone.getDefault();
         }
-        if (locale == null) {
-            locale = Locale.getDefault();
-        }
+        locale = LocaleUtils.toLocale(locale);
         final MultipartKey key = new MultipartKey(pattern, timeZone, locale);
         F format = cInstanceCache.get(key);
         if (format == null) {
@@ -118,9 +117,7 @@ abstract class FormatCache<F extends Format> {
      */
     // This must remain private, see LANG-884
     private F getDateTimeInstance(final Integer dateStyle, final Integer timeStyle, final TimeZone timeZone, Locale locale) {
-        if (locale == null) {
-            locale = Locale.getDefault();
-        }
+        locale = LocaleUtils.toLocale(locale);
         final String pattern = getPatternForStyle(dateStyle, timeStyle, locale);
         return getInstance(pattern, timeZone, locale);
     }
@@ -188,18 +185,19 @@ abstract class FormatCache<F extends Format> {
      */
     // package protected, for access from test code; do not make public or protected
     static String getPatternForStyle(final Integer dateStyle, final Integer timeStyle, final Locale locale) {
-        final MultipartKey key = new MultipartKey(dateStyle, timeStyle, locale);
+        final Locale safeLocale = LocaleUtils.toLocale(locale);
+        final MultipartKey key = new MultipartKey(dateStyle, timeStyle, safeLocale);
 
         String pattern = cDateTimeInstanceCache.get(key);
         if (pattern == null) {
             try {
                 DateFormat formatter;
                 if (dateStyle == null) {
-                    formatter = DateFormat.getTimeInstance(timeStyle.intValue(), locale);
+                    formatter = DateFormat.getTimeInstance(timeStyle.intValue(), safeLocale);
                 } else if (timeStyle == null) {
-                    formatter = DateFormat.getDateInstance(dateStyle.intValue(), locale);
+                    formatter = DateFormat.getDateInstance(dateStyle.intValue(), safeLocale);
                 } else {
-                    formatter = DateFormat.getDateTimeInstance(dateStyle.intValue(), timeStyle.intValue(), locale);
+                    formatter = DateFormat.getDateTimeInstance(dateStyle.intValue(), timeStyle.intValue(), safeLocale);
                 }
                 pattern = ((SimpleDateFormat) formatter).toPattern();
                 final String previous = cDateTimeInstanceCache.putIfAbsent(key, pattern);
@@ -210,13 +208,12 @@ abstract class FormatCache<F extends Format> {
                     pattern = previous;
                 }
             } catch (final ClassCastException ex) {
-                throw new IllegalArgumentException("No date time pattern for locale: " + locale);
+                throw new IllegalArgumentException("No date time pattern for locale: " + safeLocale);
             }
         }
         return pattern;
     }
 
-    // ----------------------------------------------------------------------
     /**
      * <p>Helper class to hold multi-part Map keys</p>
      */
diff --git a/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java b/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java
index 558b5f9..8a8513d 100644
--- a/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java
@@ -123,11 +123,30 @@ public class LocaleUtilsTest  {
     }
 
     /**
-     * Test toLocale() method.
+     * Test toLocale(Locale) method.
+     */
+    @Test
+    public void testToLocale_Locale_defaults() {
+        assertNull(LocaleUtils.toLocale((String) null));
+        assertEquals(Locale.getDefault(), LocaleUtils.toLocale((Locale) null));
+        assertEquals(Locale.getDefault(), LocaleUtils.toLocale(Locale.getDefault()));
+    }
+
+    /**
+     * Test toLocale(Locale) method.
+     */
+    @ParameterizedTest
+    @MethodSource("java.util.Locale#getAvailableLocales")
+    public void testToLocales(final Locale actualLocale) {
+        assertEquals(actualLocale, LocaleUtils.toLocale(actualLocale));
+    }
+
+    /**
+     * Test toLocale(String) method.
      */
     @Test
     public void testToLocale_1Part() {
-        assertNull(LocaleUtils.toLocale(null));
+        assertNull(LocaleUtils.toLocale((String) null));
 
         assertValidToLocale("us");
         assertValidToLocale("fr");
@@ -524,11 +543,11 @@ public class LocaleUtilsTest  {
 
     @ParameterizedTest
     @MethodSource("java.util.Locale#getAvailableLocales")
-    public void testParseAllLocales(final Locale l) {
+    public void testParseAllLocales(final Locale actualLocale) {
         // Check if it's possible to recreate the Locale using just the standard constructor
-        final Locale locale = new Locale(l.getLanguage(), l.getCountry(), l.getVariant());
-        if (l.equals(locale)) { // it is possible for LocaleUtils.toLocale to handle these Locales
-            final String str = l.toString();
+        final Locale locale = new Locale(actualLocale.getLanguage(), actualLocale.getCountry(), actualLocale.getVariant());
+        if (actualLocale.equals(locale)) { // it is possible for LocaleUtils.toLocale to handle these Locales
+            final String str = actualLocale.toString();
             // Look for the script/extension suffix
             int suff = str.indexOf("_#");
             if (suff == - 1) {
@@ -541,7 +560,7 @@ public class LocaleUtilsTest  {
                 localeStr = str.substring(0, suff);
             }
             final Locale loc = LocaleUtils.toLocale(localeStr);
-            assertEquals(l, loc);
+            assertEquals(actualLocale, loc);
         }
     }
 }