8010666: Implement Currency/LocaleNameProvider in Windows Host LocaleProviderAdapter

Reviewed-by: okutsu
This commit is contained in:
Naoto Sato 2013-04-22 13:37:07 -07:00
parent 3b5f4fc54a
commit 79174472a1
5 changed files with 297 additions and 50 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2013, 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
@ -520,14 +520,22 @@ public class HostLocaleProviderAdapterImpl {
}
private static boolean isSupportedCalendarLocale(Locale locale) {
Locale base = locale.stripExtensions();
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(locale.toLanguageTag()).replaceFirst("gregorian", "gregory");
getCalendarID(base.toLanguageTag()).replaceFirst("gregorian", "gregory");
if (requestedCalType == null) {
return Calendar.getAvailableCalendarTypes().contains(nativeCalType);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2013, 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
@ -37,6 +37,7 @@ import java.text.spi.DecimalFormatSymbolsProvider;
import java.text.spi.NumberFormatProvider;
import java.util.Calendar;
import java.util.Collections;
import java.util.Currency;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
@ -48,6 +49,8 @@ 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 sun.util.spi.CalendarProvider;
/**
@ -72,6 +75,14 @@ public class HostLocaleProviderAdapterImpl {
private static final int CD_FIRSTDAYOFWEEK = 0;
private static final int CD_MINIMALDAYSINFIRSTWEEK = 1;
// Currency/Locale display name types
private static final int DN_CURRENCY_NAME = 0;
private static final int DN_CURRENCY_SYMBOL = 1;
private static final int DN_LOCALE_LANGUAGE = 2;
private static final int DN_LOCALE_SCRIPT = 3;
private static final int DN_LOCALE_REGION = 4;
private static final int DN_LOCALE_VARIANT = 5;
// Native Calendar ID to LDML calendar type map
private static final String[] calIDToLDML = {
"",
@ -96,15 +107,25 @@ public class HostLocaleProviderAdapterImpl {
private static ConcurrentMap<Locale, SoftReference<DecimalFormatSymbols>> decimalFormatSymbolsCache = new ConcurrentHashMap<>();
private static final Set<Locale> supportedLocaleSet;
private static final String nativeDisplayLanguage;
static {
Set<Locale> tmpSet = new HashSet<>();
if (initialize()) {
// Assuming the default locales do not include any extensions, so
// no stripping is needed here.
Locale l = Locale.forLanguageTag(getDefaultLocale(CAT_FORMAT).replace('_', '-'));
tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
l = Locale.forLanguageTag(getDefaultLocale(CAT_DISPLAY).replace('_', '-'));
tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
Control c = Control.getNoFallbackControl(Control.FORMAT_DEFAULT);
String displayLocale = getDefaultLocale(CAT_DISPLAY);
Locale l = Locale.forLanguageTag(displayLocale.replace('_', '-'));
tmpSet.addAll(c.getCandidateLocales("", l));
nativeDisplayLanguage = l.getLanguage();
String formatLocale = getDefaultLocale(CAT_FORMAT);
if (!formatLocale.equals(displayLocale)) {
l = Locale.forLanguageTag(formatLocale.replace('_', '-'));
tmpSet.addAll(c.getCandidateLocales("", l));
}
} else {
nativeDisplayLanguage = "";
}
supportedLocaleSet = Collections.unmodifiableSet(tmpSet);
}
@ -392,6 +413,96 @@ public class HostLocaleProviderAdapterImpl {
};
}
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()) &&
locale.getLanguage().equals(nativeDisplayLanguage);
}
@Override
public String getSymbol(String currencyCode, Locale locale) {
// Retrieves the currency symbol by calling
// GetLocaleInfoEx(LOCALE_SCURRENCY).
// It only works with the "locale"'s currency in its native
// language.
try {
if (Currency.getInstance(locale).getCurrencyCode()
.equals(currencyCode)) {
return getDisplayString(locale.toLanguageTag(),
DN_CURRENCY_SYMBOL, currencyCode);
}
} catch (IllegalArgumentException iae) {}
return null;
}
@Override
public String getDisplayName(String currencyCode, Locale locale) {
// Retrieves the display name by calling
// GetLocaleInfoEx(LOCALE_SNATIVECURRNAME).
// It only works with the "locale"'s currency in its native
// language.
try {
if (Currency.getInstance(locale).getCurrencyCode()
.equals(currencyCode)) {
return getDisplayString(locale.toLanguageTag(),
DN_CURRENCY_NAME, currencyCode);
}
} catch (IllegalArgumentException iae) {}
return null;
}
};
}
public static LocaleNameProvider getLocaleNameProvider() {
return new LocaleNameProvider() {
@Override
public Locale[] getAvailableLocales() {
return supportedLocale;
}
@Override
public boolean isSupportedLocale(Locale locale) {
return supportedLocaleSet.contains(locale.stripExtensions()) &&
locale.getLanguage().equals(nativeDisplayLanguage);
}
@Override
public String getDisplayLanguage(String languageCode, Locale locale) {
// Retrieves the display language name by calling
// GetLocaleInfoEx(LOCALE_SLOCALIZEDLANGUAGENAME).
return getDisplayString(locale.toLanguageTag(),
DN_LOCALE_LANGUAGE, languageCode);
}
@Override
public String getDisplayCountry(String countryCode, Locale locale) {
// Retrieves the display country name by calling
// GetLocaleInfoEx(LOCALE_SLOCALIZEDCOUNTRYNAME).
return getDisplayString(locale.toLanguageTag(),
DN_LOCALE_REGION, nativeDisplayLanguage+"-"+countryCode);
}
@Override
public String getDisplayScript(String scriptCode, Locale locale) {
return null;
}
@Override
public String getDisplayVariant(String variantCode, Locale locale) {
return null;
}
};
}
private static String convertDateTimePattern(String winPattern) {
String ret = winPattern.replaceAll("dddd", "EEEE");
ret = ret.replaceAll("ddd", "EEE");
@ -413,12 +524,21 @@ public class HostLocaleProviderAdapterImpl {
}
private static boolean isSupportedCalendarLocale(Locale locale) {
Locale base = locale.stripExtensions();
Locale base = locale;
if (base.hasExtensions() || base.getVariant() != "") {
// strip off extensions and variant.
base = new Locale.Builder()
.setLocale(locale)
.clearExtensions()
.build();
}
if (!supportedLocaleSet.contains(base)) {
return false;
}
int calid = getCalendarID(locale.toLanguageTag());
int calid = getCalendarID(base.toLanguageTag());
if (calid <= 0 || calid >= calIDToLDML.length) {
return false;
}
@ -546,4 +666,7 @@ public class HostLocaleProviderAdapterImpl {
// For CalendarDataProvider
private static native int getCalendarDataValue(String langTag, int type);
// For Locale/CurrencyNameProvider
private static native String getDisplayString(String langTag, int key, String value);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2013, 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
@ -196,7 +196,7 @@ JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapte
break;
}
localeString = getJavaIDFromLangID(langid);
localeString = (char *)getJavaIDFromLangID(langid);
ret = (*env)->NewStringUTF(env, localeString);
free(localeString);
return ret;
@ -366,12 +366,14 @@ JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapte
*/
JNIEXPORT jboolean JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_isNativeDigit
(JNIEnv *env, jclass cls, jstring jlangtag) {
WCHAR buf[BUFLEN];
DWORD num;
const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
int got = getLocaleInfoWrapper(langtag, LOCALE_IDIGITSUBSTITUTION, buf, BUFLEN);
int got = getLocaleInfoWrapper(langtag,
LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER,
(LPWSTR)&num, sizeof(num));
(*env)->ReleaseStringChars(env, jlangtag, langtag);
return got && buf[0] == L'2'; // 2: native digit substitution
return got && num == 2; // 2: native digit substitution
}
/*
@ -590,25 +592,72 @@ JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterI
*/
JNIEXPORT jint JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCalendarDataValue
(JNIEnv *env, jclass cls, jstring jlangtag, jint type) {
WCHAR buf[BUFLEN];
DWORD num;
const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
int got = 0;
switch (type) {
case sun_util_locale_provider_HostLocaleProviderAdapterImpl_CD_FIRSTDAYOFWEEK:
got = getLocaleInfoWrapper(langtag, LOCALE_IFIRSTDAYOFWEEK, buf, BUFLEN);
got = getLocaleInfoWrapper(langtag,
LOCALE_IFIRSTDAYOFWEEK | LOCALE_RETURN_NUMBER,
(LPWSTR)&num, sizeof(num));
break;
}
(*env)->ReleaseStringChars(env, jlangtag, langtag);
if (got) {
return _wtoi(buf);
return num;
} else {
return -1;
}
}
/*
* Class: sun_util_locale_provider_HostLocaleProviderAdapterImpl
* Method: getDisplayString
* Signature: (Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getDisplayString
(JNIEnv *env, jclass cls, jstring jlangtag, jint type, jstring jvalue) {
LCTYPE lcType;
jstring jStr;
const jchar * pjChar;
WCHAR buf[BUFLEN];
int got = 0;
switch (type) {
case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_CURRENCY_NAME:
lcType = LOCALE_SNATIVECURRNAME;
jStr = jlangtag;
break;
case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_CURRENCY_SYMBOL:
lcType = LOCALE_SCURRENCY;
jStr = jlangtag;
break;
case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_LOCALE_LANGUAGE:
lcType = LOCALE_SLOCALIZEDLANGUAGENAME;
jStr = jvalue;
break;
case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_LOCALE_REGION:
lcType = LOCALE_SLOCALIZEDCOUNTRYNAME;
jStr = jvalue;
break;
default:
return NULL;
}
pjChar = (*env)->GetStringChars(env, jStr, JNI_FALSE);
got = getLocaleInfoWrapper(pjChar, lcType, buf, BUFLEN);
(*env)->ReleaseStringChars(env, jStr, pjChar);
if (got) {
return (*env)->NewString(env, buf, wcslen(buf));
} else {
return NULL;
}
}
int getLocaleInfoWrapper(const jchar *langtag, LCTYPE type, LPWSTR data, int buflen) {
if (pGetLocaleInfoEx) {
if (wcscmp(L"und", (LPWSTR)langtag) == 0) {
@ -642,11 +691,13 @@ int getCalendarInfoWrapper(const jchar *langtag, CALID id, LPCWSTR reserved, CAL
}
jint getCalendarID(const jchar *langtag) {
WCHAR type[BUFLEN];
int got = getLocaleInfoWrapper(langtag, LOCALE_ICALENDARTYPE, type, BUFLEN);
DWORD type;
int got = getLocaleInfoWrapper(langtag,
LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
(LPWSTR)&type, sizeof(type));
if (got) {
return _wtoi(type);
return type;
} else {
return 0;
}
@ -691,28 +742,37 @@ WCHAR * getNumberPattern(const jchar * langtag, const jint numberStyle) {
}
void getNumberPart(const jchar * langtag, const jint numberStyle, WCHAR * number) {
WCHAR buf[BUFLEN];
DWORD digits = 0;
DWORD leadingZero = 0;
WCHAR grouping[BUFLEN];
int groupingLen;
WCHAR fractionPattern[BUFLEN];
WCHAR * integerPattern = number;
int digits;
BOOL leadingZero;
WCHAR * pDest;
int groupingLen;
// Get info from Windows
if (numberStyle == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_CURRENCY) {
getLocaleInfoWrapper(langtag, LOCALE_ICURRDIGITS, buf, BUFLEN);
} else {
getLocaleInfoWrapper(langtag, LOCALE_IDIGITS, buf, BUFLEN);
switch (numberStyle) {
case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_CURRENCY:
getLocaleInfoWrapper(langtag,
LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER,
(LPWSTR)&digits, sizeof(digits));
break;
case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_INTEGER:
break;
case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_NUMBER:
case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_PERCENT:
default:
getLocaleInfoWrapper(langtag,
LOCALE_IDIGITS | LOCALE_RETURN_NUMBER,
(LPWSTR)&digits, sizeof(digits));
break;
}
if (numberStyle == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_INTEGER) {
digits = 0;
} else {
digits = _wtoi(buf);
}
getLocaleInfoWrapper(langtag, LOCALE_ILZERO, buf, BUFLEN);
leadingZero = _wtoi(buf) != 0;
getLocaleInfoWrapper(langtag,
LOCALE_ILZERO | LOCALE_RETURN_NUMBER,
(LPWSTR)&leadingZero, sizeof(leadingZero));
groupingLen = getLocaleInfoWrapper(langtag, LOCALE_SGROUPING, grouping, BUFLEN);
// fraction pattern
@ -749,7 +809,7 @@ void getNumberPart(const jchar * langtag, const jint numberStyle, WCHAR * number
}
}
if (leadingZero) {
if (leadingZero != 0) {
*pDest++ = L'0';
} else {
*pDest++ = L'#';
@ -760,29 +820,35 @@ void getNumberPart(const jchar * langtag, const jint numberStyle, WCHAR * number
}
void getFixPart(const jchar * langtag, const jint numberStyle, BOOL positive, BOOL prefix, WCHAR * ret) {
WCHAR buf[BUFLEN];
int pattern = 0;
DWORD pattern = 0;
int style = numberStyle;
int got = 0;
if (positive) {
if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_CURRENCY) {
got = getLocaleInfoWrapper(langtag, LOCALE_ICURRENCY, buf, BUFLEN);
got = getLocaleInfoWrapper(langtag,
LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER,
(LPWSTR)&pattern, sizeof(pattern));
} else if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_PERCENT) {
got = getLocaleInfoWrapper(langtag, LOCALE_IPOSITIVEPERCENT, buf, BUFLEN);
got = getLocaleInfoWrapper(langtag,
LOCALE_IPOSITIVEPERCENT | LOCALE_RETURN_NUMBER,
(LPWSTR)&pattern, sizeof(pattern));
}
} else {
if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_CURRENCY) {
got = getLocaleInfoWrapper(langtag, LOCALE_INEGCURR, buf, BUFLEN);
got = getLocaleInfoWrapper(langtag,
LOCALE_INEGCURR | LOCALE_RETURN_NUMBER,
(LPWSTR)&pattern, sizeof(pattern));
} else if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_PERCENT) {
got = getLocaleInfoWrapper(langtag, LOCALE_INEGATIVEPERCENT, buf, BUFLEN);
got = getLocaleInfoWrapper(langtag,
LOCALE_INEGATIVEPERCENT | LOCALE_RETURN_NUMBER,
(LPWSTR)&pattern, sizeof(pattern));
} else {
got = getLocaleInfoWrapper(langtag, LOCALE_INEGNUMBER, buf, BUFLEN);
got = getLocaleInfoWrapper(langtag,
LOCALE_INEGNUMBER | LOCALE_RETURN_NUMBER,
(LPWSTR)&pattern, sizeof(pattern));
}
}
if (got) {
pattern = _wtoi(buf);
}
if (numberStyle == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_INTEGER) {
style = sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_NUMBER;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2013, 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
@ -23,6 +23,7 @@
import java.text.*;
import java.text.spi.*;
import java.util.*;
import java.util.spi.*;
import sun.util.locale.provider.LocaleProviderAdapter;
public class LocaleProviders {
@ -55,6 +56,10 @@ public class LocaleProviders {
bug8001440Test();
break;
case "bug8010666Test":
bug8010666Test();
break;
default:
throw new RuntimeException("Test method '"+methodName+"' not found.");
}
@ -103,4 +108,38 @@ public class LocaleProviders {
NumberFormat nf = NumberFormat.getInstance(locale);
String nu = nf.format(1234560);
}
// This test assumes Windows localized language/country display names.
static void bug8010666Test() {
if (System.getProperty("os.name").startsWith("Windows")) {
NumberFormat nf = NumberFormat.getInstance(Locale.US);
try {
double ver = nf.parse(System.getProperty("os.version")).doubleValue();
System.out.printf("Windows version: %.1f\n", ver);
if (ver >= 6.0) {
LocaleProviderAdapter lda = LocaleProviderAdapter.getAdapter(LocaleNameProvider.class, Locale.ENGLISH);
LocaleProviderAdapter.Type type = lda.getAdapterType();
if (type == LocaleProviderAdapter.Type.HOST) {
Locale mkmk = Locale.forLanguageTag("mk-MK");
String result = mkmk.getDisplayLanguage(Locale.ENGLISH);
if (!"Macedonian (FYROM)".equals(result)) {
throw new RuntimeException("Windows locale name provider did not return expected localized language name for \"mk\". Returned name was \"" + result + "\"");
}
result = Locale.US.getDisplayLanguage(Locale.ENGLISH);
if (!"English".equals(result)) {
throw new RuntimeException("Windows locale name provider did not return expected localized language name for \"en\". Returned name was \"" + result + "\"");
}
result = Locale.US.getDisplayCountry(Locale.ENGLISH);
if (ver >= 6.1 && !"United States".equals(result)) {
throw new RuntimeException("Windows locale name provider did not return expected localized country name for \"US\". Returned name was \"" + result + "\"");
}
} else {
throw new RuntimeException("Windows Host LocaleProviderAdapter was not selected for English locale.");
}
}
} catch (ParseException pe) {
throw new RuntimeException("Parsing Windows version failed: "+pe.toString());
}
}
}
}

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2012, 2013, 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
@ -23,7 +23,7 @@
#!/bin/sh
#
# @test
# @bug 6336885 7196799 7197573 7198834 8000245 8000615 8001440
# @bug 6336885 7196799 7197573 7198834 8000245 8000615 8001440 8010666
# @summary tests for "java.locale.providers" system property
# @compile -XDignore.symbol.file LocaleProviders.java
# @run shell/timeout=600 LocaleProviders.sh
@ -258,4 +258,15 @@ PARAM2=
PARAM3=
runTest
# testing 8010666 fix.
if [ "${DEFLANG}" = "en" ]
then
METHODNAME=bug8010666Test
PREFLIST=HOST
PARAM1=
PARAM2=
PARAM3=
runTest
fi
exit $result