1 /*
   2  * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "sun_util_locale_provider_HostLocaleProviderAdapterImpl.h"
  27 #include "jni_util.h"
  28 #include <windows.h>
  29 #include <gdefs.h>
  30 #include <stdlib.h>
  31 
  32 #define BUFLEN 256
  33 
  34 // java.util.Calendar constants
  35 #define CALENDAR_FIELD_ERA              0           // Calendar.ERA
  36 #define CALENDAR_FIELD_MONTH            2           // Calendar.MONTH
  37 #define CALENDAR_STYLE_SHORT_MASK       0x00000001  // Calendar.SHORT
  38 #define CALENDAR_STYLE_STANDALONE_MASK  0x00008000  // Calendar.STANDALONE
  39 
  40 // global variables
  41 typedef int (WINAPI *PGLIE)(const jchar *, LCTYPE, LPWSTR, int);
  42 typedef int (WINAPI *PGCIE)(const jchar *, CALID, LPCWSTR, CALTYPE, LPWSTR, int, LPDWORD);
  43 typedef int (WINAPI *PECIEE)(CALINFO_ENUMPROCEXEX, const jchar *, CALID, LPCWSTR, CALTYPE, LPARAM);
  44 PGLIE pGetLocaleInfoEx;
  45 PGCIE pGetCalendarInfoEx;
  46 PECIEE pEnumCalendarInfoExEx;
  47 BOOL initialized = FALSE;
  48 
  49 // prototypes
  50 int getLocaleInfoWrapper(const jchar *langtag, LCTYPE type, LPWSTR data, int buflen);
  51 int getCalendarInfoWrapper(const jchar *langtag, CALID id, LPCWSTR reserved, CALTYPE type, LPWSTR data, int buflen, LPDWORD val);
  52 jint getCalendarID(const jchar *langtag);
  53 void replaceCalendarArrayElems(JNIEnv *env, jstring jlangtag, jint calid, jobjectArray jarray,
  54                        CALTYPE* pCalTypes, int offset, int length, int style);
  55 WCHAR * getNumberPattern(const jchar * langtag, const jint numberStyle);
  56 void getNumberPart(const jchar * langtag, const jint numberStyle, WCHAR * number);
  57 void getFixPart(const jchar * langtag, const jint numberStyle, BOOL positive, BOOL prefix, WCHAR * ret);
  58 int enumCalendarInfoWrapper(const jchar * langtag, CALID calid, CALTYPE type, LPWSTR buf, int buflen);
  59 BOOL CALLBACK EnumCalendarInfoProc(LPWSTR lpCalInfoStr, CALID calid, LPWSTR lpReserved, LPARAM lParam);
  60 jobjectArray getErasImpl(JNIEnv *env, jstring jlangtag, jint calid, jint style, jobjectArray eras);
  61 
  62 // from java_props_md.c
  63 extern __declspec(dllexport) const char * getJavaIDFromLangID(LANGID langID);
  64 
  65 CALTYPE monthsType[] = {
  66     CAL_SMONTHNAME1,
  67     CAL_SMONTHNAME2,
  68     CAL_SMONTHNAME3,
  69     CAL_SMONTHNAME4,
  70     CAL_SMONTHNAME5,
  71     CAL_SMONTHNAME6,
  72     CAL_SMONTHNAME7,
  73     CAL_SMONTHNAME8,
  74     CAL_SMONTHNAME9,
  75     CAL_SMONTHNAME10,
  76     CAL_SMONTHNAME11,
  77     CAL_SMONTHNAME12,
  78     CAL_SMONTHNAME13,
  79 };
  80 
  81 CALTYPE sMonthsType[] = {
  82     CAL_SABBREVMONTHNAME1,
  83     CAL_SABBREVMONTHNAME2,
  84     CAL_SABBREVMONTHNAME3,
  85     CAL_SABBREVMONTHNAME4,
  86     CAL_SABBREVMONTHNAME5,
  87     CAL_SABBREVMONTHNAME6,
  88     CAL_SABBREVMONTHNAME7,
  89     CAL_SABBREVMONTHNAME8,
  90     CAL_SABBREVMONTHNAME9,
  91     CAL_SABBREVMONTHNAME10,
  92     CAL_SABBREVMONTHNAME11,
  93     CAL_SABBREVMONTHNAME12,
  94     CAL_SABBREVMONTHNAME13,
  95 };
  96 
  97 #define MONTHTYPES (sizeof(monthsType) / sizeof(CALTYPE))
  98 
  99 CALTYPE wDaysType[] = {
 100     CAL_SDAYNAME7,
 101     CAL_SDAYNAME1,
 102     CAL_SDAYNAME2,
 103     CAL_SDAYNAME3,
 104     CAL_SDAYNAME4,
 105     CAL_SDAYNAME5,
 106     CAL_SDAYNAME6,
 107 };
 108 
 109 CALTYPE sWDaysType[] = {
 110     CAL_SABBREVDAYNAME7,
 111     CAL_SABBREVDAYNAME1,
 112     CAL_SABBREVDAYNAME2,
 113     CAL_SABBREVDAYNAME3,
 114     CAL_SABBREVDAYNAME4,
 115     CAL_SABBREVDAYNAME5,
 116     CAL_SABBREVDAYNAME6,
 117 };
 118 
 119 WCHAR * fixes[2][2][3][16] =
 120 {
 121     { //prefix
 122         { //positive
 123             { // number
 124                 L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"",
 125             },
 126             { // currency
 127                 L"\xA4", L"", L"\xA4 ", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"",
 128             },
 129             { // percent
 130                 L"", L"", L"%", L"% ", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"",
 131             }
 132         },
 133         { // negative
 134             { // number
 135                 L"(", L"-", L"- ", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"",
 136             },
 137             { //currency
 138                 L"(\xA4", L"-\xA4", L"\xA4-", L"\xA4", L"(", L"-", L"", L"", L"-", L"-\xA4 ", L"", L"\xA4 ", L"\xA4 -", L"", L"(\xA4 ", L"("
 139             },
 140             { // percent
 141                 L"-", L"-", L"-%", L"%-", L"%", L"", L"", L"-% ", L"", L"% ", L"% -", L"", L"", L"", L"", L"",
 142             }
 143         }
 144     },
 145     { // suffix
 146         { //positive
 147             { // number
 148                 L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L""
 149             },
 150             { // currency
 151                 L"", L"\xA4 ", L"", L" \xA4", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"",
 152             },
 153             { // percent
 154                 L" %", L"%", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"",
 155             }
 156         },
 157         { // negative
 158             { // number
 159                 L")", L"", L" ", L"-", L" -", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"",
 160             },
 161             { //currency
 162                 L")", L"", L"", L"-", L"\xA4)", L"\xA4", L"-\xA4", L"\xA4-", L" \xA4", L"", L" \xA4-", L"-", L"", L"- \xA4", L")", L" \xA4)"
 163             },
 164             { // percent
 165                 L" %", L"%", L"", L"", L"-", L"-%", L"%-", L"", L" %-", L"-", L"", L"- %", L"", L"", L"", L"",
 166             }
 167         }
 168     }
 169 };
 170 
 171 
 172 // Localized region name for unknown regions (Windows 10)
 173 #define UNKNOWN_REGION  L"Unknown Region ("
 174 #define UNKNOWN_REGION_SIZE wcslen(UNKNOWN_REGION)
 175 
 176 /*
 177  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 178  * Method:    initialize
 179  * Signature: ()Z
 180  */
 181 JNIEXPORT jboolean JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_initialize
 182   (JNIEnv *env, jclass cls) {
 183     if (!initialized) {
 184         pGetLocaleInfoEx = (PGLIE)GetProcAddress(
 185             GetModuleHandle("kernel32.dll"),
 186             "GetLocaleInfoEx");
 187         pGetCalendarInfoEx = (PGCIE)GetProcAddress(
 188             GetModuleHandle("kernel32.dll"),
 189             "GetCalendarInfoEx");
 190         pEnumCalendarInfoExEx = (PECIEE)GetProcAddress(
 191             GetModuleHandle("kernel32.dll"),
 192             "EnumCalendarInfoExEx");
 193         initialized =TRUE;
 194     }
 195 
 196     return pGetLocaleInfoEx != NULL &&
 197            pGetCalendarInfoEx != NULL &&
 198            pEnumCalendarInfoExEx != NULL;
 199 }
 200 
 201 /*
 202  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 203  * Method:    getDefaultLocale
 204  * Signature: (I)Ljava/lang/String;
 205  */
 206 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getDefaultLocale
 207   (JNIEnv *env, jclass cls, jint cat) {
 208     char * localeString = NULL;
 209     LANGID langid;
 210     jstring ret;
 211 
 212     switch (cat) {
 213         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_CAT_DISPLAY:
 214             langid = LANGIDFROMLCID(GetUserDefaultUILanguage());
 215             break;
 216         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_CAT_FORMAT:
 217         default:
 218             langid = LANGIDFROMLCID(GetUserDefaultLCID());
 219             break;
 220     }
 221 
 222     localeString = (char *)getJavaIDFromLangID(langid);
 223     if (localeString != NULL) {
 224         ret = (*env)->NewStringUTF(env, localeString);
 225         free(localeString);
 226     } else {
 227         JNU_ThrowOutOfMemoryError(env, "memory allocation error");
 228         ret = NULL;
 229     }
 230     return ret;
 231 }
 232 
 233 /*
 234  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 235  * Method:    getDateTimePattern
 236  * Signature: (IILjava/lang/String;)Ljava/lang/String;
 237  */
 238 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getDateTimePattern
 239   (JNIEnv *env, jclass cls, jint dateStyle, jint timeStyle, jstring jlangtag) {
 240     WCHAR pattern[BUFLEN];
 241     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 242     CHECK_NULL_RETURN(langtag, NULL);
 243 
 244     pattern[0] = L'\0';
 245 
 246     if (dateStyle == 0 || dateStyle == 1) {
 247         getLocaleInfoWrapper(langtag, LOCALE_SLONGDATE, pattern, BUFLEN);
 248     } else if (dateStyle == 2 || dateStyle == 3) {
 249         getLocaleInfoWrapper(langtag, LOCALE_SSHORTDATE, pattern, BUFLEN);
 250     }
 251 
 252     if (timeStyle == 0 || timeStyle == 1) {
 253         getLocaleInfoWrapper(langtag, LOCALE_STIMEFORMAT, pattern, BUFLEN);
 254     } else if (timeStyle == 2 || timeStyle == 3) {
 255         getLocaleInfoWrapper(langtag, LOCALE_SSHORTTIME, pattern, BUFLEN);
 256     }
 257 
 258     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 259 
 260     return (*env)->NewString(env, pattern, (jsize)wcslen(pattern));
 261 }
 262 
 263 /*
 264  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 265  * Method:    getCalendarID
 266  * Signature: (Ljava/lang/String;)I
 267  */
 268 JNIEXPORT jint JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCalendarID
 269   (JNIEnv *env, jclass cls, jstring jlangtag) {
 270     const jchar *langtag;
 271     jint ret;
 272     langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 273     CHECK_NULL_RETURN(langtag, 0);
 274     ret = getCalendarID(langtag);
 275     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 276     return ret;
 277 }
 278 
 279 /*
 280  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 281  * Method:    getAmPmStrings
 282  * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String;
 283  */
 284 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getAmPmStrings
 285   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray ampms) {
 286     WCHAR buf[BUFLEN];
 287     const jchar *langtag;
 288     jstring tmp_string;
 289 
 290     // AM
 291     int got;
 292     langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 293     CHECK_NULL_RETURN(langtag, NULL);
 294     got = getLocaleInfoWrapper(langtag, LOCALE_S1159, buf, BUFLEN);
 295     if (got) {
 296         tmp_string = (*env)->NewString(env, buf, (jsize)wcslen(buf));
 297         if (tmp_string != NULL) {
 298             (*env)->SetObjectArrayElement(env, ampms, 0, tmp_string);
 299         }
 300     }
 301 
 302     if (!(*env)->ExceptionCheck(env)){
 303         // PM
 304         got = getLocaleInfoWrapper(langtag, LOCALE_S2359, buf, BUFLEN);
 305         if (got) {
 306             tmp_string = (*env)->NewString(env, buf, (jsize)wcslen(buf));
 307             if (tmp_string != NULL) {
 308                 (*env)->SetObjectArrayElement(env, ampms, 1, tmp_string);
 309             }
 310         }
 311     }
 312 
 313     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 314 
 315     return ampms;
 316 }
 317 
 318 /*
 319  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 320  * Method:    getEras
 321  * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String;
 322  */
 323 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getEras
 324   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray eras) {
 325     return getErasImpl(env, jlangtag, -1, 0, eras);
 326 }
 327 
 328 /*
 329  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 330  * Method:    getMonths
 331  * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String;
 332  */
 333 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getMonths
 334   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray months) {
 335     replaceCalendarArrayElems(env, jlangtag, -1, months, monthsType,
 336                       0, MONTHTYPES, 0);
 337     return months;
 338 }
 339 
 340 /*
 341  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 342  * Method:    getShortMonths
 343  * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String;
 344  */
 345 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getShortMonths
 346   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray smonths) {
 347     replaceCalendarArrayElems(env, jlangtag, -1, smonths, sMonthsType,
 348                       0, MONTHTYPES, 0);
 349     return smonths;
 350 }
 351 
 352 /*
 353  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 354  * Method:    getWeekdays
 355  * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String;
 356  */
 357 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getWeekdays
 358   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray wdays) {
 359     replaceCalendarArrayElems(env, jlangtag, -1, wdays, wDaysType,
 360                       1, sizeof(wDaysType)/sizeof(CALTYPE), 0);
 361     return wdays;
 362 }
 363 
 364 /*
 365  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 366  * Method:    getShortWeekdays
 367  * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String;
 368  */
 369 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getShortWeekdays
 370   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray swdays) {
 371     replaceCalendarArrayElems(env, jlangtag, -1, swdays, sWDaysType,
 372                       1, sizeof(sWDaysType)/sizeof(CALTYPE), 0);
 373     return swdays;
 374 }
 375 
 376 /*
 377  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 378  * Method:    getNumberPattern
 379  * Signature: (ILjava/lang/String;)Ljava/lang/String;
 380  */
 381 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getNumberPattern
 382   (JNIEnv *env, jclass cls, jint numberStyle, jstring jlangtag) {
 383     const jchar *langtag;
 384     jstring ret;
 385     WCHAR * pattern;
 386 
 387     langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 388     CHECK_NULL_RETURN(langtag, NULL);
 389     pattern = getNumberPattern(langtag, numberStyle);
 390     CHECK_NULL_RETURN(pattern, NULL);
 391 
 392     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 393     ret = (*env)->NewString(env, pattern, (jsize)wcslen(pattern));
 394     free(pattern);
 395 
 396     return ret;
 397 }
 398 
 399 /*
 400  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 401  * Method:    isNativeDigit
 402  * Signature: (Ljava/lang/String;)Z
 403  */
 404 JNIEXPORT jboolean JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_isNativeDigit
 405   (JNIEnv *env, jclass cls, jstring jlangtag) {
 406     DWORD num;
 407     int got;
 408     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 409     CHECK_NULL_RETURN(langtag, JNI_FALSE);
 410     got = getLocaleInfoWrapper(langtag,
 411         LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER,
 412         (LPWSTR)&num, sizeof(num));
 413     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 414 
 415     return got && num == 2; // 2: native digit substitution
 416 }
 417 
 418 /*
 419  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 420  * Method:    getCurrencySymbol
 421  * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
 422  */
 423 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCurrencySymbol
 424   (JNIEnv *env, jclass cls, jstring jlangtag, jstring currencySymbol) {
 425     WCHAR buf[BUFLEN];
 426     int got;
 427     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 428     CHECK_NULL_RETURN(langtag, currencySymbol);
 429     got = getLocaleInfoWrapper(langtag, LOCALE_SCURRENCY, buf, BUFLEN);
 430     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 431 
 432     if (got) {
 433         return (*env)->NewString(env, buf, (jsize)wcslen(buf));
 434     } else {
 435         return currencySymbol;
 436     }
 437 }
 438 
 439 /*
 440  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 441  * Method:    getDecimalSeparator
 442  * Signature: (Ljava/lang/String;C)C
 443  */
 444 JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getDecimalSeparator
 445   (JNIEnv *env, jclass cls, jstring jlangtag, jchar decimalSeparator) {
 446     WCHAR buf[BUFLEN];
 447     int got;
 448     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 449     CHECK_NULL_RETURN(langtag, decimalSeparator);
 450     got = getLocaleInfoWrapper(langtag, LOCALE_SDECIMAL, buf, BUFLEN);
 451     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 452 
 453     if (got) {
 454         return buf[0];
 455     } else {
 456         return decimalSeparator;
 457     }
 458 }
 459 
 460 /*
 461  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 462  * Method:    getGroupingSeparator
 463  * Signature: (Ljava/lang/String;C)C
 464  */
 465 JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getGroupingSeparator
 466   (JNIEnv *env, jclass cls, jstring jlangtag, jchar groupingSeparator) {
 467     WCHAR buf[BUFLEN];
 468     int got;
 469     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 470     CHECK_NULL_RETURN(langtag, groupingSeparator);
 471     got = getLocaleInfoWrapper(langtag, LOCALE_STHOUSAND, buf, BUFLEN);
 472     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 473 
 474     if (got) {
 475         return buf[0];
 476     } else {
 477         return groupingSeparator;
 478     }
 479 }
 480 
 481 /*
 482  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 483  * Method:    getInfinity
 484  * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
 485  */
 486 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getInfinity
 487   (JNIEnv *env, jclass cls, jstring jlangtag, jstring infinity) {
 488     WCHAR buf[BUFLEN];
 489     int got;
 490     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 491     CHECK_NULL_RETURN(langtag, infinity);
 492     got = getLocaleInfoWrapper(langtag, LOCALE_SPOSINFINITY, buf, BUFLEN);
 493     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 494 
 495     if (got) {
 496         return (*env)->NewString(env, buf, (jsize)wcslen(buf));
 497     } else {
 498         return infinity;
 499     }
 500 }
 501 
 502 /*
 503  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 504  * Method:    getInternationalCurrencySymbol
 505  * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
 506  */
 507 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getInternationalCurrencySymbol
 508   (JNIEnv *env, jclass cls, jstring jlangtag, jstring internationalCurrencySymbol) {
 509     WCHAR buf[BUFLEN];
 510     int got;
 511     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 512     CHECK_NULL_RETURN(langtag, internationalCurrencySymbol);
 513     got = getLocaleInfoWrapper(langtag, LOCALE_SINTLSYMBOL, buf, BUFLEN);
 514     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 515 
 516     if (got) {
 517         return (*env)->NewString(env, buf, (jsize)wcslen(buf));
 518     } else {
 519         return internationalCurrencySymbol;
 520     }
 521 }
 522 
 523 /*
 524  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 525  * Method:    getMinusSign
 526  * Signature: (Ljava/lang/String;C)C
 527  */
 528 JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getMinusSign
 529   (JNIEnv *env, jclass cls, jstring jlangtag, jchar minusSign) {
 530     WCHAR buf[BUFLEN];
 531     int got;
 532     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 533     CHECK_NULL_RETURN(langtag, minusSign);
 534     got = getLocaleInfoWrapper(langtag, LOCALE_SNEGATIVESIGN, buf, BUFLEN);
 535     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 536 
 537     if (got) {
 538         return buf[0];
 539     } else {
 540         return minusSign;
 541     }
 542 }
 543 
 544 /*
 545  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 546  * Method:    getMonetaryDecimalSeparator
 547  * Signature: (Ljava/lang/String;C)C
 548  */
 549 JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getMonetaryDecimalSeparator
 550   (JNIEnv *env, jclass cls, jstring jlangtag, jchar monetaryDecimalSeparator) {
 551     WCHAR buf[BUFLEN];
 552     int got;
 553     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 554     CHECK_NULL_RETURN(langtag, monetaryDecimalSeparator);
 555     got = getLocaleInfoWrapper(langtag, LOCALE_SMONDECIMALSEP, buf, BUFLEN);
 556     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 557 
 558     if (got) {
 559         return buf[0];
 560     } else {
 561         return monetaryDecimalSeparator;
 562     }
 563 }
 564 
 565 /*
 566  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 567  * Method:    getNaN
 568  * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
 569  */
 570 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getNaN
 571   (JNIEnv *env, jclass cls, jstring jlangtag, jstring nan) {
 572     WCHAR buf[BUFLEN];
 573     int got;
 574     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 575     CHECK_NULL_RETURN(langtag, nan);
 576     got = getLocaleInfoWrapper(langtag, LOCALE_SNAN, buf, BUFLEN);
 577     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 578 
 579     if (got) {
 580         return (*env)->NewString(env, buf, (jsize)wcslen(buf));
 581     } else {
 582         return nan;
 583     }
 584 }
 585 
 586 /*
 587  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 588  * Method:    getPercent
 589  * Signature: (Ljava/lang/String;C)C
 590  */
 591 JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getPercent
 592   (JNIEnv *env, jclass cls, jstring jlangtag, jchar percent) {
 593     WCHAR buf[BUFLEN];
 594     int got;
 595     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 596     CHECK_NULL_RETURN(langtag, percent);
 597     got = getLocaleInfoWrapper(langtag, LOCALE_SPERCENT, buf, BUFLEN);
 598     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 599 
 600     if (got) {
 601         return buf[0];
 602     } else {
 603         return percent;
 604     }
 605 }
 606 
 607 /*
 608  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 609  * Method:    getPerMill
 610  * Signature: (Ljava/lang/String;C)C
 611  */
 612 JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getPerMill
 613   (JNIEnv *env, jclass cls, jstring jlangtag, jchar perMill) {
 614     WCHAR buf[BUFLEN];
 615     const jchar *langtag;
 616     int got;
 617     langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 618     CHECK_NULL_RETURN(langtag, perMill);
 619     got = getLocaleInfoWrapper(langtag, LOCALE_SPERMILLE, buf, BUFLEN);
 620 
 621     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 622 
 623     if (got) {
 624         return buf[0];
 625     } else {
 626         return perMill;
 627     }
 628 }
 629 
 630 /*
 631  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 632  * Method:    getZeroDigit
 633  * Signature: (Ljava/lang/String;C)C
 634  */
 635 JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getZeroDigit
 636   (JNIEnv *env, jclass cls, jstring jlangtag, jchar zeroDigit) {
 637     WCHAR buf[BUFLEN];
 638     const jchar *langtag;
 639     int got;
 640     langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 641     CHECK_NULL_RETURN(langtag, zeroDigit);
 642     got = getLocaleInfoWrapper(langtag, LOCALE_SNATIVEDIGITS, buf, BUFLEN);
 643 
 644     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 645 
 646     if (got) {
 647         return buf[0];
 648     } else {
 649         return zeroDigit;
 650     }
 651 }
 652 
 653 /*
 654  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 655  * Method:    getCalendarDataValue
 656  * Signature: (Ljava/lang/String;I)I
 657  */
 658 JNIEXPORT jint JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCalendarDataValue
 659   (JNIEnv *env, jclass cls, jstring jlangtag, jint type) {
 660     DWORD num;
 661     const jchar *langtag;
 662     int got = 0;
 663 
 664     langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 665     CHECK_NULL_RETURN(langtag, -1);
 666     switch (type) {
 667     case sun_util_locale_provider_HostLocaleProviderAdapterImpl_CD_FIRSTDAYOFWEEK:
 668         got = getLocaleInfoWrapper(langtag,
 669             LOCALE_IFIRSTDAYOFWEEK | LOCALE_RETURN_NUMBER,
 670             (LPWSTR)&num, sizeof(num));
 671         break;
 672     }
 673 
 674     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 675 
 676     if (got) {
 677         return num;
 678     } else {
 679         return -1;
 680     }
 681 }
 682 
 683 /*
 684  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 685  * Method:    getCalendarDisplayStrings
 686  * Signature: (Ljava/lang/String;III)[Ljava/lang/String;
 687  */
 688 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCalendarDisplayStrings
 689   (JNIEnv *env, jclass cls, jstring jlangtag, jint calid, jint field, jint style) {
 690     jobjectArray ret = NULL;
 691     CALTYPE * pCalType = NULL;
 692 
 693     switch (field) {
 694     case CALENDAR_FIELD_ERA:
 695         return getErasImpl(env, jlangtag, calid, style, NULL);
 696 
 697     case CALENDAR_FIELD_MONTH:
 698         ret = (*env)->NewObjectArray(env, MONTHTYPES,
 699                 (*env)->FindClass(env, "java/lang/String"), NULL);
 700         if (ret != NULL) {
 701             if (style & CALENDAR_STYLE_SHORT_MASK) {
 702                 pCalType = sMonthsType;
 703             } else {
 704                 pCalType = monthsType;
 705             }
 706 
 707             replaceCalendarArrayElems(env, jlangtag, calid, ret, pCalType,
 708                           0, MONTHTYPES, style);
 709         }
 710         return ret;
 711 
 712     default:
 713         // not supported
 714         return NULL;
 715     }
 716 }
 717 
 718 /*
 719  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
 720  * Method:    getDisplayString
 721  * Signature: (Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;
 722  */
 723 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getDisplayString
 724   (JNIEnv *env, jclass cls, jstring jlangtag, jint type, jstring jvalue) {
 725     LCTYPE lcType;
 726     jstring jStr;
 727     const jchar * pjChar;
 728     WCHAR buf[BUFLEN];
 729     int got = 0;
 730 
 731     switch (type) {
 732         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_CURRENCY_NAME:
 733             lcType = LOCALE_SNATIVECURRNAME;
 734             jStr = jlangtag;
 735             break;
 736         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_CURRENCY_SYMBOL:
 737             lcType = LOCALE_SCURRENCY;
 738             jStr = jlangtag;
 739             break;
 740         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_LOCALE_LANGUAGE:
 741             lcType = LOCALE_SLOCALIZEDLANGUAGENAME;
 742             jStr = jvalue;
 743             break;
 744         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_LOCALE_REGION:
 745             lcType = LOCALE_SLOCALIZEDCOUNTRYNAME;
 746             jStr = jvalue;
 747             break;
 748         default:
 749             return NULL;
 750     }
 751 
 752     pjChar = (*env)->GetStringChars(env, jStr, JNI_FALSE);
 753     CHECK_NULL_RETURN(pjChar, NULL);
 754     got = getLocaleInfoWrapper(pjChar, lcType, buf, BUFLEN);
 755     (*env)->ReleaseStringChars(env, jStr, pjChar);
 756 
 757     if (got) {
 758         // Hack: Windows 10 returns "Unknown Region (XX)" for localized XX region name.
 759         // Take that as not known.
 760         if (type != sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_LOCALE_REGION ||
 761             wcsncmp(UNKNOWN_REGION, buf, UNKNOWN_REGION_SIZE) != 0) {
 762             return (*env)->NewString(env, buf, (jsize)wcslen(buf));
 763         }
 764     }
 765 
 766     return NULL;
 767 }
 768 
 769 int getLocaleInfoWrapper(const jchar *langtag, LCTYPE type, LPWSTR data, int buflen) {
 770     if (pGetLocaleInfoEx) {
 771         if (wcscmp(L"und", (LPWSTR)langtag) == 0) {
 772             // defaults to "en"
 773             return pGetLocaleInfoEx(L"en", type, data, buflen);
 774         } else {
 775             return pGetLocaleInfoEx((LPWSTR)langtag, type, data, buflen);
 776         }
 777     } else {
 778         // If we ever wanted to support WinXP, we will need extra module from
 779         // MS...
 780         // return GetLocaleInfo(DownlevelLocaleNameToLCID(langtag, 0), type, data, buflen);
 781         return 0;
 782     }
 783 }
 784 
 785 int getCalendarInfoWrapper(const jchar *langtag, CALID id, LPCWSTR reserved, CALTYPE type, LPWSTR data, int buflen, LPDWORD val) {
 786     if (pGetCalendarInfoEx) {
 787         if (wcscmp(L"und", (LPWSTR)langtag) == 0) {
 788             // defaults to "en"
 789             return pGetCalendarInfoEx(L"en", id, reserved, type, data, buflen, val);
 790         } else {
 791             return pGetCalendarInfoEx((LPWSTR)langtag, id, reserved, type, data, buflen, val);
 792         }
 793     } else {
 794         // If we ever wanted to support WinXP, we will need extra module from
 795         // MS...
 796         // return GetCalendarInfo(DownlevelLocaleNameToLCID(langtag, 0), ...);
 797         return 0;
 798     }
 799 }
 800 
 801 jint getCalendarID(const jchar *langtag) {
 802     DWORD type = -1;
 803     int got = getLocaleInfoWrapper(langtag,
 804         LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
 805         (LPWSTR)&type, sizeof(type));
 806 
 807     if (got) {
 808         switch (type) {
 809             case CAL_GREGORIAN:
 810             case CAL_GREGORIAN_US:
 811             case CAL_JAPAN:
 812             case CAL_TAIWAN:
 813             case CAL_HIJRI:
 814             case CAL_THAI:
 815             case CAL_GREGORIAN_ME_FRENCH:
 816             case CAL_GREGORIAN_ARABIC:
 817             case CAL_GREGORIAN_XLIT_ENGLISH:
 818             case CAL_GREGORIAN_XLIT_FRENCH:
 819             case CAL_UMALQURA:
 820                 break;
 821 
 822             default:
 823                 // non-supported calendars return -1
 824                 type = -1;
 825                 break;
 826         }
 827     }
 828 
 829     return type;
 830 }
 831 
 832 void replaceCalendarArrayElems(JNIEnv *env, jstring jlangtag, jint calid, jobjectArray jarray, CALTYPE* pCalTypes, int offset, int length, int style) {
 833     WCHAR name[BUFLEN];
 834     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
 835     jstring tmp_string;
 836     CALTYPE isGenitive;
 837 
 838     CHECK_NULL(langtag);
 839 
 840     if (calid < 0) {
 841         calid = getCalendarID(langtag);
 842     }
 843 
 844     if (calid != -1) {
 845         int i;
 846 
 847         if (!(style & CALENDAR_STYLE_STANDALONE_MASK)) {
 848             isGenitive = CAL_RETURN_GENITIVE_NAMES;
 849         }
 850 
 851         for (i = 0; i < length; i++) {
 852             if (getCalendarInfoWrapper(langtag, calid, NULL,
 853                               pCalTypes[i] | isGenitive, name, BUFLEN, NULL) != 0) {
 854                 tmp_string = (*env)->NewString(env, name, (jsize)wcslen(name));
 855                 if (tmp_string != NULL) {
 856                     (*env)->SetObjectArrayElement(env, jarray, i + offset, tmp_string);
 857                 }
 858             }
 859         }
 860     }
 861 
 862     (*env)->ReleaseStringChars(env, jlangtag, langtag);
 863 }
 864 
 865 WCHAR * getNumberPattern(const jchar * langtag, const jint numberStyle) {
 866     WCHAR ret[BUFLEN];
 867     WCHAR number[BUFLEN];
 868     WCHAR fix[BUFLEN];
 869 
 870     getFixPart(langtag, numberStyle, TRUE, TRUE, ret); // "+"
 871     getNumberPart(langtag, numberStyle, number);
 872     wcscat_s(ret, BUFLEN-wcslen(ret), number);      // "+12.34"
 873     getFixPart(langtag, numberStyle, TRUE, FALSE, fix);
 874     wcscat_s(ret, BUFLEN-wcslen(ret), fix);         // "+12.34$"
 875     wcscat_s(ret, BUFLEN-wcslen(ret), L";");        // "+12.34$;"
 876     getFixPart(langtag, numberStyle, FALSE, TRUE, fix);
 877     wcscat_s(ret, BUFLEN-wcslen(ret), fix);         // "+12.34$;("
 878     wcscat_s(ret, BUFLEN-wcslen(ret), number);      // "+12.34$;(12.34"
 879     getFixPart(langtag, numberStyle, FALSE, FALSE, fix);
 880     wcscat_s(ret, BUFLEN-wcslen(ret), fix);         // "+12.34$;(12.34$)"
 881 
 882     return _wcsdup(ret);
 883 }
 884 
 885 void getNumberPart(const jchar * langtag, const jint numberStyle, WCHAR * number) {
 886     DWORD digits = 0;
 887     DWORD leadingZero = 0;
 888     WCHAR grouping[BUFLEN];
 889     int groupingLen;
 890     WCHAR fractionPattern[BUFLEN];
 891     WCHAR * integerPattern = number;
 892     WCHAR * pDest;
 893 
 894     // Get info from Windows
 895     switch (numberStyle) {
 896         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_CURRENCY:
 897             getLocaleInfoWrapper(langtag,
 898                 LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER,
 899                 (LPWSTR)&digits, sizeof(digits));
 900             break;
 901 
 902         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_INTEGER:
 903             break;
 904 
 905         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_NUMBER:
 906         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_PERCENT:
 907         default:
 908             getLocaleInfoWrapper(langtag,
 909                 LOCALE_IDIGITS | LOCALE_RETURN_NUMBER,
 910                 (LPWSTR)&digits, sizeof(digits));
 911             break;
 912     }
 913 
 914     getLocaleInfoWrapper(langtag,
 915         LOCALE_ILZERO | LOCALE_RETURN_NUMBER,
 916         (LPWSTR)&leadingZero, sizeof(leadingZero));
 917     groupingLen = getLocaleInfoWrapper(langtag, LOCALE_SGROUPING, grouping, BUFLEN);
 918 
 919     // fraction pattern
 920     if (digits > 0) {
 921         int i;
 922         for(i = digits;  i > 0; i--) {
 923             fractionPattern[i] = L'0';
 924         }
 925         fractionPattern[0] = L'.';
 926         fractionPattern[digits+1] = L'\0';
 927     } else {
 928         fractionPattern[0] = L'\0';
 929     }
 930 
 931     // integer pattern
 932     pDest = integerPattern;
 933     if (groupingLen > 0) {
 934         int cur = groupingLen - 1;// subtracting null terminator
 935         while (--cur >= 0) {
 936             int repnum;
 937 
 938             if (grouping[cur] == L';') {
 939                 continue;
 940             }
 941 
 942             repnum = grouping[cur] - 0x30;
 943             if (repnum > 0) {
 944                 *pDest++ = L'#';
 945                 *pDest++ = L',';
 946                 while(--repnum > 0) {
 947                     *pDest++ = L'#';
 948                 }
 949             }
 950         }
 951     }
 952 
 953     if (leadingZero != 0) {
 954         *pDest++ = L'0';
 955     } else {
 956         *pDest++ = L'#';
 957     }
 958     *pDest = L'\0';
 959 
 960     wcscat_s(integerPattern, BUFLEN, fractionPattern);
 961 }
 962 
 963 void getFixPart(const jchar * langtag, const jint numberStyle, BOOL positive, BOOL prefix, WCHAR * ret) {
 964     DWORD pattern = 0;
 965     int style = numberStyle;
 966     int got = 0;
 967 
 968     if (positive) {
 969         if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_CURRENCY) {
 970             got = getLocaleInfoWrapper(langtag,
 971                 LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER,
 972                 (LPWSTR)&pattern, sizeof(pattern));
 973         } else if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_PERCENT) {
 974             got = getLocaleInfoWrapper(langtag,
 975                 LOCALE_IPOSITIVEPERCENT | LOCALE_RETURN_NUMBER,
 976                 (LPWSTR)&pattern, sizeof(pattern));
 977         }
 978     } else {
 979         if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_CURRENCY) {
 980             got = getLocaleInfoWrapper(langtag,
 981                 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER,
 982                 (LPWSTR)&pattern, sizeof(pattern));
 983         } else if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_PERCENT) {
 984             got = getLocaleInfoWrapper(langtag,
 985                 LOCALE_INEGATIVEPERCENT | LOCALE_RETURN_NUMBER,
 986                 (LPWSTR)&pattern, sizeof(pattern));
 987         } else {
 988             got = getLocaleInfoWrapper(langtag,
 989                 LOCALE_INEGNUMBER | LOCALE_RETURN_NUMBER,
 990                 (LPWSTR)&pattern, sizeof(pattern));
 991         }
 992     }
 993 
 994     if (numberStyle == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_INTEGER) {
 995         style = sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_NUMBER;
 996     }
 997 
 998     wcscpy(ret, fixes[!prefix][!positive][style][pattern]);
 999 }
