1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package libcore.icu; 18 19 import java.util.LinkedHashSet; 20 import java.util.Locale; 21 import libcore.util.BasicLruCache; 22 23 /** 24 * Makes ICU data accessible to Java. 25 */ 26 public final class ICU { 27 private static final BasicLruCache<String, String> CACHED_PATTERNS = 28 new BasicLruCache<String, String>(8); 29 30 private static Locale[] availableLocalesCache; 31 32 private static String[] isoCountries; 33 34 private static String[] isoLanguages; 35 36 /** 37 * Returns an array of two-letter ISO 639-1 language codes, either from ICU or our cache. 38 */ 39 public static String[] getISOLanguages() { 40 if (isoLanguages == null) { 41 isoLanguages = getISOLanguagesNative(); 42 } 43 return isoLanguages.clone(); 44 } 45 46 /** 47 * Returns an array of two-letter ISO 3166 country codes, either from ICU or our cache. 48 */ 49 public static String[] getISOCountries() { 50 if (isoCountries == null) { 51 isoCountries = getISOCountriesNative(); 52 } 53 return isoCountries.clone(); 54 } 55 56 /** 57 * Returns the appropriate {@code Locale} given a {@code String} of the form returned 58 * by {@code toString}. This is very lenient, and doesn't care what's between the underscores: 59 * this method can parse strings that {@code Locale.toString} won't produce. 60 * Used to remove duplication. 61 */ 62 public static Locale localeFromString(String localeName) { 63 int first = localeName.indexOf('_'); 64 int second = localeName.indexOf('_', first + 1); 65 if (first == -1) { 66 // Language only ("ja"). 67 return new Locale(localeName); 68 } else if (second == -1) { 69 // Language and country ("ja_JP"). 70 String language = localeName.substring(0, first); 71 String country = localeName.substring(first + 1); 72 return new Locale(language, country); 73 } else { 74 // Language and country and variant ("ja_JP_TRADITIONAL"). 75 String language = localeName.substring(0, first); 76 String country = localeName.substring(first + 1, second); 77 String variant = localeName.substring(second + 1); 78 return new Locale(language, country, variant); 79 } 80 } 81 82 public static Locale[] localesFromStrings(String[] localeNames) { 83 // We need to remove duplicates caused by the conversion of "he" to "iw", et cetera. 84 // Java needs the obsolete code, ICU needs the modern code, but we let ICU know about 85 // both so that we never need to convert back when talking to it. 86 LinkedHashSet<Locale> set = new LinkedHashSet<Locale>(); 87 for (String localeName : localeNames) { 88 set.add(localeFromString(localeName)); 89 } 90 return set.toArray(new Locale[set.size()]); 91 } 92 93 public static Locale[] getAvailableLocales() { 94 if (availableLocalesCache == null) { 95 availableLocalesCache = localesFromStrings(getAvailableLocalesNative()); 96 } 97 return availableLocalesCache.clone(); 98 } 99 100 public static Locale[] getAvailableBreakIteratorLocales() { 101 return localesFromStrings(getAvailableBreakIteratorLocalesNative()); 102 } 103 104 public static Locale[] getAvailableCalendarLocales() { 105 return localesFromStrings(getAvailableCalendarLocalesNative()); 106 } 107 108 public static Locale[] getAvailableCollatorLocales() { 109 return localesFromStrings(getAvailableCollatorLocalesNative()); 110 } 111 112 public static Locale[] getAvailableDateFormatLocales() { 113 return localesFromStrings(getAvailableDateFormatLocalesNative()); 114 } 115 116 public static Locale[] getAvailableDateFormatSymbolsLocales() { 117 return getAvailableDateFormatLocales(); 118 } 119 120 public static Locale[] getAvailableDecimalFormatSymbolsLocales() { 121 return getAvailableNumberFormatLocales(); 122 } 123 124 public static Locale[] getAvailableNumberFormatLocales() { 125 return localesFromStrings(getAvailableNumberFormatLocalesNative()); 126 } 127 128 public static String getBestDateTimePattern(String skeleton, String localeName) { 129 String key = skeleton + "\t" + localeName; 130 synchronized (CACHED_PATTERNS) { 131 String pattern = CACHED_PATTERNS.get(key); 132 if (pattern == null) { 133 pattern = getBestDateTimePatternNative(skeleton, localeName); 134 CACHED_PATTERNS.put(key, pattern); 135 } 136 return pattern; 137 } 138 } 139 140 private static native String getBestDateTimePatternNative(String skeleton, String localeName); 141 142 public static char[] getDateFormatOrder(String pattern) { 143 char[] result = new char[3]; 144 int resultIndex = 0; 145 boolean sawDay = false; 146 boolean sawMonth = false; 147 boolean sawYear = false; 148 149 for (int i = 0; i < pattern.length(); ++i) { 150 char ch = pattern.charAt(i); 151 if (ch == 'd' || ch == 'L' || ch == 'M' || ch == 'y') { 152 if (ch == 'd' && !sawDay) { 153 result[resultIndex++] = 'd'; 154 sawDay = true; 155 } else if ((ch == 'L' || ch == 'M') && !sawMonth) { 156 result[resultIndex++] = 'M'; 157 sawMonth = true; 158 } else if ((ch == 'y') && !sawYear) { 159 result[resultIndex++] = 'y'; 160 sawYear = true; 161 } 162 } else if (ch == 'G') { 163 // Ignore the era specifier, if present. 164 } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { 165 throw new IllegalArgumentException("Bad pattern character '" + ch + "' in " + pattern); 166 } else if (ch == '\'') { 167 if (i < pattern.length() - 1 && pattern.charAt(i + 1) == '\'') { 168 ++i; 169 } else { 170 i = pattern.indexOf('\'', i + 1); 171 if (i == -1) { 172 throw new IllegalArgumentException("Bad quoting in " + pattern); 173 } 174 ++i; 175 } 176 } else { 177 // Ignore spaces and punctuation. 178 } 179 } 180 return result; 181 } 182 183 /** 184 * Returns the version of the CLDR data in use, such as "22.1.1". 185 */ 186 public static native String getCldrVersion(); 187 188 /** 189 * Returns the icu4c version in use, such as "50.1.1". 190 */ 191 public static native String getIcuVersion(); 192 193 /** 194 * Returns the Unicode version our ICU supports, such as "6.2". 195 */ 196 public static native String getUnicodeVersion(); 197 198 // --- Case mapping. 199 200 public static native String toLowerCase(String s, String localeName); 201 public static native String toUpperCase(String s, String localeName); 202 203 // --- Errors. 204 205 // Just the subset of error codes needed by CharsetDecoderICU/CharsetEncoderICU. 206 public static final int U_ZERO_ERROR = 0; 207 public static final int U_INVALID_CHAR_FOUND = 10; 208 public static final int U_TRUNCATED_CHAR_FOUND = 11; 209 public static final int U_ILLEGAL_CHAR_FOUND = 12; 210 public static final int U_BUFFER_OVERFLOW_ERROR = 15; 211 212 public static boolean U_FAILURE(int error) { 213 return error > U_ZERO_ERROR; 214 } 215 216 // --- Native methods accessing ICU's database. 217 218 private static native String[] getAvailableBreakIteratorLocalesNative(); 219 private static native String[] getAvailableCalendarLocalesNative(); 220 private static native String[] getAvailableCollatorLocalesNative(); 221 private static native String[] getAvailableDateFormatLocalesNative(); 222 private static native String[] getAvailableLocalesNative(); 223 private static native String[] getAvailableNumberFormatLocalesNative(); 224 225 public static native String[] getAvailableCurrencyCodes(); 226 public static native String getCurrencyCode(String countryCode); 227 public static native String getCurrencyDisplayName(String locale, String currencyCode); 228 public static native int getCurrencyFractionDigits(String currencyCode); 229 public static native String getCurrencySymbol(String locale, String currencyCode); 230 231 public static native String getDisplayCountryNative(String countryCode, String locale); 232 public static native String getDisplayLanguageNative(String languageCode, String locale); 233 public static native String getDisplayVariantNative(String variantCode, String locale); 234 235 public static native String getISO3CountryNative(String locale); 236 public static native String getISO3LanguageNative(String locale); 237 238 public static native String addLikelySubtags(String locale); 239 public static native String getScript(String locale); 240 241 private static native String[] getISOLanguagesNative(); 242 private static native String[] getISOCountriesNative(); 243 244 static native boolean initLocaleDataNative(String locale, LocaleData result); 245 } 246