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 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; 524 525 if (base.hasExtensions() || base.getVariant() != "") { 526 base = new Locale.Builder() 527 .setLocale(locale) 528 .clearExtensions() 529 .build(); 530 } 531 532 if (!supportedLocaleSet.contains(base)) { 533 return false; 534 } 535 536 String requestedCalType = locale.getUnicodeLocaleType("ca"); 537 String nativeCalType = 538 getCalendarID(base.toLanguageTag()).replaceFirst("gregorian", "gregory"); 539 540 if (requestedCalType == null) { 541 return Calendar.getAvailableCalendarTypes().contains(nativeCalType); 542 } else { 543 return requestedCalType.equals(nativeCalType); 544 } 545 } 546 547 private static boolean isJapaneseCalendar() { 548 return getCalendarID("ja-JP").equals("japanese"); 549 } 550 551 private static Locale getCalendarLocale(Locale locale) { 552 String nativeCalType = getCalendarID(locale.toLanguageTag()) 553 .replaceFirst("gregorian", "gregory"); 554 if (Calendar.getAvailableCalendarTypes().contains(nativeCalType)) { 555 return new Locale.Builder() 556 .setLocale(locale) 557 .setUnicodeLocaleKeyword("ca", nativeCalType) 558 .build(); 559 } else { 560 return locale; 561 } 562 } 563 564 // The following methods are copied from CLDRConverter build tool. 565 private static String translateDateFormatLetters(String calendarType, String cldrFormat) { 566 String pattern = cldrFormat; 567 int length = pattern.length(); 568 boolean inQuote = false; 569 StringBuilder jrePattern = new StringBuilder(length); 570 int count = 0; 571 char lastLetter = 0; 572 573 for (int i = 0; i < length; i++) { 574 char c = pattern.charAt(i); 575 576 if (c == '\'') { 577 // '' is treated as a single quote regardless of being 578 // in a quoted section. 579 if ((i + 1) < length) { 580 char nextc = pattern.charAt(i + 1); 581 if (nextc == '\'') { 582 i++; 583 if (count != 0) { 584 convert(calendarType, lastLetter, count, jrePattern); 585 lastLetter = 0; 586 count = 0; 587 } 588 jrePattern.append("''"); 589 continue; 590 } 591 } 592 if (!inQuote) { 593 if (count != 0) { 594 convert(calendarType, lastLetter, count, jrePattern); 595 lastLetter = 0; 596 count = 0; 597 } 598 inQuote = true; 599 } else { 600 inQuote = false; 601 } 602 jrePattern.append(c); 603 continue; 604 } 605 if (inQuote) { 606 jrePattern.append(c); 607 continue; 608 } 609 if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) { 610 if (count != 0) { 611 convert(calendarType, lastLetter, count, jrePattern); 612 lastLetter = 0; 613 count = 0; 614 } 615 jrePattern.append(c); 616 continue; 617 } 618 619 if (lastLetter == 0 || lastLetter == c) { 620 lastLetter = c; 621 count++; 622 continue; 623 } 624 convert(calendarType, lastLetter, count, jrePattern); 625 lastLetter = c; 626 count = 1; 627 } 628 629 if (count != 0) { 630 convert(calendarType, lastLetter, count, jrePattern); 631 } 632 if (cldrFormat.contentEquals(jrePattern)) { 633 return cldrFormat; 634 } 635 return jrePattern.toString(); 636 } 637 638 private static void convert(String calendarType, char cldrLetter, int count, StringBuilder sb) { 639 switch (cldrLetter) { 640 case 'G': 641 if (!calendarType.equals("gregorian")) { 642 // Adjust the number of 'G's for JRE SimpleDateFormat 643 if (count == 5) { 644 // CLDR narrow -> JRE short 645 count = 1; 646 } else if (count == 1) { 647 // CLDR abbr -> JRE long 648 count = 4; 649 } 650 } 651 appendN(cldrLetter, count, sb); 652 break; 653 654 // TODO: support 'c' and 'e' in JRE SimpleDateFormat 655 // Use 'u' and 'E' for now. 656 case 'c': 657 case 'e': 658 switch (count) { 659 case 1: 660 sb.append('u'); 661 break; 662 case 3: 663 case 4: 664 appendN('E', count, sb); 665 break; 666 case 5: 667 appendN('E', 3, sb); 668 break; 669 } 670 break; 671 672 case 'v': 673 case 'V': 674 appendN('z', count, sb); 675 break; 676 677 case 'Z': 678 if (count == 4 || count == 5) { 679 sb.append("XXX"); 680 } 681 break; 682 683 case 'u': 684 case 'U': 685 case 'q': 686 case 'Q': 687 case 'l': 688 case 'g': 689 case 'j': 690 case 'A': 691 // Unsupported letter. Just append it within quotes 692 sb.append('\''); 693 sb.append(cldrLetter); 694 sb.append('\''); 695 break; 696 697 default: 698 appendN(cldrLetter, count, sb); 699 break; 700 } 701 } 702 703 private static void appendN(char c, int n, StringBuilder sb) { 704 for (int i = 0; i < n; i++) { 705 sb.append(c); 706 } 707 } 708 709 // initialize 710 private static native String getDefaultLocale(int cat); 711 712 // For DateFormatProvider 713 private static native String getDateTimePatternNative(int dateStyle, int timeStyle, String langtag); 714 private static native String getCalendarID(String langTag); 715 716 // For NumberFormatProvider 717 private static native String getNumberPatternNative(int style, String langtag); 718 719 // For DateFormatSymbolsProvider 720 private static native String[] getAmPmStrings(String langTag, String[] ampm); 721 private static native String[] getEras(String langTag, String[] eras); 722 private static native String[] getMonths(String langTag, String[] months); 723 private static native String[] getShortMonths(String langTag, String[] smonths); 724 private static native String[] getWeekdays(String langTag, String[] wdays); 725 private static native String[] getShortWeekdays(String langTag, String[] swdays); 726 727 // For DecimalFormatSymbolsProvider 728 private static native String getCurrencySymbol(String langTag, String currencySymbol); 729 private static native char getDecimalSeparator(String langTag, char decimalSeparator); 730 private static native char getGroupingSeparator(String langTag, char groupingSeparator); 731 private static native String getInfinity(String langTag, String infinity); 732 private static native String getInternationalCurrencySymbol(String langTag, String internationalCurrencySymbol); 733 private static native char getMinusSign(String langTag, char minusSign); 734 private static native char getMonetaryDecimalSeparator(String langTag, char monetaryDecimalSeparator); 735 private static native String getNaN(String langTag, String nan); 736 private static native char getPercent(String langTag, char percent); 737 private static native char getPerMill(String langTag, char perMill); 738 private static native char getZeroDigit(String langTag, char zeroDigit); 739 private static native String getExponentSeparator(String langTag, String exponent); 740 741 // For CalendarDataProvider 742 private static native int getCalendarInt(String langTag, int type); 743 744 // For Locale/CurrencyNameProvider 745 private static native String getDisplayString(String langTag, int key, String value); 746 747 // For TimeZoneNameProvider 748 private static native String getTimeZoneDisplayString(String langTag, int style, String value); 749 }