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