1 /* 2 * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package sun.util.locale.provider; 26 27 import java.lang.ref.SoftReference; 28 import java.text.DateFormat; 29 import java.text.DateFormatSymbols; 30 import java.text.DecimalFormat; 31 import java.text.DecimalFormatSymbols; 32 import java.text.NumberFormat; 33 import java.text.SimpleDateFormat; 34 import java.text.spi.DateFormatProvider; 35 import java.text.spi.DateFormatSymbolsProvider; 36 import java.text.spi.DecimalFormatSymbolsProvider; 37 import java.text.spi.NumberFormatProvider; 38 import java.util.Calendar; 39 import java.util.Collections; 40 import java.util.Currency; 41 import java.util.HashMap; 42 import java.util.HashSet; 43 import java.util.Locale; 44 import java.util.Map; 45 import java.util.ResourceBundle.Control; 46 import java.util.Set; 47 import java.util.TimeZone; 48 import java.util.concurrent.ConcurrentHashMap; 49 import java.util.concurrent.ConcurrentMap; 50 import java.util.concurrent.atomic.AtomicReferenceArray; 51 import java.util.spi.CalendarDataProvider; 52 import java.util.spi.CalendarNameProvider; 53 import java.util.spi.CurrencyNameProvider; 54 import java.util.spi.LocaleNameProvider; 55 import sun.util.spi.CalendarProvider; 56 57 /** 58 * LocaleProviderdapter implementation for the Windows locale data. 59 * 60 * @author Naoto Sato 61 */ 62 public class HostLocaleProviderAdapterImpl { 63 64 // locale categories 65 private static final int CAT_DISPLAY = 0; 66 private static final int CAT_FORMAT = 1; 67 68 // NumberFormat styles 69 private static final int NF_NUMBER = 0; 70 private static final int NF_CURRENCY = 1; 71 private static final int NF_PERCENT = 2; 72 private static final int NF_INTEGER = 3; 73 private static final int NF_MAX = NF_INTEGER; 74 75 // CalendarData value types 76 private static final int CD_FIRSTDAYOFWEEK = 0; 77 private static final int CD_MINIMALDAYSINFIRSTWEEK = 1; 78 79 // Currency/Locale display name types 80 private static final int DN_CURRENCY_NAME = 0; 81 private static final int DN_CURRENCY_SYMBOL = 1; 82 private static final int DN_LOCALE_LANGUAGE = 2; 83 private static final int DN_LOCALE_SCRIPT = 3; 84 private static final int DN_LOCALE_REGION = 4; 85 private static final int DN_LOCALE_VARIANT = 5; 86 87 // Windows Calendar IDs 88 private static final int CAL_JAPAN = 3; 89 90 // Native Calendar ID to LDML calendar type map 91 private static final String[] calIDToLDML = { 92 "", 93 "gregory", 94 "gregory_en-US", 95 "japanese", 96 "roc", 97 "", // No appropriate type for CAL_KOREA 98 "islamic", 99 "buddhist", 100 "hebrew", 101 "gregory_fr", 102 "gregory_ar", 103 "gregory_en", 104 "gregory_fr", "", "", "", "", "", "", "", "", "", "", 105 "islamic-umalqura", 106 }; 107 108 // Caches 109 private static final ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> dateFormatCache = new ConcurrentHashMap<>(); 110 private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> dateFormatSymbolsCache = new ConcurrentHashMap<>(); 111 private static final ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> numberFormatCache = new ConcurrentHashMap<>(); 112 private static final ConcurrentMap<Locale, SoftReference<DecimalFormatSymbols>> decimalFormatSymbolsCache = new ConcurrentHashMap<>(); 113 114 private static final Set<Locale> supportedLocaleSet; 115 private static final String nativeDisplayLanguage; 116 static { 117 Set<Locale> tmpSet = new HashSet<>(); 118 if (initialize()) { 119 // Assuming the default locales do not include any extensions, so 120 // no stripping is needed here. 121 Control c = Control.getNoFallbackControl(Control.FORMAT_DEFAULT); 122 String displayLocale = getDefaultLocale(CAT_DISPLAY); 123 Locale l = Locale.forLanguageTag(displayLocale.replace('_', '-')); 124 tmpSet.addAll(c.getCandidateLocales("", l)); 125 nativeDisplayLanguage = l.getLanguage(); 126 127 String formatLocale = getDefaultLocale(CAT_FORMAT); 128 if (!formatLocale.equals(displayLocale)) { 129 l = Locale.forLanguageTag(formatLocale.replace('_', '-')); 130 tmpSet.addAll(c.getCandidateLocales("", l)); 131 } 132 } else { 133 nativeDisplayLanguage = ""; 134 } 135 supportedLocaleSet = Collections.unmodifiableSet(tmpSet); 136 } 137 private static final Locale[] supportedLocale = supportedLocaleSet.toArray(new Locale[0]); 138 139 public static DateFormatProvider getDateFormatProvider() { 140 return new DateFormatProvider() { 141 @Override 142 public Locale[] getAvailableLocales() { 143 return getSupportedCalendarLocales(); 144 } 145 146 @Override 147 public boolean isSupportedLocale(Locale locale) { 148 return isSupportedCalendarLocale(locale); 149 } 150 151 @Override 152 public DateFormat getDateInstance(int style, Locale locale) { 153 AtomicReferenceArray<String> patterns = getDateTimePatterns(locale); 154 return new SimpleDateFormat(patterns.get(style/2), 155 getCalendarLocale(locale)); 156 } 157 158 @Override 159 public DateFormat getTimeInstance(int style, Locale locale) { 160 AtomicReferenceArray<String> patterns = getDateTimePatterns(locale); 161 return new SimpleDateFormat(patterns.get(style/2+2), 162 getCalendarLocale(locale)); 163 } 164 165 @Override 166 public DateFormat getDateTimeInstance(int dateStyle, 167 int timeStyle, Locale locale) { 168 AtomicReferenceArray<String> patterns = getDateTimePatterns(locale); 169 String pattern = new StringBuilder(patterns.get(dateStyle/2)) 170 .append(" ") 171 .append(patterns.get(timeStyle/2+2)) 172 .toString(); 173 return new SimpleDateFormat(pattern, getCalendarLocale(locale)); 174 } 175 176 private AtomicReferenceArray<String> getDateTimePatterns(Locale locale) { 177 AtomicReferenceArray<String> patterns; 178 SoftReference<AtomicReferenceArray<String>> ref = dateFormatCache.get(locale); 179 180 if (ref == null || (patterns = ref.get()) == null) { 181 String langtag = removeExtensions(locale).toLanguageTag(); 182 patterns = new AtomicReferenceArray<>(4); 183 patterns.compareAndSet(0, null, convertDateTimePattern( 184 getDateTimePattern(DateFormat.LONG, -1, langtag))); 185 patterns.compareAndSet(1, null, convertDateTimePattern( 186 getDateTimePattern(DateFormat.SHORT, -1, langtag))); 187 patterns.compareAndSet(2, null, convertDateTimePattern( 188 getDateTimePattern(-1, DateFormat.LONG, langtag))); 189 patterns.compareAndSet(3, null, convertDateTimePattern( 190 getDateTimePattern(-1, DateFormat.SHORT, langtag))); 191 ref = new SoftReference<>(patterns); 192 dateFormatCache.put(locale, ref); 193 } 194 195 return patterns; 196 } 197 }; 198 } 199 200 public static DateFormatSymbolsProvider getDateFormatSymbolsProvider() { 201 return new DateFormatSymbolsProvider() { 202 203 @Override 204 public Locale[] getAvailableLocales() { 205 return getSupportedCalendarLocales(); 206 } 207 208 @Override 209 public boolean isSupportedLocale(Locale locale) { 210 return isSupportedCalendarLocale(locale); 211 } 212 213 @Override 214 public DateFormatSymbols getInstance(Locale locale) { 215 DateFormatSymbols dfs; 216 SoftReference<DateFormatSymbols> ref = 217 dateFormatSymbolsCache.get(locale); 218 219 if (ref == null || (dfs = ref.get()) == null) { 220 dfs = new DateFormatSymbols(locale); 221 String langTag = removeExtensions(locale).toLanguageTag(); 222 223 dfs.setAmPmStrings(getAmPmStrings(langTag, dfs.getAmPmStrings())); 224 dfs.setEras(getEras(langTag, dfs.getEras())); 225 dfs.setMonths(getMonths(langTag, dfs.getMonths())); 226 dfs.setShortMonths(getShortMonths(langTag, dfs.getShortMonths())); 227 dfs.setWeekdays(getWeekdays(langTag, dfs.getWeekdays())); 228 dfs.setShortWeekdays(getShortWeekdays(langTag, dfs.getShortWeekdays())); 229 ref = new SoftReference<>(dfs); 230 dateFormatSymbolsCache.put(locale, ref); 231 } 232 return (DateFormatSymbols)dfs.clone(); 233 } 234 }; 235 } 236 237 public static NumberFormatProvider getNumberFormatProvider() { 238 return new NumberFormatProvider() { 239 240 @Override 241 public Locale[] getAvailableLocales() { 242 return getSupportedNativeDigitLocales(); 243 } 244 245 @Override 246 public boolean isSupportedLocale(Locale locale) { 247 return isSupportedNativeDigitLocale(locale); 248 } 249 250 @Override 251 public NumberFormat getCurrencyInstance(Locale locale) { 252 AtomicReferenceArray<String> patterns = getNumberPatterns(locale); 253 return new DecimalFormat(patterns.get(NF_CURRENCY), 254 DecimalFormatSymbols.getInstance(locale)); 255 } 256 257 @Override 258 public NumberFormat getIntegerInstance(Locale locale) { 259 AtomicReferenceArray<String> patterns = getNumberPatterns(locale); 260 return new DecimalFormat(patterns.get(NF_INTEGER), 261 DecimalFormatSymbols.getInstance(locale)); 262 } 263 264 @Override 265 public NumberFormat getNumberInstance(Locale locale) { 266 AtomicReferenceArray<String> patterns = getNumberPatterns(locale); 267 return new DecimalFormat(patterns.get(NF_NUMBER), 268 DecimalFormatSymbols.getInstance(locale)); 269 } 270 271 @Override 272 public NumberFormat getPercentInstance(Locale locale) { 273 AtomicReferenceArray<String> patterns = getNumberPatterns(locale); 274 return new DecimalFormat(patterns.get(NF_PERCENT), 275 DecimalFormatSymbols.getInstance(locale)); 276 } 277 278 private AtomicReferenceArray<String> getNumberPatterns(Locale locale) { 279 AtomicReferenceArray<String> patterns; 280 SoftReference<AtomicReferenceArray<String>> ref = numberFormatCache.get(locale); 281 282 if (ref == null || (patterns = ref.get()) == null) { 283 String langtag = locale.toLanguageTag(); 284 patterns = new AtomicReferenceArray<>(NF_MAX+1); 285 for (int i = 0; i <= NF_MAX; i++) { 286 patterns.compareAndSet(i, null, getNumberPattern(i, langtag)); 287 } 288 ref = new SoftReference<>(patterns); 289 numberFormatCache.put(locale, ref); 290 } 291 return patterns; 292 } 293 }; 294 } 295 296 public static DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() { 297 return new DecimalFormatSymbolsProvider() { 298 299 @Override 300 public Locale[] getAvailableLocales() { 301 return getSupportedNativeDigitLocales(); 302 } 303 304 @Override 305 public boolean isSupportedLocale(Locale locale) { 306 return isSupportedNativeDigitLocale(locale); 307 } 308 309 @Override 310 public DecimalFormatSymbols getInstance(Locale locale) { 311 DecimalFormatSymbols dfs; 312 SoftReference<DecimalFormatSymbols> ref = 313 decimalFormatSymbolsCache.get(locale); 314 315 if (ref == null || (dfs = ref.get()) == null) { 316 dfs = new DecimalFormatSymbols(getNumberLocale(locale)); 317 String langTag = removeExtensions(locale).toLanguageTag(); 318 319 // DecimalFormatSymbols.setInternationalCurrencySymbol() has 320 // a side effect of setting the currency symbol as well. So 321 // the calling order is relevant here. 322 dfs.setInternationalCurrencySymbol(getInternationalCurrencySymbol(langTag, dfs.getInternationalCurrencySymbol())); 323 dfs.setCurrencySymbol(getCurrencySymbol(langTag, dfs.getCurrencySymbol())); 324 dfs.setDecimalSeparator(getDecimalSeparator(langTag, dfs.getDecimalSeparator())); 325 dfs.setGroupingSeparator(getGroupingSeparator(langTag, dfs.getGroupingSeparator())); 326 dfs.setInfinity(getInfinity(langTag, dfs.getInfinity())); 327 dfs.setMinusSign(getMinusSign(langTag, dfs.getMinusSign())); 328 dfs.setMonetaryDecimalSeparator(getMonetaryDecimalSeparator(langTag, dfs.getMonetaryDecimalSeparator())); 329 dfs.setNaN(getNaN(langTag, dfs.getNaN())); 330 dfs.setPercent(getPercent(langTag, dfs.getPercent())); 331 dfs.setPerMill(getPerMill(langTag, dfs.getPerMill())); 332 dfs.setZeroDigit(getZeroDigit(langTag, dfs.getZeroDigit())); 333 ref = new SoftReference<>(dfs); 334 decimalFormatSymbolsCache.put(locale, ref); 335 } 336 return (DecimalFormatSymbols)dfs.clone(); 337 } 338 }; 339 } 340 341 public static CalendarDataProvider getCalendarDataProvider() { 342 return new CalendarDataProvider() { 343 @Override 344 public Locale[] getAvailableLocales() { 345 return getSupportedCalendarLocales(); 346 } 347 348 @Override 349 public boolean isSupportedLocale(Locale locale) { 350 return isSupportedCalendarLocale(locale); 351 } 352 353 @Override 354 public int getFirstDayOfWeek(Locale locale) { 355 int first = getCalendarDataValue( 356 removeExtensions(locale).toLanguageTag(), 357 CD_FIRSTDAYOFWEEK); 358 if (first != -1) { 359 return (first + 1) % 7 + 1; 360 } else { 361 return 0; 362 } 363 } 364 365 @Override 366 public int getMinimalDaysInFirstWeek(Locale locale) { 367 return 0; 368 } 369 }; 370 } 371 372 public static CalendarNameProvider getCalendarNameProvider() { 373 return new CalendarNameProvider() { 374 @Override 375 public Locale[] getAvailableLocales() { 376 return getSupportedCalendarLocales(); 377 } 378 379 @Override 380 public boolean isSupportedLocale(Locale locale) { 381 return isSupportedCalendarLocale(locale); 382 } 383 384 @Override 385 public String getDisplayName(String calendarType, int field, 386 int value, int style, Locale locale) { 387 String[] names = getCalendarDisplayStrings(removeExtensions(locale).toLanguageTag(), 388 getCalendarIDFromLDMLType(calendarType), field, style); 389 if (names != null && value >= 0 && value < names.length) { 390 return names[value]; 391 } else { 392 return null; 393 } 394 } 395 396 @Override 397 public Map<String, Integer> getDisplayNames(String calendarType, 398 int field, int style, Locale locale) { 399 Map<String, Integer> map = null; 400 String[] names = getCalendarDisplayStrings(removeExtensions(locale).toLanguageTag(), 401 getCalendarIDFromLDMLType(calendarType), field, style); 402 if (names != null) { 403 map = new HashMap<>(); 404 for (int value = 0; value < names.length; value++) { 405 if (names[value] != null) { 406 map.put(names[value], value); 407 } 408 } 409 map = map.isEmpty() ? null : map; 410 } 411 return map; 412 } 413 }; 414 } 415 416 public static CalendarProvider getCalendarProvider() { 417 return new CalendarProvider() { 418 @Override 419 public Locale[] getAvailableLocales() { 420 return getSupportedCalendarLocales(); 421 } 422 423 @Override 424 public boolean isSupportedLocale(Locale locale) { 425 return isSupportedCalendarLocale(locale); 426 } 427 428 @Override 429 public Calendar getInstance(TimeZone zone, Locale locale) { 430 return new Calendar.Builder() 431 .setLocale(getCalendarLocale(locale)) 432 .setTimeZone(zone) 433 .setInstant(System.currentTimeMillis()) 434 .build(); 435 } 436 }; 437 } 438 439 public static CurrencyNameProvider getCurrencyNameProvider() { 440 return new CurrencyNameProvider() { 441 @Override 442 public Locale[] getAvailableLocales() { 443 return supportedLocale; 444 } 445 446 @Override 447 public boolean isSupportedLocale(Locale locale) { 448 // Ignore the extensions for now 449 return supportedLocaleSet.contains(locale.stripExtensions()) && 450 locale.getLanguage().equals(nativeDisplayLanguage); 451 } 452 453 @Override 454 public String getSymbol(String currencyCode, Locale locale) { 455 // Retrieves the currency symbol by calling 456 // GetLocaleInfoEx(LOCALE_SCURRENCY). 457 // It only works with the "locale"'s currency in its native 458 // language. 459 try { 460 if (Currency.getInstance(locale).getCurrencyCode() 461 .equals(currencyCode)) { 462 return getDisplayString(locale.toLanguageTag(), 463 DN_CURRENCY_SYMBOL, currencyCode); 464 } 465 } catch (IllegalArgumentException iae) {} 466 return null; 467 } 468 469 @Override 470 public String getDisplayName(String currencyCode, Locale locale) { 471 // Retrieves the display name by calling 472 // GetLocaleInfoEx(LOCALE_SNATIVECURRNAME). 473 // It only works with the "locale"'s currency in its native 474 // language. 475 try { 476 if (Currency.getInstance(locale).getCurrencyCode() 477 .equals(currencyCode)) { 478 return getDisplayString(locale.toLanguageTag(), 479 DN_CURRENCY_NAME, currencyCode); 480 } 481 } catch (IllegalArgumentException iae) {} 482 return null; 483 } 484 }; 485 } 486 487 public static LocaleNameProvider getLocaleNameProvider() { 488 return new LocaleNameProvider() { 489 @Override 490 public Locale[] getAvailableLocales() { 491 return supportedLocale; 492 } 493 494 @Override 495 public boolean isSupportedLocale(Locale locale) { 496 return supportedLocaleSet.contains(locale.stripExtensions()) && 497 locale.getLanguage().equals(nativeDisplayLanguage); 498 } 499 500 @Override 501 public String getDisplayLanguage(String languageCode, Locale locale) { 502 // Retrieves the display language name by calling 503 // GetLocaleInfoEx(LOCALE_SLOCALIZEDLANGUAGENAME). 504 return getDisplayString(locale.toLanguageTag(), 505 DN_LOCALE_LANGUAGE, languageCode); 506 } 507 508 @Override 509 public String getDisplayCountry(String countryCode, Locale locale) { 510 // Retrieves the display country name by calling 511 // GetLocaleInfoEx(LOCALE_SLOCALIZEDCOUNTRYNAME). 512 return getDisplayString(locale.toLanguageTag(), 513 DN_LOCALE_REGION, nativeDisplayLanguage+"-"+countryCode); 514 } 515 516 @Override 517 public String getDisplayScript(String scriptCode, Locale locale) { 518 return null; 519 } 520 521 @Override 522 public String getDisplayVariant(String variantCode, Locale locale) { 523 return null; 524 } 525 }; 526 } 527 528 529 private static String convertDateTimePattern(String winPattern) { 530 String ret = winPattern.replaceAll("dddd", "EEEE"); 531 ret = ret.replaceAll("ddd", "EEE"); 532 ret = ret.replaceAll("tt", "aa"); 533 ret = ret.replaceAll("g", "GG"); 534 return ret; 535 } 536 537 private static Locale[] getSupportedCalendarLocales() { 538 if (supportedLocale.length != 0 && 539 supportedLocaleSet.contains(Locale.JAPAN) && 540 isJapaneseCalendar()) { 541 Locale[] sup = new Locale[supportedLocale.length+1]; 542 sup[0] = JRELocaleConstants.JA_JP_JP; 543 System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length); 544 return sup; 545 } 546 return supportedLocale; 547 } 548 549 private static boolean isSupportedCalendarLocale(Locale locale) { 550 Locale base = stripVariantAndExtensions(locale); 551 552 if (!supportedLocaleSet.contains(base)) { 553 return false; 554 } 555 556 int calid = getCalendarID(base.toLanguageTag()); 557 if (calid <= 0 || calid >= calIDToLDML.length) { 558 return false; 559 } 560 561 String requestedCalType = locale.getUnicodeLocaleType("ca"); 562 String nativeCalType = calIDToLDML[calid] 563 .replaceFirst("_.*", ""); // remove locale part. 564 565 if (requestedCalType == null) { 566 return Calendar.getAvailableCalendarTypes().contains(nativeCalType); 567 } else { 568 return requestedCalType.equals(nativeCalType); 569 } 570 } 571 572 private static Locale[] getSupportedNativeDigitLocales() { 573 if (supportedLocale.length != 0 && 574 supportedLocaleSet.contains(JRELocaleConstants.TH_TH) && 575 isNativeDigit("th-TH")) { 576 Locale[] sup = new Locale[supportedLocale.length+1]; 577 sup[0] = JRELocaleConstants.TH_TH_TH; 578 System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length); 579 return sup; 580 } 581 return supportedLocale; 582 } 583 584 private static boolean isSupportedNativeDigitLocale(Locale locale) { 585 // special case for th_TH_TH 586 if (JRELocaleConstants.TH_TH_TH.equals(locale)) { 587 return isNativeDigit("th-TH"); 588 } 589 590 String numtype = null; 591 Locale base = locale; 592 if (locale.hasExtensions()) { 593 numtype = locale.getUnicodeLocaleType("nu"); 594 base = locale.stripExtensions(); 595 } 596 597 if (supportedLocaleSet.contains(base)) { 598 // Only supports Latin or Thai (in thai locales) digits. 599 if (numtype == null || numtype.equals("latn")) { 600 return true; 601 } else if (locale.getLanguage().equals("th")) { 602 return "thai".equals(numtype) && 603 isNativeDigit(locale.toLanguageTag()); 604 } 605 } 606 607 return false; 608 } 609 610 private static Locale removeExtensions(Locale src) { 611 return new Locale.Builder().setLocale(src).clearExtensions().build(); 612 } 613 614 private static boolean isJapaneseCalendar() { 615 return getCalendarID("ja-JP") == CAL_JAPAN; 616 } 617 618 private static Locale stripVariantAndExtensions(Locale locale) { 619 if (locale.hasExtensions() || locale.getVariant() != "") { 620 // strip off extensions and variant. 621 locale = new Locale.Builder() 622 .setLocale(locale) 623 .clearExtensions() 624 .build(); 625 } 626 627 return locale; 628 } 629 630 private static Locale getCalendarLocale(Locale locale) { 631 int calid = getCalendarID(stripVariantAndExtensions(locale).toLanguageTag()); 632 if (calid > 0 && calid < calIDToLDML.length) { 633 Locale.Builder lb = new Locale.Builder(); 634 String[] caltype = calIDToLDML[calid].split("_"); 635 if (caltype.length > 1) { 636 lb.setLocale(Locale.forLanguageTag(caltype[1])); 637 } else { 638 lb.setLocale(locale); 639 } 640 lb.setUnicodeLocaleKeyword("ca", caltype[0]); 641 return lb.build(); 642 } 643 644 return locale; 645 } 646 647 private static int getCalendarIDFromLDMLType(String ldmlType) { 648 for (int i = 0; i < calIDToLDML.length; i++) { 649 if (calIDToLDML[i].startsWith(ldmlType)) { 650 return i; 651 } 652 } 653 return -1; 654 } 655 656 private static Locale getNumberLocale(Locale src) { 657 if (JRELocaleConstants.TH_TH.equals(src)) { 658 if (isNativeDigit("th-TH")) { 659 Locale.Builder lb = new Locale.Builder().setLocale(src); 660 lb.setUnicodeLocaleKeyword("nu", "thai"); 661 return lb.build(); 662 } 663 } 664 665 return src; 666 } 667 668 // native methods 669 670 // initialize 671 private static native boolean initialize(); 672 private static native String getDefaultLocale(int cat); 673 674 // For DateFormatProvider 675 private static native String getDateTimePattern(int dateStyle, int timeStyle, String langTag); 676 private static native int getCalendarID(String langTag); 677 678 // For DateFormatSymbolsProvider 679 private static native String[] getAmPmStrings(String langTag, String[] ampm); 680 private static native String[] getEras(String langTag, String[] eras); 681 private static native String[] getMonths(String langTag, String[] months); 682 private static native String[] getShortMonths(String langTag, String[] smonths); 683 private static native String[] getWeekdays(String langTag, String[] wdays); 684 private static native String[] getShortWeekdays(String langTag, String[] swdays); 685 686 // For NumberFormatProvider 687 private static native String getNumberPattern(int numberStyle, String langTag); 688 private static native boolean isNativeDigit(String langTag); 689 690 // For DecimalFormatSymbolsProvider 691 private static native String getCurrencySymbol(String langTag, String currencySymbol); 692 private static native char getDecimalSeparator(String langTag, char decimalSeparator); 693 private static native char getGroupingSeparator(String langTag, char groupingSeparator); 694 private static native String getInfinity(String langTag, String infinity); 695 private static native String getInternationalCurrencySymbol(String langTag, String internationalCurrencySymbol); 696 private static native char getMinusSign(String langTag, char minusSign); 697 private static native char getMonetaryDecimalSeparator(String langTag, char monetaryDecimalSeparator); 698 private static native String getNaN(String langTag, String nan); 699 private static native char getPercent(String langTag, char percent); 700 private static native char getPerMill(String langTag, char perMill); 701 private static native char getZeroDigit(String langTag, char zeroDigit); 702 703 // For CalendarDataProvider 704 private static native int getCalendarDataValue(String langTag, int type); 705 706 // For CalendarNameProvider 707 private static native String[] getCalendarDisplayStrings(String langTag, int calid, int field, int style); 708 709 // For Locale/CurrencyNameProvider 710 private static native String getDisplayString(String langTag, int key, String value); 711 }