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