changeset 13414:398904912c77

8039301: [ja] Host Locale Provider uses non-translated Calendar field names 8039969: Old Korean Calendar conflicts with Host Locale 8049835: [ar/HOST adapter] Hijri calendar era is used but date number follows gregorian 8054010: [HOST provider, not gregory] Return NULL when calling Calendar.getDisplayNames for Calendar.ERA 8054482: [HOST provider] only return standalone-style month display name 8055258: [HOST provider] Short era display name is not returned Reviewed-by: okutsu
author naoto
date Tue, 05 Jan 2016 10:15:54 -0800
parents c8dd830a0393
children 2efa37c9e038 e63cf6b55a95
files src/java.base/share/classes/java/util/Calendar.java src/java.base/share/classes/java/util/JapaneseImperialCalendar.java src/java.base/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java src/java.base/windows/native/libjava/HostLocaleProviderAdapter_md.c test/java/util/Locale/LocaleProviders.java test/java/util/Locale/LocaleProviders.sh
diffstat 6 files changed, 316 insertions(+), 77 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/util/Calendar.java	Tue Jan 05 18:53:27 2016 +0100
+++ b/src/java.base/share/classes/java/util/Calendar.java	Tue Jan 05 10:15:54 2016 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -2085,8 +2085,10 @@
 
         String calendarType = getCalendarType();
         int fieldValue = get(field);
