diff --git a/src/main/java/org/apache/commons/lang3/LocaleUtils.java b/src/main/java/org/apache/commons/lang3/LocaleUtils.java index 39b81a49042..176a4372710 100644 --- a/src/main/java/org/apache/commons/lang3/LocaleUtils.java +++ b/src/main/java/org/apache/commons/lang3/LocaleUtils.java @@ -255,16 +255,17 @@ public static List localeLookupList(final Locale locale, final Locale de *

* * @param str the String to parse as a Locale. + * @param supportDash support strings with '-'. * @return a Locale parsed from the given String. * @throws IllegalArgumentException if the given String can not be parsed. * @see Locale */ - private static Locale parseLocale(final String str) { + private static Locale parseLocale(final String str, final boolean supportDash) { if (isISO639LanguageCode(str)) { return new Locale(str); } final int limit = 3; - final char separator = str.indexOf(UNDERSCORE) != -1 ? UNDERSCORE : DASH; + final char separator = (!supportDash || str.indexOf(UNDERSCORE) != -1) ? UNDERSCORE : DASH; final String[] segments = str.split(String.valueOf(separator), 3); final String language = segments[0]; if (segments.length == 2) { @@ -305,7 +306,6 @@ public static Locale toLocale(final Locale locale) { * LocaleUtils.toLocale("") = new Locale("", "") * LocaleUtils.toLocale("en") = new Locale("en", "") * LocaleUtils.toLocale("en_GB") = new Locale("en", "GB") - * LocaleUtils.toLocale("en-GB") = new Locale("en", "GB") * LocaleUtils.toLocale("en_001") = new Locale("en", "001") * LocaleUtils.toLocale("en_GB_xxx") = new Locale("en", "GB", "xxx") (#) * @@ -317,16 +317,58 @@ public static Locale toLocale(final Locale locale) { *

This method validates the input strictly. * The language code must be lowercase. * The country code must be uppercase. - * The separator must be an underscore or a dash. + * The separator must be an underscore. * The length must be correct. *

* * @param str the locale String to convert, null returns null * @return a Locale, null if null input * @throws IllegalArgumentException if the string is an invalid format - * @see Locale#forLanguageTag(String) + * @see Locale */ public static Locale toLocale(final String str) { + return toLocale(str, false); + } + + /** + * Converts a String to a Locale. + * + *

This method takes a string format of a locale or language tag + * if supportDash is true and creates the locale object from it.

+ * + *
+     *   LocaleUtils.toLocale("", false)          = new Locale("", "")
+     *   LocaleUtils.toLocale("", true)           = new Locale("", "")
+     *   LocaleUtils.toLocale("en", false)        = new Locale("en", "")
+     *   LocaleUtils.toLocale("en", true)         = new Locale("en", "")
+     *   LocaleUtils.toLocale("en_GB", false)     = new Locale("en", "GB")
+     *   LocaleUtils.toLocale("en_GB", true)      = new Locale("en", "GB")
+     *   LocaleUtils.toLocale("en-GB", false)     = throws IllegalArgumentException()
+     *   LocaleUtils.toLocale("en-GB", true)      = new Locale("en", "GB")
+     *   LocaleUtils.toLocale("en_001", false)    = new Locale("en", "001")
+     *   LocaleUtils.toLocale("en_001", true)     = new Locale("en", "001")
+     *   LocaleUtils.toLocale("en_GB_xxx", false) = new Locale("en", "GB", "xxx")   (#)
+     *   LocaleUtils.toLocale("en_GB_xxx", true)  = new Locale("en", "GB", "xxx")   (#)
+     * 
+ * + *

(#) The behavior of the JDK variant constructor changed between JDK1.3 and JDK1.4. + * In JDK1.3, the constructor upper cases the variant, in JDK1.4, it doesn't. + * Thus, the result from getVariant() may vary depending on your JDK.

+ * + *

This method validates the input strictly. + * The language code must be lowercase. + * The country code must be uppercase. + * The separator must be an underscore. + * The length must be correct. + *

+ * + * @param str the locale String to convert, null returns null + * @param supportDash support strings with '-'. + * @return a Locale, null if null input + * @throws IllegalArgumentException if the string is an invalid format + * @see Locale + */ + protected static Locale toLocale(final String str, final boolean supportDash) { if (str == null) { // TODO Should this return the default locale? return null; @@ -342,7 +384,7 @@ public static Locale toLocale(final String str) { throw new IllegalArgumentException("Invalid locale format: " + str); } final char ch0 = str.charAt(0); - if (ch0 == UNDERSCORE || ch0 == DASH) { + if (ch0 == UNDERSCORE || (supportDash && ch0 == DASH)) { if (len < 3) { throw new IllegalArgumentException("Invalid locale format: " + str); } @@ -363,7 +405,7 @@ public static Locale toLocale(final String str) { return new Locale(StringUtils.EMPTY, str.substring(1, 3), str.substring(4)); } - return parseLocale(str); + return parseLocale(str, supportDash); } /** diff --git a/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java b/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java index dc9e3969d0b..0b82ac62297 100644 --- a/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/LocaleUtilsTest.java @@ -157,7 +157,7 @@ private static void assertValidToLocale(final String language) { } /** - * Pass in a valid language, test toLocale. + * Pass in a valid locale, test toLocale. * * @param localeString to pass to toLocale() * @param language of the resulting Locale @@ -173,7 +173,23 @@ private static void assertValidToLocale(final String localeString, final String } /** - * Pass in a valid language, test toLocale. + * Pass in a valid language tag, test toLocale. + * + * @param localeString to pass to toLocale() + * @param language of the resulting Locale + * @param country of the resulting Locale + */ + private static void assertValidToLocaleIncludingDash(final String localeString, final String language, final String country) { + final Locale locale = LocaleUtils.toLocale(localeString, true); + assertNotNull(locale, "valid locale"); + assertEquals(language, locale.getLanguage()); + assertEquals(country, locale.getCountry()); + //variant is empty + assertTrue(StringUtils.isEmpty(locale.getVariant())); + } + + /** + * Pass in a valid locale, test toLocale. * * @param localeString to pass to toLocale() * @param language of the resulting Locale @@ -190,6 +206,23 @@ private static void assertValidToLocale( assertEquals(variant, locale.getVariant()); } + /** + * Pass in a valid language tag, test toLocale. + * + * @param localeString to pass to toLocale() + * @param language of the resulting Locale + * @param country of the resulting Locale + * @param variant of the resulting Locale + */ + private static void assertValidToLocaleIncludingDash( + final String localeString, final String language, + final String country, final String variant) { + final Locale locale = LocaleUtils.toLocale(localeString, true); + assertNotNull(locale, "valid locale"); + assertEquals(language, locale.getLanguage()); + assertEquals(country, locale.getCountry()); + assertEquals(variant, locale.getVariant()); + } @BeforeEach public void setUp() { // Testing #LANG-304. Must be called before availableLocaleSet is called. @@ -496,10 +529,14 @@ public void testToLocale_1Part() { @Test public void testToLocale_2Part() { assertValidToLocale("us_EN", "us", "EN"); - assertValidToLocale("us-EN", "us", "EN"); + assertValidToLocaleIncludingDash("us-EN", "us", "EN"); //valid though doesn't exist assertValidToLocale("us_ZH", "us", "ZH"); + assertThrows( + IllegalArgumentException.class, + () -> LocaleUtils.toLocale("us-EN"), + "Should fail dash not supported"); assertThrows( IllegalArgumentException.class, () -> LocaleUtils.toLocale("us_En"), @@ -528,7 +565,7 @@ public void testToLocale_2Part() { @Test public void testToLocale_3Part() { assertValidToLocale("us_EN_A", "us", "EN", "A"); - assertValidToLocale("us-EN-A", "us", "EN", "A"); + assertValidToLocaleIncludingDash("us-EN-A", "us", "EN", "A"); // this isn't pretty, but was caused by a jdk bug it seems // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4210525 if (SystemUtils.isJavaVersionAtLeast(JAVA_1_4)) { @@ -538,6 +575,7 @@ public void testToLocale_3Part() { assertValidToLocale("us_EN_a", "us", "EN", "A"); assertValidToLocale("us_EN_SFsafdFDsdfF", "us", "EN", "SFSAFDFDSDFF"); } + assertThrows(IllegalArgumentException.class, () -> LocaleUtils.toLocale("us-EN-A"), "Should fail as dash is not supported"); assertThrows(IllegalArgumentException.class, () -> LocaleUtils.toLocale("us_EN-a"), "Should fail as no consistent delimiter"); assertThrows(IllegalArgumentException.class, () -> LocaleUtils.toLocale("uu_UU_"), "Must be 3, 5 or 7+ in length"); // LANG-1741