1 /*
2  * Copyright (c) 2012, 2019, 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  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
173  * Method:    initialize
174  * Signature: ()Z
175  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_initialize(JNIEnv * env,jclass cls)176 JNIEXPORT jboolean JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_initialize
177   (JNIEnv *env, jclass cls) {
178     if (!initialized) {
179         pGetLocaleInfoEx = (PGLIE)GetProcAddress(
180             GetModuleHandle("kernel32.dll"),
181             "GetLocaleInfoEx");
182         pGetCalendarInfoEx = (PGCIE)GetProcAddress(
183             GetModuleHandle("kernel32.dll"),
184             "GetCalendarInfoEx");
185         pEnumCalendarInfoExEx = (PECIEE)GetProcAddress(
186             GetModuleHandle("kernel32.dll"),
187             "EnumCalendarInfoExEx");
188         initialized =TRUE;
189     }
190 
191     return pGetLocaleInfoEx != NULL &&
192            pGetCalendarInfoEx != NULL &&
193            pEnumCalendarInfoExEx != NULL;
194 }
195 
196 /*
197  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
198  * Method:    getDefaultLocale
199  * Signature: (I)Ljava/lang/String;
200  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getDefaultLocale(JNIEnv * env,jclass cls,jint cat)201 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getDefaultLocale
202   (JNIEnv *env, jclass cls, jint cat) {
203     char * localeString = NULL;
204     LANGID langid;
205     jstring ret;
206 
207     switch (cat) {
208         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_CAT_DISPLAY:
209             langid = LANGIDFROMLCID(GetUserDefaultUILanguage());
210             break;
211         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_CAT_FORMAT:
212         default:
213             langid = LANGIDFROMLCID(GetUserDefaultLCID());
214             break;
215     }
216 
217     localeString = (char *)getJavaIDFromLangID(langid);
218     if (localeString != NULL) {
219         ret = (*env)->NewStringUTF(env, localeString);
220         free(localeString);
221     } else {
222         JNU_ThrowOutOfMemoryError(env, "memory allocation error");
223         ret = NULL;
224     }
225     return ret;
226 }
227 
228 /*
229  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
230  * Method:    getDateTimePattern
231  * Signature: (IILjava/lang/String;)Ljava/lang/String;
232  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getDateTimePattern(JNIEnv * env,jclass cls,jint dateStyle,jint timeStyle,jstring jlangtag)233 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getDateTimePattern
234   (JNIEnv *env, jclass cls, jint dateStyle, jint timeStyle, jstring jlangtag) {
235     WCHAR pattern[BUFLEN];
236     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
237     CHECK_NULL_RETURN(langtag, NULL);
238 
239     pattern[0] = L'\0';
240 
241     if (dateStyle == 0 || dateStyle == 1) {
242         getLocaleInfoWrapper(langtag, LOCALE_SLONGDATE, pattern, BUFLEN);
243     } else if (dateStyle == 2 || dateStyle == 3) {
244         getLocaleInfoWrapper(langtag, LOCALE_SSHORTDATE, pattern, BUFLEN);
245     }
246 
247     if (timeStyle == 0 || timeStyle == 1) {
248         getLocaleInfoWrapper(langtag, LOCALE_STIMEFORMAT, pattern, BUFLEN);
249     } else if (timeStyle == 2 || timeStyle == 3) {
250         getLocaleInfoWrapper(langtag, LOCALE_SSHORTTIME, pattern, BUFLEN);
251     }
252 
253     (*env)->ReleaseStringChars(env, jlangtag, langtag);
254 
255     return (*env)->NewString(env, pattern, (jsize)wcslen(pattern));
256 }
257 
258 /*
259  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
260  * Method:    getCalendarID
261  * Signature: (Ljava/lang/String;)I
262  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCalendarID(JNIEnv * env,jclass cls,jstring jlangtag)263 JNIEXPORT jint JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCalendarID
264   (JNIEnv *env, jclass cls, jstring jlangtag) {
265     const jchar *langtag;
266     jint ret;
267     langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
268     CHECK_NULL_RETURN(langtag, 0);
269     ret = getCalendarID(langtag);
270     (*env)->ReleaseStringChars(env, jlangtag, langtag);
271     return ret;
272 }
273 
274 /*
275  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
276  * Method:    getAmPmStrings
277  * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String;
278  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getAmPmStrings(JNIEnv * env,jclass cls,jstring jlangtag,jobjectArray ampms)279 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getAmPmStrings
280   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray ampms) {
281     WCHAR buf[BUFLEN];
282     const jchar *langtag;
283     jstring tmp_string;
284 
285     // AM
286     int got;
287     langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
288     CHECK_NULL_RETURN(langtag, NULL);
289     got = getLocaleInfoWrapper(langtag, LOCALE_S1159, buf, BUFLEN);
290     if (got) {
291         tmp_string = (*env)->NewString(env, buf, (jsize)wcslen(buf));
292         if (tmp_string != NULL) {
293             (*env)->SetObjectArrayElement(env, ampms, 0, tmp_string);
294         }
295     }
296 
297     if (!(*env)->ExceptionCheck(env)){
298         // PM
299         got = getLocaleInfoWrapper(langtag, LOCALE_S2359, buf, BUFLEN);
300         if (got) {
301             tmp_string = (*env)->NewString(env, buf, (jsize)wcslen(buf));
302             if (tmp_string != NULL) {
303                 (*env)->SetObjectArrayElement(env, ampms, 1, tmp_string);
304             }
305         }
306     }
307 
308     (*env)->ReleaseStringChars(env, jlangtag, langtag);
309 
310     return ampms;
311 }
312 
313 /*
314  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
315  * Method:    getEras
316  * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String;
317  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getEras(JNIEnv * env,jclass cls,jstring jlangtag,jobjectArray eras)318 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getEras
319   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray eras) {
320     return getErasImpl(env, jlangtag, -1, 0, eras);
321 }
322 
323 /*
324  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
325  * Method:    getMonths
326  * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String;
327  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getMonths(JNIEnv * env,jclass cls,jstring jlangtag,jobjectArray months)328 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getMonths
329   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray months) {
330     replaceCalendarArrayElems(env, jlangtag, -1, months, monthsType,
331                       0, MONTHTYPES, 0);
332     return months;
333 }
334 
335 /*
336  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
337  * Method:    getShortMonths
338  * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String;
339  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getShortMonths(JNIEnv * env,jclass cls,jstring jlangtag,jobjectArray smonths)340 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getShortMonths
341   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray smonths) {
342     replaceCalendarArrayElems(env, jlangtag, -1, smonths, sMonthsType,
343                       0, MONTHTYPES, 0);
344     return smonths;
345 }
346 
347 /*
348  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
349  * Method:    getWeekdays
350  * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String;
351  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getWeekdays(JNIEnv * env,jclass cls,jstring jlangtag,jobjectArray wdays)352 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getWeekdays
353   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray wdays) {
354     replaceCalendarArrayElems(env, jlangtag, -1, wdays, wDaysType,
355                       1, sizeof(wDaysType)/sizeof(CALTYPE), 0);
356     return wdays;
357 }
358 
359 /*
360  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
361  * Method:    getShortWeekdays
362  * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String;
363  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getShortWeekdays(JNIEnv * env,jclass cls,jstring jlangtag,jobjectArray swdays)364 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getShortWeekdays
365   (JNIEnv *env, jclass cls, jstring jlangtag, jobjectArray swdays) {
366     replaceCalendarArrayElems(env, jlangtag, -1, swdays, sWDaysType,
367                       1, sizeof(sWDaysType)/sizeof(CALTYPE), 0);
368     return swdays;
369 }
370 
371 /*
372  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
373  * Method:    getNumberPattern
374  * Signature: (ILjava/lang/String;)Ljava/lang/String;
375  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getNumberPattern(JNIEnv * env,jclass cls,jint numberStyle,jstring jlangtag)376 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getNumberPattern
377   (JNIEnv *env, jclass cls, jint numberStyle, jstring jlangtag) {
378     const jchar *langtag;
379     jstring ret;
380     WCHAR * pattern;
381 
382     langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
383     CHECK_NULL_RETURN(langtag, NULL);
384     pattern = getNumberPattern(langtag, numberStyle);
385     CHECK_NULL_RETURN(pattern, NULL);
386 
387     (*env)->ReleaseStringChars(env, jlangtag, langtag);
388     ret = (*env)->NewString(env, pattern, (jsize)wcslen(pattern));
389     free(pattern);
390 
391     return ret;
392 }
393 
394 /*
395  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
396  * Method:    isNativeDigit
397  * Signature: (Ljava/lang/String;)Z
398  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_isNativeDigit(JNIEnv * env,jclass cls,jstring jlangtag)399 JNIEXPORT jboolean JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_isNativeDigit
400   (JNIEnv *env, jclass cls, jstring jlangtag) {
401     DWORD num;
402     int got;
403     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
404     CHECK_NULL_RETURN(langtag, JNI_FALSE);
405     got = getLocaleInfoWrapper(langtag,
406         LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER,
407         (LPWSTR)&num, sizeof(num));
408     (*env)->ReleaseStringChars(env, jlangtag, langtag);
409 
410     return got && num == 2; // 2: native digit substitution
411 }
412 
413 /*
414  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
415  * Method:    getCurrencySymbol
416  * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
417  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCurrencySymbol(JNIEnv * env,jclass cls,jstring jlangtag,jstring currencySymbol)418 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCurrencySymbol
419   (JNIEnv *env, jclass cls, jstring jlangtag, jstring currencySymbol) {
420     WCHAR buf[BUFLEN];
421     int got;
422     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
423     CHECK_NULL_RETURN(langtag, currencySymbol);
424     got = getLocaleInfoWrapper(langtag, LOCALE_SCURRENCY, buf, BUFLEN);
425     (*env)->ReleaseStringChars(env, jlangtag, langtag);
426 
427     if (got) {
428         return (*env)->NewString(env, buf, (jsize)wcslen(buf));
429     } else {
430         return currencySymbol;
431     }
432 }
433 
434 /*
435  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
436  * Method:    getDecimalSeparator
437  * Signature: (Ljava/lang/String;C)C
438  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getDecimalSeparator(JNIEnv * env,jclass cls,jstring jlangtag,jchar decimalSeparator)439 JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getDecimalSeparator
440   (JNIEnv *env, jclass cls, jstring jlangtag, jchar decimalSeparator) {
441     WCHAR buf[BUFLEN];
442     int got;
443     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
444     CHECK_NULL_RETURN(langtag, decimalSeparator);
445     got = getLocaleInfoWrapper(langtag, LOCALE_SDECIMAL, buf, BUFLEN);
446     (*env)->ReleaseStringChars(env, jlangtag, langtag);
447 
448     if (got) {
449         return buf[0];
450     } else {
451         return decimalSeparator;
452     }
453 }
454 
455 /*
456  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
457  * Method:    getGroupingSeparator
458  * Signature: (Ljava/lang/String;C)C
459  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getGroupingSeparator(JNIEnv * env,jclass cls,jstring jlangtag,jchar groupingSeparator)460 JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getGroupingSeparator
461   (JNIEnv *env, jclass cls, jstring jlangtag, jchar groupingSeparator) {
462     WCHAR buf[BUFLEN];
463     int got;
464     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
465     CHECK_NULL_RETURN(langtag, groupingSeparator);
466     got = getLocaleInfoWrapper(langtag, LOCALE_STHOUSAND, buf, BUFLEN);
467     (*env)->ReleaseStringChars(env, jlangtag, langtag);
468 
469     if (got) {
470         return buf[0];
471     } else {
472         return groupingSeparator;
473     }
474 }
475 
476 /*
477  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
478  * Method:    getInfinity
479  * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
480  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getInfinity(JNIEnv * env,jclass cls,jstring jlangtag,jstring infinity)481 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getInfinity
482   (JNIEnv *env, jclass cls, jstring jlangtag, jstring infinity) {
483     WCHAR buf[BUFLEN];
484     int got;
485     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
486     CHECK_NULL_RETURN(langtag, infinity);
487     got = getLocaleInfoWrapper(langtag, LOCALE_SPOSINFINITY, buf, BUFLEN);
488     (*env)->ReleaseStringChars(env, jlangtag, langtag);
489 
490     if (got) {
491         return (*env)->NewString(env, buf, (jsize)wcslen(buf));
492     } else {
493         return infinity;
494     }
495 }
496 
497 /*
498  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
499  * Method:    getInternationalCurrencySymbol
500  * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
501  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getInternationalCurrencySymbol(JNIEnv * env,jclass cls,jstring jlangtag,jstring internationalCurrencySymbol)502 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getInternationalCurrencySymbol
503   (JNIEnv *env, jclass cls, jstring jlangtag, jstring internationalCurrencySymbol) {
504     WCHAR buf[BUFLEN];
505     int got;
506     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
507     CHECK_NULL_RETURN(langtag, internationalCurrencySymbol);
508     got = getLocaleInfoWrapper(langtag, LOCALE_SINTLSYMBOL, buf, BUFLEN);
509     (*env)->ReleaseStringChars(env, jlangtag, langtag);
510 
511     if (got) {
512         return (*env)->NewString(env, buf, (jsize)wcslen(buf));
513     } else {
514         return internationalCurrencySymbol;
515     }
516 }
517 
518 /*
519  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
520  * Method:    getMinusSign
521  * Signature: (Ljava/lang/String;C)C
522  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getMinusSign(JNIEnv * env,jclass cls,jstring jlangtag,jchar minusSign)523 JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getMinusSign
524   (JNIEnv *env, jclass cls, jstring jlangtag, jchar minusSign) {
525     WCHAR buf[BUFLEN];
526     int got;
527     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
528     CHECK_NULL_RETURN(langtag, minusSign);
529     got = getLocaleInfoWrapper(langtag, LOCALE_SNEGATIVESIGN, buf, BUFLEN);
530     (*env)->ReleaseStringChars(env, jlangtag, langtag);
531 
532     if (got) {
533         return buf[0];
534     } else {
535         return minusSign;
536     }
537 }
538 
539 /*
540  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
541  * Method:    getMonetaryDecimalSeparator
542  * Signature: (Ljava/lang/String;C)C
543  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getMonetaryDecimalSeparator(JNIEnv * env,jclass cls,jstring jlangtag,jchar monetaryDecimalSeparator)544 JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getMonetaryDecimalSeparator
545   (JNIEnv *env, jclass cls, jstring jlangtag, jchar monetaryDecimalSeparator) {
546     WCHAR buf[BUFLEN];
547     int got;
548     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
549     CHECK_NULL_RETURN(langtag, monetaryDecimalSeparator);
550     got = getLocaleInfoWrapper(langtag, LOCALE_SMONDECIMALSEP, buf, BUFLEN);
551     (*env)->ReleaseStringChars(env, jlangtag, langtag);
552 
553     if (got) {
554         return buf[0];
555     } else {
556         return monetaryDecimalSeparator;
557     }
558 }
559 
560 /*
561  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
562  * Method:    getNaN
563  * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
564  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getNaN(JNIEnv * env,jclass cls,jstring jlangtag,jstring nan)565 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getNaN
566   (JNIEnv *env, jclass cls, jstring jlangtag, jstring nan) {
567     WCHAR buf[BUFLEN];
568     int got;
569     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
570     CHECK_NULL_RETURN(langtag, nan);
571     got = getLocaleInfoWrapper(langtag, LOCALE_SNAN, buf, BUFLEN);
572     (*env)->ReleaseStringChars(env, jlangtag, langtag);
573 
574     if (got) {
575         return (*env)->NewString(env, buf, (jsize)wcslen(buf));
576     } else {
577         return nan;
578     }
579 }
580 
581 /*
582  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
583  * Method:    getPercent
584  * Signature: (Ljava/lang/String;C)C
585  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getPercent(JNIEnv * env,jclass cls,jstring jlangtag,jchar percent)586 JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getPercent
587   (JNIEnv *env, jclass cls, jstring jlangtag, jchar percent) {
588     WCHAR buf[BUFLEN];
589     int got;
590     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
591     CHECK_NULL_RETURN(langtag, percent);
592     got = getLocaleInfoWrapper(langtag, LOCALE_SPERCENT, buf, BUFLEN);
593     (*env)->ReleaseStringChars(env, jlangtag, langtag);
594 
595     if (got) {
596         return buf[0];
597     } else {
598         return percent;
599     }
600 }
601 
602 /*
603  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
604  * Method:    getPerMill
605  * Signature: (Ljava/lang/String;C)C
606  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getPerMill(JNIEnv * env,jclass cls,jstring jlangtag,jchar perMill)607 JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getPerMill
608   (JNIEnv *env, jclass cls, jstring jlangtag, jchar perMill) {
609     WCHAR buf[BUFLEN];
610     const jchar *langtag;
611     int got;
612     langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
613     CHECK_NULL_RETURN(langtag, perMill);
614     got = getLocaleInfoWrapper(langtag, LOCALE_SPERMILLE, buf, BUFLEN);
615 
616     (*env)->ReleaseStringChars(env, jlangtag, langtag);
617 
618     if (got) {
619         return buf[0];
620     } else {
621         return perMill;
622     }
623 }
624 
625 /*
626  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
627  * Method:    getZeroDigit
628  * Signature: (Ljava/lang/String;C)C
629  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getZeroDigit(JNIEnv * env,jclass cls,jstring jlangtag,jchar zeroDigit)630 JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getZeroDigit
631   (JNIEnv *env, jclass cls, jstring jlangtag, jchar zeroDigit) {
632     WCHAR buf[BUFLEN];
633     const jchar *langtag;
634     int got;
635     langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
636     CHECK_NULL_RETURN(langtag, zeroDigit);
637     got = getLocaleInfoWrapper(langtag, LOCALE_SNATIVEDIGITS, buf, BUFLEN);
638 
639     (*env)->ReleaseStringChars(env, jlangtag, langtag);
640 
641     if (got) {
642         return buf[0];
643     } else {
644         return zeroDigit;
645     }
646 }
647 
648 /*
649  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
650  * Method:    getCalendarDataValue
651  * Signature: (Ljava/lang/String;I)I
652  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCalendarDataValue(JNIEnv * env,jclass cls,jstring jlangtag,jint type)653 JNIEXPORT jint JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCalendarDataValue
654   (JNIEnv *env, jclass cls, jstring jlangtag, jint type) {
655     DWORD num;
656     const jchar *langtag;
657     int got = 0;
658 
659     langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
660     CHECK_NULL_RETURN(langtag, -1);
661     switch (type) {
662     case sun_util_locale_provider_HostLocaleProviderAdapterImpl_CD_FIRSTDAYOFWEEK:
663         got = getLocaleInfoWrapper(langtag,
664             LOCALE_IFIRSTDAYOFWEEK | LOCALE_RETURN_NUMBER,
665             (LPWSTR)&num, sizeof(num));
666         break;
667     }
668 
669     (*env)->ReleaseStringChars(env, jlangtag, langtag);
670 
671     if (got) {
672         return num;
673     } else {
674         return -1;
675     }
676 }
677 
678 /*
679  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
680  * Method:    getCalendarDisplayStrings
681  * Signature: (Ljava/lang/String;III)[Ljava/lang/String;
682  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCalendarDisplayStrings(JNIEnv * env,jclass cls,jstring jlangtag,jint calid,jint field,jint style)683 JNIEXPORT jobjectArray JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCalendarDisplayStrings
684   (JNIEnv *env, jclass cls, jstring jlangtag, jint calid, jint field, jint style) {
685     jobjectArray ret = NULL;
686     CALTYPE * pCalType = NULL;
687 
688     switch (field) {
689     case CALENDAR_FIELD_ERA:
690         return getErasImpl(env, jlangtag, calid, style, NULL);
691 
692     case CALENDAR_FIELD_MONTH:
693         ret = (*env)->NewObjectArray(env, MONTHTYPES,
694                 (*env)->FindClass(env, "java/lang/String"), NULL);
695         if (ret != NULL) {
696             if (style & CALENDAR_STYLE_SHORT_MASK) {
697                 pCalType = sMonthsType;
698             } else {
699                 pCalType = monthsType;
700             }
701 
702             replaceCalendarArrayElems(env, jlangtag, calid, ret, pCalType,
703                           0, MONTHTYPES, style);
704         }
705         return ret;
706 
707     default:
708         // not supported
709         return NULL;
710     }
711 }
712 
713 /*
714  * Class:     sun_util_locale_provider_HostLocaleProviderAdapterImpl
715  * Method:    getDisplayString
716  * Signature: (Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;
717  */
Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getDisplayString(JNIEnv * env,jclass cls,jstring jlangtag,jint type,jstring jvalue)718 JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getDisplayString
719   (JNIEnv *env, jclass cls, jstring jlangtag, jint type, jstring jvalue) {
720     LCTYPE lcType;
721     jstring jStr;
722     const jchar * pjChar;
723     WCHAR buf[BUFLEN];
724     int got = 0;
725 
726     switch (type) {
727         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_CURRENCY_NAME:
728             lcType = LOCALE_SNATIVECURRNAME;
729             jStr = jlangtag;
730             break;
731         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_CURRENCY_SYMBOL:
732             lcType = LOCALE_SCURRENCY;
733             jStr = jlangtag;
734             break;
735         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_LOCALE_LANGUAGE:
736             lcType = LOCALE_SLOCALIZEDLANGUAGENAME;
737             jStr = jvalue;
738             break;
739         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_LOCALE_REGION:
740             lcType = LOCALE_SLOCALIZEDCOUNTRYNAME;
741             jStr = jvalue;
742             break;
743         default:
744             return NULL;
745     }
746 
747     pjChar = (*env)->GetStringChars(env, jStr, JNI_FALSE);
748     CHECK_NULL_RETURN(pjChar, NULL);
749     got = getLocaleInfoWrapper(pjChar, lcType, buf, BUFLEN);
750     (*env)->ReleaseStringChars(env, jStr, pjChar);
751 
752     if (got) {
753         return (*env)->NewString(env, buf, (jsize)wcslen(buf));
754     } else {
755         return NULL;
756     }
757 }
758 
getLocaleInfoWrapper(const jchar * langtag,LCTYPE type,LPWSTR data,int buflen)759 int getLocaleInfoWrapper(const jchar *langtag, LCTYPE type, LPWSTR data, int buflen) {
760     if (pGetLocaleInfoEx) {
761         if (wcscmp(L"und", (LPWSTR)langtag) == 0) {
762             // defaults to "en"
763             return pGetLocaleInfoEx(L"en", type, data, buflen);
764         } else {
765             return pGetLocaleInfoEx((LPWSTR)langtag, type, data, buflen);
766         }
767     } else {
768         // If we ever wanted to support WinXP, we will need extra module from
769         // MS...
770         // return GetLocaleInfo(DownlevelLocaleNameToLCID(langtag, 0), type, data, buflen);
771         return 0;
772     }
773 }
774 
getCalendarInfoWrapper(const jchar * langtag,CALID id,LPCWSTR reserved,CALTYPE type,LPWSTR data,int buflen,LPDWORD val)775 int getCalendarInfoWrapper(const jchar *langtag, CALID id, LPCWSTR reserved, CALTYPE type, LPWSTR data, int buflen, LPDWORD val) {
776     if (pGetCalendarInfoEx) {
777         if (wcscmp(L"und", (LPWSTR)langtag) == 0) {
778             // defaults to "en"
779             return pGetCalendarInfoEx(L"en", id, reserved, type, data, buflen, val);
780         } else {
781             return pGetCalendarInfoEx((LPWSTR)langtag, id, reserved, type, data, buflen, val);
782         }
783     } else {
784         // If we ever wanted to support WinXP, we will need extra module from
785         // MS...
786         // return GetCalendarInfo(DownlevelLocaleNameToLCID(langtag, 0), ...);
787         return 0;
788     }
789 }
790 
getCalendarID(const jchar * langtag)791 jint getCalendarID(const jchar *langtag) {
792     DWORD type = -1;
793     int got = getLocaleInfoWrapper(langtag,
794         LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
795         (LPWSTR)&type, sizeof(type));
796 
797     if (got) {
798         switch (type) {
799             case CAL_GREGORIAN:
800             case CAL_GREGORIAN_US:
801             case CAL_JAPAN:
802             case CAL_TAIWAN:
803             case CAL_HIJRI:
804             case CAL_THAI:
805             case CAL_GREGORIAN_ME_FRENCH:
806             case CAL_GREGORIAN_ARABIC:
807             case CAL_GREGORIAN_XLIT_ENGLISH:
808             case CAL_GREGORIAN_XLIT_FRENCH:
809             case CAL_UMALQURA:
810                 break;
811 
812             default:
813                 // non-supported calendars return -1
814                 type = -1;
815                 break;
816         }
817     }
818 
819     return type;
820 }
821 
replaceCalendarArrayElems(JNIEnv * env,jstring jlangtag,jint calid,jobjectArray jarray,CALTYPE * pCalTypes,int offset,int length,int style)822 void replaceCalendarArrayElems(JNIEnv *env, jstring jlangtag, jint calid, jobjectArray jarray, CALTYPE* pCalTypes, int offset, int length, int style) {
823     WCHAR name[BUFLEN];
824     const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
825     jstring tmp_string;
826     CALTYPE isGenitive;
827 
828     CHECK_NULL(langtag);
829 
830     if (calid < 0) {
831         calid = getCalendarID(langtag);
832     }
833 
834     if (calid != -1) {
835         int i;
836 
837         if (!(style & CALENDAR_STYLE_STANDALONE_MASK)) {
838             isGenitive = CAL_RETURN_GENITIVE_NAMES;
839         }
840 
841         for (i = 0; i < length; i++) {
842             if (getCalendarInfoWrapper(langtag, calid, NULL,
843                               pCalTypes[i] | isGenitive, name, BUFLEN, NULL) != 0) {
844                 tmp_string = (*env)->NewString(env, name, (jsize)wcslen(name));
845                 if (tmp_string != NULL) {
846                     (*env)->SetObjectArrayElement(env, jarray, i + offset, tmp_string);
847                 }
848             }
849         }
850     }
851 
852     (*env)->ReleaseStringChars(env, jlangtag, langtag);
853 }
854 
getNumberPattern(const jchar * langtag,const jint numberStyle)855 WCHAR * getNumberPattern(const jchar * langtag, const jint numberStyle) {
856     WCHAR ret[BUFLEN];
857     WCHAR number[BUFLEN];
858     WCHAR fix[BUFLEN];
859 
860     getFixPart(langtag, numberStyle, TRUE, TRUE, ret); // "+"
861     getNumberPart(langtag, numberStyle, number);
862     wcscat_s(ret, BUFLEN-wcslen(ret), number);      // "+12.34"
863     getFixPart(langtag, numberStyle, TRUE, FALSE, fix);
864     wcscat_s(ret, BUFLEN-wcslen(ret), fix);         // "+12.34$"
865     wcscat_s(ret, BUFLEN-wcslen(ret), L";");        // "+12.34$;"
866     getFixPart(langtag, numberStyle, FALSE, TRUE, fix);
867     wcscat_s(ret, BUFLEN-wcslen(ret), fix);         // "+12.34$;("
868     wcscat_s(ret, BUFLEN-wcslen(ret), number);      // "+12.34$;(12.34"
869     getFixPart(langtag, numberStyle, FALSE, FALSE, fix);
870     wcscat_s(ret, BUFLEN-wcslen(ret), fix);         // "+12.34$;(12.34$)"
871 
872     return _wcsdup(ret);
873 }
874 
getNumberPart(const jchar * langtag,const jint numberStyle,WCHAR * number)875 void getNumberPart(const jchar * langtag, const jint numberStyle, WCHAR * number) {
876     DWORD digits = 0;
877     DWORD leadingZero = 0;
878     WCHAR grouping[BUFLEN];
879     int groupingLen;
880     WCHAR fractionPattern[BUFLEN];
881     WCHAR * integerPattern = number;
882     WCHAR * pDest;
883 
884     // Get info from Windows
885     switch (numberStyle) {
886         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_CURRENCY:
887             getLocaleInfoWrapper(langtag,
888                 LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER,
889                 (LPWSTR)&digits, sizeof(digits));
890             break;
891 
892         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_INTEGER:
893             break;
894 
895         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_NUMBER:
896         case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_PERCENT:
897         default:
898             getLocaleInfoWrapper(langtag,
899                 LOCALE_IDIGITS | LOCALE_RETURN_NUMBER,
900                 (LPWSTR)&digits, sizeof(digits));
901             break;
902     }
903 
904     getLocaleInfoWrapper(langtag,
905         LOCALE_ILZERO | LOCALE_RETURN_NUMBER,
906         (LPWSTR)&leadingZero, sizeof(leadingZero));
907     groupingLen = getLocaleInfoWrapper(langtag, LOCALE_SGROUPING, grouping, BUFLEN);
908 
909     // fraction pattern
910     if (digits > 0) {
911         int i;
912         for(i = digits;  i > 0; i--) {
913             fractionPattern[i] = L'0';
914         }
915         fractionPattern[0] = L'.';
916         fractionPattern[digits+1] = L'\0';
917     } else {
918         fractionPattern[0] = L'\0';
919     }
920 
921     // integer pattern
922     pDest = integerPattern;
923     if (groupingLen > 0) {
924         int cur = groupingLen - 1;// subtracting null terminator
925         while (--cur >= 0) {
926             int repnum;
927 
928             if (grouping[cur] == L';') {
929                 continue;
930             }
931 
932             repnum = grouping[cur] - 0x30;
933             if (repnum > 0) {
934                 *pDest++ = L'#';
935                 *pDest++ = L',';
936                 while(--repnum > 0) {
937                     *pDest++ = L'#';
938                 }
939             }
940         }
941     }
942 
943     if (leadingZero != 0) {
944         *pDest++ = L'0';
945     } else {
946         *pDest++ = L'#';
947     }
948     *pDest = L'\0';
949 
950     wcscat_s(integerPattern, BUFLEN, fractionPattern);
951 }
952 
getFixPart(const jchar * langtag,const jint numberStyle,BOOL positive,BOOL prefix,WCHAR * ret)953 void getFixPart(const jchar * langtag, const jint numberStyle, BOOL positive, BOOL prefix, WCHAR * ret) {
954     DWORD pattern = 0;
955     int style = numberStyle;
956     int got = 0;
957 
958     if (positive) {
959         if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_CURRENCY) {
960             got = getLocaleInfoWrapper(langtag,
961                 LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER,
962                 (LPWSTR)&pattern, sizeof(pattern));
963         } else if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_PERCENT) {
964             got = getLocaleInfoWrapper(langtag,
965                 LOCALE_IPOSITIVEPERCENT | LOCALE_RETURN_NUMBER,
966                 (LPWSTR)&pattern, sizeof(pattern));
967         }
968     } else {
969         if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_CURRENCY) {
970             got = getLocaleInfoWrapper(langtag,
971                 LOCALE_INEGCURR | 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_INEGATIVEPERCENT | LOCALE_RETURN_NUMBER,
976                 (LPWSTR)&pattern, sizeof(pattern));
977         } else {
978             got = getLocaleInfoWrapper(langtag,
979                 LOCALE_INEGNUMBER | LOCALE_RETURN_NUMBER,
980                 (LPWSTR)&pattern, sizeof(pattern));
981         }
982     }
983 
984     if (numberStyle == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_INTEGER) {
985         style = sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_NUMBER;
986     }
987 
988     wcscpy(ret, fixes[!prefix][!positive][style][pattern]);
989 }
990 
enumCalendarInfoWrapper(const jchar * langtag,CALID calid,CALTYPE type,LPWSTR buf,int buflen)991 int enumCalendarInfoWrapper(const jchar *langtag, CALID calid, CALTYPE type, LPWSTR buf, int buflen) {
992     if (pEnumCalendarInfoExEx) {
993         if (wcscmp(L"und", (LPWSTR)langtag) == 0) {
994             // defaults to "en"
995             return pEnumCalendarInfoExEx(&EnumCalendarInfoProc, L"en",
996                 calid, NULL, type, (LPARAM)buf);
997         } else {
998             return pEnumCalendarInfoExEx(&EnumCalendarInfoProc, langtag,
999                 calid, NULL, type, (LPARAM)buf);
1000         }
1001     } else {
1002         return 0;
1003     }
1004 }
1005 
EnumCalendarInfoProc(LPWSTR lpCalInfoStr,CALID calid,LPWSTR lpReserved,LPARAM lParam)1006 BOOL CALLBACK EnumCalendarInfoProc(LPWSTR lpCalInfoStr, CALID calid, LPWSTR lpReserved, LPARAM lParam) {
1007     wcscat_s((LPWSTR)lParam, BUFLEN, lpCalInfoStr);
1008     wcscat_s((LPWSTR)lParam, BUFLEN, L",");
1009     return TRUE;
1010 }
1011 
getErasImpl(JNIEnv * env,jstring jlangtag,jint calid,jint style,jobjectArray eras)1012 jobjectArray getErasImpl(JNIEnv *env, jstring jlangtag, jint calid, jint style, jobjectArray eras) {
1013     const jchar * langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE);
1014     WCHAR buf[BUFLEN];
1015     jobjectArray ret = eras;
1016     CALTYPE type;
1017 
1018     CHECK_NULL_RETURN(langtag, ret);
1019 
1020     buf[0] = '\0';
1021     if (style & CALENDAR_STYLE_SHORT_MASK) {
1022         type = CAL_SABBREVERASTRING;
1023     } else {
1024         type = CAL_SERASTRING;
1025     }
1026 
1027     if (calid < 0) {
1028         calid = getCalendarID(langtag);
1029     }
1030 
1031     if (calid != -1 && enumCalendarInfoWrapper(langtag, calid, type, buf, BUFLEN)) {
1032         // format in buf: "era0,era1,era2," where era0 is the current one
1033         int eraCount;
1034         LPWSTR current;
1035         jsize array_length;
1036 
1037         for(eraCount = 0, current = buf; *current != '\0'; current++) {
1038             if (*current == L',') {
1039                 eraCount ++;
1040             }
1041         }
1042 
1043         if (eras != NULL) {
1044             array_length = (*env)->GetArrayLength(env, eras);
1045         } else {
1046             // +1 for the "before" era, e.g., BC, which Windows does not return.
1047             array_length = (jsize)eraCount + 1;
1048             ret = (*env)->NewObjectArray(env, array_length,
1049                 (*env)->FindClass(env, "java/lang/String"), NULL);
1050         }
1051 
1052         if (ret != NULL) {
1053             int eraIndex;
1054             LPWSTR era;
1055 
1056             for(eraIndex = 0, era = current = buf; eraIndex < eraCount; era = current, eraIndex++) {
1057                 while (*current != L',') {
1058                     current++;
1059                 }
1060                 *current++ = '\0';
1061 
1062                 if (eraCount - eraIndex < array_length &&
1063                     *era != '\0') {
1064                     (*env)->SetObjectArrayElement(env, ret,
1065                         (jsize)(eraCount - eraIndex),
1066                         (*env)->NewString(env, era, (jsize)wcslen(era)));
1067                 }
1068             }
1069 
1070             // Hack for the Japanese Imperial Calendar to insert Gregorian era for
1071             // "Before Meiji"
1072             if (calid == CAL_JAPAN) {
1073                 buf[0] = '\0';
1074                 if (enumCalendarInfoWrapper(langtag, CAL_GREGORIAN, type, buf, BUFLEN)) {
1075                     jsize len = (jsize)wcslen(buf);
1076                     buf[--len] = '\0'; // remove the last ','
1077                     (*env)->SetObjectArrayElement(env, ret, 0, (*env)->NewString(env, buf, len));
1078                 }
1079             }
1080         }
1081     }
1082 
1083     (*env)->ReleaseStringChars(env, jlangtag, langtag);
1084     return ret;
1085 }
1086