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