# HG changeset patch # User naoto # Date 1366663027 25200 # Node ID 5e5039c3181d7bf2b3a10a6bac20165a472f028b # Parent b7d3500f2516b2aa304af7a2e5a5a8bb561874e6 8010666: Implement Currency/LocaleNameProvider in Windows Host LocaleProviderAdapter Reviewed-by: okutsu diff -r b7d3500f2516 -r 5e5039c3181d jdk/src/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java --- a/jdk/src/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java Wed Apr 17 11:34:31 2013 +0200 +++ b/jdk/src/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java Mon Apr 22 13:37:07 2013 -0700 @@ -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 @@ } 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); diff -r b7d3500f2516 -r 5e5039c3181d jdk/src/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java --- a/jdk/src/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java Wed Apr 17 11:34:31 2013 +0200 +++ b/jdk/src/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java Mon Apr 22 13:37:07 2013 -0700 @@ -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.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.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 @@ 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 @@ private static ConcurrentMap> decimalFormatSymbolsCache = new ConcurrentHashMap<>(); private static final Set supportedLocaleSet; + private static final String nativeDisplayLanguage; static { Set 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 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 @@ } 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 @@ // 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); } diff -r b7d3500f2516 -r 5e5039c3181d jdk/src/windows/native/sun/util/locale/provider/HostLocaleProviderAdapter_md.c --- a/jdk/src/windows/native/sun/util/locale/provider/HostLocaleProviderAdapter_md.c Wed Apr 17 11:34:31 2013 +0200 +++ b/jdk/src/windows/native/sun/util/locale/provider/HostLocaleProviderAdapter_md.c Mon Apr 22 13:37:07 2013 -0700 @@ -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 @@ break; } - localeString = getJavaIDFromLangID(langid); + localeString = (char *)getJavaIDFromLangID(langid); ret = (*env)->NewStringUTF(env, localeString); free(localeString); return ret; @@ -366,12 +366,14 @@ */ 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 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 @@ } 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 @@ } 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 @@ } } - if (leadingZero) { + if (leadingZero != 0) { *pDest++ = L'0'; } else { *pDest++ = L'#'; @@ -760,29 +820,35 @@ } 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; diff -r b7d3500f2516 -r 5e5039c3181d jdk/test/java/util/Locale/LocaleProviders.java --- a/jdk/test/java/util/Locale/LocaleProviders.java Wed Apr 17 11:34:31 2013 +0200 +++ b/jdk/test/java/util/Locale/LocaleProviders.java Mon Apr 22 13:37:07 2013 -0700 @@ -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 @@ bug8001440Test(); break; + case "bug8010666Test": + bug8010666Test(); + break; + default: throw new RuntimeException("Test method '"+methodName+"' not found."); } @@ -103,4 +108,38 @@ 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()); + } + } + } } diff -r b7d3500f2516 -r 5e5039c3181d jdk/test/java/util/Locale/LocaleProviders.sh --- a/jdk/test/java/util/Locale/LocaleProviders.sh Wed Apr 17 11:34:31 2013 +0200 +++ b/jdk/test/java/util/Locale/LocaleProviders.sh Mon Apr 22 13:37:07 2013 -0700 @@ -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 @@ PARAM3= runTest +# testing 8010666 fix. +if [ "${DEFLANG}" = "en" ] +then + METHODNAME=bug8010666Test + PREFLIST=HOST + PARAM1= + PARAM2= + PARAM3= + runTest +fi + exit $result