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 = convertMacOSXLocaleToJavaLocale(getDefaultLocale(CAT_FORMAT)); 104 tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l)); 105 l = convertMacOSXLocaleToJavaLocale(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 convertMacOSXLocaleToJavaLocale(String macosxloc) { 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 = macosxloc.split("@"); 120 String langTag = tmp[0].replace('_', '-'); 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 .setTimeZone(zone) 424 .setInstant(System.currentTimeMillis()) 425 .build(); 426 } 427 }; 428 } 429 430 public static CurrencyNameProvider getCurrencyNameProvider() { 431 return new CurrencyNameProvider() { 432 @Override 433 public Locale[] getAvailableLocales() { 434 return supportedLocale; 435 } 436 437 @Override 438 public boolean isSupportedLocale(Locale locale) { 439 // Ignore the extensions for now 440 return supportedLocaleSet.contains(locale.stripExtensions()); 441 } 442 443 @Override 444 public String getDisplayName(String code, Locale locale) { 445 return getDisplayString(locale.toLanguageTag(), DN_CURRENCY_CODE, code); 446 } 447 448 @Override 449 public String getSymbol(String code, Locale locale) { 450 return getDisplayString(locale.toLanguageTag(), DN_CURRENCY_SYMBOL, code); 451 } 452 }; 453 } 454 455 public static LocaleNameProvider getLocaleNameProvider() { 456 return new LocaleNameProvider() { 457 @Override 458 public Locale[] getAvailableLocales() { 459 return supportedLocale; 460 } 461 462 @Override 463 public boolean isSupportedLocale(Locale locale) { 464 // Ignore the extensions for now 465 return supportedLocaleSet.contains(locale.stripExtensions()); 466 } 467 468 @Override 469 public String getDisplayLanguage(String languageCode, Locale locale) { 470 return getDisplayString(locale.toLanguageTag(), DN_LOCALE_LANGUAGE, languageCode); 471 } 472 473 @Override 474 public String getDisplayCountry(String countryCode, Locale locale) { 475 return getDisplayString(locale.toLanguageTag(), DN_LOCALE_REGION, countryCode); 476 } 477 478 @Override 479 public String getDisplayScript(String scriptCode, Locale locale) { 480 return getDisplayString(locale.toLanguageTag(), DN_LOCALE_SCRIPT, scriptCode); 481 } 482 483 @Override 484 public String getDisplayVariant(String variantCode, Locale locale) { 485 return getDisplayString(locale.toLanguageTag(), DN_LOCALE_VARIANT, variantCode); 486 } 487 }; 488 } 489 490 public static TimeZoneNameProvider getTimeZoneNameProvider() { 491 return new TimeZoneNameProvider() { 492 @Override 493 public Locale[] getAvailableLocales() { 494 return supportedLocale; 495 } 496 497 @Override 498 public boolean isSupportedLocale(Locale locale) { 499 // Ignore the extensions for now 500 return supportedLocaleSet.contains(locale.stripExtensions()); 501 } 502 503 @Override 504 public String getDisplayName(String ID, boolean daylight, int style, Locale locale) { 505 return getTimeZoneDisplayString(locale.toLanguageTag(), style * 2 + (daylight ? 1 : 0), ID); 506 } 507 }; 508 } 509 510 private static Locale[] getSupportedCalendarLocales() { 511 if (supportedLocale.length != 0 && 512 supportedLocaleSet.contains(Locale.JAPAN) && 513 isJapaneseCalendar()) { 514 Locale[] sup = new Locale[supportedLocale.length+1]; 515 sup[0] = JRELocaleConstants.JA_JP_JP; 516 System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length); 517 return sup; 518 } 519 return supportedLocale; 520 } 521 522 private static boolean isSupportedCalendarLocale(Locale locale) { 523 Locale base = locale.stripExtensions(); 524 if (!supportedLocaleSet.contains(base)) { 525 return false; 526 } 527 528 String requestedCalType = locale.getUnicodeLocaleType("ca"); 529 String nativeCalType = 530 getCalendarID(locale.toLanguageTag()).replaceFirst("gregorian", "gregory"); 531 532 if (requestedCalType == null) { 533 return Calendar.getAvailableCalendarTypes().contains(nativeCalType); 534 } else { 535 return requestedCalType.equals(nativeCalType); 536 } 537 } 538 539 private static boolean isJapaneseCalendar() { 540 return getCalendarID("ja-JP").equals("japanese"); 541 } 542 543 private static Locale getCalendarLocale(Locale locale) { 544 String nativeCalType = getCalendarID(locale.toLanguageTag()) 545 .replaceFirst("gregorian", "gregory"); 546 if (Calendar.getAvailableCalendarTypes().contains(nativeCalType)) { 547 return new Locale.Builder() 548 .setLocale(locale) 549 .setUnicodeLocaleKeyword("ca", nativeCalType) 550 .build(); 551 } else { 552 return locale; 553 } 554 } 555 556 // The following methods are copied from CLDRConverter build tool. 557 private static String translateDateFormatLetters(String calendarType, String cldrFormat) { 558 String pattern = cldrFormat; 559 int length = pattern.length(); 560 boolean inQuote = false; 561 StringBuilder jrePattern = new StringBuilder(length); 562 int count = 0; 563 char lastLetter = 0; 564 565 for (int i = 0; i < length; i++) { 566 char c = pattern.charAt(i); 567 568 if (c == '\'') { 569 // '' is treated as a single quote regardless of being 570 // in a quoted section. 571 if ((i + 1) < length) { 572 char nextc = pattern.charAt(i + 1); 573 if (nextc == '\'') { 574 i++; 575 if (count != 0) { 576 convert(calendarType, lastLetter, count, jrePattern); 577 lastLetter = 0; 578 count = 0; 579 } 580 jrePattern.append("''"); 581 continue; 582 } 583 } 584 if (!inQuote) { 585 if (count != 0) { 586 convert(calendarType, lastLetter, count, jrePattern); 587 lastLetter = 0; 588 count = 0; 589 } 590 inQuote = true; 591 } else { 592 inQuote = false; 593 } 594 jrePattern.append(c); 595 continue; 596 } 597 if (inQuote) { 598 jrePattern.append(c); 599 continue; 600 } 601 if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) { 602 if (count != 0) { 603 convert(calendarType, lastLetter, count, jrePattern); 604 lastLetter = 0; 605 count = 0; 606 } 607 jrePattern.append(c); 608 continue; 609 } 610 611 if (lastLetter == 0 || lastLetter == c) { 612 lastLetter = c; 613 count++; 614 continue; 615 } 616 convert(calendarType, lastLetter, count, jrePattern); 617 lastLetter = c; 618 count = 1; 619 } 620 621 if (count != 0) { 622 convert(calendarType, lastLetter, count, jrePattern); 623 } 624 if (cldrFormat.contentEquals(jrePattern)) { 625 return cldrFormat; 626 } 627 return jrePattern.toString(); 628 } 629 630 private static void convert(String calendarType, char cldrLetter, int count, StringBuilder sb) { 631 switch (cldrLetter) { 632 case 'G': 633 if (!calendarType.equals("gregorian")) { 634 // Adjust the number of 'G's for JRE SimpleDateFormat 635 if (count == 5) { 636 // CLDR narrow -> JRE short 637 count = 1; 638 } else if (count == 1) { 639 // CLDR abbr -> JRE long 640 count = 4; 641 } 642 } 643 appendN(cldrLetter, count, sb); 644 break; 645 646 // TODO: support 'c' and 'e' in JRE SimpleDateFormat 647 // Use 'u' and 'E' for now. 648 case 'c': 649 case 'e': 650 switch (count) { 651 case 1: 652 sb.append('u'); 653 break; 654 case 3: 655 case 4: 656 appendN('E', count, sb); 657 break; 658 case 5: 659 appendN('E', 3, sb); 660 break; 661 } 662 break; 663 664 case 'v': 665 case 'V': 666 appendN('z', count, sb); 667 break; 668 669 case 'Z': 670 if (count == 4 || count == 5) { 671 sb.append("XXX"); 672 } 673 break; 674 675 case 'u': 676 case 'U': 677 case 'q': 678 case 'Q': 679 case 'l': 680 case 'g': 681 case 'j': 682 case 'A': 683 // Unsupported letter. Just append it within quotes 684 sb.append('\''); 685 sb.append(cldrLetter); 686 sb.append('\''); 687 break; 688 689 default: 690 appendN(cldrLetter, count, sb); 691 break; 692 } 693 } 694 695 private static void appendN(char c, int n, StringBuilder sb) { 696 for (int i = 0; i < n; i++) { 697 sb.append(c); 698 } 699 } 700 701 // initialize 702 private static native String getDefaultLocale(int cat); 703 704 // For DateFormatProvider 705 private static native String getDateTimePatternNative(int dateStyle, int timeStyle, String langtag); 706 private static native String getCalendarID(String langTag); 707 708 // For NumberFormatProvider 709 private static native String getNumberPatternNative(int style, String langtag); 710 711 // For DateFormatSymbolsProvider 712 private static native String[] getAmPmStrings(String langTag, String[] ampm); 713 private static native String[] getEras(String langTag, String[] eras); 714 private static native String[] getMonths(String langTag, String[] months); 715 private static native String[] getShortMonths(String langTag, String[] smonths); 716 private static native String[] getWeekdays(String langTag, String[] wdays); 717 private static native String[] getShortWeekdays(String langTag, String[] swdays); 718 719 // For DecimalFormatSymbolsProvider 720 private static native String getCurrencySymbol(String langTag, String currencySymbol); 721 private static native char getDecimalSeparator(String langTag, char decimalSeparator); 722 private static native char getGroupingSeparator(String langTag, char groupingSeparator); 723 private static native String getInfinity(String langTag, String infinity); 724 private static native String getInternationalCurrencySymbol(String langTag, String internationalCurrencySymbol); 725 private static native char getMinusSign(String langTag, char minusSign); 726 private static native char getMonetaryDecimalSeparator(String langTag, char monetaryDecimalSeparator); 727 private static native String getNaN(String langTag, String nan); 728 private static native char getPercent(String langTag, char percent); 729 private static native char getPerMill(String langTag, char perMill); 730 private static native char getZeroDigit(String langTag, char zeroDigit); 731 private static native String getExponentSeparator(String langTag, String exponent); 732 733 // For CalendarDataProvider 734 private static native int getCalendarInt(String langTag, int type); 735 736 // For Locale/CurrencyNameProvider 737 private static native String getDisplayString(String langTag, int key, String value); 738 739 // For TimeZoneNameProvider 740 private static native String getTimeZoneDisplayString(String langTag, int style, String value); 741 }