001// Generated by delombok at Sat Feb 25 17:58:34 UTC 2023 002package io.cui.tools.lang; 003 004import java.util.Locale; 005import io.cui.tools.string.MoreStrings; 006 007/** 008 * <p> 009 * Operations to assist when working with a {@link Locale}. 010 * </p> 011 * 012 * <p> 013 * This class tries to handle {@code null} input gracefully. 014 * An exception will not be thrown for a {@code null} input. 015 * Each method documents its behavior in more detail. 016 * </p> 017 * 018 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/LocaleUtils.java 019 */ 020public final class LocaleUtils { 021 private static final String INVALID_LOCALE_FORMAT = "Invalid locale format: "; 022 023 // owolff: Original code 024 /** 025 * <p> 026 * Converts a String to a Locale. 027 * </p> 028 * 029 * <p> 030 * This method takes the string format of a locale and creates the 031 * locale object from it. 032 * </p> 033 * 034 * <pre> 035 * LocaleUtils.toLocale("") = new Locale("", "") 036 * LocaleUtils.toLocale("en") = new Locale("en", "") 037 * LocaleUtils.toLocale("en_GB") = new Locale("en", "GB") 038 * LocaleUtils.toLocale("en_001") = new Locale("en", "001") 039 * LocaleUtils.toLocale("en_GB_xxx") = new Locale("en", "GB", "xxx") (#) 040 * </pre> 041 * 042 * <p> 043 * This method validates the input strictly. 044 * The language code must be lowercase. 045 * The country code must be uppercase. 046 * The separator must be an underscore. 047 * The length must be correct. 048 * </p> 049 * 050 * @param str the locale String to convert, null returns null 051 * @return a Locale, null if null input 052 * @throws IllegalArgumentException if the string is an invalid format 053 * @see Locale#forLanguageTag(String) 054 */ 055 @SuppressWarnings("squid:S3776") 056 public static Locale toLocale(final String str) { 057 if (str == null) { 058 return null; 059 } 060 if (str.isEmpty()) { 061 // LANG-941 - JDK 8 introduced an empty locale where all fields are 062 // blank 063 return new Locale(MoreStrings.EMPTY, MoreStrings.EMPTY); 064 } 065 if (str.contains("#")) { 066 // LANG-879 - Cannot handle Java 7 script & extensions 067 throw new IllegalArgumentException(INVALID_LOCALE_FORMAT + str); 068 } 069 final var len = str.length(); 070 if (len < 2) { 071 throw new IllegalArgumentException(INVALID_LOCALE_FORMAT + str); 072 } 073 final var ch0 = str.charAt(0); 074 if (ch0 == '_') { 075 if (len < 3) { 076 throw new IllegalArgumentException(INVALID_LOCALE_FORMAT + str); 077 } 078 final var ch1 = str.charAt(1); 079 final var ch2 = str.charAt(2); 080 if (!Character.isUpperCase(ch1) || !Character.isUpperCase(ch2)) { 081 throw new IllegalArgumentException(INVALID_LOCALE_FORMAT + str); 082 } 083 if (len == 3) { 084 return new Locale(MoreStrings.EMPTY, str.substring(1, 3)); 085 } 086 if (len < 5 || str.charAt(3) != '_') { 087 throw new IllegalArgumentException(INVALID_LOCALE_FORMAT + str); 088 } 089 return new Locale(MoreStrings.EMPTY, str.substring(1, 3), str.substring(4)); 090 } 091 return parseLocale(str); 092 } 093 094 /** 095 * Tries to parse a locale from the given String. 096 * 097 * @param str the String to parse a locale from. 098 * 099 * @return a Locale instance parsed from the given String. 100 * @throws IllegalArgumentException if the given String can not be parsed. 101 */ 102 private static Locale parseLocale(final String str) { 103 if (isISO639LanguageCode(str)) { 104 return new Locale(str); 105 } 106 final var segments = str.split("_", -1); 107 final var language = segments[0]; 108 if (segments.length == 2) { 109 final var country = segments[1]; 110 if (isISO639LanguageCode(language) && isISO3166CountryCode(country) || isNumericAreaCode(country)) { 111 return new Locale(language, country); 112 } 113 } else if (segments.length == 3) { 114 final var country = segments[1]; 115 final var variant = segments[2]; 116 if (isISO639LanguageCode(language) && (country.isEmpty() || isISO3166CountryCode(country) || isNumericAreaCode(country)) && !variant.isEmpty()) { 117 return new Locale(language, country, variant); 118 } 119 } 120 throw new IllegalArgumentException(INVALID_LOCALE_FORMAT + str); 121 } 122 123 /** 124 * Checks whether the given String is a ISO 639 compliant language code. 125 * 126 * @param str the String to check. 127 * 128 * @return true, if the given String is a ISO 639 compliant language code. 129 */ 130 private static boolean isISO639LanguageCode(final String str) { 131 return MoreStrings.isAllLowerCase(str) && (str.length() == 2 || str.length() == 3); 132 } 133 134 /** 135 * Checks whether the given String is a ISO 3166 alpha-2 country code. 136 * 137 * @param str the String to check 138 * 139 * @return true, is the given String is a ISO 3166 compliant country code. 140 */ 141 private static boolean isISO3166CountryCode(final String str) { 142 return MoreStrings.isAllUpperCase(str) && str.length() == 2; 143 } 144 145 /** 146 * Checks whether the given String is a UN M.49 numeric area code. 147 * 148 * @param str the String to check 149 * 150 * @return true, is the given String is a UN M.49 numeric area code. 151 */ 152 private static boolean isNumericAreaCode(final String str) { 153 return MoreStrings.isNumeric(str) && str.length() == 3; 154 } 155 156 @java.lang.SuppressWarnings("all") 157 @lombok.Generated 158 private LocaleUtils() { 159 throw new java.lang.UnsupportedOperationException("This is a utility class and cannot be instantiated"); 160 } 161}