Fix casing for newly added test (#9889)
[platform/upstream/coreclr.git] / src / classlibnative / nls / calendardata.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 ////////////////////////////////////////////////////////////////////////////
5 //
6 //  Class:    CalendarData
7 //
8
9 //
10 //  Purpose:  This module implements the methods of the CalendarData
11 //            class.  These methods are the helper functions for the
12 //            Locale class.
13 //
14 //  Date:     July 4, 2007
15 //
16 ////////////////////////////////////////////////////////////////////////////
17
18 #include "common.h"
19 #include "object.h"
20 #include "excep.h"
21 #include "vars.hpp"
22 #include "interoputil.h"
23 #include "corhost.h"
24
25 #include <winnls.h>
26
27 #include "calendardata.h"
28 #include "nlsinfo.h"
29 #include "newapis.h"
30
31 ////////////////////////////////////////////////////////////////////////
32 //
33 // Call the Win32 GetCalendarInfoEx() using the specified calendar and LCTYPE.
34 // The return value can be INT32 or an allocated managed string object, depending on
35 // which version's called.
36 //
37 // Parameters:
38 //      OUT pOutputInt32    The output int32 value.
39 //      OUT pOutputRef      The output string value.
40 //
41 ////////////////////////////////////////////////////////////////////////
42 BOOL CalendarData::CallGetCalendarInfoEx(LPCWSTR localeName, int calendar, int calType, INT32* pOutputInt32)
43 {
44     CONTRACTL
45     {
46         GC_NOTRIGGER;
47         MODE_ANY;
48         SO_TOLERANT;
49     } CONTRACTL_END;
50
51     int result = 0;
52
53     BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result; )
54
55     // Just stick it right into the output int
56     _ASSERT((calType & CAL_RETURN_NUMBER) != 0);
57     result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, NULL, 0, (LPDWORD)pOutputInt32);
58
59     END_SO_INTOLERANT_CODE
60
61     return (result != 0);
62 }
63
64 BOOL CalendarData::CallGetCalendarInfoEx(LPCWSTR localeName, int calendar, int calType, STRINGREF* pOutputStrRef)
65 {
66     CONTRACTL
67     {
68         THROWS;                 // We can throw since we are allocating managed string.
69         DISABLED(GC_TRIGGERS); // Disabled 'cause it don't work right now
70         MODE_ANY;
71         SO_TOLERANT;
72     } CONTRACTL_END;
73
74     // The maximum size for values returned from GetLocaleInfo is 80 characters.
75     WCHAR buffer[80];
76     int result = 0;
77
78     BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result; )
79
80     _ASSERT((calType & CAL_RETURN_NUMBER) == 0);
81     result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL);
82
83     if (result != 0)
84     {
85         _ASSERTE(pOutputStrRef != NULL);
86         *pOutputStrRef = StringObject::NewString(buffer, result - 1);
87     }
88
89     END_SO_INTOLERANT_CODE
90
91     return (result != 0);
92 }
93
94 ////////////////////////////////////////////////////////////////////////
95 //
96 // Get the native day names
97 //
98 // NOTE: There's a disparity between .Net & windows day orders, the input day should
99 //           start with Sunday
100 //
101 // Parameters:
102 //      OUT pOutputStrings      The output string[] value.
103 //
104 ////////////////////////////////////////////////////////////////////////
105 BOOL CalendarData::GetCalendarDayInfo(LPCWSTR localeName, int calendar, int calType, PTRARRAYREF* pOutputStrings)
106 {
107     CONTRACTL
108     {
109         THROWS;                 // We can throw since we are allocating managed string.
110         INJECT_FAULT(COMPlusThrowOM());
111         GC_TRIGGERS;
112         MODE_COOPERATIVE;
113         SO_TOLERANT;
114     } CONTRACTL_END;
115
116     // The maximum size for values returned from GetLocaleInfo is 80 characters.
117     WCHAR buffer[80];
118     int result = 0;
119
120     _ASSERT((calType & CAL_RETURN_NUMBER) == 0);
121
122     BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result;)
123
124     //
125     // We'll need a new array of 7 items
126     //
127     // Allocate the array of STRINGREFs.  We don't need to check for null because the GC will throw
128     // an OutOfMemoryException if there's not enough memory.
129     //
130     PTRARRAYREF ResultArray = (PTRARRAYREF)AllocateObjectArray(7, g_pStringClass);
131     
132     GCPROTECT_BEGIN(ResultArray);
133
134     // Get each one of them
135     for (int i = 0; i < 7; i++, calType++)
136     {
137         result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL);
138
139         // Note that the returned string is null terminated, so even an empty string will be 1
140         if (result != 0)
141         {
142             // Make a string for this entry
143             STRINGREF stringResult = StringObject::NewString(buffer, result - 1);
144             ResultArray->SetAt(i, (OBJECTREF)stringResult);
145         }
146
147         // On the first iteration we need to go from CAL_SDAYNAME7 to CAL_SDAYNAME1, so subtract 7 before the ++ happens
148         // This is because the framework starts on sunday and windows starts on monday when counting days
149         if (i == 0) calType -= 7;
150     }
151     GCPROTECT_END();
152
153     _ASSERTE(pOutputStrings != NULL);
154     *pOutputStrings = ResultArray;
155
156     END_SO_INTOLERANT_CODE
157
158     return (result != 0);
159 }
160
161
162
163 ////////////////////////////////////////////////////////////////////////
164 //
165 // Get the native month names
166 //
167 // Parameters:
168 //      OUT pOutputStrings      The output string[] value.
169 //
170 ////////////////////////////////////////////////////////////////////////
171 BOOL CalendarData::GetCalendarMonthInfo(LPCWSTR localeName, int calendar, int calType, PTRARRAYREF* pOutputStrings)
172 {
173     CONTRACTL
174     {
175         THROWS;                 // We can throw since we are allocating managed string.
176         INJECT_FAULT(COMPlusThrowOM());
177         GC_TRIGGERS;
178         MODE_COOPERATIVE;
179         SO_TOLERANT;
180     } CONTRACTL_END;
181
182     // The maximum size for values returned from GetLocaleInfo is 80 characters.
183     WCHAR buffer[80];
184     int result = 0;
185
186     _ASSERT((calType & CAL_RETURN_NUMBER) == 0);
187
188     BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result;)
189
190     //
191     // We'll need a new array of 13 items
192     //
193     // Allocate the array of STRINGREFs.  We don't need to check for null because the GC will throw
194     // an OutOfMemoryException if there's not enough memory.
195     //
196     PTRARRAYREF ResultArray = (PTRARRAYREF)AllocateObjectArray(13, g_pStringClass);
197     
198     GCPROTECT_BEGIN(ResultArray);
199
200     // Get each one of them
201     for (int i = 0; i < 13; i++, calType++)
202     {
203         result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL);
204
205         // If we still have failure, then mark as empty string
206         if (result == 0)
207         {
208             buffer[0] = W('0');
209             result = 1;
210
211
212         }
213
214         // Note that the returned string is null terminated, so even an empty string will be 1
215         // Make a string for this entry
216         STRINGREF stringResult = StringObject::NewString(buffer, result - 1);
217         ResultArray->SetAt(i, (OBJECTREF)stringResult);
218     }
219     GCPROTECT_END();
220
221     _ASSERTE(pOutputStrings != NULL);
222     *pOutputStrings = ResultArray;
223
224     END_SO_INTOLERANT_CODE
225
226     return (result != 0);
227 }
228
229 //
230 // struct to help our calendar data enumaration callback
231 //
232 struct enumData
233 {
234     int     count;          // # of strings found so far
235     LPWSTR  userOverride;   // pointer to user override string if used
236     LPWSTR  stringsBuffer;  // pointer to a buffer to use for the strings.
237     LPWSTR  endOfBuffer;    // pointer to the end of the stringsBuffer ( must be < this to write)
238 };
239
240 //
241 // callback itself
242 //
243 BOOL CALLBACK EnumCalendarInfoCallback(__in_z LPWSTR lpCalendarInfoString, __in CALID Calendar, __in_opt LPWSTR pReserved, __in LPARAM lParam)
244 {
245     CONTRACTL
246     {
247         GC_NOTRIGGER;
248         MODE_COOPERATIVE;
249         SO_TOLERANT;
250         PRECONDITION(CheckPointer(lpCalendarInfoString));
251         PRECONDITION(CheckPointer((LPVOID)lParam));
252     } CONTRACTL_END;
253
254     // Cast our data to the right type
255     enumData* pData = (enumData*)lParam;
256
257     // If we had a user override, check to make sure this differs
258     if (pData->userOverride == NULL ||
259         wcscmp(pData->userOverride, lpCalendarInfoString) != 0)
260     {
261         // They're different, add it to our buffer
262         LPWSTR pStart = pData->stringsBuffer;
263         LPCWSTR pEnd = pData->endOfBuffer;
264         while (pStart < pEnd && *lpCalendarInfoString != 0)
265         {
266             *(pStart++) = *(lpCalendarInfoString++);
267         }
268
269         // Add a \0
270         if (pStart < pEnd)
271         {
272             *(pStart++) = 0;
273
274             // Did it finish?
275             if (pStart <= pEnd)
276             {
277                 // It finished, use it
278                 pData->count++;
279                 pData->stringsBuffer = pStart;
280             }
281         }
282     }
283
284     return TRUE;
285 }
286
287 //
288 // CallEnumCalendarInfo
289 //
290 // Get the list of whichever calendar property from the OS.  If user override is passed in, then check GetLocaleInfo as well
291 // to see if a user override is set.
292 //
293 // We build a list of strings, first calling getlocaleinfo if necessary, and then the enums.  The strings are null terminated,
294 // with a double null ending the list.  Once we have the list we can allocate our COMStrings and arrays from the count.
295 //
296 // We need a helper structure to pass as an lParam
297 //
298 BOOL CalendarData::CallEnumCalendarInfo(__in_z LPCWSTR localeName, __in int calendar, __in int calType,
299                                         __in int lcType, __inout PTRARRAYREF* pOutputStrings)
300 {
301     CONTRACTL
302     {
303         THROWS;                 // We can throw since we are allocating managed string.
304         INJECT_FAULT(COMPlusThrowOM());
305         DISABLED(GC_TRIGGERS); // Disabled 'cause it don't work right now
306         MODE_COOPERATIVE;
307         SO_TOLERANT;
308     } CONTRACTL_END;
309
310     BOOL result = TRUE;
311
312     // Our longest string in culture.xml is shorter than this and it has lots of \x type characters, so this should be long enough by far.
313     WCHAR   stringBuffer[512];
314
315     struct enumData data;
316     data.count = 0;
317     data.userOverride = NULL;
318     data.stringsBuffer = stringBuffer;
319     data.endOfBuffer = stringBuffer + 512;   // We're adding WCHAR sizes
320
321     // First call GetLocaleInfo if necessary
322     if ((lcType && ((lcType & LOCALE_NOUSEROVERRIDE) == 0)) &&
323         // Get user locale, see if it matches localeName.
324         // Note that they should match exactly, including letter case
325         NewApis::GetUserDefaultLocaleName(stringBuffer, 512) && wcscmp(localeName, stringBuffer) == 0)
326     {
327         // They want user overrides, see if the user calendar matches the input calendar
328         CALID userCalendar = 0;
329         NewApis::GetLocaleInfoEx(localeName, LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER, (LPWSTR)&userCalendar,
330                                             sizeof(userCalendar) / sizeof(WCHAR) );
331
332         // If the calendars were the same, see if the locales were the same
333         if ((int)userCalendar == calendar) // todo: cast to compile on MAC
334         {
335             // They matched, get the user override since locale & calendar match
336             int i = NewApis::GetLocaleInfoEx(localeName, lcType, stringBuffer, 512);
337
338             // if it succeeded, advance the pointer and remember the override for the later callers
339             if (i > 0)
340             {
341                 // Remember this was the override (so we can look for duplicates later in the enum function)
342                 data.userOverride = data.stringsBuffer;
343
344                 // Advance to the next free spot (i includes counting the \0)
345                 data.stringsBuffer += i;
346
347                 // And our count...
348                 data.count++;
349             }
350         }
351     }
352
353     // Now call the enumeration API. Work is done by our callback function
354     NewApis::EnumCalendarInfoExEx(EnumCalendarInfoCallback, localeName, calendar, calType, (LPARAM)&data);
355
356     // Now we have a list of data, fail if we didn't find anything.
357     if (data.count == 0) return FALSE;
358
359     // Now we need to allocate our stringarray and populate it
360     STATIC_CONTRACT_SO_TOLERANT;
361
362     BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return (result); )
363
364     // Get our array object (will throw, don't have to check it)
365     PTRARRAYREF dataArray = (PTRARRAYREF) AllocateObjectArray(data.count, g_pStringClass);
366
367     GCPROTECT_BEGIN(dataArray);
368     LPCWSTR buffer = stringBuffer;   // Restart @ buffer beginning
369     for(DWORD i = 0; i < (DWORD)data.count; i++)
370     {
371         OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer);
372
373         if (calType == CAL_SABBREVERASTRING || calType == CAL_SERASTRING)
374         {
375             // Eras are enumerated backwards.  (oldest era name first, but
376             // Japanese calendar has newest era first in array, and is only
377             // calendar with multiple eras)
378             dataArray->SetAt((DWORD)data.count - i - 1, o);
379         }
380         else
381         {
382             dataArray->SetAt(i, o);
383         }
384
385         buffer += (lstrlenW(buffer) + 1);
386     }
387     GCPROTECT_END();
388
389     _ASSERTE(pOutputStrings != NULL);
390     *pOutputStrings = dataArray;
391
392     END_SO_INTOLERANT_CODE
393
394     return result;
395 }
396
397 ////////////////////////////////////////////////////////////////////////
398 //
399 // For calendars like Gregorain US/Taiwan/UmAlQura, they are not available
400 // in all OS or all localized versions of OS.
401 // If OS does not support these calendars, we will fallback by using the
402 // appropriate fallback calendar and locale combination to retrieve data from OS.
403 //
404 // Parameters:
405 //  __deref_inout pCalendarInt:
406 //    Pointer to the calendar ID. This will be updated to new fallback calendar ID if needed.
407 //  __in_out pLocaleNameStackBuffer
408 //    Pointer to the StackSString object which holds the locale name to be checked.
409 //    This will be updated to new fallback locale name if needed.
410 //
411 ////////////////////////////////////////////////////////////////////////
412
413 void CalendarData::CheckSpecialCalendar(INT32* pCalendarInt, StackSString* pLocaleNameStackBuffer)
414 {
415     // Gregorian-US isn't always available in the OS, however it is the same for all locales
416     switch (*pCalendarInt)
417     {
418         case CAL_GREGORIAN_US:
419             // See if this works
420             if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
421             {
422                 // Failed, set it to a locale (fa-IR) that's alway has Gregorian US available in the OS
423                 pLocaleNameStackBuffer->Set(W("fa-IR"), 5);
424             }
425             // See if that works
426             if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
427             {
428                 // Failed again, just use en-US with the gregorian calendar
429                 pLocaleNameStackBuffer->Set(W("en-US"), 5);
430                 *pCalendarInt = CAL_GREGORIAN;
431             }
432             break;
433         case CAL_TAIWAN:
434             // Taiwan calendar data is not always in all language version of OS due to Geopolical reasons.
435             // It is only available in zh-TW localized versions of Windows.
436             // Let's check if OS supports it.  If not, fallback to Greogrian localized for Taiwan calendar.
437             if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
438             {
439                 *pCalendarInt = CAL_GREGORIAN;
440             }
441             break;
442         case CAL_UMALQURA:
443             // UmAlQura is only available in Vista and above, so we will need to fallback to Hijri if it is not available in the OS.
444             if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
445             {
446                 // There are no differences in DATA between UmAlQura and Hijri, and
447                 // UmAlQura isn't available before Vista, so just use Hijri..
448                 *pCalendarInt = CAL_HIJRI;
449             }
450             break;
451     }
452 }
453
454 ////////////////////////////////////////////////////////////////////////
455 //
456 //  Implementation for CalendarInfo.nativeGetCalendarData
457 //
458 //  Retrieve calendar properties from the native side
459 //
460 //  Parameters:
461 //      pCalendarData: This is passed from a managed structure CalendarData.cs
462 //      pLocaleNameUNSAFE: Locale name associated with the locale for this calendar
463 //      calendar: Calendar ID
464 //
465 //  NOTE: Calendars depend on the locale name that creates it.  Only a few
466 //            properties are available without locales using CalendarData.GetCalendar(int)
467 //
468 ////////////////////////////////////////////////////////////////////////
469
470 FCIMPL3(FC_BOOL_RET, CalendarData::nativeGetCalendarData, CalendarData* calendarDataUNSAFE, StringObject* pLocaleNameUNSAFE, INT32 calendar)
471 {
472     CONTRACTL
473     {
474         FCALL_CHECK;
475         PRECONDITION(CheckPointer(pLocaleNameUNSAFE));
476     } CONTRACTL_END;
477
478
479     // The maximum allowed string length in GetLocaleInfo is 80 WCHARs.
480     BOOL ret = TRUE;
481
482     struct _gc
483     {
484         STRINGREF       localeName;
485         CALENDARDATAREF calendarData;
486     } gc;
487
488     // Dereference our gc objects
489     gc.localeName = (STRINGREF)pLocaleNameUNSAFE;
490     gc.calendarData = (CALENDARDATAREF)calendarDataUNSAFE;
491
492     // Need to set up the frame since we will be allocating managed strings.
493     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
494
495     // create a local copy of the string in order to pass it to helper methods that trigger GCs like GetCalendarDayInfo and GetCalendarMonthInfo
496     StackSString localeNameStackBuffer( gc.localeName->GetBuffer() );
497
498     // Conveniently this is the same as LOCALE_NOUSEROVERRIDE, so we can use this for both
499     int useOverrides = (gc.calendarData->bUseUserOverrides) ? 0 : CAL_NOUSEROVERRIDE;
500
501     // Helper string
502     STRINGREF stringResult = NULL;
503
504     //
505     // Windows doesn't support some calendars right now, so remap those.
506     //
507     if (calendar >= 13 && calendar < 23)
508     {
509         switch (calendar)
510         {
511             case RESERVED_CAL_PERSIAN: // don't change if we have Persian calendar
512                 break;
513
514             case RESERVED_CAL_JAPANESELUNISOLAR:    // Data looks like Japanese
515                 calendar=CAL_JAPAN;
516                 break;
517             case RESERVED_CAL_JULIAN:               // Data looks like gregorian US
518             case RESERVED_CAL_CHINESELUNISOLAR:     // Algorithmic, so actual data is irrelevent
519             case RESERVED_CAL_SAKA:                 // reserved to match Office but not implemented in our code, so data is irrelevent
520             case RESERVED_CAL_LUNAR_ETO_CHN:        // reserved to match Office but not implemented in our code, so data is irrelevent
521             case RESERVED_CAL_LUNAR_ETO_KOR:        // reserved to match Office but not implemented in our code, so data is irrelevent
522             case RESERVED_CAL_LUNAR_ETO_ROKUYOU:    // reserved to match Office but not implemented in our code, so data is irrelevent
523             case RESERVED_CAL_KOREANLUNISOLAR:      // Algorithmic, so actual data is irrelevent
524             case RESERVED_CAL_TAIWANLUNISOLAR:      // Algorithmic, so actual data is irrelevent
525             default:
526                 calendar = CAL_GREGORIAN_US;
527                 break;
528         }
529     }
530
531     //
532     // Speical handling for some special calendar due to OS limitation.
533     // This includes calendar like Taiwan calendar, UmAlQura calendar, etc.
534     //
535     CheckSpecialCalendar(&calendar, &localeNameStackBuffer);
536
537
538     // Numbers
539     ret &= CallGetCalendarInfoEx(localeNameStackBuffer, calendar,
540                                  CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER | useOverrides,
541                                  &(gc.calendarData->iTwoDigitYearMax));
542
543     if (ret == FALSE) // failed call
544     {
545         _ASSERTE(!"nativeGetCalendarData could not read CAL_ITWODIGITYEARMAX");
546     }
547
548     _ASSERTE(ret == TRUE);
549
550     // Strings
551     if (CallGetCalendarInfoEx(localeNameStackBuffer, calendar, CAL_SCALNAME , &stringResult))
552         SetObjectReference((OBJECTREF*)&(gc.calendarData->sNativeName), stringResult, NULL);
553     else
554     {
555         _ASSERTE(!"nativeGetCalendarData could not read CAL_SCALNAME");
556         ret = FALSE;
557     }
558     if (CallGetCalendarInfoEx(localeNameStackBuffer, calendar, CAL_SMONTHDAY | useOverrides, &stringResult))
559         SetObjectReference((OBJECTREF*)&(gc.calendarData->sMonthDay), stringResult, NULL);
560     else
561     {
562         _ASSERTE(!"nativeGetCalendarData could not read RESERVED_CAL_SMONTHDAY");
563         ret = FALSE;
564     }
565
566     // String Arrays
567     // Formats
568     PTRARRAYREF array = NULL;
569     if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SSHORTDATE, LOCALE_SSHORTDATE | useOverrides, &array))
570         SetObjectReference((OBJECTREF*)&(gc.calendarData->saShortDates), array, NULL);
571     else
572     {
573         _ASSERTE(!"nativeGetCalendarData could not read CAL_SSHORTDATE");
574         ret = FALSE;
575     }
576     if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SLONGDATE, LOCALE_SLONGDATE | useOverrides, &array))
577         SetObjectReference((OBJECTREF*)&(gc.calendarData->saLongDates), array, NULL);
578     else
579     {
580         _ASSERTE(!"nativeGetCalendarData could not read CAL_SLONGDATE");
581         ret = FALSE;
582     }
583
584     // Get the YearMonth pattern.
585     // Before Windows Vista, NLS would not write the Year/Month pattern into the reg key.
586     // This causes GetLocaleInfo() to retrieve the Gregorian localized calendar pattern even when the calendar is not Gregorian localized.
587     // So we will call GetLocaleInfo() only when the reg key for sYearMonth is there.
588     //
589     // If the key does not exist, leave yearMonthPattern to be null, so that we will pick up the default table value.
590
591     int useOverridesForYearMonthPattern = useOverrides;
592     if (useOverridesForYearMonthPattern == 0)
593     {
594         HKEY hkey = NULL;
595         useOverridesForYearMonthPattern = CAL_NOUSEROVERRIDE;
596         if (WszRegOpenKeyEx(HKEY_CURRENT_USER, W("Control Panel\\International"), 0, KEY_READ, &hkey) == ERROR_SUCCESS)
597         {
598             if (WszRegQueryValueEx(hkey, W("sYearMonth"), 0, NULL, NULL, NULL) == ERROR_SUCCESS)
599             {
600                 // The sYearMonth key exists.  Call GetLocaleInfo() to read it.
601                 useOverridesForYearMonthPattern = 0; // now we can use the overrides
602             }
603             RegCloseKey(hkey);
604         }
605     }
606
607     if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SYEARMONTH, LOCALE_SYEARMONTH | useOverridesForYearMonthPattern, &array))
608         SetObjectReference((OBJECTREF*)&(gc.calendarData->saYearMonths), array, NULL);
609     else
610     {
611         _ASSERTE(!"nativeGetCalendarData could not read CAL_SYEARMONTH");
612         ret = FALSE;
613     }
614
615     // Day & Month Names
616     // These are all single calType entries, 1 per day, so we have to make 7 or 13 calls to collect all the names
617
618     // Day
619     // Note that we're off-by-one since managed starts on sunday and windows starts on monday
620     if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SDAYNAME7, &array))
621         SetObjectReference((OBJECTREF*)&(gc.calendarData->saDayNames), array, NULL);
622     else
623     {
624         _ASSERTE(!"nativeGetCalendarData could not read CAL_SDAYNAME7");
625         ret = FALSE;
626     }
627     if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SABBREVDAYNAME7, &array))
628         SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevDayNames), array, NULL);
629     else
630     {
631         _ASSERTE(!"nativeGetCalendarData could not read CAL_SABBREVDAYNAME7");
632         ret = FALSE;
633     }
634
635     // Month names
636     if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SMONTHNAME1, &array))
637         SetObjectReference((OBJECTREF*)&(gc.calendarData->saMonthNames), array, NULL);
638     else
639     {
640         _ASSERTE(!"nativeGetCalendarData could not read CAL_SMONTHNAME1");
641         ret = FALSE;
642     }
643
644     if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SABBREVMONTHNAME1, &array))
645         SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevMonthNames), array, NULL);
646     else
647     {
648         _ASSERTE(!"nativeGetCalendarData could not read CAL_SABBREVMONTHNAME1");
649         ret = FALSE;
650     }
651
652     //
653     // The following LCTYPE are not supported in some platforms.  If the call fails,
654     // don't return a failure.
655     //
656     if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SSHORTESTDAYNAME7, &array))
657         SetObjectReference((OBJECTREF*)&(gc.calendarData->saSuperShortDayNames), array, NULL);
658
659
660     // Gregorian may have genitive month names
661     if (calendar == CAL_GREGORIAN)
662     {
663         if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, &array))
664             SetObjectReference((OBJECTREF*)&(gc.calendarData->saMonthGenitiveNames), array, NULL);
665         // else we ignore the error and let managed side copy the normal month names
666         if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SABBREVMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, &array))
667             SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevMonthGenitiveNames), array, NULL);
668         // else we ignore the error and let managed side copy the normal month names
669     }
670
671 //  leap year names are only different for month 6 in Hebrew calendar
672 //    PTRARRAYREF saLeapYearMonthNames      ; // Multiple strings for the month names in a leap year. (Hebrew's the only one that has these)
673
674     // Calendar Parts Names
675     if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SERASTRING, NULL, &array))
676         SetObjectReference((OBJECTREF*)&(gc.calendarData->saEraNames), array, NULL);
677     // else we set the era in managed code
678     if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SABBREVERASTRING, NULL, &array))
679         SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevEraNames), array, NULL);
680     // else we set the era in managed code
681
682     //    PTRARRAYREF saAbbrevEnglishEraNames   ; // Abbreviated Era Names in English
683
684     //
685     // Calendar Era Info
686     // Note that calendar era data (offsets, etc) is hard coded for each calendar since this
687     // data is implementation specific and not dynamic (except perhaps Japanese)
688     //
689
690     HELPER_METHOD_FRAME_END();
691
692     FC_RETURN_BOOL(ret);
693 }
694 FCIMPLEND
695
696 //
697 // Get the system two digit year max value for the specified calendar
698 //
699 FCIMPL1(INT32, CalendarData::nativeGetTwoDigitYearMax, INT32 calendar)
700 {
701     FCALL_CONTRACT;
702
703     DWORD dwTwoDigitYearMax = (DWORD) -1;
704
705     HELPER_METHOD_FRAME_BEGIN_RET_0();
706
707     WCHAR strName[LOCALE_NAME_MAX_LENGTH];
708
709     // Really we should just be able to pass NULL for the locale name since that
710     // causes the OS to look up the user default locale name.  The downlevel APIS could
711     // emulate this as necessary
712     if (NewApis::GetUserDefaultLocaleName(strName,NumItems(strName)) == 0 ||
713         NewApis::GetCalendarInfoEx(strName, calendar, NULL, CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER, NULL, 0,&dwTwoDigitYearMax) == 0)
714     {
715         dwTwoDigitYearMax = (DWORD) -1;
716         goto lExit;
717     }
718
719 lExit: ;
720     HELPER_METHOD_FRAME_END();
721
722     return (dwTwoDigitYearMax);
723 }
724 FCIMPLEND
725
726 //
727 // nativeGetCalendars
728 //
729 // Get the list of acceptable calendars for this user/locale
730 //
731 // Might be a better way to marshal the int[] for calendars
732 // We expect the input array to be 23 ints long.  We then fill up the first "count" ints and return the count.
733 // The caller should then make it a smaller array.
734 //
735
736 // Perhaps we could do something more like this...
737 //U1ARRAYREF rgbOut = (U1ARRAYREF) AllocatePrimitiveArray(ELEMENT_TYPE_U1, cb);
738 //memcpyNoGCRefs(rgbOut->GetDirectPointerToNonObjectElements(), rgbKey, cb * sizeof(BYTE));
739 //
740 //refRetVal = rgbOut;
741 //
742 //HELPER_METHOD_FRAME_END();
743 //return (U1Array*) OBJECTREFToObject(refRetVal);
744
745 //
746 // struct to help our calendar data enumaration callback
747 //
748 struct enumCalendarsData
749 {
750     int     count;          // # of strings found so far
751     CALID   userOverride;   // user override value (if found)
752     INT32*  calendarList;   // list of calendars found so far
753 };
754
755 //
756 // callback itself
757 //
758 BOOL CALLBACK EnumCalendarsCallback(__in_z LPWSTR lpCalendarInfoString, __in CALID Calendar, __in_opt LPWSTR pReserved, __in LPARAM lParam)
759 {
760     CONTRACTL
761     {
762         GC_NOTRIGGER;
763         MODE_COOPERATIVE;
764         SO_TOLERANT;
765         PRECONDITION(CheckPointer(lpCalendarInfoString));
766         PRECONDITION(CheckPointer((LPVOID)lParam));
767     } CONTRACTL_END;
768
769     // Cast our data to the right type
770     enumCalendarsData* pData = (enumCalendarsData*)lParam;
771
772     // If we had a user override, check to make sure this differs
773     if (pData->userOverride == Calendar)
774     {
775         // Its the same, just return
776         return TRUE;
777     }
778
779     // They're different, add it to our buffer, check we have room
780     if (pData->count < 23)
781     {
782         pData->calendarList[pData->count++] = Calendar;
783     }
784
785     return TRUE;
786 }
787
788 FCIMPL3(INT32, CalendarData::nativeGetCalendars, StringObject* pLocaleNameUNSAFE, CLR_BOOL useOverrides, I4Array* calendarsUNSAFE)
789 {
790     CONTRACTL
791     {
792         FCALL_CHECK;
793         PRECONDITION(CheckPointer(pLocaleNameUNSAFE));
794     } CONTRACTL_END;
795
796     int ret = 0;
797
798     struct _gc
799     {
800         STRINGREF                localeName;
801         I4ARRAYREF               calendarsRef;
802     } gc;
803
804     // Dereference our string
805     gc.localeName   = (STRINGREF)pLocaleNameUNSAFE;
806     gc.calendarsRef = (I4ARRAYREF)calendarsUNSAFE;
807     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
808
809     int    calendarBuffer[23];
810     struct enumCalendarsData data;
811     data.count = 0;
812     data.userOverride = 0;
813     data.calendarList = calendarBuffer;
814
815     // First call GetLocaleInfo if necessary
816     if (useOverrides)
817     {
818         // They want user overrides, see if the user calendar matches the input calendar
819
820         CALID userCalendar = 0;
821         NewApis::GetLocaleInfoEx( gc.localeName->GetBuffer(), LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
822                                             (LPWSTR)&userCalendar, sizeof(userCalendar) / sizeof(WCHAR) );
823
824         // If we got a default, then use it as the first calendar
825         if (userCalendar != 0)
826         {
827             data.userOverride = userCalendar;
828             data.calendarList[data.count++] = userCalendar;
829         }
830     }
831
832     // Now call the enumeration API. Work is done by our callback function
833     NewApis::EnumCalendarInfoExEx(EnumCalendarsCallback, gc.localeName->GetBuffer(), ENUM_ALL_CALENDARS, CAL_ICALINTVALUE, (LPARAM)&(data));
834
835     // Copy to the output array
836     for (int i = 0; i < data.count; i++)
837     {
838         (gc.calendarsRef->GetDirectPointerToNonObjectElements())[i] = calendarBuffer[i];
839     }
840
841     ret = data.count;
842     HELPER_METHOD_FRAME_END();
843
844     // Now we have a list of data, return the count
845     return ret;
846 }
847 FCIMPLEND
848
849 //
850 // nativeEnumTimeFormats
851 //
852 // Enumerate all of the time formats (long times) on the system.
853 // Windows only has 1 time format so there's nothing like an LCTYPE here.
854 //
855 // Note that if the locale is the user default locale windows ALWAYS returns the user override value first.
856 // (ie: there's no no-user-override option for this API)
857 //
858 // We reuse the enumData structure since it works for us.
859 //
860
861 //
862 // callback itself
863 //
864 BOOL CALLBACK EnumTimeFormatsCallback(__in_z LPCWSTR lpTimeFormatString, __in LPARAM lParam)
865 {
866     CONTRACTL
867     {
868         GC_NOTRIGGER;
869         MODE_COOPERATIVE;
870         SO_TOLERANT;
871         PRECONDITION(CheckPointer(lpTimeFormatString));
872         PRECONDITION(CheckPointer((LPVOID)lParam));
873     } CONTRACTL_END;
874
875     // Cast our data to the right type
876     enumData* pData = (enumData*)lParam;
877
878     // Don't have to worry about user overrides (the enum adds them)
879     // add it to our buffer
880     LPWSTR pStart = pData->stringsBuffer;
881     LPCWSTR pEnd = pData->endOfBuffer;
882     while (pStart < pEnd && *lpTimeFormatString != 0)
883     {
884         *(pStart++) = *(lpTimeFormatString++);
885     }
886
887     // Add a \0
888     if (pStart < pEnd)
889     {
890         *(pStart++) = 0;
891
892         // Did it finish?
893         if (pStart <= pEnd)
894         {
895             // It finished, use it
896             pData->count++;
897             pData->stringsBuffer = pStart;
898         }
899     }
900
901     return TRUE;
902 }
903
904 //
905 // nativeEnumTimeFormats that calls the callback above
906 //
907 FCIMPL3(Object*, CalendarData::nativeEnumTimeFormats,
908         StringObject* pLocaleNameUNSAFE, INT32 dwFlags, CLR_BOOL useUserOverride)
909 {
910     CONTRACTL
911     {
912         FCALL_CHECK;
913         PRECONDITION(CheckPointer(pLocaleNameUNSAFE));
914     } CONTRACTL_END;
915
916
917     struct _gc
918     {
919         STRINGREF       localeName;
920         PTRARRAYREF     timeFormatsArray;
921     } gc;
922
923     // Dereference our gc objects
924     gc.localeName = (STRINGREF)pLocaleNameUNSAFE;
925     gc.timeFormatsArray = NULL;
926
927     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
928
929     // Our longest string in culture.xml is shorter than this and it has lots of \x type characters, so this should be long enough by far.
930     WCHAR   stringBuffer[512];
931     struct enumData data;
932     data.count = 0;
933     data.userOverride = NULL;
934     data.stringsBuffer = stringBuffer;
935     data.endOfBuffer = stringBuffer + 512; // We're adding WCHAR sizes
936
937     // Now call the enumeration API. Work is done by our callback function
938     NewApis::EnumTimeFormatsEx((TIMEFMT_ENUMPROCEX)EnumTimeFormatsCallback, gc.localeName->GetBuffer(), dwFlags, (LPARAM)&data);
939
940     if (data.count > 0)
941     {
942         // Now we need to allocate our stringarray and populate it
943         // Get our array object (will throw, don't have to check it)
944         gc.timeFormatsArray = (PTRARRAYREF) AllocateObjectArray(data.count, g_pStringClass);
945
946         LPCWSTR buffer = stringBuffer;   // Restart @ buffer beginning
947         for(DWORD i = 0; i < (DWORD)data.count; i++) // todo: cast to compile on Mac
948         {
949             OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer);
950             gc.timeFormatsArray->SetAt(i, o);
951
952             buffer += (lstrlenW(buffer) + 1);
953         }
954
955         if(!useUserOverride && data.count > 1)
956         {
957             // Since there is no "NoUserOverride" aware EnumTimeFormatsEx, we always get an override
958             // The override is the first entry if it is overriden.
959             // We can check if we have overrides by checking the GetLocaleInfo with no override
960             // If we do have an override, we don't know if it is a user defined override or if the
961             // user has just selected one of the predefined formats so we can't just remove it
962             // but we can move it down.
963             WCHAR timeFormatNoUserOverride[LOCALE_NAME_MAX_LENGTH];
964             DWORD lcType = (dwFlags == TIME_NOSECONDS) ? LOCALE_SSHORTTIME : LOCALE_STIMEFORMAT;
965             lcType |= LOCALE_NOUSEROVERRIDE;
966             int result = NewApis::GetLocaleInfoEx(gc.localeName->GetBuffer(), lcType, timeFormatNoUserOverride, LOCALE_NAME_MAX_LENGTH);
967             if(result != 0)
968             {
969                 STRINGREF firstTimeFormat = (STRINGREF)gc.timeFormatsArray->GetAt(0);
970                 if(wcscmp(timeFormatNoUserOverride, firstTimeFormat->GetBuffer())!=0)
971                 {
972                     gc.timeFormatsArray->SetAt(0, gc.timeFormatsArray->GetAt(1));
973                     gc.timeFormatsArray->SetAt(1, firstTimeFormat);
974                 }
975             }
976         }
977
978     }
979
980     HELPER_METHOD_FRAME_END();
981     return OBJECTREFToObject(gc.timeFormatsArray);
982 }
983 FCIMPLEND
984
985