-        // the standalone and narrow styles are supported only through CalendarDataProviders.
-        if (isStandaloneStyle(style) || isNarrowFormatStyle(style)) {
+        // the standalone/narrow styles and short era are supported only through
+        // CalendarNameProviders.
+        if (isStandaloneStyle(style) || isNarrowFormatStyle(style) ||
+            field == ERA && (style & SHORT) == SHORT) {
             String val = CalendarDataUtility.retrieveFieldValueName(calendarType,
                                                                     field, fieldValue,
                                                                     style, locale);
--- a/src/java.base/share/classes/java/util/JapaneseImperialCalendar.java	Tue Jan 05 18:53:27 2016 +0100
+++ b/src/java.base/share/classes/java/util/JapaneseImperialCalendar.java	Tue Jan 05 10:15:54 2016 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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
@@ -1037,14 +1037,16 @@
                 }
                 if (size < eras.length) {
                     int baseStyle = getBaseStyle(style);
-                    for (int i = size; i < eras.length; i++) {
-                        Era era = eras[i];
-                        if (baseStyle == ALL_STYLES || baseStyle == SHORT
-                                || baseStyle == NARROW_FORMAT) {
-                            names.put(era.getAbbreviation(), i);
-                        }
-                        if (baseStyle == ALL_STYLES || baseStyle == LONG) {
-                            names.put(era.getName(), i);
+                    for (int i = 0; i < eras.length; i++) {
+                        if (!names.values().contains(i)) {
+                            Era era = eras[i];
+                            if (baseStyle == ALL_STYLES || baseStyle == SHORT
+                                    || baseStyle == NARROW_FORMAT) {
+                                names.put(era.getAbbreviation(), i);
+                            }
+                            if (baseStyle == ALL_STYLES || baseStyle == LONG) {
+                                names.put(era.getName(), i);
+                            }
                         }
                     }
                 }
--- a/src/java.base/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java	Tue Jan 05 18:53:27 2016 +0100
+++ b/src/java.base/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java	Tue Jan 05 10:15:54 2016 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -38,8 +38,10 @@
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.Currency;
+import java.util.HashMap;
 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;
@@ -47,6 +49,7 @@
 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;
@@ -81,6 +84,9 @@
     private static final int DN_LOCALE_REGION   = 4;
     private static final int DN_LOCALE_VARIANT  = 5;
 
+    // Windows Calendar IDs
+    private static final int CAL_JAPAN  = 3;
+
     // Native Calendar ID to LDML calendar type map
     private static final String[] calIDToLDML = {
         "",
@@ -95,7 +101,8 @@
         "gregory_fr",
         "gregory_ar",
         "gregory_en",
-        "gregory_fr",
+        "gregory_fr", "", "", "", "", "", "", "", "", "", "",
+        "islamic-umalqura",
     };
 
     // Caches
@@ -362,6 +369,50 @@
         };
     }
 
+    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 calendarType, int field,
+                int value, int style, Locale locale) {
+                String[] names = getCalendarDisplayStrings(removeExtensions(locale).toLanguageTag(),
+                            getCalendarIDFromLDMLType(calendarType), field, style);
+                if (names != null && value >= 0 && value < names.length) {
+                    return names[value];
+                } else {
+                    return null;
+                }
+            }
+
+            @Override
+            public Map<String, Integer> getDisplayNames(String calendarType,
+                int field, int style, Locale locale) {
+                Map<String, Integer> map = null;
+                String[] names = getCalendarDisplayStrings(removeExtensions(locale).toLanguageTag(),
+                            getCalendarIDFromLDMLType(calendarType), field, style);
+                if (names != null) {
+                    map = new HashMap<>();
+                    for (int value = 0; value < names.length; value++) {
+                        if (names[value] != null) {
+                            map.put(names[value], value);
+                        }
+                    }
+                    map = map.isEmpty() ? null : map;
+                }
+                return map;
+            }
+        };
+    }
+
     public static CalendarProvider getCalendarProvider() {
         return new CalendarProvider() {
             @Override
@@ -496,15 +547,7 @@
     }
 
     private static boolean isSupportedCalendarLocale(Locale locale) {
-        Locale base = locale;
-
-        if (base.hasExtensions() || base.getVariant() != "") {
-            // strip off extensions and variant.
-            base = new Locale.Builder()
-                            .setLocale(locale)
-                            .clearExtensions()
-                            .build();
-        }
+        Locale base = stripVariantAndExtensions(locale);
 
         if (!supportedLocaleSet.contains(base)) {
             return false;
@@ -569,11 +612,23 @@
     }
 
     private static boolean isJapaneseCalendar() {
-        return getCalendarID("ja-JP") == 3; // 3: CAL_JAPAN
+        return getCalendarID("ja-JP") == CAL_JAPAN;
+    }
+
+    private static Locale stripVariantAndExtensions(Locale locale) {
+        if (locale.hasExtensions() || locale.getVariant() != "") {
+            // strip off extensions and variant.
+            locale = new Locale.Builder()
+                            .setLocale(locale)
+                            .clearExtensions()
+                            .build();
+        }
+
+        return locale;
     }
 
     private static Locale getCalendarLocale(Locale locale) {
-        int calid = getCalendarID(locale.toLanguageTag());
+        int calid = getCalendarID(stripVariantAndExtensions(locale).toLanguageTag());
         if (calid > 0 && calid < calIDToLDML.length) {
             Locale.Builder lb = new Locale.Builder();
             String[] caltype = calIDToLDML[calid].split("_");
@@ -589,6 +644,15 @@
         return locale;
     }
 
+    private static int getCalendarIDFromLDMLType(String ldmlType) {
+        for (int i = 0; i < calIDToLDML.length; i++) {
+            if (calIDToLDML[i].startsWith(ldmlType)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
     private static Locale getNumberLocale(Locale src) {
         if (JRELocaleConstants.TH_TH.equals(src)) {
             if (isNativeDigit("th-TH")) {
@@ -639,6 +703,9 @@
     // For CalendarDataProvider
     private static native int getCalendarDataValue(String langTag, int type);
 
+    // For CalendarNameProvider
+    private static native String[] getCalendarDisplayStrings(String langTag, int calid, int field, int style);
+
     // For Locale/CurrencyNameProvider
     private static native String getDisplayString(String langTag, int key, String value);
 }
--- a/src/java.base/windows/native/libjava/HostLocaleProviderAdapter_md.c	Tue Jan 05 18:53:27 2016 +0100
+++ b/src/java.base/windows/native/libjava/HostLocaleProviderAdapter_md.c	Tue Jan 05 10:15:54 2016 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -31,22 +31,33 @@
 
 #define BUFLEN 256
 
+// java.util.Calendar constants
+#define CALENDAR_FIELD_ERA              0           // Calendar.ERA
+#define CALENDAR_FIELD_MONTH            2           // Calendar.MONTH
+#define CALENDAR_STYLE_SHORT_MASK       0x00000001  // Calendar.SHORT
+#define CALENDAR_STYLE_STANDALONE_MASK  0x00008000  // Calendar.STANDALONE
+
 // global variables
 typedef int (WINAPI *PGLIE)(const jchar *, LCTYPE, LPWSTR, int);
 typedef int (WINAPI *PGCIE)(const jchar *, CALID, LPCWSTR, CALTYPE, LPWSTR, int, LPDWORD);
+typedef int (WINAPI *PECIEE)(CALINFO_ENUMPROCEXEX, const jchar *, CALID, LPCWSTR, CALTYPE, LPARAM);
 PGLIE pGetLocaleInfoEx;
 PGCIE pGetCalendarInfoEx;
+PECIEE pEnumCalendarInfoExEx;
 BOOL initialized = FALSE;
 
 // prototypes
 int getLocaleInfoWrapper(const jchar *langtag, LCTYPE type, LPWSTR data, int buflen);
 int getCalendarInfoWrapper(const jchar *langtag, CALID id, LPCWSTR reserved, CALTYPE type, LPWSTR data, int buflen, LPDWORD val);
 jint getCalendarID(const jchar *langtag);
-void replaceCalendarArrayElems(JNIEnv *env, jstring jlangtag, jobjectArray jarray,
-                       CALTYPE* pCalTypes, int offset, int length);
+void replaceCalendarArrayElems(JNIEnv *env, jstring jlangtag, jint calid, jobjectArray jarray,
+                       CALTYPE* pCalTypes, int offset, int length, int style);
 WCHAR * getNumberPattern(const jchar * langtag, const jint numberStyle);
 void getNumberPart(const jchar * langtag, const jint numberStyle, WCHAR * number);
 void getFixPart(const jchar * langtag, const jint numberStyle, BOOL positive, BOOL prefix, WCHAR * ret);
+int enumCalendarInfoWrapper(const jchar * langtag, CALID calid, CALTYPE type, LPWSTR buf, int buflen);
+BOOL CALLBACK EnumCalendarInfoProc(LPWSTR lpCalInfoStr, CALID calid, LPWSTR lpReserved, LPARAM lParam);
+jobjectArray getErasImpl(JNIEnv *env, jstring jlangtag, jint calid, jint style, jobjectArray eras);
 
 // from java_props_md.c
 extern __declspec(dllexport) const char * getJavaIDFromLangID(LANGID langID);
@@ -83,6 +94,8 @@
     CAL_SABBREVMONTHNAME13,
 };
 
+#define MONTHTYPES (sizeof(monthsType) / sizeof(CALTYPE))
+
 CALTYPE wDaysType[] = {
     CAL_SDAYNAME7,
     CAL_SDAYNAME1,
@@ -155,6 +168,11 @@
     }
 };
 
+
+// Localized region name for unknown regions (Windows 10)
+#define UNKNOWN_REGION  L"Unknown Region ("
+#define UNKNOWN_REGION_SIZE wcslen(UNKNOWN_REGION)
+
 /*
  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
  * Method:    initialize
@@ -169,11 +187,15 @@
         pGetCalendarInfoEx = (PGCIE)GetProcAddress(
             GetModuleHandle("kernel32.dll"),
             "GetCalendarInfoEx");
+        pEnumCalendarInfoExEx = (PECIEE)GetProcAddress(
+            GetModuleHandle("kernel32.dll"),
+            "EnumCalendarInfoExEx");
         initialized =TRUE;
     }
 
     return pGetLocaleInfoEx != NULL &&
-           pGetCalendarInfoEx != NULL;
+           pGetCalendarInfoEx != NULL &&
+           pEnumCalendarInfoExEx != NULL;
 }
 
 /*
@@ -300,23 +322,7 @@
  */
 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getEras
   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray eras) {
-    WCHAR ad[BUFLEN];
-    const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
-    jstring tmp_string;
-    CHECK_NULL_RETURN(langtag, eras);
-
-    getCalendarInfoWrapper(langtag, getCalendarID(langtag), NULL,
-                      CAL_SERASTRING, ad, BUFLEN, NULL);
-
-    // Windows does not provide B.C. era.
-    tmp_string = (*env)->NewString(env, ad, (jsize)wcslen(ad));
-    if (tmp_string != NULL) {
-        (*env)->SetObjectArrayElement(env, eras, 1, tmp_string);
-    }
-
-    (*env)->ReleaseStringChars(env, jlangtag, langtag);
-
-    return eras;
+    return getErasImpl(env, jlangtag, -1, 0, eras);
 }
 
 /*
@@ -326,8 +332,8 @@
  */
 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getMonths
   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray months) {
-    replaceCalendarArrayElems(env, jlangtag, months, monthsType,
-                      0, sizeof(monthsType)/sizeof(CALTYPE));
+    replaceCalendarArrayElems(env, jlangtag, -1, months, monthsType,
+                      0, MONTHTYPES, 0);
     return months;
 }
 
@@ -338,8 +344,8 @@
  */
 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getShortMonths
   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray smonths) {
-    replaceCalendarArrayElems(env, jlangtag, smonths, sMonthsType,
-                      0, sizeof(sMonthsType)/sizeof(CALTYPE));
+    replaceCalendarArrayElems(env, jlangtag, -1, smonths, sMonthsType,
+                      0, MONTHTYPES, 0);
     return smonths;
 }
 
@@ -350,8 +356,8 @@
  */
 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getWeekdays
   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray wdays) {
-    replaceCalendarArrayElems(env, jlangtag, wdays, wDaysType,
-                      1, sizeof(wDaysType)/sizeof(CALTYPE));
+    replaceCalendarArrayElems(env, jlangtag, -1, wdays, wDaysType,
+                      1, sizeof(wDaysType)/sizeof(CALTYPE), 0);
     return wdays;
 }
 
@@ -362,8 +368,8 @@
  */
 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getShortWeekdays
   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray swdays) {
-    replaceCalendarArrayElems(env, jlangtag, swdays, sWDaysType,
-                      1, sizeof(sWDaysType)/sizeof(CALTYPE));
+    replaceCalendarArrayElems(env, jlangtag, -1, swdays, sWDaysType,
+                      1, sizeof(sWDaysType)/sizeof(CALTYPE), 0);
     return swdays;
 }
 
@@ -676,6 +682,41 @@
 
 /*
  * 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 calid, jint field, jint style) {
+    jobjectArray ret = NULL;
+    CALTYPE * pCalType = NULL;
+
+    switch (field) {
+    case CALENDAR_FIELD_ERA:
+        return getErasImpl(env, jlangtag, calid, style, NULL);
+
+    case CALENDAR_FIELD_MONTH:
+        ret = (*env)->NewObjectArray(env, MONTHTYPES,
+                (*env)->FindClass(env, "java/lang/String"), NULL);
+        if (ret != NULL) {
+            if (style & CALENDAR_STYLE_SHORT_MASK) {
+                pCalType = sMonthsType;
+            } else {
+                pCalType = monthsType;
+            }
+
+            replaceCalendarArrayElems(env, jlangtag, calid, ret, pCalType,
+                          0, MONTHTYPES, style);
+        }
+        return ret;
+
+    default:
+        // not supported
+        return NULL;
+    }
+}
+
+/*
+ * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
  * Method:    getDisplayString
  * Signature: (Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;
  */
@@ -714,10 +755,15 @@
     (*env)->ReleaseStringChars(env, jStr, pjChar);
 
     if (got) {
-        return (*env)->NewString(env, buf, (jsize)wcslen(buf));
-    } else {
-        return NULL;
+        // Hack: Windows 10 returns "Unknown Region (XX)" for localized XX region name.
+        // Take that as not known.
+        if (type != sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_LOCALE_REGION ||
+            wcsncmp(UNKNOWN_REGION, buf, UNKNOWN_REGION_SIZE) != 0) {
+            return (*env)->NewString(env, buf, (jsize)wcslen(buf));
+        }
     }
+
+    return NULL;
 }
 
 int getLocaleInfoWrapper(const jchar *langtag, LCTYPE type, LPWSTR data, int buflen) {
@@ -753,35 +799,62 @@
 }
 
 jint getCalendarID(const jchar *langtag) {
-    DWORD type;
+    DWORD type = -1;
     int got = getLocaleInfoWrapper(langtag,
         LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
         (LPWSTR)&type, sizeof(type));
 
     if (got) {
-        return type;
-    } else {
-        return 0;
+        switch (type) {
+            case CAL_GREGORIAN:
+            case CAL_GREGORIAN_US:
+            case CAL_JAPAN:
+            case CAL_TAIWAN:
+            case CAL_HIJRI:
+            case CAL_THAI:
+            case CAL_GREGORIAN_ME_FRENCH:
+            case CAL_GREGORIAN_ARABIC:
+            case CAL_GREGORIAN_XLIT_ENGLISH:
+            case CAL_GREGORIAN_XLIT_FRENCH:
+            case CAL_UMALQURA:
+                break;
+
+            default:
+                // non-supported calendars return -1
+                type = -1;
+                break;
+        }
     }
+
+    return type;
 }
 
