1 /* 2 * Copyright (c) 2012, 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 26 package sun.util.locale.provider; 27 28 import java.lang.ref.SoftReference; 29 import java.text.*; 30 import java.text.spi.DateFormatProvider; 31 import java.text.spi.DateFormatSymbolsProvider; 32 import java.text.spi.DecimalFormatSymbolsProvider; 33 import java.text.spi.NumberFormatProvider; 34 import java.util.Collections; 35 import java.util.HashSet; 36 import java.util.Locale; 37 import java.util.Map; 38 import java.util.ResourceBundle.Control; 39 import java.util.Set; 40 import java.util.concurrent.ConcurrentHashMap; 41 import java.util.concurrent.ConcurrentMap; 42 import java.util.concurrent.atomic.AtomicReferenceArray; 43 import java.util.spi.CalendarDataProvider; 44 import java.util.spi.CurrencyNameProvider; 45 import java.util.spi.LocaleNameProvider; 46 import java.util.spi.TimeZoneNameProvider; 47 48 /** 49 * LocaleProviderAdapter implementation for the Mac OS X locale data 50 * 51 * @author Naoto Sato 52 */ 53 public class HostLocaleProviderAdapterImpl { 54 55 // per supported locale instances 56 private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> dateFormatPatternsMap = 57 new ConcurrentHashMap<>(2); 58 private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> numberFormatPatternsMap = 59 new ConcurrentHashMap<>(2); 60 private static ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> dateFormatSymbolsMap = 61 new ConcurrentHashMap<>(2); 62 private static ConcurrentMap<Locale, SoftReference<DecimalFormatSymbols>> decimalFormatSymbolsMap = 63 new ConcurrentHashMap<>(2); 64 65 // locale categories 66 private static final int CAT_DISPLAY = 0; 67 private static final int CAT_FORMAT = 1; 68 69 // NumberFormat styles 70 private static final int NF_NUMBER = 0; 71 private static final int NF_CURRENCY = 1; 72 private static final int NF_PERCENT = 2; 73 private static final int NF_INTEGER = 3; 74 private static final int NF_MAX = NF_INTEGER; 75 76 // CalendarData value types 77 private static final int CD_FIRSTDAYOFWEEK = 0; 78 private static final int CD_MINIMALDAYSINFIRSTWEEK = 1; 79 80 // Locale/Currency display name types 81 private static final int DN_LOCALE_LANGUAGE = 0; 82 private static final int DN_LOCALE_SCRIPT = 1; 83 private static final int DN_LOCALE_REGION = 2; 84 private static final int DN_LOCALE_VARIANT = 3; 85 private static final int DN_CURRENCY_CODE = 4; 86 private static final int DN_CURRENCY_SYMBOL = 5; 87 88 // TimeZone display name types 89 private static final int DN_TZ_SHORT_STANDARD = 0; 90 private static final int DN_TZ_SHORT_DST = 1; 91 private static final int DN_TZ_LONG_STANDARD = 2; 92 private static final int DN_TZ_LONG_DST = 3; 93 94 private static final Set<Locale> supportedLocaleSet; 95 static { 96 Set<Locale> tmpSet = new HashSet<Locale>(); 97 // Assuming the default locales do not include any extensions, so 98 // no stripping is needed here. 99 Locale l = Locale.forLanguageTag(getDefaultLocale(CAT_FORMAT).replaceAll("_","-")); 100 tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l)); 101 l = Locale.forLanguageTag(getDefaultLocale(CAT_DISPLAY).replaceAll("_","-")); 102 tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l)); 103 supportedLocaleSet = Collections.unmodifiableSet(tmpSet); 104 } 105 private final static Locale[] supportedLocale = supportedLocaleSet.toArray(new Locale[0]); 106 107 public static DateFormatProvider getDateFormatProvider() { 108 return new DateFormatProvider() { 109 110 @Override 111 public Locale[] getAvailableLocales() { 112 return getSupportedCalendarLocales(); 113 } 114 115 @Override 116 public boolean isSupportedLocale(Locale locale) { 117 return isSupportedCalendarLocale(locale); 118 } 119 120 @Override 121 public DateFormat getDateInstance(int style, Locale locale) { 122 return new SimpleDateFormat(getDateTimePattern(style, -1, locale), 123 getCalendarLocale(locale)); 124 } 125 126 @Override 127 public DateFormat getTimeInstance(int style, Locale locale) { 128 return new SimpleDateFormat(getDateTimePattern(-1, style, locale), 129 getCalendarLocale(locale)); 130 } 131 132 @Override 133 public DateFormat getDateTimeInstance(int dateStyle, 134 int timeStyle, Locale locale) { 135 return new SimpleDateFormat(getDateTimePattern(dateStyle, timeStyle, locale), 136 getCalendarLocale(locale)); 137 } 138 139 private String getDateTimePattern(int dateStyle, int timeStyle, Locale locale) { 140 AtomicReferenceArray<String> dateFormatPatterns; 141 SoftReference<AtomicReferenceArray<String>> ref = dateFormatPatternsMap.get(locale); 142 143 if (ref == null || (dateFormatPatterns = ref.get()) == null) { 144 dateFormatPatterns = new AtomicReferenceArray<>(5 * 5); 145 ref = new SoftReference<>(dateFormatPatterns); 146 dateFormatPatternsMap.put(locale, ref); 147 } 148 149 int index = (dateStyle + 1) * 5 + timeStyle + 1; 150 String pattern = dateFormatPatterns.get(index); 151 if (pattern == null) { 152 String langTag = locale.toLanguageTag(); 153 pattern = translateDateFormatLetters(getCalendarID(langTag), 154 getDateTimePatternNative(dateStyle, timeStyle, langTag)); 155 if (!dateFormatPatterns.compareAndSet(index, null, pattern)) { 156 pattern = dateFormatPatterns.get(index); 157 } 158 } 159 160 return pattern; 161 } 162 }; 163 } 164 165 public static DateFormatSymbolsProvider getDateFormatSymbolsProvider() { 166 return new DateFormatSymbolsProvider() { 167 @Override 168 public Locale[] getAvailableLocales() { 169 if (isSupportedLocale(Locale.getDefault(Locale.Category.FORMAT))) { 170 return supportedLocale; 171 } 172 173 return new Locale[0]; 174 } 175 176 @Override 177 public boolean isSupportedLocale(Locale locale) { 178 // Only supports the locale with Gregorian calendar 179 Locale base = locale.stripExtensions(); 180 if (supportedLocaleSet.contains(base)) { 181 return getCalendarID(locale.toLanguageTag()).equals("gregorian"); 182 } 183 return false; 184 } 185 186 @Override 187 public DateFormatSymbols getInstance(Locale locale) { 188 DateFormatSymbols dateFormatSymbols; 189 SoftReference<DateFormatSymbols> ref = dateFormatSymbolsMap.get(locale); 190 191 if (ref == null || (dateFormatSymbols = ref.get()) == null) { 192 dateFormatSymbols = new DateFormatSymbols(locale); 193 String langTag = locale.toLanguageTag(); 194 dateFormatSymbols.setAmPmStrings(getAmPmStrings(langTag, dateFormatSymbols.getAmPmStrings())); 195 dateFormatSymbols.setEras(getEras(langTag, dateFormatSymbols.getEras())); 196 dateFormatSymbols.setMonths(getMonths(langTag, dateFormatSymbols.getMonths())); 197 dateFormatSymbols.setShortMonths(getShortMonths(langTag, dateFormatSymbols.getShortMonths())); 198 dateFormatSymbols.setWeekdays(getWeekdays(langTag, dateFormatSymbols.getWeekdays())); 199 dateFormatSymbols.setShortWeekdays(getShortWeekdays(langTag, dateFormatSymbols.getShortWeekdays())); 200 ref = new SoftReference<>(dateFormatSymbols); 201 dateFormatSymbolsMap.put(locale, ref); 202 } 203 return (DateFormatSymbols)dateFormatSymbols.clone(); 204 } 205 }; 206 } 207 208 public static NumberFormatProvider getNumberFormatProvider() { 209 return new NumberFormatProvider() { 210 @Override 211 public Locale[] getAvailableLocales() { 212 return supportedLocale; 213 } 214 215 @Override 216 public boolean isSupportedLocale(Locale locale) { 217 // Ignore the extensions for now 218 return supportedLocaleSet.contains(locale.stripExtensions()); 219 } 220 221 @Override 222 public NumberFormat getCurrencyInstance(Locale locale) { 223 return new DecimalFormat(getNumberPattern(NF_CURRENCY, locale), 224 DecimalFormatSymbols.getInstance(locale)); 225 } 226 227 @Override 228 public NumberFormat getIntegerInstance(Locale locale) { 229 return new DecimalFormat(getNumberPattern(NF_INTEGER, locale), 230 DecimalFormatSymbols.getInstance(locale)); 231 } 232 233 @Override 234 public NumberFormat getNumberInstance(Locale locale) { 235 return new DecimalFormat(getNumberPattern(NF_NUMBER, locale), 236 DecimalFormatSymbols.getInstance(locale)); 237 } 238 239 @Override 240 public NumberFormat getPercentInstance(Locale locale) { 241 return new DecimalFormat(getNumberPattern(NF_PERCENT, locale), 242 DecimalFormatSymbols.getInstance(locale)); 243 } 244 245 private String getNumberPattern(int style, Locale locale) { 246 AtomicReferenceArray<String> numberFormatPatterns; 247 SoftReference<AtomicReferenceArray<String>> ref = numberFormatPatternsMap.get(locale); 248 249 if (ref == null || (numberFormatPatterns = ref.get()) == null) { 250 numberFormatPatterns = new AtomicReferenceArray<>(4); 251 ref = new SoftReference<>(numberFormatPatterns); 252 numberFormatPatternsMap.put(locale, ref); 253 } 254 255 String pattern = numberFormatPatterns.get(style); 256 if (pattern == null) { 257 pattern = getNumberPatternNative(style, locale.toLanguageTag()); 258 if (!numberFormatPatterns.compareAndSet(style, null, pattern)) { 259 pattern = numberFormatPatterns.get(style); 260 } 261 } 262 263 return pattern; 264 } 265 }; 266 } 267 268 public static DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() { 269 return new DecimalFormatSymbolsProvider() { 270 271 @Override 272 public Locale[] getAvailableLocales() { 273 return supportedLocale; 274 } 275 276 @Override 277 public boolean isSupportedLocale(Locale locale) { 278 // Ignore the extensions for now 279 return supportedLocaleSet.contains(locale.stripExtensions()); 280 } 281 282 @Override 283 public DecimalFormatSymbols getInstance(Locale locale) { 284 DecimalFormatSymbols decimalFormatSymbols; 285 SoftReference<DecimalFormatSymbols> ref = decimalFormatSymbolsMap.get(locale); 286 287 if (ref == null || (decimalFormatSymbols = ref.get()) == null) { 288 decimalFormatSymbols = new DecimalFormatSymbols(locale); 289 String langTag = locale.toLanguageTag(); 290 291 // DecimalFormatSymbols.setInternationalCurrencySymbol() has 292 // a side effect of setting the currency symbol as well. So 293 // the calling order is relevant here. 294 decimalFormatSymbols.setInternationalCurrencySymbol(getInternationalCurrencySymbol(langTag, decimalFormatSymbols.getInternationalCurrencySymbol())); 295 decimalFormatSymbols.setCurrencySymbol(getCurrencySymbol(langTag, decimalFormatSymbols.getCurrencySymbol())); 296 decimalFormatSymbols.setDecimalSeparator(getDecimalSeparator(langTag, decimalFormatSymbols.getDecimalSeparator())); 297 decimalFormatSymbols.setGroupingSeparator(getGroupingSeparator(langTag, decimalFormatSymbols.getGroupingSeparator())); 298 decimalFormatSymbols.setInfinity(getInfinity(langTag, decimalFormatSymbols.getInfinity())); 299 decimalFormatSymbols.setMinusSign(getMinusSign(langTag, decimalFormatSymbols.getMinusSign())); 300 decimalFormatSymbols.setMonetaryDecimalSeparator(getMonetaryDecimalSeparator(langTag, decimalFormatSymbols.getMonetaryDecimalSeparator())); 301 decimalFormatSymbols.setNaN(getNaN(langTag, decimalFormatSymbols.getNaN())); 302 decimalFormatSymbols.setPercent(getPercent(langTag, decimalFormatSymbols.getPercent())); 303 decimalFormatSymbols.setPerMill(getPerMill(langTag, decimalFormatSymbols.getPerMill())); 304 decimalFormatSymbols.setZeroDigit(getZeroDigit(langTag, decimalFormatSymbols.getZeroDigit())); 305 decimalFormatSymbols.setExponentSeparator(getExponentSeparator(langTag, decimalFormatSymbols.getExponentSeparator())); 306 ref = new SoftReference<>(decimalFormatSymbols); 307 decimalFormatSymbolsMap.put(locale, ref); 308 } 309 return (DecimalFormatSymbols)decimalFormatSymbols.clone(); 310 } 311 }; 312 } 313 314 public static CalendarDataProvider getCalendarDataProvider() { 315 return new CalendarDataProvider() { 316 @Override 317 public Locale[] getAvailableLocales() { 318 return getSupportedCalendarLocales(); 319 } 320 321 @Override 322 public boolean isSupportedLocale(Locale locale) { 323 return isSupportedCalendarLocale(locale); 324 } 325 326 @Override 327 public String getDisplayName(String calType, int field, int value, 328 int style, Locale locale) { 329 return null; 330 } 331 332 @Override 333 public Map<String, Integer> getDisplayNames(String calType, 334 int field, int style, Locale locale) { 335 return null; 336 } 337 338 @Override 339 public int getFirstDayOfWeek(Locale locale) { 340 return getCalendarInt(locale.toLanguageTag(), CD_FIRSTDAYOFWEEK); 341 } 342 343 @Override 344 public int getMinimalDaysInFirstWeek(Locale locale) { 345 return getCalendarInt(locale.toLanguageTag(), CD_MINIMALDAYSINFIRSTWEEK); 346 } 347 }; 348 } 349 350 public static CurrencyNameProvider getCurrencyNameProvider() { 351 return new CurrencyNameProvider() { 352 @Override 353 public Locale[] getAvailableLocales() { 354 return supportedLocale; 355 } 356 357 @Override 358 public boolean isSupportedLocale(Locale locale) { 359 // Ignore the extensions for now 360 return supportedLocaleSet.contains(locale.stripExtensions()); 361 } 362 363 @Override 364 public String getDisplayName(String code, Locale locale) { 365 return getDisplayString(locale.toLanguageTag(), DN_CURRENCY_CODE, code); 366 } 367 368 @Override 369 public String getSymbol(String code, Locale locale) { 370 return getDisplayString(locale.toLanguageTag(), DN_CURRENCY_SYMBOL, code); 371 } 372 }; 373 } 374 375 public static LocaleNameProvider getLocaleNameProvider() { 376 return new LocaleNameProvider() { 377 @Override 378 public Locale[] getAvailableLocales() { 379 return supportedLocale; 380 } 381 382 @Override 383 public boolean isSupportedLocale(Locale locale) { 384 // Ignore the extensions for now 385 return supportedLocaleSet.contains(locale.stripExtensions()); 386 } 387 388 @Override 389 public String getDisplayLanguage(String languageCode, Locale locale) { 390 return getDisplayString(locale.toLanguageTag(), DN_LOCALE_LANGUAGE, languageCode); 391 } 392 393 @Override 394 public String getDisplayCountry(String countryCode, Locale locale) { 395 return getDisplayString(locale.toLanguageTag(), DN_LOCALE_REGION, countryCode); 396 } 397 398 @Override 399 public String getDisplayScript(String scriptCode, Locale locale) { 400 return getDisplayString(locale.toLanguageTag(), DN_LOCALE_SCRIPT, scriptCode); 401 } 402 403 @Override 404 public String getDisplayVariant(String variantCode, Locale locale) { 405 return getDisplayString(locale.toLanguageTag(), DN_LOCALE_VARIANT, variantCode); 406 } 407 }; 408 } 409 410 public static TimeZoneNameProvider getTimeZoneNameProvider() { 411 return new TimeZoneNameProvider() { 412 @Override 413 public Locale[] getAvailableLocales() { 414 return supportedLocale; 415 } 416 417 @Override 418 public boolean isSupportedLocale(Locale locale) { 419 // Ignore the extensions for now 420 return supportedLocaleSet.contains(locale.stripExtensions()); 421 } 422 423 @Override 424 public String getDisplayName(String ID, boolean daylight, int style, Locale locale) { 425 return getTimeZoneDisplayString(locale.toLanguageTag(), style * 2 + (daylight ? 1 : 0), ID); 426 } 427 }; 428 } 429 430 private static Locale[] getSupportedCalendarLocales() { 431 if (supportedLocale.length != 0 && 432 supportedLocaleSet.contains(Locale.JAPAN) && 433 isJapaneseCalendar()) { 434 Locale[] sup = new Locale[supportedLocale.length+1]; 435 sup[0] = JRELocaleConstants.JA_JP_JP; 436 System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length); 437 return sup; 438 } 439 return supportedLocale; 440 } 441 442 private static boolean isSupportedCalendarLocale(Locale locale) { 443 // special case for ja_JP_JP 444 if (JRELocaleConstants.JA_JP_JP.equals(locale)) { 445 return isJapaneseCalendar(); 446 } 447 448 Locale base = locale.stripExtensions(); 449 if (!supportedLocaleSet.contains(base)) { 450 return false; 451 } 452 453 String caltype = locale.getUnicodeLocaleType("ca"); 454 if (caltype == null) { 455 return true; 456 } 457 458 return caltype.replaceFirst("gregory", "gregorian").equals( 459 getCalendarID(locale.toLanguageTag())); 460 } 461 462 private static boolean isJapaneseCalendar() { 463 return getCalendarID("ja-JP").equals("japanese"); 464 } 465 466 private static Locale getCalendarLocale(Locale locale) { 467 Locale.Builder lb = new Locale.Builder().setLocale(locale); 468 String calid = getCalendarID(locale.toLanguageTag()); 469 switch (calid) { 470 case "gregorian": 471 calid = "gregory"; 472 // FALL THROUGH! 473 case "japanese": 474 case "buddhist": 475 lb.setUnicodeLocaleKeyword("ca", calid); 476 return lb.build(); 477 default: 478 return locale; 479 } 480 } 481 482 // The following methods are copied from CLDRConverter build tool. 483 private static String translateDateFormatLetters(String calendarType, String cldrFormat) { 484 String pattern = cldrFormat; 485 int length = pattern.length(); 486 boolean inQuote = false; 487 StringBuilder jrePattern = new StringBuilder(length); 488 int count = 0; 489 char lastLetter = 0; 490 491 for (int i = 0; i < length; i++) { 492 char c = pattern.charAt(i); 493 494 if (c == '\'') { 495 // '' is treated as a single quote regardless of being 496 // in a quoted section. 497 if ((i + 1) < length) { 498 char nextc = pattern.charAt(i + 1); 499 if (nextc == '\'') { 500 i++; 501 if (count != 0) { 502 convert(calendarType, lastLetter, count, jrePattern); 503 lastLetter = 0; 504 count = 0; 505 } 506 jrePattern.append("''"); 507 continue; 508 } 509 } 510 if (!inQuote) { 511 if (count != 0) { 512 convert(calendarType, lastLetter, count, jrePattern); 513 lastLetter = 0; 514 count = 0; 515 } 516 inQuote = true; 517 } else { 518 inQuote = false; 519 } 520 jrePattern.append(c); 521 continue; 522 } 523 if (inQuote) { 524 jrePattern.append(c); 525 continue; 526 } 527 if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) { 528 if (count != 0) { 529 convert(calendarType, lastLetter, count, jrePattern); 530 lastLetter = 0; 531 count = 0; 532 } 533 jrePattern.append(c); 534 continue; 535 } 536 537 if (lastLetter == 0 || lastLetter == c) { 538 lastLetter = c; 539 count++; 540 continue; 541 } 542 convert(calendarType, lastLetter, count, jrePattern); 543 lastLetter = c; 544 count = 1; 545 } 546 547 if (count != 0) { 548 convert(calendarType, lastLetter, count, jrePattern); 549 } 550 if (cldrFormat.contentEquals(jrePattern)) { 551 return cldrFormat; 552 } 553 return jrePattern.toString(); 554 } 555 556 private static void convert(String calendarType, char cldrLetter, int count, StringBuilder sb) { 557 switch (cldrLetter) { 558 case 'G': 559 if (!calendarType.equals("gregorian")) { 560 // Adjust the number of 'G's for JRE SimpleDateFormat 561 if (count == 5) { 562 // CLDR narrow -> JRE short 563 count = 1; 564 } else if (count == 1) { 565 // CLDR abbr -> JRE long 566 count = 4; 567 } 568 } 569 appendN(cldrLetter, count, sb); 570 break; 571 572 // TODO: support 'c' and 'e' in JRE SimpleDateFormat 573 // Use 'u' and 'E' for now. 574 case 'c': 575 case 'e': 576 switch (count) { 577 case 1: 578 sb.append('u'); 579 break; 580 case 3: 581 case 4: 582 appendN('E', count, sb); 583 break; 584 case 5: 585 appendN('E', 3, sb); 586 break; 587 } 588 break; 589 590 case 'v': 591 case 'V': 592 appendN('z', count, sb); 593 break; 594 595 case 'Z': 596 if (count == 4 || count == 5) { 597 sb.append("XXX"); 598 } 599 break; 600 601 case 'u': 602 case 'U': 603 case 'q': 604 case 'Q': 605 case 'l': 606 case 'g': 607 case 'j': 608 case 'A': 609 // Unsupported letter. Just append it within quotes 610 sb.append('\''); 611 sb.append(cldrLetter); 612 sb.append('\''); 613 break; 614 615 default: 616 appendN(cldrLetter, count, sb); 617 break; 618 } 619 } 620 621 private static void appendN(char c, int n, StringBuilder sb) { 622 for (int i = 0; i < n; i++) { 623 sb.append(c); 624 } 625 } 626 627 // initialize 628 private static native String getDefaultLocale(int cat); 629 630 // For DateFormatProvider 631 private static native String getDateTimePatternNative(int dateStyle, int timeStyle, String langtag); 632 private static native String getCalendarID(String langTag); 633 634 // For NumberFormatProvider 635 private static native String getNumberPatternNative(int style, String langtag); 636 637 // For DateFormatSymbolsProvider 638 private static native String[] getAmPmStrings(String langTag, String[] ampm); 639 private static native String[] getEras(String langTag, String[] eras); 640 private static native String[] getMonths(String langTag, String[] months); 641 private static native String[] getShortMonths(String langTag, String[] smonths); 642 private static native String[] getWeekdays(String langTag, String[] wdays); 643 private static native String[] getShortWeekdays(String langTag, String[] swdays); 644 645 // For DecimalFormatSymbolsProvider 646 private static native String getCurrencySymbol(String langTag, String currencySymbol); 647 private static native char getDecimalSeparator(String langTag, char decimalSeparator); 648 private static native char getGroupingSeparator(String langTag, char groupingSeparator); 649 private static native String getInfinity(String langTag, String infinity); 650 private static native String getInternationalCurrencySymbol(String langTag, String internationalCurrencySymbol); 651 private static native char getMinusSign(String langTag, char minusSign); 652 private static native char getMonetaryDecimalSeparator(String langTag, char monetaryDecimalSeparator); 653 private static native String getNaN(String langTag, String nan); 654 private static native char getPercent(String langTag, char percent); 655 private static native char getPerMill(String langTag, char perMill); 656 private static native char getZeroDigit(String langTag, char zeroDigit); 657 private static native String getExponentSeparator(String langTag, String exponent); 658 659 // For CalendarDataProvider 660 private static native int getCalendarInt(String langTag, int type); 661 662 // For Locale/CurrencyNameProvider 663 private static native String getDisplayString(String langTag, int key, String value); 664 665 // For TimeZoneNameProvider 666 private static native String getTimeZoneDisplayString(String langTag, int style, String value); 667 }