mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
909 lines
36 KiB
Java
909 lines
36 KiB
Java
/*
|
|
* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package sun.util.locale.provider;
|
|
|
|
import java.lang.ref.SoftReference;
|
|
import java.text.*;
|
|
import java.text.spi.DateFormatProvider;
|
|
import java.text.spi.DateFormatSymbolsProvider;
|
|
import java.text.spi.DecimalFormatSymbolsProvider;
|
|
import java.text.spi.NumberFormatProvider;
|
|
import java.util.Collections;
|
|
import java.util.Calendar;
|
|
import java.util.HashSet;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.ResourceBundle.Control;
|
|
import java.util.Set;
|
|
import java.util.TimeZone;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.ConcurrentMap;
|
|
import java.util.concurrent.atomic.AtomicReferenceArray;
|
|
import java.util.spi.CalendarDataProvider;
|
|
import java.util.spi.CalendarNameProvider;
|
|
import java.util.spi.CurrencyNameProvider;
|
|
import java.util.spi.LocaleNameProvider;
|
|
import java.util.spi.TimeZoneNameProvider;
|
|
import sun.text.spi.JavaTimeDateTimePatternProvider;
|
|
import sun.util.spi.CalendarProvider;
|
|
|
|
/**
|
|
* LocaleProviderAdapter implementation for the Mac OS X locale data
|
|
*
|
|
* @author Naoto Sato
|
|
*/
|
|
public class HostLocaleProviderAdapterImpl {
|
|
|
|
// per supported locale instances
|
|
private static final ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> dateFormatPatternsMap =
|
|
new ConcurrentHashMap<>(2);
|
|
private static final ConcurrentMap<Locale, SoftReference<AtomicReferenceArray<String>>> numberFormatPatternsMap =
|
|
new ConcurrentHashMap<>(2);
|
|
private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> dateFormatSymbolsMap =
|
|
new ConcurrentHashMap<>(2);
|
|
private static final ConcurrentMap<Locale, SoftReference<DecimalFormatSymbols>> decimalFormatSymbolsMap =
|
|
new ConcurrentHashMap<>(2);
|
|
|
|
// locale categories
|
|
private static final int CAT_DISPLAY = 0;
|
|
private static final int CAT_FORMAT = 1;
|
|
|
|
// NumberFormat styles
|
|
private static final int NF_NUMBER = 0;
|
|
private static final int NF_CURRENCY = 1;
|
|
private static final int NF_PERCENT = 2;
|
|
private static final int NF_INTEGER = 3;
|
|
private static final int NF_MAX = NF_INTEGER;
|
|
|
|
// CalendarData value types
|
|
private static final int CD_FIRSTDAYOFWEEK = 0;
|
|
private static final int CD_MINIMALDAYSINFIRSTWEEK = 1;
|
|
|
|
// Locale/Currency display name types
|
|
private static final int DN_LOCALE_LANGUAGE = 0;
|
|
private static final int DN_LOCALE_SCRIPT = 1;
|
|
private static final int DN_LOCALE_REGION = 2;
|
|
private static final int DN_LOCALE_VARIANT = 3;
|
|
private static final int DN_CURRENCY_CODE = 4;
|
|
private static final int DN_CURRENCY_SYMBOL = 5;
|
|
|
|
// TimeZone display name types
|
|
private static final int DN_TZ_SHORT_STANDARD = 0;
|
|
private static final int DN_TZ_SHORT_DST = 1;
|
|
private static final int DN_TZ_LONG_STANDARD = 2;
|
|
private static final int DN_TZ_LONG_DST = 3;
|
|
|
|
private static final Set<Locale> supportedLocaleSet;
|
|
static {
|
|
Set<Locale> tmpSet = new HashSet<>();
|
|
// Assuming the default locales do not include any extensions, so
|
|
// no stripping is needed here.
|
|
Locale l = convertMacOSXLocaleToJavaLocale(getDefaultLocale(CAT_FORMAT));
|
|
tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
|
|
l = convertMacOSXLocaleToJavaLocale(getDefaultLocale(CAT_DISPLAY));
|
|
tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
|
|
supportedLocaleSet = Collections.unmodifiableSet(tmpSet);
|
|
}
|
|
private static final Locale[] supportedLocale = supportedLocaleSet.toArray(new Locale[0]);
|
|
|
|
@SuppressWarnings("fallthrough")
|
|
private static Locale convertMacOSXLocaleToJavaLocale(String macosxloc) {
|
|
// MacOSX may return ICU notation, here is the quote from CFLocale doc:
|
|
// "The corresponding value is a CFString containing the POSIX locale
|
|
// identifier as used by ICU, such as "ja_JP". If you have a variant
|
|
// locale or a different currency or calendar, it can be as complex as
|
|
// "en_US_POSIX@calendar=japanese;currency=EUR" or
|
|
// "az_Cyrl_AZ@calendar=buddhist;currency=JPY".
|
|
String[] tmp = macosxloc.split("@");
|
|
String langTag = tmp[0].replace('_', '-');
|
|
if (tmp.length > 1) {
|
|
String[] ext = tmp[1].split(";");
|
|
for (String keyval : ext) {
|
|
// We are only interested in "calendar" value for now.
|
|
if (keyval.startsWith("calendar=")) {
|
|
String calid = keyval.substring(keyval.indexOf('=')+1);
|
|
switch (calid) {
|
|
case "gregorian":
|
|
langTag += "-u-ca-gregory";
|
|
break;
|
|
case "japanese":
|
|
// Tweak for ja_JP_JP
|
|
if (tmp[0].equals("ja_JP")) {
|
|
return JRELocaleConstants.JA_JP_JP;
|
|
}
|
|
|
|
// fall through
|
|
|
|
default:
|
|
langTag += "-u-ca-" + calid;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Locale.forLanguageTag(langTag);
|
|
}
|
|
|
|
public static JavaTimeDateTimePatternProvider getJavaTimeDateTimePatternProvider() {
|
|
return new JavaTimeDateTimePatternProvider() {
|
|
@Override
|
|
public Locale[] getAvailableLocales() {
|
|
return getSupportedCalendarLocales();
|
|
}
|
|
|
|
@Override
|
|
public boolean isSupportedLocale(Locale locale) {
|
|
return isSupportedCalendarLocale(locale);
|
|
}
|
|
|
|
@Override
|
|
public String getJavaTimeDateTimePattern(int timeStyle, int dateStyle, String calType, Locale locale) {
|
|
return toJavaTimeDateTimePattern(calType, getDateTimePattern(dateStyle, timeStyle, locale));
|
|
|
|
}
|
|
|
|
private String getDateTimePattern(int dateStyle, int timeStyle, Locale locale) {
|
|
AtomicReferenceArray<String> dateFormatPatterns;
|
|
SoftReference<AtomicReferenceArray<String>> ref = dateFormatPatternsMap.get(locale);
|
|
|
|
if (ref == null || (dateFormatPatterns = ref.get()) == null) {
|
|
dateFormatPatterns = new AtomicReferenceArray<>(5 * 5);
|
|
ref = new SoftReference<>(dateFormatPatterns);
|
|
dateFormatPatternsMap.put(locale, ref);
|
|
}
|
|
int index = (dateStyle + 1) * 5 + timeStyle + 1;
|
|
String pattern = dateFormatPatterns.get(index);
|
|
if (pattern == null) {
|
|
String langTag = locale.toLanguageTag();
|
|
pattern = translateDateFormatLetters(getCalendarID(langTag),
|
|
getDateTimePatternNative(dateStyle, timeStyle, langTag));
|
|
if (!dateFormatPatterns.compareAndSet(index, null, pattern)) {
|
|
pattern = dateFormatPatterns.get(index);
|
|
}
|
|
}
|
|
return pattern;
|
|
}
|
|
|
|
/**
|
|
* This method will convert JRE Date/time Pattern String to JSR310
|
|
* type Date/Time Pattern
|
|
*/
|
|
private String toJavaTimeDateTimePattern(String calendarType, String jrePattern) {
|
|
int length = jrePattern.length();
|
|
StringBuilder sb = new StringBuilder(length);
|
|
boolean inQuote = false;
|
|
int count = 0;
|
|
char lastLetter = 0;
|
|
for (int i = 0; i < length; i++) {
|
|
char c = jrePattern.charAt(i);
|
|
if (c == '\'') {
|
|
// '' is treated as a single quote regardless of being
|
|
// in a quoted section.
|
|
if ((i + 1) < length) {
|
|
char nextc = jrePattern.charAt(i + 1);
|
|
if (nextc == '\'') {
|
|
i++;
|
|
if (count != 0) {
|
|
convert(calendarType, lastLetter, count, sb);
|
|
lastLetter = 0;
|
|
count = 0;
|
|
}
|
|
sb.append("''");
|
|
continue;
|
|
}
|
|
}
|
|
if (!inQuote) {
|
|
if (count != 0) {
|
|
convert(calendarType, lastLetter, count, sb);
|
|
lastLetter = 0;
|
|
count = 0;
|
|
}
|
|
inQuote = true;
|
|
} else {
|
|
inQuote = false;
|
|
}
|
|
sb.append(c);
|
|
continue;
|
|
}
|
|
if (inQuote) {
|
|
sb.append(c);
|
|
continue;
|
|
}
|
|
if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
|
|
if (count != 0) {
|
|
convert(calendarType, lastLetter, count, sb);
|
|
lastLetter = 0;
|
|
count = 0;
|
|
}
|
|
sb.append(c);
|
|
continue;
|
|
}
|
|
if (lastLetter == 0 || lastLetter == c) {
|
|
lastLetter = c;
|
|
count++;
|
|
continue;
|
|
}
|
|
convert(calendarType, lastLetter, count, sb);
|
|
lastLetter = c;
|
|
count = 1;
|
|
}
|
|
if (inQuote) {
|
|
// should not come here.
|
|
// returning null so that FALLBACK provider will kick in.
|
|
return null;
|
|
}
|
|
if (count != 0) {
|
|
convert(calendarType, lastLetter, count, sb);
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
private void convert(String calendarType, char letter, int count, StringBuilder sb) {
|
|
switch (letter) {
|
|
case 'G':
|
|
if (calendarType.equals("japanese")) {
|
|
if (count >= 4) {
|
|
count = 1;
|
|
} else {
|
|
count = 5;
|
|
}
|
|
} else if (!calendarType.equals("iso8601")) {
|
|
// Gregorian calendar is iso8601 for java.time
|
|
// Adjust the number of 'G's
|
|
if (count >= 4) {
|
|
// JRE full -> JavaTime full
|
|
count = 4;
|
|
} else {
|
|
// JRE short -> JavaTime short
|
|
count = 1;
|
|
}
|
|
}
|
|
break;
|
|
case 'y':
|
|
if (calendarType.equals("japanese") && count >= 4) {
|
|
// JRE specific "gan-nen" support
|
|
count = 1;
|
|
}
|
|
break;
|
|
default:
|
|
// JSR 310 and CLDR define 5-letter patterns for narrow text.
|
|
if (count > 4) {
|
|
count = 4;
|
|
}
|
|
break;
|
|
}
|
|
appendN(letter, count, sb);
|
|
}
|
|
|
|
private void appendN(char c, int n, StringBuilder sb) {
|
|
for (int i = 0; i < n; i++) {
|
|
sb.append(c);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
public static DateFormatProvider getDateFormatProvider() {
|
|
return new DateFormatProvider() {
|
|
|
|
@Override
|
|
public Locale[] getAvailableLocales() {
|
|
return getSupportedCalendarLocales();
|
|
}
|
|
|
|
@Override
|
|
public boolean isSupportedLocale(Locale locale) {
|
|
return isSupportedCalendarLocale(locale);
|
|
}
|
|
|
|
@Override
|
|
public DateFormat getDateInstance(int style, Locale locale) {
|
|
return new SimpleDateFormat(getDateTimePattern(style, -1, locale),
|
|
getCalendarLocale(locale));
|
|
}
|
|
|
|
@Override
|
|
public DateFormat getTimeInstance(int style, Locale locale) {
|
|
return new SimpleDateFormat(getDateTimePattern(-1, style, locale),
|
|
getCalendarLocale(locale));
|
|
}
|
|
|
|
@Override
|
|
public DateFormat getDateTimeInstance(int dateStyle,
|
|
int timeStyle, Locale locale) {
|
|
return new SimpleDateFormat(getDateTimePattern(dateStyle, timeStyle, locale),
|
|
getCalendarLocale(locale));
|
|
}
|
|
|
|
private String getDateTimePattern(int dateStyle, int timeStyle, Locale locale) {
|
|
AtomicReferenceArray<String> dateFormatPatterns;
|
|
SoftReference<AtomicReferenceArray<String>> ref = dateFormatPatternsMap.get(locale);
|
|
|
|
if (ref == null || (dateFormatPatterns = ref.get()) == null) {
|
|
dateFormatPatterns = new AtomicReferenceArray<>(5 * 5);
|
|
ref = new SoftReference<>(dateFormatPatterns);
|
|
dateFormatPatternsMap.put(locale, ref);
|
|
}
|
|
|
|
int index = (dateStyle + 1) * 5 + timeStyle + 1;
|
|
String pattern = dateFormatPatterns.get(index);
|
|
if (pattern == null) {
|
|
String langTag = locale.toLanguageTag();
|
|
pattern = translateDateFormatLetters(getCalendarID(langTag),
|
|
getDateTimePatternNative(dateStyle, timeStyle, langTag));
|
|
if (!dateFormatPatterns.compareAndSet(index, null, pattern)) {
|
|
pattern = dateFormatPatterns.get(index);
|
|
}
|
|
}
|
|
|
|
return pattern;
|
|
}
|
|
};
|
|
}
|
|
|
|
public static DateFormatSymbolsProvider getDateFormatSymbolsProvider() {
|
|
return new DateFormatSymbolsProvider() {
|
|
@Override
|
|
public Locale[] getAvailableLocales() {
|
|
if (isSupportedLocale(Locale.getDefault(Locale.Category.FORMAT))) {
|
|
return supportedLocale;
|
|
}
|
|
return new Locale[0];
|
|
}
|
|
|
|
@Override
|
|
public boolean isSupportedLocale(Locale locale) {
|
|
// Only supports the locale with Gregorian calendar
|
|
Locale base = locale.stripExtensions();
|
|
if (supportedLocaleSet.contains(base)) {
|
|
return getCalendarID(locale.toLanguageTag()).equals("gregorian");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public DateFormatSymbols getInstance(Locale locale) {
|
|
DateFormatSymbols dateFormatSymbols;
|
|
SoftReference<DateFormatSymbols> ref = dateFormatSymbolsMap.get(locale);
|
|
|
|
if (ref == null || (dateFormatSymbols = ref.get()) == null) {
|
|
dateFormatSymbols = new DateFormatSymbols(locale);
|
|
String langTag = locale.toLanguageTag();
|
|
dateFormatSymbols.setAmPmStrings(getAmPmStrings(langTag, dateFormatSymbols.getAmPmStrings()));
|
|
dateFormatSymbols.setEras(getEras(langTag, dateFormatSymbols.getEras()));
|
|
dateFormatSymbols.setMonths(getMonths(langTag, dateFormatSymbols.getMonths()));
|
|
dateFormatSymbols.setShortMonths(getShortMonths(langTag, dateFormatSymbols.getShortMonths()));
|
|
dateFormatSymbols.setWeekdays(getWeekdays(langTag, dateFormatSymbols.getWeekdays()));
|
|
dateFormatSymbols.setShortWeekdays(getShortWeekdays(langTag, dateFormatSymbols.getShortWeekdays()));
|
|
ref = new SoftReference<>(dateFormatSymbols);
|
|
dateFormatSymbolsMap.put(locale, ref);
|
|
}
|
|
return (DateFormatSymbols)dateFormatSymbols.clone();
|
|
}
|
|
};
|
|
}
|
|
|
|
public static NumberFormatProvider getNumberFormatProvider() {
|
|
return new NumberFormatProvider() {
|
|
@Override
|
|
public Locale[] getAvailableLocales() {
|
|
return supportedLocale;
|
|
}
|
|
|
|
@Override
|
|
public boolean isSupportedLocale(Locale locale) {
|
|
// Ignore the extensions for now
|
|
return supportedLocaleSet.contains(locale.stripExtensions());
|
|
}
|
|
|
|
@Override
|
|
public NumberFormat getCurrencyInstance(Locale locale) {
|
|
return new DecimalFormat(getNumberPattern(NF_CURRENCY, locale),
|
|
DecimalFormatSymbols.getInstance(locale));
|
|
}
|
|
|
|
@Override
|
|
public NumberFormat getIntegerInstance(Locale locale) {
|
|
return new DecimalFormat(getNumberPattern(NF_INTEGER, locale),
|
|
DecimalFormatSymbols.getInstance(locale));
|
|
}
|
|
|
|
@Override
|
|
public NumberFormat getNumberInstance(Locale locale) {
|
|
return new DecimalFormat(getNumberPattern(NF_NUMBER, locale),
|
|
DecimalFormatSymbols.getInstance(locale));
|
|
}
|
|
|
|
@Override
|
|
public NumberFormat getPercentInstance(Locale locale) {
|
|
return new DecimalFormat(getNumberPattern(NF_PERCENT, locale),
|
|
DecimalFormatSymbols.getInstance(locale));
|
|
}
|
|
|
|
private String getNumberPattern(int style, Locale locale) {
|
|
AtomicReferenceArray<String> numberFormatPatterns;
|
|
SoftReference<AtomicReferenceArray<String>> ref = numberFormatPatternsMap.get(locale);
|
|
|
|
if (ref == null || (numberFormatPatterns = ref.get()) == null) {
|
|
numberFormatPatterns = new AtomicReferenceArray<>(4);
|
|
ref = new SoftReference<>(numberFormatPatterns);
|
|
numberFormatPatternsMap.put(locale, ref);
|
|
}
|
|
|
|
String pattern = numberFormatPatterns.get(style);
|
|
if (pattern == null) {
|
|
pattern = getNumberPatternNative(style, locale.toLanguageTag());
|
|
if (!numberFormatPatterns.compareAndSet(style, null, pattern)) {
|
|
pattern = numberFormatPatterns.get(style);
|
|
}
|
|
}
|
|
|
|
return pattern;
|
|
}
|
|
};
|
|
}
|
|
|
|
public static DecimalFormatSymbolsProvider getDecimalFormatSymbolsProvider() {
|
|
return new DecimalFormatSymbolsProvider() {
|
|
|
|
@Override
|
|
public Locale[] getAvailableLocales() {
|
|
return supportedLocale;
|
|
}
|
|
|
|
@Override
|
|
public boolean isSupportedLocale(Locale locale) {
|
|
// Ignore the extensions for now
|
|
return supportedLocaleSet.contains(locale.stripExtensions());
|
|
}
|
|
|
|
@Override
|
|
public DecimalFormatSymbols getInstance(Locale locale) {
|
|
DecimalFormatSymbols decimalFormatSymbols;
|
|
SoftReference<DecimalFormatSymbols> ref = decimalFormatSymbolsMap.get(locale);
|
|
|
|
if (ref == null || (decimalFormatSymbols = ref.get()) == null) {
|
|
decimalFormatSymbols = new DecimalFormatSymbols(locale);
|
|
String langTag = locale.toLanguageTag();
|
|
|
|
// DecimalFormatSymbols.setInternationalCurrencySymbol() has
|
|
// a side effect of setting the currency symbol as well. So
|
|
// the calling order is relevant here.
|
|
decimalFormatSymbols.setInternationalCurrencySymbol(getInternationalCurrencySymbol(langTag, decimalFormatSymbols.getInternationalCurrencySymbol()));
|
|
decimalFormatSymbols.setCurrencySymbol(getCurrencySymbol(langTag, decimalFormatSymbols.getCurrencySymbol()));
|
|
decimalFormatSymbols.setDecimalSeparator(getDecimalSeparator(langTag, decimalFormatSymbols.getDecimalSeparator()));
|
|
decimalFormatSymbols.setGroupingSeparator(getGroupingSeparator(langTag, decimalFormatSymbols.getGroupingSeparator()));
|
|
decimalFormatSymbols.setInfinity(getInfinity(langTag, decimalFormatSymbols.getInfinity()));
|
|
decimalFormatSymbols.setMinusSign(getMinusSign(langTag, decimalFormatSymbols.getMinusSign()));
|
|
decimalFormatSymbols.setMonetaryDecimalSeparator(getMonetaryDecimalSeparator(langTag, decimalFormatSymbols.getMonetaryDecimalSeparator()));
|
|
decimalFormatSymbols.setNaN(getNaN(langTag, decimalFormatSymbols.getNaN()));
|
|
decimalFormatSymbols.setPercent(getPercent(langTag, decimalFormatSymbols.getPercent()));
|
|
decimalFormatSymbols.setPerMill(getPerMill(langTag, decimalFormatSymbols.getPerMill()));
|
|
decimalFormatSymbols.setZeroDigit(getZeroDigit(langTag, decimalFormatSymbols.getZeroDigit()));
|
|
decimalFormatSymbols.setExponentSeparator(getExponentSeparator(langTag, decimalFormatSymbols.getExponentSeparator()));
|
|
ref = new SoftReference<>(decimalFormatSymbols);
|
|
decimalFormatSymbolsMap.put(locale, ref);
|
|
}
|
|
return (DecimalFormatSymbols)decimalFormatSymbols.clone();
|
|
}
|
|
};
|
|
}
|
|
|
|
public static CalendarDataProvider getCalendarDataProvider() {
|
|
return new CalendarDataProvider() {
|
|
@Override
|
|
public Locale[] getAvailableLocales() {
|
|
return getSupportedCalendarLocales();
|
|
}
|
|
|
|
@Override
|
|
public boolean isSupportedLocale(Locale locale) {
|
|
return isSupportedCalendarLocale(locale);
|
|
}
|
|
|
|
@Override
|
|
public int getFirstDayOfWeek(Locale locale) {
|
|
return getCalendarInt(locale.toLanguageTag(), CD_FIRSTDAYOFWEEK);
|
|
}
|
|
|
|
@Override
|
|
public int getMinimalDaysInFirstWeek(Locale locale) {
|
|
return getCalendarInt(locale.toLanguageTag(), CD_MINIMALDAYSINFIRSTWEEK);
|
|
}
|
|
};
|
|
}
|
|
|
|
public static CalendarNameProvider getCalendarNameProvider() {
|
|
return new CalendarNameProvider() {
|
|
@Override
|
|
public Locale[] getAvailableLocales() {
|
|
return getSupportedCalendarLocales();
|
|
}
|
|
|
|
@Override
|
|
public boolean isSupportedLocale(Locale locale) {
|
|
return isSupportedCalendarLocale(locale);
|
|
}
|
|
|
|
@Override
|
|
public String getDisplayName(String calType, int field, int value,
|
|
int style, Locale locale) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public Map<String, Integer> getDisplayNames(String calType,
|
|
int field, int style, Locale locale) {
|
|
return null;
|
|
}
|
|
};
|
|
}
|
|
|
|
public static CalendarProvider getCalendarProvider() {
|
|
return new CalendarProvider() {
|
|
@Override
|
|
public Locale[] getAvailableLocales() {
|
|
return getSupportedCalendarLocales();
|
|
}
|
|
|
|
@Override
|
|
public boolean isSupportedLocale(Locale locale) {
|
|
return isSupportedCalendarLocale(locale);
|
|
}
|
|
|
|
@Override
|
|
public Calendar getInstance(TimeZone zone, Locale locale) {
|
|
return new Calendar.Builder()
|
|
.setLocale(locale)
|
|
.setCalendarType(getCalendarID(locale.toLanguageTag()))
|
|
.setTimeZone(zone)
|
|
.setInstant(System.currentTimeMillis())
|
|
.build();
|
|
}
|
|
};
|
|
}
|
|
|
|
public static CurrencyNameProvider getCurrencyNameProvider() {
|
|
return new CurrencyNameProvider() {
|
|
@Override
|
|
public Locale[] getAvailableLocales() {
|
|
return supportedLocale;
|
|
}
|
|
|
|
@Override
|
|
public boolean isSupportedLocale(Locale locale) {
|
|
// Ignore the extensions for now
|
|
return supportedLocaleSet.contains(locale.stripExtensions());
|
|
}
|
|
|
|
@Override
|
|
public String getDisplayName(String code, Locale locale) {
|
|
return getDisplayString(locale.toLanguageTag(), DN_CURRENCY_CODE, code);
|
|
}
|
|
|
|
@Override
|
|
public String getSymbol(String code, Locale locale) {
|
|
return getDisplayString(locale.toLanguageTag(), DN_CURRENCY_SYMBOL, code);
|
|
}
|
|
};
|
|
}
|
|
|
|
public static LocaleNameProvider getLocaleNameProvider() {
|
|
return new LocaleNameProvider() {
|
|
@Override
|
|
public Locale[] getAvailableLocales() {
|
|
return supportedLocale;
|
|
}
|
|
|
|
@Override
|
|
public boolean isSupportedLocale(Locale locale) {
|
|
// Ignore the extensions for now
|
|
return supportedLocaleSet.contains(locale.stripExtensions());
|
|
}
|
|
|
|
@Override
|
|
public String getDisplayLanguage(String languageCode, Locale locale) {
|
|
return getDisplayString(locale.toLanguageTag(), DN_LOCALE_LANGUAGE, languageCode);
|
|
}
|
|
|
|
@Override
|
|
public String getDisplayCountry(String countryCode, Locale locale) {
|
|
return getDisplayString(locale.toLanguageTag(), DN_LOCALE_REGION, countryCode);
|
|
}
|
|
|
|
@Override
|
|
public String getDisplayScript(String scriptCode, Locale locale) {
|
|
return getDisplayString(locale.toLanguageTag(), DN_LOCALE_SCRIPT, scriptCode);
|
|
}
|
|
|
|
@Override
|
|
public String getDisplayVariant(String variantCode, Locale locale) {
|
|
return getDisplayString(locale.toLanguageTag(), DN_LOCALE_VARIANT, variantCode);
|
|
}
|
|
};
|
|
}
|
|
|
|
public static TimeZoneNameProvider getTimeZoneNameProvider() {
|
|
return new TimeZoneNameProvider() {
|
|
@Override
|
|
public Locale[] getAvailableLocales() {
|
|
return supportedLocale;
|
|
}
|
|
|
|
@Override
|
|
public boolean isSupportedLocale(Locale locale) {
|
|
// Ignore the extensions for now
|
|
return supportedLocaleSet.contains(locale.stripExtensions());
|
|
}
|
|
|
|
@Override
|
|
public String getDisplayName(String ID, boolean daylight, int style, Locale locale) {
|
|
return getTimeZoneDisplayString(locale.toLanguageTag(), style * 2 + (daylight ? 1 : 0), ID);
|
|
}
|
|
};
|
|
}
|
|
|
|
private static Locale[] getSupportedCalendarLocales() {
|
|
if (supportedLocale.length != 0 &&
|
|
supportedLocaleSet.contains(Locale.JAPAN) &&
|
|
isJapaneseCalendar()) {
|
|
Locale[] sup = new Locale[supportedLocale.length+1];
|
|
sup[0] = JRELocaleConstants.JA_JP_JP;
|
|
System.arraycopy(supportedLocale, 0, sup, 1, supportedLocale.length);
|
|
return sup;
|
|
}
|
|
return supportedLocale;
|
|
}
|
|
|
|
private static boolean isSupportedCalendarLocale(Locale locale) {
|
|
Locale base = locale;
|
|
|
|
if (base.hasExtensions() || base.getVariant() != "") {
|
|
base = new Locale.Builder()
|
|
.setLocale(locale)
|
|
.clearExtensions()
|
|
.build();
|
|
}
|
|
|
|
if (!supportedLocaleSet.contains(base)) {
|
|
return false;
|
|
}
|
|
|
|
String requestedCalType = locale.getUnicodeLocaleType("ca");
|
|
String nativeCalType =
|
|
getCalendarID(base.toLanguageTag()).replaceFirst("gregorian", "gregory");
|
|
|
|
if (requestedCalType == null) {
|
|
return Calendar.getAvailableCalendarTypes().contains(nativeCalType);
|
|
} else {
|
|
return requestedCalType.equals(nativeCalType);
|
|
}
|
|
}
|
|
|
|
private static boolean isJapaneseCalendar() {
|
|
return getCalendarID("ja-JP").equals("japanese");
|
|
}
|
|
|
|
private static Locale getCalendarLocale(Locale locale) {
|
|
String nativeCalType = getCalendarID(locale.toLanguageTag())
|
|
.replaceFirst("gregorian", "gregory");
|
|
if (Calendar.getAvailableCalendarTypes().contains(nativeCalType)) {
|
|
return new Locale.Builder()
|
|
.setLocale(locale)
|
|
.setUnicodeLocaleKeyword("ca", nativeCalType)
|
|
.build();
|
|
} else {
|
|
return locale;
|
|
}
|
|
}
|
|
|
|
// The following methods are copied from CLDRConverter build tool.
|
|
private static String translateDateFormatLetters(String calendarType, String cldrFormat) {
|
|
String pattern = cldrFormat;
|
|
int length = pattern.length();
|
|
boolean inQuote = false;
|
|
StringBuilder jrePattern = new StringBuilder(length);
|
|
int count = 0;
|
|
char lastLetter = 0;
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
char c = pattern.charAt(i);
|
|
|
|
if (c == '\'') {
|
|
// '' is treated as a single quote regardless of being
|
|
// in a quoted section.
|
|
if ((i + 1) < length) {
|
|
char nextc = pattern.charAt(i + 1);
|
|
if (nextc == '\'') {
|
|
i++;
|
|
if (count != 0) {
|
|
convert(calendarType, lastLetter, count, jrePattern);
|
|
lastLetter = 0;
|
|
count = 0;
|
|
}
|
|
jrePattern.append("''");
|
|
continue;
|
|
}
|
|
}
|
|
if (!inQuote) {
|
|
if (count != 0) {
|
|
convert(calendarType, lastLetter, count, jrePattern);
|
|
lastLetter = 0;
|
|
count = 0;
|
|
}
|
|
inQuote = true;
|
|
} else {
|
|
inQuote = false;
|
|
}
|
|
jrePattern.append(c);
|
|
continue;
|
|
}
|
|
if (inQuote) {
|
|
jrePattern.append(c);
|
|
continue;
|
|
}
|
|
if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
|
|
if (count != 0) {
|
|
convert(calendarType, lastLetter, count, jrePattern);
|
|
lastLetter = 0;
|
|
count = 0;
|
|
}
|
|
jrePattern.append(c);
|
|
continue;
|
|
}
|
|
|
|
if (lastLetter == 0 || lastLetter == c) {
|
|
lastLetter = c;
|
|
count++;
|
|
continue;
|
|
}
|
|
convert(calendarType, lastLetter, count, jrePattern);
|
|
lastLetter = c;
|
|
count = 1;
|
|
}
|
|
|
|
if (count != 0) {
|
|
convert(calendarType, lastLetter, count, jrePattern);
|
|
}
|
|
if (cldrFormat.contentEquals(jrePattern)) {
|
|
return cldrFormat;
|
|
}
|
|
return jrePattern.toString();
|
|
}
|
|
|
|
private static void convert(String calendarType, char cldrLetter, int count, StringBuilder sb) {
|
|
switch (cldrLetter) {
|
|
case 'G':
|
|
if (!calendarType.equals("gregorian")) {
|
|
// Adjust the number of 'G's for JRE SimpleDateFormat
|
|
if (count == 5) {
|
|
// CLDR narrow -> JRE short
|
|
count = 1;
|
|
} else if (count == 1) {
|
|
// CLDR abbr -> JRE long
|
|
count = 4;
|
|
}
|
|
}
|
|
appendN(cldrLetter, count, sb);
|
|
break;
|
|
|
|
// TODO: support 'c' and 'e' in JRE SimpleDateFormat
|
|
// Use 'u' and 'E' for now.
|
|
case 'c':
|
|
case 'e':
|
|
switch (count) {
|
|
case 1:
|
|
sb.append('u');
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
appendN('E', count, sb);
|
|
break;
|
|
case 5:
|
|
appendN('E', 3, sb);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 'v':
|
|
case 'V':
|
|
appendN('z', count, sb);
|
|
break;
|
|
|
|
case 'Z':
|
|
if (count == 4 || count == 5) {
|
|
sb.append("XXX");
|
|
}
|
|
break;
|
|
|
|
case 'u':
|
|
case 'U':
|
|
case 'q':
|
|
case 'Q':
|
|
case 'l':
|
|
case 'g':
|
|
case 'j':
|
|
case 'A':
|
|
// Unsupported letter. Just append it within quotes
|
|
sb.append('\'');
|
|
sb.append(cldrLetter);
|
|
sb.append('\'');
|
|
break;
|
|
|
|
default:
|
|
appendN(cldrLetter, count, sb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static void appendN(char c, int n, StringBuilder sb) {
|
|
for (int i = 0; i < n; i++) {
|
|
sb.append(c);
|
|
}
|
|
}
|
|
|
|
// initialize
|
|
private static native String getDefaultLocale(int cat);
|
|
|
|
// For DateFormatProvider
|
|
private static native String getDateTimePatternNative(int dateStyle, int timeStyle, String langtag);
|
|
private static native String getCalendarID(String langTag);
|
|
|
|
// For NumberFormatProvider
|
|
private static native String getNumberPatternNative(int style, String langtag);
|
|
|
|
// For DateFormatSymbolsProvider
|
|
private static native String[] getAmPmStrings(String langTag, String[] ampm);
|
|
private static native String[] getEras(String langTag, String[] eras);
|
|
private static native String[] getMonths(String langTag, String[] months);
|
|
private static native String[] getShortMonths(String langTag, String[] smonths);
|
|
private static native String[] getWeekdays(String langTag, String[] wdays);
|
|
private static native String[] getShortWeekdays(String langTag, String[] swdays);
|
|
|
|
// For DecimalFormatSymbolsProvider
|
|
private static native String getCurrencySymbol(String langTag, String currencySymbol);
|
|
private static native char getDecimalSeparator(String langTag, char decimalSeparator);
|
|
private static native char getGroupingSeparator(String langTag, char groupingSeparator);
|
|
private static native String getInfinity(String langTag, String infinity);
|
|
private static native String getInternationalCurrencySymbol(String langTag, String internationalCurrencySymbol);
|
|
private static native char getMinusSign(String langTag, char minusSign);
|
|
private static native char getMonetaryDecimalSeparator(String langTag, char monetaryDecimalSeparator);
|
|
private static native String getNaN(String langTag, String nan);
|
|
private static native char getPercent(String langTag, char percent);
|
|
private static native char getPerMill(String langTag, char perMill);
|
|
private static native char getZeroDigit(String langTag, char zeroDigit);
|
|
private static native String getExponentSeparator(String langTag, String exponent);
|
|
|
|
// For CalendarDataProvider
|
|
private static native int getCalendarInt(String langTag, int type);
|
|
|
|
// For Locale/CurrencyNameProvider
|
|
private static native String getDisplayString(String langTag, int key, String value);
|
|
|
|
// For TimeZoneNameProvider
|
|
private static native String getTimeZoneDisplayString(String langTag, int style, String value);
|
|
}
|