--- old/src/java.base/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java 2019-11-06 17:51:50.000000000 -0800 +++ new/src/java.base/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java 2019-11-06 17:51:49.000000000 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, 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 @@ -33,6 +33,7 @@ import java.text.spi.NumberFormatProvider; import java.util.Collections; import java.util.Calendar; +import java.util.HashMap; import java.util.HashSet; import java.util.Locale; import java.util.Map; @@ -550,15 +551,33 @@ } @Override - public String getDisplayName(String calType, int field, int value, - int style, Locale locale) { - return null; + public String getDisplayName(String calendarType, int field, + int value, int style, Locale locale) { + String[] names = getCalendarDisplayStrings(locale.toLanguageTag(), + field, style); + if (names != null && value >= 0 && value < names.length) { + return names[value]; + } else { + return null; + } } @Override - public Map getDisplayNames(String calType, - int field, int style, Locale locale) { - return null; + public Map getDisplayNames(String calendarType, + int field, int style, Locale locale) { + Map map = null; + String[] names = getCalendarDisplayStrings(locale.toLanguageTag(), + field, style); + if (names != null) { + map = new HashMap<>((int)Math.ceil(names.length / 0.75)); + for (int value = 0; value < names.length; value++) { + if (names[value] != null) { + map.put(names[value], value); + } + } + map = map.isEmpty() ? null : map; + } + return map; } }; } @@ -901,6 +920,9 @@ // For CalendarDataProvider private static native int getCalendarInt(String langTag, int type); + // For CalendarNameProvider + private static native String[] getCalendarDisplayStrings(String langTag, int field, int style); + // For Locale/CurrencyNameProvider private static native String getDisplayString(String langTag, int key, String value); --- old/src/java.base/macosx/native/libjava/HostLocaleProviderAdapter_md.c 2019-11-06 17:51:51.000000000 -0800 +++ new/src/java.base/macosx/native/libjava/HostLocaleProviderAdapter_md.c 2019-11-06 17:51:51.000000000 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, 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 @@ -30,11 +30,20 @@ #define BUFLEN 256 +// java.util.Calendar constants +#define CALENDAR_FIELD_ERA 0 // Calendar.ERA +#define CALENDAR_FIELD_DAY_OF_WEEK 7 // Calendar.DAY_OF_WEEK +#define CALENDAR_FIELD_AM_PM 9 // Calendar.AM_PM +#define JAPANESE_MEIJI_INDEX 232 + static CFDateFormatterStyle convertDateFormatterStyle(jint javaStyle); static CFNumberFormatterStyle convertNumberFormatterStyle(jint javaStyle); static void copyArrayElements(JNIEnv *env, CFArrayRef cfarray, jobjectArray jarray, CFIndex sindex, int dindex, int count); static jstring getNumberSymbolString(JNIEnv *env, jstring jlangtag, jstring jdefault, CFStringRef type); static jchar getNumberSymbolChar(JNIEnv *env, jstring jlangtag, jchar jdefault, CFStringRef type); +jobjectArray getErasImpl(JNIEnv *env, jstring jlangtag, jint style, jobjectArray eras); +jobjectArray getWeekdaysImpl(JNIEnv *env, jclass cls, jstring jlangtag, jint style, jobjectArray wdays); +jobjectArray getAmPmImpl(JNIEnv *env, jclass cls, jstring jlangtag, jint style, jobjectArray ampms); // from java_props_macosx.c extern char * getMacOSXLocale(int cat); @@ -131,41 +140,7 @@ */ JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getAmPmStrings (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray ampms) { - CFLocaleRef cflocale = CFLocaleCopyCurrent(); - jstring tmp_string; - if (cflocale != NULL) { - CFDateFormatterRef df = CFDateFormatterCreate(kCFAllocatorDefault, - cflocale, - kCFDateFormatterFullStyle, - kCFDateFormatterFullStyle); - if (df != NULL) { - char buf[BUFLEN]; - CFStringRef amStr = CFDateFormatterCopyProperty(df, kCFDateFormatterAMSymbol); - if (amStr != NULL) { - CFStringGetCString(amStr, buf, BUFLEN, kCFStringEncodingUTF8); - CFRelease(amStr); - tmp_string = (*env)->NewStringUTF(env, buf); - if (tmp_string != NULL) { - (*env)->SetObjectArrayElement(env, ampms, 0, tmp_string); - } - } - if (!(*env)->ExceptionCheck(env)){ - CFStringRef pmStr = CFDateFormatterCopyProperty(df, kCFDateFormatterPMSymbol); - if (pmStr != NULL) { - CFStringGetCString(pmStr, buf, BUFLEN, kCFStringEncodingUTF8); - CFRelease(pmStr); - tmp_string = (*env)->NewStringUTF(env, buf); - if (tmp_string != NULL) { - (*env)->SetObjectArrayElement(env, ampms, 1, tmp_string); - } - } - } - CFRelease(df); - } - CFRelease(cflocale); - } - - return ampms; + return getAmPmImpl(env, cls, jlangtag, 0, ampms); } /* @@ -175,24 +150,7 @@ */ JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getEras (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray eras) { - CFLocaleRef cflocale = CFLocaleCopyCurrent(); - if (cflocale != NULL) { - CFDateFormatterRef df = CFDateFormatterCreate(kCFAllocatorDefault, - cflocale, - kCFDateFormatterFullStyle, - kCFDateFormatterFullStyle); - if (df != NULL) { - CFArrayRef cferas = CFDateFormatterCopyProperty(df, kCFDateFormatterEraSymbols); - if (cferas != NULL) { - copyArrayElements(env, cferas, eras, 0, 0, CFArrayGetCount(cferas)); - CFRelease(cferas); - } - CFRelease(df); - } - CFRelease(cflocale); - } - - return eras; + return getErasImpl(env, jlangtag, 0, eras); } /* @@ -255,25 +213,8 @@ * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String; */ JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getWeekdays - (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray wdays) { - CFLocaleRef cflocale = CFLocaleCopyCurrent(); - if (cflocale != NULL) { - CFDateFormatterRef df = CFDateFormatterCreate(kCFAllocatorDefault, - cflocale, - kCFDateFormatterFullStyle, - kCFDateFormatterFullStyle); - if (df != NULL) { - CFArrayRef cfwdays = CFDateFormatterCopyProperty(df, kCFDateFormatterWeekdaySymbols); - if (cfwdays != NULL) { - copyArrayElements(env, cfwdays, wdays, 0, 1, CFArrayGetCount(cfwdays)); - CFRelease(cfwdays); - } - CFRelease(df); - } - CFRelease(cflocale); - } - - return wdays; + (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray wdays) { + return getWeekdaysImpl(env, cls, jlangtag, 0, wdays); } /* @@ -508,6 +449,29 @@ /* * Class: sun_util_locale_provider_HostLocaleProviderAdapterImpl + * Method: getCalendarDisplayStrings + * Signature: (Ljava/lang/String;III)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCalendarDisplayStrings + (JNIEnv *env, jclass cls, jstring jlangtag, jint field, jint style) { + switch (field) { + case CALENDAR_FIELD_ERA: + return getErasImpl(env, jlangtag, style, NULL); + + case CALENDAR_FIELD_DAY_OF_WEEK: + return getWeekdaysImpl(env, cls, jlangtag, style, NULL); + + case CALENDAR_FIELD_AM_PM: + return getAmPmImpl(env, cls, jlangtag, style, NULL); + + default: + // not supported + return NULL; + } +} + +/* + * Class: sun_util_locale_provider_HostLocaleProviderAdapterImpl * Method: getDisplayString * Signature: (Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String; */ @@ -725,3 +689,104 @@ return ret; } + +jobjectArray getErasImpl(JNIEnv *env, jstring jlangtag, jint style, jobjectArray eras) { + jobjectArray ret = eras; + CFLocaleRef cflocale = CFLocaleCopyCurrent(); + if (cflocale != NULL) { + CFDateFormatterRef df = CFDateFormatterCreate(kCFAllocatorDefault, + cflocale, + convertDateFormatterStyle(style), + convertDateFormatterStyle(style)); + if (df != NULL) { + CFArrayRef cferas = CFDateFormatterCopyProperty(df, kCFDateFormatterEraSymbols); + if (cferas != NULL) { + int eraCount = CFArrayGetCount(cferas); + + if (eras == NULL) { + ret = (*env)->NewObjectArray(env, (jsize)eraCount, + (*env)->FindClass(env, "java/lang/String"), NULL); + } + CFTypeRef cal = CFLocaleGetValue(cflocale, kCFLocaleCalendarIdentifier); + int sindex = cal == kCFJapaneseCalendar ? JAPANESE_MEIJI_INDEX : 0; + int dindex = cal == kCFJapaneseCalendar ? 1 : 0; // 0 is "BeforeMeiji" in JCal + copyArrayElements(env, cferas, ret, sindex, dindex, eraCount - sindex); + CFRelease(cferas); + } + CFRelease(df); + } + CFRelease(cflocale); + } + + return ret; +} + +jobjectArray getWeekdaysImpl(JNIEnv *env, jclass cls, jstring jlangtag, jint style, jobjectArray wdays) { + jobjectArray ret = wdays; + CFLocaleRef cflocale = CFLocaleCopyCurrent(); + if (cflocale != NULL) { + CFDateFormatterRef df = CFDateFormatterCreate(kCFAllocatorDefault, + cflocale, + convertDateFormatterStyle(style), + convertDateFormatterStyle(style)); + if (df != NULL) { + CFArrayRef cfwdays = CFDateFormatterCopyProperty(df, kCFDateFormatterWeekdaySymbols); + if (cfwdays != NULL) { + int dayCount = CFArrayGetCount(cfwdays); + + if (wdays == NULL) { + ret = (*env)->NewObjectArray(env, dayCount + 1, + (*env)->FindClass(env, "java/lang/String"), NULL); + } + copyArrayElements(env, cfwdays, ret, 0, 1, dayCount); + CFRelease(cfwdays); + } + CFRelease(df); + } + CFRelease(cflocale); + } + + return ret; +} + +jobjectArray getAmPmImpl(JNIEnv *env, jclass cls, jstring jlangtag, jint style, jobjectArray ampms) { + CFLocaleRef cflocale = CFLocaleCopyCurrent(); + jstring tmp_string; + if (cflocale != NULL) { + CFDateFormatterRef df = CFDateFormatterCreate(kCFAllocatorDefault, + cflocale, + convertDateFormatterStyle(style), + convertDateFormatterStyle(style)); + if (df != NULL) { + char buf[BUFLEN]; + if (ampms == NULL) { + ampms = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "java/lang/String"), NULL); + } + CFStringRef amStr = CFDateFormatterCopyProperty(df, kCFDateFormatterAMSymbol); + if (amStr != NULL) { + CFStringGetCString(amStr, buf, BUFLEN, kCFStringEncodingUTF8); + CFRelease(amStr); + tmp_string = (*env)->NewStringUTF(env, buf); + if (tmp_string != NULL) { + (*env)->SetObjectArrayElement(env, ampms, 0, tmp_string); + } + } + if (!(*env)->ExceptionCheck(env)){ + CFStringRef pmStr = CFDateFormatterCopyProperty(df, kCFDateFormatterPMSymbol); + if (pmStr != NULL) { + CFStringGetCString(pmStr, buf, BUFLEN, kCFStringEncodingUTF8); + CFRelease(pmStr); + tmp_string = (*env)->NewStringUTF(env, buf); + if (tmp_string != NULL) { + (*env)->SetObjectArrayElement(env, ampms, 1, tmp_string); + } + } + } + CFRelease(df); + } + CFRelease(cflocale); + } + + return ampms; +} --- old/test/jdk/java/util/Locale/LocaleProviders.java 2019-11-06 17:51:52.000000000 -0800 +++ new/test/jdk/java/util/Locale/LocaleProviders.java 2019-11-06 17:51:52.000000000 -0800 @@ -29,6 +29,7 @@ public class LocaleProviders { private static final boolean IS_WINDOWS = System.getProperty("os.name").startsWith("Windows"); + private static final boolean IS_MAC = System.getProperty("os.name").startsWith("Mac"); public static void main(String[] args) { String methodName = args[0]; @@ -82,6 +83,10 @@ bug8228465Test(); break; + case "bug8232871Test": + bug8232871Test(); + break; + default: throw new RuntimeException("Test method '"+methodName+"' not found."); } @@ -286,4 +291,40 @@ } } } + + static void bug8232871Test() { + LocaleProviderAdapter lda = LocaleProviderAdapter.getAdapter(CalendarNameProvider.class, Locale.US); + LocaleProviderAdapter.Type type = lda.getAdapterType(); + var lang = Locale.getDefault().getLanguage(); + var cal = Calendar.getInstance(); + var calType = cal.getCalendarType(); + var expected = "\u4ee4\u548c1\u5e745\u67081\u65e5 \u6c34\u66dc\u65e5 \u5348\u524d0:00:00 \u30a2\u30e1\u30ea\u30ab\u592a\u5e73\u6d0b\u590f\u6642\u9593"; + + if (type == LocaleProviderAdapter.Type.HOST && + IS_MAC && + lang.equals("ja") && + calType.equals("japanese")) { + cal.set(1, 4, 1, 0, 0, 0); + cal.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); + DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, + Locale.JAPAN); + df.setCalendar(cal); + var result = df.format(cal.getTime()); + if (result.equals(expected)) { + System.out.println("bug8232871Test succeeded."); + } else { + throw new RuntimeException( + "Japanese calendar names mismatch. result: " + + result + + ", expected: " + + expected); + } + } else { + System.out.println("Test ignored. Either :-\n" + + "OS is not macOS, or\n" + + "provider is not HOST: " + type + ", or\n" + + "Language is not Japanese: " + lang + ", or\n" + + "native calendar is not JapaneseCalendar: " + calType); + } + } } --- old/test/jdk/java/util/Locale/LocaleProvidersRun.java 2019-11-06 17:51:53.000000000 -0800 +++ new/test/jdk/java/util/Locale/LocaleProvidersRun.java 2019-11-06 17:51:53.000000000 -0800 @@ -25,7 +25,7 @@ * @test * @bug 6336885 7196799 7197573 7198834 8000245 8000615 8001440 8008577 * 8010666 8013086 8013233 8013903 8015960 8028771 8054482 8062006 - * 8150432 8215913 8220227 8228465 + * 8150432 8215913 8220227 8228465 8232871 * @summary tests for "java.locale.providers" system property * @library /test/lib * @build LocaleProviders @@ -156,6 +156,9 @@ //testing 8228465 fix. (Windows only) testRun("HOST", "bug8228465Test", "", "", ""); + + //testing 8232871 fix. (macOS only) + testRun("HOST", "bug8232871Test", "", "", ""); } private static void testRun(String prefList, String methodName,