1000 
1001 int enumCalendarInfoWrapper(const jchar *langtag, CALID calid, CALTYPE type, LPWSTR buf, int buflen) {
1002     if (pEnumCalendarInfoExEx) {
1003         if (wcscmp(L"und", (LPWSTR)langtag) == 0) {
1004             // defaults to "en"
1005             return pEnumCalendarInfoExEx(&EnumCalendarInfoProc, L"en",
1006                 calid, NULL, type, (LPARAM)buf);
1007         } else {
1008             return pEnumCalendarInfoExEx(&EnumCalendarInfoProc, langtag,
1009                 calid, NULL, type, (LPARAM)buf);
1010         }
1011     } else {
1012         return 0;
1013     }
1014 }
1015 
1016 BOOL CALLBACK EnumCalendarInfoProc(LPWSTR lpCalInfoStr, CALID calid, LPWSTR lpReserved, LPARAM lParam) {
1017     wcscat_s((LPWSTR)lParam, BUFLEN, lpCalInfoStr);
1018     wcscat_s((LPWSTR)lParam, BUFLEN, L",");
1019     return TRUE;
1020 }
1021 
1022 jobjectArray getErasImpl(JNIEnv *env, jstring jlangtag, jint calid, jint style, jobjectArray eras) {
1023     const jchar * langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
1024     WCHAR buf[BUFLEN];
1025     jobjectArray ret = eras;
1026     CALTYPE type;
1027 
1028     CHECK_NULL_RETURN(langtag, ret);
1029 
1030     buf[0] = '\0';
1031     if (style & CALENDAR_STYLE_SHORT_MASK) {
1032         type = CAL_SABBREVERASTRING;
1033     } else {
1034         type = CAL_SERASTRING;
1035     }
1036 
1037     if (calid < 0) {
1038         calid = getCalendarID(langtag);
1039     }
1040 
1041     if (calid != -1 && enumCalendarInfoWrapper(langtag, calid, type, buf, BUFLEN)) {
1042         // format in buf: "era0,era1,era2," where era0 is the current one
1043         int eraCount;
1044         LPWSTR current;
1045         jsize array_length;
1046 
1047         for(eraCount = 0, current = buf; *current != '\0'; current++) {
1048             if (*current == L',') {
1049                 eraCount ++;
1050             }
1051         }
1052 
1053         if (eras != NULL) {
1054             array_length = (*env)->GetArrayLength(env, eras);
1055         } else {
1056             // +1 for the "before" era, e.g., BC, which Windows does not return.
1057             array_length = (jsize)eraCount + 1;
1058             ret = (*env)->NewObjectArray(env, array_length,
1059                 (*env)->FindClass(env, "java/lang/String"), NULL);
1060         }
1061 
1062         if (ret != NULL) {
1063             int eraIndex;
1064             LPWSTR era;
1065 
1066             for(eraIndex = 0, era = current = buf; eraIndex < eraCount; era = current, eraIndex++) {
1067                 while (*current != L',') {
1068                     current++;
1069                 }
1070                 *current++ = '\0';
1071 
1072                 if (eraCount - eraIndex < array_length &&
1073                     *era != '\0') {
1074                     (*env)->SetObjectArrayElement(env, ret,
1075                         (jsize)(eraCount - eraIndex),
1076                         (*env)->NewString(env, era, (jsize)wcslen(era)));
1077                 }
1078             }
1079 
1080             // Hack for the Japanese Imperial Calendar to insert Gregorian era for
1081             // "Before Meiji"
1082             if (calid == CAL_JAPAN) {
1083                 buf[0] = '\0';
1084                 if (enumCalendarInfoWrapper(langtag, CAL_GREGORIAN, type, buf, BUFLEN)) {
1085                     jsize len = (jsize)wcslen(buf);
1086                     buf[--len] = '\0'; // remove the last ','
1087                     (*env)->SetObjectArrayElement(env, ret, 0, (*env)->NewString(env, buf, len));
1088                 }
1089             }
1090         }
1091     }
1092 
1093     (*env)->ReleaseStringChars(env, jlangtag, langtag);
1094     return ret;
1095 }