src/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java

Print this page
rev 6663 : imported patch 8008576


  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.CalendarNameProvider;
  45 import java.util.spi.CurrencyNameProvider;
  46 import java.util.spi.LocaleNameProvider;
  47 import java.util.spi.TimeZoneNameProvider;

  48 
  49 /**
  50  * LocaleProviderAdapter implementation for the Mac OS X locale data
  51  *
  52  * @author Naoto Sato
  53  */
  54 public class HostLocaleProviderAdapterImpl {
  55 
  56     // per supported locale instances
  57     private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> dateFormatPatternsMap =
  58         new ConcurrentHashMap<>(2);
  59     private static ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> numberFormatPatternsMap =
  60         new ConcurrentHashMap<>(2);
  61     private static ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> dateFormatSymbolsMap =
  62         new ConcurrentHashMap<>(2);
  63     private static ConcurrentMap<Locale, SoftReference<DecimalFormatSymbols>> decimalFormatSymbolsMap =
  64         new ConcurrentHashMap<>(2);
  65 
  66     // locale categories
  67     private static final int CAT_DISPLAY = 0;


  77     // CalendarData value types
  78     private static final int CD_FIRSTDAYOFWEEK = 0;
  79     private static final int CD_MINIMALDAYSINFIRSTWEEK = 1;
  80 
  81     // Locale/Currency display name types
  82     private static final int DN_LOCALE_LANGUAGE = 0;
  83     private static final int DN_LOCALE_SCRIPT   = 1;
  84     private static final int DN_LOCALE_REGION   = 2;
  85     private static final int DN_LOCALE_VARIANT  = 3;
  86     private static final int DN_CURRENCY_CODE   = 4;
  87     private static final int DN_CURRENCY_SYMBOL = 5;
  88 
  89     // TimeZone display name types
  90     private static final int DN_TZ_SHORT_STANDARD = 0;
  91     private static final int DN_TZ_SHORT_DST      = 1;
  92     private static final int DN_TZ_LONG_STANDARD  = 2;
  93     private static final int DN_TZ_LONG_DST       = 3;
  94 
  95     private static final Set<Locale> supportedLocaleSet;
  96     static {
  97         Set<Locale> tmpSet = new HashSet<Locale>();
  98         // Assuming the default locales do not include any extensions, so
  99         // no stripping is needed here.
 100         Locale l = Locale.forLanguageTag(getDefaultLocale(CAT_FORMAT).replaceAll("_","-"));
 101         tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
 102         l = Locale.forLanguageTag(getDefaultLocale(CAT_DISPLAY).replaceAll("_","-"));
 103         tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
 104         supportedLocaleSet = Collections.unmodifiableSet(tmpSet);
 105     }
 106     private final static Locale[] supportedLocale = supportedLocaleSet.toArray(new Locale[0]);
 107 







































 108     public static DateFormatProvider getDateFormatProvider() {
 109         return new DateFormatProvider() {
 110 
 111             @Override
 112             public Locale[] getAvailableLocales() {
 113                 return getSupportedCalendarLocales();
 114             }
 115 
 116             @Override
 117             public boolean isSupportedLocale(Locale locale) {
 118                 return isSupportedCalendarLocale(locale);
 119             }
 120 
 121             @Override
 122             public DateFormat getDateInstance(int style, Locale locale) {
 123                 return new SimpleDateFormat(getDateTimePattern(style, -1, locale),
 124                                             getCalendarLocale(locale));
 125             }
 126 
 127             @Override


 153                     String langTag = locale.toLanguageTag();
 154                     pattern = translateDateFormatLetters(getCalendarID(langTag),
 155                             getDateTimePatternNative(dateStyle, timeStyle, langTag));
 156                     if (!dateFormatPatterns.compareAndSet(index, null, pattern)) {
 157                         pattern = dateFormatPatterns.get(index);
 158                     }
 159                 }
 160 
 161                 return pattern;
 162             }
 163         };
 164     }
 165 
 166     public static DateFormatSymbolsProvider getDateFormatSymbolsProvider() {
 167         return new DateFormatSymbolsProvider() {
 168             @Override
 169             public Locale[] getAvailableLocales() {
 170                 if (isSupportedLocale(Locale.getDefault(Locale.Category.FORMAT))) {
 171                     return supportedLocale;
 172                 }
 173 
 174                         return new Locale[0];
 175                     }
 176 
 177             @Override
 178             public boolean isSupportedLocale(Locale locale) {
 179                 // Only supports the locale with Gregorian calendar
 180                 Locale base = locale.stripExtensions();
 181                 if (supportedLocaleSet.contains(base)) {
 182                     return getCalendarID(locale.toLanguageTag()).equals("gregorian");
 183                 }
 184                 return false;
 185             }
 186 
 187             @Override
 188             public DateFormatSymbols getInstance(Locale locale) {
 189                 DateFormatSymbols dateFormatSymbols;
 190                 SoftReference<DateFormatSymbols> ref = dateFormatSymbolsMap.get(locale);
 191 
 192                 if (ref == null || (dateFormatSymbols = ref.get()) == null) {
 193                     dateFormatSymbols = new DateFormatSymbols(locale);


 345 
 346             @Override
 347             public boolean isSupportedLocale(Locale locale) {
 348                 return isSupportedCalendarLocale(locale);
 349             }
 350 
 351             @Override
 352             public String getDisplayName(String calType, int field, int value,
 353                                          int style, Locale locale) {
 354                 return null;
 355             }
 356 
 357             @Override
 358             public Map<String, Integer> getDisplayNames(String calType,
 359                                          int field, int style, Locale locale) {
 360                 return null;
 361             }
 362         };
 363     }
 364 
























 365     public static CurrencyNameProvider getCurrencyNameProvider() {
 366         return new CurrencyNameProvider() {
 367             @Override
 368             public Locale[] getAvailableLocales() {
 369                 return supportedLocale;
 370             }
 371 
 372             @Override
 373             public boolean isSupportedLocale(Locale locale) {
 374                 // Ignore the extensions for now
 375                 return supportedLocaleSet.contains(locale.stripExtensions());
 376             }
 377 
 378             @Override
 379             public String getDisplayName(String code, Locale locale) {
 380                 return getDisplayString(locale.toLanguageTag(), DN_CURRENCY_CODE, code);
 381             }
 382 
 383             @Override
 384             public String getSymbol(String code, Locale locale) {


 438             @Override
 439             public String getDisplayName(String ID, boolean daylight, int style, Locale locale) {
 440                 return getTimeZoneDisplayString(locale.toLanguageTag(), style * 2 + (daylight ? 1 : 0), ID);
 441             }
 442         };
 443     }
 444 
 445     private static Locale[] getSupportedCalendarLocales() {
 446         if (supportedLocale.length != 0 &&
 447             supportedLocaleSet.contains(Locale.JAPAN) &&
 448             isJapaneseCalendar()) {
 449             Locale[] sup = new Locale[supportedLocale.length+1];
 450             sup[0] = JRELocaleConstants.JA_JP_JP;
 451             System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);
 452             return sup;
 453         }
 454         return supportedLocale;
 455     }
 456 
 457     private static boolean isSupportedCalendarLocale(Locale locale) {
 458         // special case for ja_JP_JP
 459         if (JRELocaleConstants.JA_JP_JP.equals(locale)) {
 460             return isJapaneseCalendar();
 461         }
 462 
 463         Locale base = locale.stripExtensions();
 464         if (!supportedLocaleSet.contains(base)) {
 465             return false;
 466         }
 467 
 468         String caltype = locale.getUnicodeLocaleType("ca");
 469         if (caltype == null) {
 470             return true;
 471         }
 472 
 473         return caltype.replaceFirst("gregory", "gregorian").equals(
 474                 getCalendarID(locale.toLanguageTag()));


 475     }

 476 
 477     private static boolean isJapaneseCalendar() {
 478         return getCalendarID("ja-JP").equals("japanese");
 479     }
 480 
 481     private static Locale getCalendarLocale(Locale locale) {
 482         Locale.Builder lb = new Locale.Builder().setLocale(locale);
 483         String calid = getCalendarID(locale.toLanguageTag());
 484         switch (calid) {
 485             case "gregorian":
 486                 calid = "gregory";
 487                 // FALL THROUGH!
 488             case "japanese":
 489             case "buddhist":
 490                 lb.setUnicodeLocaleKeyword("ca", calid);
 491                 return lb.build();
 492             default:
 493                 return locale;
 494         }
 495     }
 496 
 497     // The following methods are copied from CLDRConverter build tool.
 498     private static String translateDateFormatLetters(String calendarType, String cldrFormat) {
 499         String pattern = cldrFormat;
 500         int length = pattern.length();
 501         boolean inQuote = false;
 502         StringBuilder jrePattern = new StringBuilder(length);
 503         int count = 0;
 504         char lastLetter = 0;
 505 
 506         for (int i = 0; i < length; i++) {
 507             char c = pattern.charAt(i);
 508 
 509             if (c == '\'') {
 510                 // '' is treated as a single quote regardless of being
 511                 // in a quoted section.
 512                 if ((i + 1) < length) {




  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;


  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


 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);


 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                              .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) {


 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) {