ICU.java revision 2496a680ade790744df6815bbb6ed144a7028b87
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
17package libcore.icu;
18
19import java.util.LinkedHashSet;
20import java.util.Locale;
21import libcore.util.BasicLruCache;
22
23/**
24 * Makes ICU data accessible to Java.
25 */
26public 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