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
41typedef int (WINAPI *PGLIE)(const jchar *, LCTYPE, LPWSTR, int);
42typedef int (WINAPI *PGCIE)(const jchar *, CALID, LPCWSTR, CALTYPE, LPWSTR, int, LPDWORD);
43typedef int (WINAPI *PECIEE)(CALINFO_ENUMPROCEXEX, const jchar *, CALID, LPCWSTR, CALTYPE, LPARAM);
44PGLIE pGetLocaleInfoEx;
45PGCIE pGetCalendarInfoEx;
46PECIEE pEnumCalendarInfoExEx;
47BOOL initialized = FALSE;
48
49// prototypes
50int getLocaleInfoWrapper(const jchar *langtag, LCTYPE type, LPWSTR data, int buflen);
51int getCalendarInfoWrapper(const jchar *langtag, CALID id, LPCWSTR reserved, CALTYPE type, LPWSTR data, int buflen, LPDWORD val);
52jint getCalendarID(const jchar *langtag);
53void replaceCalendarArrayElems(JNIEnv *env, jstring jlangtag, jint calid, jobjectArray jarray,
54                       CALTYPE* pCalTypes, int offset, int length, int style);
55WCHAR * getNumberPattern(const jchar * langtag, const jint numberStyle);
56void getNumberPart(const jchar * langtag, const jint numberStyle, WCHAR * number);
57void getFixPart(const jchar * langtag, const jint numberStyle, BOOL positive, BOOL prefix, WCHAR * ret);
58int enumCalendarInfoWrapper(const jchar * langtag, CALID calid, CALTYPE type, LPWSTR buf, int buflen);
59BOOL CALLBACK EnumCalendarInfoProc(LPWSTR lpCalInfoStr, CALID calid, LPWSTR lpReserved, LPARAM lParam);
60jobjectArray getErasImpl(JNIEnv *env, jstring jlangtag, jint calid, jint style, jobjectArray eras);
61
62// from java_props_md.c
63extern __declspec(dllexport) const char * getJavaIDFromLangID(LANGID langID);
64
65CALTYPE 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
81CALTYPE 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
99CALTYPE wDaysType[] = {
100    CAL_SDAYNAME7,
101    CAL_SDAYNAME1,
102    CAL_SDAYNAME2,
103    CAL_SDAYNAME3,
104    CAL_SDAYNAME4,
105    CAL_SDAYNAME5,
106    CAL_SDAYNAME6,
107};
108
109CALTYPE sWDaysType[] = {
110    CAL_SABBREVDAYNAME7,
111    CAL_SABBREVDAYNAME1,
112    CAL_SABBREVDAYNAME2,
113    CAL_SABBREVDAYNAME3,
114    CAL_SABBREVDAYNAME4,
115    CAL_SABBREVDAYNAME5,
116    CAL_SABBREVDAYNAME6,
117};
118
119WCHAR * 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 */
181JNIEXPORT 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 */
206JNIEXPORT 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 */
238JNIEXPORT 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 */
268JNIEXPORT 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 */
284JNIEXPORT 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 */
323JNIEXPORT 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 */
333JNIEXPORT 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 */
345JNIEXPORT 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 */
357JNIEXPORT 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 */
369JNIEXPORT 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 */
381JNIEXPORT 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 */
404JNIEXPORT 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 */
423JNIEXPORT 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 */
444JNIEXPORT 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 */
465JNIEXPORT 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 */
486JNIEXPORT 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 */
507JNIEXPORT 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 */
528JNIEXPORT 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 */
549JNIEXPORT 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 */
570JNIEXPORT 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 */
591JNIEXPORT 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 */
612JNIEXPORT 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 */
635JNIEXPORT 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 */
658JNIEXPORT 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 */
688JNIEXPORT 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 */
723JNIEXPORT 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
769int 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
785int 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
801jint 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
832void 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
865WCHAR * 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
885void 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
963void 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
1001int 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
1016BOOL 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
1022jobjectArray 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}
1096