-void replaceCalendarArrayElems(JNIEnv *env, jstring jlangtag, jobjectArray jarray, CALTYPE* pCalTypes, int offset, int length) {
+void replaceCalendarArrayElems(JNIEnv *env, jstring jlangtag, jint calid, jobjectArray jarray, CALTYPE* pCalTypes, int offset, int length, int style) {
     WCHAR name[BUFLEN];
     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
-    int calid;
     jstring tmp_string;
+    CALTYPE isGenitive;
 
     CHECK_NULL(langtag);
-    calid = getCalendarID(langtag);
+
+    if (calid < 0) {
+        calid = getCalendarID(langtag);
+    }
 
     if (calid != -1) {
         int i;
+
+        if (!(style & CALENDAR_STYLE_STANDALONE_MASK)) {
+            isGenitive = CAL_RETURN_GENITIVE_NAMES;
+        }
+
         for (i = 0; i < length; i++) {
-            getCalendarInfoWrapper(langtag, calid, NULL,
-                              pCalTypes[i], name, BUFLEN, NULL);
-            tmp_string = (*env)->NewString(env, name, (jsize)wcslen(name));
-            if (tmp_string != NULL) {
-                (*env)->SetObjectArrayElement(env, jarray, i + offset, tmp_string);
+            if (getCalendarInfoWrapper(langtag, calid, NULL,
+                              pCalTypes[i] | isGenitive, name, BUFLEN, NULL) != 0) {
+                tmp_string = (*env)->NewString(env, name, (jsize)wcslen(name));
+                if (tmp_string != NULL) {
+                    (*env)->SetObjectArrayElement(env, jarray, i + offset, tmp_string);
+                }
             }
         }
     }
@@ -924,3 +997,99 @@
 
     wcscpy(ret, fixes[!prefix][!positive][style][pattern]);
 }
+
+int enumCalendarInfoWrapper(const jchar *langtag, CALID calid, CALTYPE type, LPWSTR buf, int buflen) {
+    if (pEnumCalendarInfoExEx) {
+        if (wcscmp(L"und", (LPWSTR)langtag) == 0) {
+            // defaults to "en"
+            return pEnumCalendarInfoExEx(&EnumCalendarInfoProc, L"en",
+                calid, NULL, type, (LPARAM)buf);
+        } else {
+            return pEnumCalendarInfoExEx(&EnumCalendarInfoProc, langtag,
+                calid, NULL, type, (LPARAM)buf);
+        }
+    } else {
+        return 0;
+    }
+}
+
+BOOL CALLBACK EnumCalendarInfoProc(LPWSTR lpCalInfoStr, CALID calid, LPWSTR lpReserved, LPARAM lParam) {
+    wcscat_s((LPWSTR)lParam, BUFLEN, lpCalInfoStr);
+    wcscat_s((LPWSTR)lParam, BUFLEN, L",");
+    return TRUE;
+}
+
+jobjectArray getErasImpl(JNIEnv *env, jstring jlangtag, jint calid, jint style, jobjectArray eras) {
+    const jchar * langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
+    WCHAR buf[BUFLEN];
+    jobjectArray ret = eras;
+    CALTYPE type;
+
+    CHECK_NULL_RETURN(langtag, ret);
+
+    buf[0] = '\0';
+    if (style & CALENDAR_STYLE_SHORT_MASK) {
+        type = CAL_SABBREVERASTRING;
+    } else {
+        type = CAL_SERASTRING;
+    }
+
+    if (calid < 0) {
+        calid = getCalendarID(langtag);
+    }
+
+    if (calid != -1 && enumCalendarInfoWrapper(langtag, calid, type, buf, BUFLEN)) {
+        // format in buf: "era0,era1,era2," where era0 is the current one
+        int eraCount;
+        LPWSTR current;
+        jsize array_length;
+
+        for(eraCount = 0, current = buf; *current != '\0'; current++) {
+            if (*current == L',') {
+                eraCount ++;
+            }
+        }
+
+        if (eras != NULL) {
+            array_length = (*env)->GetArrayLength(env, eras);
+        } else {
+            // +1 for the "before" era, e.g., BC, which Windows does not return.
+            array_length = (jsize)eraCount + 1;
+            ret = (*env)->NewObjectArray(env, array_length,
+                (*env)->FindClass(env, "java/lang/String"), NULL);
+        }
+
+        if (ret != NULL) {
+            int eraIndex;
+            LPWSTR era;
+
+            for(eraIndex = 0, era = current = buf; eraIndex < eraCount; era = current, eraIndex++) {
+                while (*current != L',') {
+                    current++;
+                }
+                *current++ = '\0';
+
+                if (eraCount - eraIndex < array_length &&
+                    *era != '\0') {
+                    (*env)->SetObjectArrayElement(env, ret,
+                        (jsize)(eraCount - eraIndex),
+                        (*env)->NewString(env, era, (jsize)wcslen(era)));
+                }
+            }
+
+            // Hack for the Japanese Imperial Calendar to insert Gregorian era for
+            // "Before Meiji"
+            if (calid == CAL_JAPAN) {
+                buf[0] = '\0';
+                if (enumCalendarInfoWrapper(langtag, CAL_GREGORIAN, type, buf, BUFLEN)) {
+                    jsize len = (jsize)wcslen(buf);
+                    buf[--len] = '\0'; // remove the last ','
+                    (*env)->SetObjectArrayElement(env, ret, 0, (*env)->NewString(env, buf, len));
+                }
+            }
+        }
+    }
+
+    (*env)->ReleaseStringChars(env, jlangtag, langtag);
+    return ret;
+}
--- a/test/java/util/Locale/LocaleProviders.java	Tue Jan 05 18:53:27 2016 +0100
+++ b/test/java/util/Locale/LocaleProviders.java	Tue Jan 05 10:15:54 2016 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -213,7 +213,7 @@
     static void bug8013903Test() {
         if (System.getProperty("os.name").startsWith("Windows")) {
             Date sampleDate = new Date(0x10000000000L);
-            String fallbackResult = "Heisei 16.Nov.03 (Wed) AM 11:53:47";
+            String hostResult = "\u5e73\u6210 16.11.03 (Wed) AM 11:53:47";
             String jreResult = "\u5e73\u6210 16.11.03 (\u6c34) \u5348\u524d 11:53:47";
             Locale l = new Locale("ja", "JP", "JP");
             SimpleDateFormat sdf = new SimpleDateFormat("GGGG yyyy.MMM.dd '('E')' a hh:mm:ss", l);
@@ -227,11 +227,10 @@
                         result + "\", expected: \"" + jreResult);
                 }
             } else {
-                // should be FALLBACK, as Windows HOST does not return
-                // display names
-                if (!fallbackResult.equals(result)) {
+                // Windows display names. Subject to change if Windows changes its format.
+                if (!hostResult.equals(result)) {
                     throw new RuntimeException("Format failed. result: \"" +
-                        result + "\", expected: \"" + fallbackResult);
+                        result + "\", expected: \"" + hostResult);
                 }
             }
         }
--- a/test/java/util/Locale/LocaleProviders.sh	Tue Jan 05 18:53:27 2016 +0100
+++ b/test/java/util/Locale/LocaleProviders.sh	Tue Jan 05 10:15:54 2016 -0800
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-# Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+# 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
@@ -24,7 +24,7 @@
 #
 # @test
 # @bug 6336885 7196799 7197573 7198834 8000245 8000615 8001440 8008577
-#      8010666 8013086 8013233 8013903 8015960 8028771 8062006
+#      8010666 8013086 8013233 8013903 8015960 8028771 8054482 8062006
 # @summary tests for "java.locale.providers" system property
 # @compile -XDignore.symbol.file LocaleProviders.java
 # @run shell/timeout=600 LocaleProviders.sh