summaryrefslogtreecommitdiff
path: root/src/classlibnative/nls/calendardata.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/classlibnative/nls/calendardata.cpp')
-rw-r--r--src/classlibnative/nls/calendardata.cpp985
1 files changed, 985 insertions, 0 deletions
diff --git a/src/classlibnative/nls/calendardata.cpp b/src/classlibnative/nls/calendardata.cpp
new file mode 100644
index 0000000000..95d071cc93
--- /dev/null
+++ b/src/classlibnative/nls/calendardata.cpp
@@ -0,0 +1,985 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+////////////////////////////////////////////////////////////////////////////
+//
+// Class: CalendarData
+//
+
+//
+// Purpose: This module implements the methods of the CalendarData
+// class. These methods are the helper functions for the
+// Locale class.
+//
+// Date: July 4, 2007
+//
+////////////////////////////////////////////////////////////////////////////
+
+#include "common.h"
+#include "object.h"
+#include "excep.h"
+#include "vars.hpp"
+#include "interoputil.h"
+#include "corhost.h"
+
+#include <winnls.h>
+
+#include "calendardata.h"
+#include "nlsinfo.h"
+#include "newapis.h"
+
+////////////////////////////////////////////////////////////////////////
+//
+// Call the Win32 GetCalendarInfoEx() using the specified calendar and LCTYPE.
+// The return value can be INT32 or an allocated managed string object, depending on
+// which version's called.
+//
+// Parameters:
+// OUT pOutputInt32 The output int32 value.
+// OUT pOutputRef The output string value.
+//
+////////////////////////////////////////////////////////////////////////
+BOOL CalendarData::CallGetCalendarInfoEx(LPCWSTR localeName, int calendar, int calType, INT32* pOutputInt32)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ int result = 0;
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result; )
+
+ // Just stick it right into the output int
+ _ASSERT((calType & CAL_RETURN_NUMBER) != 0);
+ result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, NULL, 0, (LPDWORD)pOutputInt32);
+
+ END_SO_INTOLERANT_CODE
+
+ return (result != 0);
+}
+
+BOOL CalendarData::CallGetCalendarInfoEx(LPCWSTR localeName, int calendar, int calType, STRINGREF* pOutputStrRef)
+{
+ CONTRACTL
+ {
+ THROWS; // We can throw since we are allocating managed string.
+ DISABLED(GC_TRIGGERS); // Disabled 'cause it don't work right now
+ MODE_ANY;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ // The maximum size for values returned from GetLocaleInfo is 80 characters.
+ WCHAR buffer[80];
+ int result = 0;
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result; )
+
+ _ASSERT((calType & CAL_RETURN_NUMBER) == 0);
+ result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL);
+
+ if (result != 0)
+ {
+ _ASSERTE(pOutputStrRef != NULL);
+ *pOutputStrRef = StringObject::NewString(buffer, result - 1);
+ }
+
+ END_SO_INTOLERANT_CODE
+
+ return (result != 0);
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// Get the native day names
+//
+// NOTE: There's a disparity between .Net & windows day orders, the input day should
+// start with Sunday
+//
+// Parameters:
+// OUT pOutputStrings The output string[] value.
+//
+////////////////////////////////////////////////////////////////////////
+BOOL CalendarData::GetCalendarDayInfo(LPCWSTR localeName, int calendar, int calType, PTRARRAYREF* pOutputStrings)
+{
+ CONTRACTL
+ {
+ THROWS; // We can throw since we are allocating managed string.
+ INJECT_FAULT(COMPlusThrowOM());
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ // The maximum size for values returned from GetLocaleInfo is 80 characters.
+ WCHAR buffer[80];
+ int result = 0;
+
+ _ASSERT((calType & CAL_RETURN_NUMBER) == 0);
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result;)
+
+ //
+ // We'll need a new array of 7 items
+ //
+ // Allocate the array of STRINGREFs. We don't need to check for null because the GC will throw
+ // an OutOfMemoryException if there's not enough memory.
+ //
+ PTRARRAYREF ResultArray = (PTRARRAYREF)AllocateObjectArray(7, g_pStringClass);
+
+ GCPROTECT_BEGIN(ResultArray);
+
+ // Get each one of them
+ for (int i = 0; i < 7; i++, calType++)
+ {
+ result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL);
+
+ // Note that the returned string is null terminated, so even an empty string will be 1
+ if (result != 0)
+ {
+ // Make a string for this entry
+ STRINGREF stringResult = StringObject::NewString(buffer, result - 1);
+ ResultArray->SetAt(i, (OBJECTREF)stringResult);
+ }
+
+ // On the first iteration we need to go from CAL_SDAYNAME7 to CAL_SDAYNAME1, so subtract 7 before the ++ happens
+ // This is because the framework starts on sunday and windows starts on monday when counting days
+ if (i == 0) calType -= 7;
+ }
+ GCPROTECT_END();
+
+ _ASSERTE(pOutputStrings != NULL);
+ *pOutputStrings = ResultArray;
+
+ END_SO_INTOLERANT_CODE
+
+ return (result != 0);
+}
+
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Get the native month names
+//
+// Parameters:
+// OUT pOutputStrings The output string[] value.
+//
+////////////////////////////////////////////////////////////////////////
+BOOL CalendarData::GetCalendarMonthInfo(LPCWSTR localeName, int calendar, int calType, PTRARRAYREF* pOutputStrings)
+{
+ CONTRACTL
+ {
+ THROWS; // We can throw since we are allocating managed string.
+ INJECT_FAULT(COMPlusThrowOM());
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ // The maximum size for values returned from GetLocaleInfo is 80 characters.
+ WCHAR buffer[80];
+ int result = 0;
+
+ _ASSERT((calType & CAL_RETURN_NUMBER) == 0);
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result;)
+
+ //
+ // We'll need a new array of 13 items
+ //
+ // Allocate the array of STRINGREFs. We don't need to check for null because the GC will throw
+ // an OutOfMemoryException if there's not enough memory.
+ //
+ PTRARRAYREF ResultArray = (PTRARRAYREF)AllocateObjectArray(13, g_pStringClass);
+
+ GCPROTECT_BEGIN(ResultArray);
+
+ // Get each one of them
+ for (int i = 0; i < 13; i++, calType++)
+ {
+ result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL);
+
+ // If we still have failure, then mark as empty string
+ if (result == 0)
+ {
+ buffer[0] = W('0');
+ result = 1;
+
+
+ }
+
+ // Note that the returned string is null terminated, so even an empty string will be 1
+ // Make a string for this entry
+ STRINGREF stringResult = StringObject::NewString(buffer, result - 1);
+ ResultArray->SetAt(i, (OBJECTREF)stringResult);
+ }
+ GCPROTECT_END();
+
+ _ASSERTE(pOutputStrings != NULL);
+ *pOutputStrings = ResultArray;
+
+ END_SO_INTOLERANT_CODE
+
+ return (result != 0);
+}
+
+//
+// struct to help our calendar data enumaration callback
+//
+struct enumData
+{
+ int count; // # of strings found so far
+ LPWSTR userOverride; // pointer to user override string if used
+ LPWSTR stringsBuffer; // pointer to a buffer to use for the strings.
+ LPWSTR endOfBuffer; // pointer to the end of the stringsBuffer ( must be < this to write)
+};
+
+//
+// callback itself
+//
+BOOL CALLBACK EnumCalendarInfoCallback(__in_z LPWSTR lpCalendarInfoString, __in CALID Calendar, __in_opt LPWSTR pReserved, __in LPARAM lParam)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(lpCalendarInfoString));
+ PRECONDITION(CheckPointer((LPVOID)lParam));
+ } CONTRACTL_END;
+
+ // Cast our data to the right type
+ enumData* pData = (enumData*)lParam;
+
+ // If we had a user override, check to make sure this differs
+ if (pData->userOverride == NULL ||
+ wcscmp(pData->userOverride, lpCalendarInfoString) != 0)
+ {
+ // They're different, add it to our buffer
+ LPWSTR pStart = pData->stringsBuffer;
+ LPCWSTR pEnd = pData->endOfBuffer;
+ while (pStart < pEnd && *lpCalendarInfoString != 0)
+ {
+ *(pStart++) = *(lpCalendarInfoString++);
+ }
+
+ // Add a \0
+ if (pStart < pEnd)
+ {
+ *(pStart++) = 0;
+
+ // Did it finish?
+ if (pStart <= pEnd)
+ {
+ // It finished, use it
+ pData->count++;
+ pData->stringsBuffer = pStart;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+//
+// CallEnumCalendarInfo
+//
+// Get the list of whichever calendar property from the OS. If user override is passed in, then check GetLocaleInfo as well
+// to see if a user override is set.
+//
+// We build a list of strings, first calling getlocaleinfo if necessary, and then the enums. The strings are null terminated,
+// with a double null ending the list. Once we have the list we can allocate our COMStrings and arrays from the count.
+//
+// We need a helper structure to pass as an lParam
+//
+BOOL CalendarData::CallEnumCalendarInfo(__in_z LPCWSTR localeName, __in int calendar, __in int calType,
+ __in int lcType, __inout PTRARRAYREF* pOutputStrings)
+{
+ CONTRACTL
+ {
+ THROWS; // We can throw since we are allocating managed string.
+ INJECT_FAULT(COMPlusThrowOM());
+ DISABLED(GC_TRIGGERS); // Disabled 'cause it don't work right now
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ BOOL result = TRUE;
+
+ // 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.
+ WCHAR stringBuffer[512];
+
+ struct enumData data;
+ data.count = 0;
+ data.userOverride = NULL;
+ data.stringsBuffer = stringBuffer;
+ data.endOfBuffer = stringBuffer + 512; // We're adding WCHAR sizes
+
+ // First call GetLocaleInfo if necessary
+ if ((lcType && ((lcType & LOCALE_NOUSEROVERRIDE) == 0)) &&
+ // Get user locale, see if it matches localeName.
+ // Note that they should match exactly, including letter case
+ NewApis::GetUserDefaultLocaleName(stringBuffer, 512) && wcscmp(localeName, stringBuffer) == 0)
+ {
+ // They want user overrides, see if the user calendar matches the input calendar
+ CALID userCalendar = 0;
+ NewApis::GetLocaleInfoEx(localeName, LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER, (LPWSTR)&userCalendar,
+ sizeof(userCalendar) / sizeof(WCHAR) );
+
+ // If the calendars were the same, see if the locales were the same
+ if ((int)userCalendar == calendar) // todo: cast to compile on MAC
+ {
+ // They matched, get the user override since locale & calendar match
+ int i = NewApis::GetLocaleInfoEx(localeName, lcType, stringBuffer, 512);
+
+ // if it succeeded, advance the pointer and remember the override for the later callers
+ if (i > 0)
+ {
+ // Remember this was the override (so we can look for duplicates later in the enum function)
+ data.userOverride = data.stringsBuffer;
+
+ // Advance to the next free spot (i includes counting the \0)
+ data.stringsBuffer += i;
+
+ // And our count...
+ data.count++;
+ }
+ }
+ }
+
+ // Now call the enumeration API. Work is done by our callback function
+ NewApis::EnumCalendarInfoExEx(EnumCalendarInfoCallback, localeName, calendar, calType, (LPARAM)&data);
+
+ // Now we have a list of data, fail if we didn't find anything.
+ if (data.count == 0) return FALSE;
+
+ // Now we need to allocate our stringarray and populate it
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return (result); )
+
+ // Get our array object (will throw, don't have to check it)
+ PTRARRAYREF dataArray = (PTRARRAYREF) AllocateObjectArray(data.count, g_pStringClass);
+
+ GCPROTECT_BEGIN(dataArray);
+ LPCWSTR buffer = stringBuffer; // Restart @ buffer beginning
+ for(DWORD i = 0; i < (DWORD)data.count; i++)
+ {
+ OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer);
+
+ if (calType == CAL_SABBREVERASTRING || calType == CAL_SERASTRING)
+ {
+ // Eras are enumerated backwards. (oldest era name first, but
+ // Japanese calendar has newest era first in array, and is only
+ // calendar with multiple eras)
+ dataArray->SetAt((DWORD)data.count - i - 1, o);
+ }
+ else
+ {
+ dataArray->SetAt(i, o);
+ }
+
+ buffer += (lstrlenW(buffer) + 1);
+ }
+ GCPROTECT_END();
+
+ _ASSERTE(pOutputStrings != NULL);
+ *pOutputStrings = dataArray;
+
+ END_SO_INTOLERANT_CODE
+
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// For calendars like Gregorain US/Taiwan/UmAlQura, they are not available
+// in all OS or all localized versions of OS.
+// If OS does not support these calendars, we will fallback by using the
+// appropriate fallback calendar and locale combination to retrieve data from OS.
+//
+// Parameters:
+// __deref_inout pCalendarInt:
+// Pointer to the calendar ID. This will be updated to new fallback calendar ID if needed.
+// __in_out pLocaleNameStackBuffer
+// Pointer to the StackSString object which holds the locale name to be checked.
+// This will be updated to new fallback locale name if needed.
+//
+////////////////////////////////////////////////////////////////////////
+
+void CalendarData::CheckSpecialCalendar(INT32* pCalendarInt, StackSString* pLocaleNameStackBuffer)
+{
+ // Gregorian-US isn't always available in the OS, however it is the same for all locales
+ switch (*pCalendarInt)
+ {
+ case CAL_GREGORIAN_US:
+ // See if this works
+ if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
+ {
+ // Failed, set it to a locale (fa-IR) that's alway has Gregorian US available in the OS
+ pLocaleNameStackBuffer->Set(W("fa-IR"), 5);
+ }
+ // See if that works
+ if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
+ {
+ // Failed again, just use en-US with the gregorian calendar
+ pLocaleNameStackBuffer->Set(W("en-US"), 5);
+ *pCalendarInt = CAL_GREGORIAN;
+ }
+ break;
+ case CAL_TAIWAN:
+ // Taiwan calendar data is not always in all language version of OS due to Geopolical reasons.
+ // It is only available in zh-TW localized versions of Windows.
+ // Let's check if OS supports it. If not, fallback to Greogrian localized for Taiwan calendar.
+ if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
+ {
+ *pCalendarInt = CAL_GREGORIAN;
+ }
+ break;
+ case CAL_UMALQURA:
+ // UmAlQura is only available in Vista and above, so we will need to fallback to Hijri if it is not available in the OS.
+ if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
+ {
+ // There are no differences in DATA between UmAlQura and Hijri, and
+ // UmAlQura isn't available before Vista, so just use Hijri..
+ *pCalendarInt = CAL_HIJRI;
+ }
+ break;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// Implementation for CalendarInfo.nativeGetCalendarData
+//
+// Retrieve calendar properties from the native side
+//
+// Parameters:
+// pCalendarData: This is passed from a managed structure CalendarData.cs
+// pLocaleNameUNSAFE: Locale name associated with the locale for this calendar
+// calendar: Calendar ID
+//
+// NOTE: Calendars depend on the locale name that creates it. Only a few
+// properties are available without locales using CalendarData.GetCalendar(int)
+//
+////////////////////////////////////////////////////////////////////////
+
+FCIMPL3(FC_BOOL_RET, CalendarData::nativeGetCalendarData, CalendarData* calendarDataUNSAFE, StringObject* pLocaleNameUNSAFE, INT32 calendar)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pLocaleNameUNSAFE));
+ } CONTRACTL_END;
+
+
+ // The maximum allowed string length in GetLocaleInfo is 80 WCHARs.
+ BOOL ret = TRUE;
+
+ struct _gc
+ {
+ STRINGREF localeName;
+ CALENDARDATAREF calendarData;
+ } gc;
+
+ // Dereference our gc objects
+ gc.localeName = (STRINGREF)pLocaleNameUNSAFE;
+ gc.calendarData = (CALENDARDATAREF)calendarDataUNSAFE;
+
+ // Need to set up the frame since we will be allocating managed strings.
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ // create a local copy of the string in order to pass it to helper methods that trigger GCs like GetCalendarDayInfo and GetCalendarMonthInfo
+ StackSString localeNameStackBuffer( gc.localeName->GetBuffer() );
+
+ // Conveniently this is the same as LOCALE_NOUSEROVERRIDE, so we can use this for both
+ int useOverrides = (gc.calendarData->bUseUserOverrides) ? 0 : CAL_NOUSEROVERRIDE;
+
+ // Helper string
+ STRINGREF stringResult = NULL;
+
+ //
+ // Windows doesn't support some calendars right now, so remap those.
+ //
+ if (calendar >= 13 && calendar < 23)
+ {
+ switch (calendar)
+ {
+ case RESERVED_CAL_PERSIAN: // don't change if we have Persian calendar
+ break;
+
+ case RESERVED_CAL_JAPANESELUNISOLAR: // Data looks like Japanese
+ calendar=CAL_JAPAN;
+ break;
+ case RESERVED_CAL_JULIAN: // Data looks like gregorian US
+ case RESERVED_CAL_CHINESELUNISOLAR: // Algorithmic, so actual data is irrelevent
+ case RESERVED_CAL_SAKA: // reserved to match Office but not implemented in our code, so data is irrelevent
+ case RESERVED_CAL_LUNAR_ETO_CHN: // reserved to match Office but not implemented in our code, so data is irrelevent
+ case RESERVED_CAL_LUNAR_ETO_KOR: // reserved to match Office but not implemented in our code, so data is irrelevent
+ case RESERVED_CAL_LUNAR_ETO_ROKUYOU: // reserved to match Office but not implemented in our code, so data is irrelevent
+ case RESERVED_CAL_KOREANLUNISOLAR: // Algorithmic, so actual data is irrelevent
+ case RESERVED_CAL_TAIWANLUNISOLAR: // Algorithmic, so actual data is irrelevent
+ default:
+ calendar = CAL_GREGORIAN_US;
+ break;
+ }
+ }
+
+ //
+ // Speical handling for some special calendar due to OS limitation.
+ // This includes calendar like Taiwan calendar, UmAlQura calendar, etc.
+ //
+ CheckSpecialCalendar(&calendar, &localeNameStackBuffer);
+
+
+ // Numbers
+ ret &= CallGetCalendarInfoEx(localeNameStackBuffer, calendar,
+ CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER | useOverrides,
+ &(gc.calendarData->iTwoDigitYearMax));
+
+ if (ret == FALSE) // failed call
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_ITWODIGITYEARMAX");
+ }
+
+ _ASSERTE(ret == TRUE);
+
+ // Strings
+ if (CallGetCalendarInfoEx(localeNameStackBuffer, calendar, CAL_SCALNAME , &stringResult))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->sNativeName), stringResult, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SCALNAME");
+ ret = FALSE;
+ }
+ if (CallGetCalendarInfoEx(localeNameStackBuffer, calendar, CAL_SMONTHDAY | useOverrides, &stringResult))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->sMonthDay), stringResult, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read RESERVED_CAL_SMONTHDAY");
+ ret = FALSE;
+ }
+
+ // String Arrays
+ // Formats
+ PTRARRAYREF array = NULL;
+ if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SSHORTDATE, LOCALE_SSHORTDATE | useOverrides, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saShortDates), array, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SSHORTDATE");
+ ret = FALSE;
+ }
+ if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SLONGDATE, LOCALE_SLONGDATE | useOverrides, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saLongDates), array, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SLONGDATE");
+ ret = FALSE;
+ }
+
+ // Get the YearMonth pattern.
+ // Before Windows Vista, NLS would not write the Year/Month pattern into the reg key.
+ // This causes GetLocaleInfo() to retrieve the Gregorian localized calendar pattern even when the calendar is not Gregorian localized.
+ // So we will call GetLocaleInfo() only when the reg key for sYearMonth is there.
+ //
+ // If the key does not exist, leave yearMonthPattern to be null, so that we will pick up the default table value.
+
+ int useOverridesForYearMonthPattern = useOverrides;
+ if (useOverridesForYearMonthPattern == 0)
+ {
+ HKEY hkey = NULL;
+ useOverridesForYearMonthPattern = CAL_NOUSEROVERRIDE;
+ if (WszRegOpenKeyEx(HKEY_CURRENT_USER, W("Control Panel\\International"), 0, KEY_READ, &hkey) == ERROR_SUCCESS)
+ {
+ if (WszRegQueryValueEx(hkey, W("sYearMonth"), 0, NULL, NULL, NULL) == ERROR_SUCCESS)
+ {
+ // The sYearMonth key exists. Call GetLocaleInfo() to read it.
+ useOverridesForYearMonthPattern = 0; // now we can use the overrides
+ }
+ RegCloseKey(hkey);
+ }
+ }
+
+ if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SYEARMONTH, LOCALE_SYEARMONTH | useOverridesForYearMonthPattern, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saYearMonths), array, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SYEARMONTH");
+ ret = FALSE;
+ }
+
+ // Day & Month Names
+ // These are all single calType entries, 1 per day, so we have to make 7 or 13 calls to collect all the names
+
+ // Day
+ // Note that we're off-by-one since managed starts on sunday and windows starts on monday
+ if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SDAYNAME7, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saDayNames), array, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SDAYNAME7");
+ ret = FALSE;
+ }
+ if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SABBREVDAYNAME7, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevDayNames), array, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SABBREVDAYNAME7");
+ ret = FALSE;
+ }
+
+ // Month names
+ if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SMONTHNAME1, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saMonthNames), array, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SMONTHNAME1");
+ ret = FALSE;
+ }
+
+ if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SABBREVMONTHNAME1, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevMonthNames), array, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SABBREVMONTHNAME1");
+ ret = FALSE;
+ }
+
+ //
+ // The following LCTYPE are not supported in some platforms. If the call fails,
+ // don't return a failure.
+ //
+ if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SSHORTESTDAYNAME7, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saSuperShortDayNames), array, NULL);
+
+
+ // Gregorian may have genitive month names
+ if (calendar == CAL_GREGORIAN)
+ {
+ if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saMonthGenitiveNames), array, NULL);
+ // else we ignore the error and let managed side copy the normal month names
+ if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SABBREVMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevMonthGenitiveNames), array, NULL);
+ // else we ignore the error and let managed side copy the normal month names
+ }
+
+// leap year names are only different for month 6 in Hebrew calendar
+// PTRARRAYREF saLeapYearMonthNames ; // Multiple strings for the month names in a leap year. (Hebrew's the only one that has these)
+
+ // Calendar Parts Names
+ if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SERASTRING, NULL, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saEraNames), array, NULL);
+ // else we set the era in managed code
+ if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SABBREVERASTRING, NULL, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevEraNames), array, NULL);
+ // else we set the era in managed code
+
+ // PTRARRAYREF saAbbrevEnglishEraNames ; // Abbreviated Era Names in English
+
+ //
+ // Calendar Era Info
+ // Note that calendar era data (offsets, etc) is hard coded for each calendar since this
+ // data is implementation specific and not dynamic (except perhaps Japanese)
+ //
+
+ HELPER_METHOD_FRAME_END();
+
+ FC_RETURN_BOOL(ret);
+}
+FCIMPLEND
+
+//
+// Get the system two digit year max value for the specified calendar
+//
+FCIMPL1(INT32, CalendarData::nativeGetTwoDigitYearMax, INT32 calendar)
+{
+ FCALL_CONTRACT;
+
+ DWORD dwTwoDigitYearMax = (DWORD) -1;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_0();
+
+ WCHAR strName[LOCALE_NAME_MAX_LENGTH];
+
+ // Really we should just be able to pass NULL for the locale name since that
+ // causes the OS to look up the user default locale name. The downlevel APIS could
+ // emulate this as necessary
+ if (NewApis::GetUserDefaultLocaleName(strName,NumItems(strName)) == 0 ||
+ NewApis::GetCalendarInfoEx(strName, calendar, NULL, CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER, NULL, 0,&dwTwoDigitYearMax) == 0)
+ {
+ dwTwoDigitYearMax = (DWORD) -1;
+ goto lExit;
+ }
+
+lExit: ;
+ HELPER_METHOD_FRAME_END();
+
+ return (dwTwoDigitYearMax);
+}
+FCIMPLEND
+
+//
+// nativeGetCalendars
+//
+// Get the list of acceptable calendars for this user/locale
+//
+// Might be a better way to marshal the int[] for calendars
+// We expect the input array to be 23 ints long. We then fill up the first "count" ints and return the count.
+// The caller should then make it a smaller array.
+//
+
+// Perhaps we could do something more like this...
+//U1ARRAYREF rgbOut = (U1ARRAYREF) AllocatePrimitiveArray(ELEMENT_TYPE_U1, cb);
+//memcpyNoGCRefs(rgbOut->GetDirectPointerToNonObjectElements(), rgbKey, cb * sizeof(BYTE));
+//
+//refRetVal = rgbOut;
+//
+//HELPER_METHOD_FRAME_END();
+//return (U1Array*) OBJECTREFToObject(refRetVal);
+
+//
+// struct to help our calendar data enumaration callback
+//
+struct enumCalendarsData
+{
+ int count; // # of strings found so far
+ CALID userOverride; // user override value (if found)
+ INT32* calendarList; // list of calendars found so far
+};
+
+//
+// callback itself
+//
+BOOL CALLBACK EnumCalendarsCallback(__in_z LPWSTR lpCalendarInfoString, __in CALID Calendar, __in_opt LPWSTR pReserved, __in LPARAM lParam)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(lpCalendarInfoString));
+ PRECONDITION(CheckPointer((LPVOID)lParam));
+ } CONTRACTL_END;
+
+ // Cast our data to the right type
+ enumCalendarsData* pData = (enumCalendarsData*)lParam;
+
+ // If we had a user override, check to make sure this differs
+ if (pData->userOverride == Calendar)
+ {
+ // Its the same, just return
+ return TRUE;
+ }
+
+ // They're different, add it to our buffer, check we have room
+ if (pData->count < 23)
+ {
+ pData->calendarList[pData->count++] = Calendar;
+ }
+
+ return TRUE;
+}
+
+FCIMPL3(INT32, CalendarData::nativeGetCalendars, StringObject* pLocaleNameUNSAFE, CLR_BOOL useOverrides, I4Array* calendarsUNSAFE)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pLocaleNameUNSAFE));
+ } CONTRACTL_END;
+
+ int ret = 0;
+
+ struct _gc
+ {
+ STRINGREF localeName;
+ I4ARRAYREF calendarsRef;
+ } gc;
+
+ // Dereference our string
+ gc.localeName = (STRINGREF)pLocaleNameUNSAFE;
+ gc.calendarsRef = (I4ARRAYREF)calendarsUNSAFE;
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ int calendarBuffer[23];
+ struct enumCalendarsData data;
+ data.count = 0;
+ data.userOverride = 0;
+ data.calendarList = calendarBuffer;
+
+ // First call GetLocaleInfo if necessary
+ if (useOverrides)
+ {
+ // They want user overrides, see if the user calendar matches the input calendar
+
+ CALID userCalendar = 0;
+ NewApis::GetLocaleInfoEx( gc.localeName->GetBuffer(), LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
+ (LPWSTR)&userCalendar, sizeof(userCalendar) / sizeof(WCHAR) );
+
+ // If we got a default, then use it as the first calendar
+ if (userCalendar != 0)
+ {
+ data.userOverride = userCalendar;
+ data.calendarList[data.count++] = userCalendar;
+ }
+ }
+
+ // Now call the enumeration API. Work is done by our callback function
+ NewApis::EnumCalendarInfoExEx(EnumCalendarsCallback, gc.localeName->GetBuffer(), ENUM_ALL_CALENDARS, CAL_ICALINTVALUE, (LPARAM)&(data));
+
+ // Copy to the output array
+ for (int i = 0; i < data.count; i++)
+ {
+ (gc.calendarsRef->GetDirectPointerToNonObjectElements())[i] = calendarBuffer[i];
+ }
+
+ ret = data.count;
+ HELPER_METHOD_FRAME_END();
+
+ // Now we have a list of data, return the count
+ return ret;
+}
+FCIMPLEND
+
+//
+// nativeEnumTimeFormats
+//
+// Enumerate all of the time formats (long times) on the system.
+// Windows only has 1 time format so there's nothing like an LCTYPE here.
+//
+// Note that if the locale is the user default locale windows ALWAYS returns the user override value first.
+// (ie: there's no no-user-override option for this API)
+//
+// We reuse the enumData structure since it works for us.
+//
+
+//
+// callback itself
+//
+BOOL CALLBACK EnumTimeFormatsCallback(__in_z LPCWSTR lpTimeFormatString, __in LPARAM lParam)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(lpTimeFormatString));
+ PRECONDITION(CheckPointer((LPVOID)lParam));
+ } CONTRACTL_END;
+
+ // Cast our data to the right type
+ enumData* pData = (enumData*)lParam;
+
+ // Don't have to worry about user overrides (the enum adds them)
+ // add it to our buffer
+ LPWSTR pStart = pData->stringsBuffer;
+ LPCWSTR pEnd = pData->endOfBuffer;
+ while (pStart < pEnd && *lpTimeFormatString != 0)
+ {
+ *(pStart++) = *(lpTimeFormatString++);
+ }
+
+ // Add a \0
+ if (pStart < pEnd)
+ {
+ *(pStart++) = 0;
+
+ // Did it finish?
+ if (pStart <= pEnd)
+ {
+ // It finished, use it
+ pData->count++;
+ pData->stringsBuffer = pStart;
+ }
+ }
+
+ return TRUE;
+}
+
+//
+// nativeEnumTimeFormats that calls the callback above
+//
+FCIMPL3(Object*, CalendarData::nativeEnumTimeFormats,
+ StringObject* pLocaleNameUNSAFE, INT32 dwFlags, CLR_BOOL useUserOverride)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pLocaleNameUNSAFE));
+ } CONTRACTL_END;
+
+
+ struct _gc
+ {
+ STRINGREF localeName;
+ PTRARRAYREF timeFormatsArray;
+ } gc;
+
+ // Dereference our gc objects
+ gc.localeName = (STRINGREF)pLocaleNameUNSAFE;
+ gc.timeFormatsArray = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ // 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.
+ WCHAR stringBuffer[512];
+ struct enumData data;
+ data.count = 0;
+ data.userOverride = NULL;
+ data.stringsBuffer = stringBuffer;
+ data.endOfBuffer = stringBuffer + 512; // We're adding WCHAR sizes
+
+ // Now call the enumeration API. Work is done by our callback function
+ NewApis::EnumTimeFormatsEx((TIMEFMT_ENUMPROCEX)EnumTimeFormatsCallback, gc.localeName->GetBuffer(), dwFlags, (LPARAM)&data);
+
+ if (data.count > 0)
+ {
+ // Now we need to allocate our stringarray and populate it
+ // Get our array object (will throw, don't have to check it)
+ gc.timeFormatsArray = (PTRARRAYREF) AllocateObjectArray(data.count, g_pStringClass);
+
+ LPCWSTR buffer = stringBuffer; // Restart @ buffer beginning
+ for(DWORD i = 0; i < (DWORD)data.count; i++) // todo: cast to compile on Mac
+ {
+ OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer);
+ gc.timeFormatsArray->SetAt(i, o);
+
+ buffer += (lstrlenW(buffer) + 1);
+ }
+
+ if(!useUserOverride && data.count > 1)
+ {
+ // Since there is no "NoUserOverride" aware EnumTimeFormatsEx, we always get an override
+ // The override is the first entry if it is overriden.
+ // We can check if we have overrides by checking the GetLocaleInfo with no override
+ // If we do have an override, we don't know if it is a user defined override or if the
+ // user has just selected one of the predefined formats so we can't just remove it
+ // but we can move it down.
+ WCHAR timeFormatNoUserOverride[LOCALE_NAME_MAX_LENGTH];
+ DWORD lcType = (dwFlags == TIME_NOSECONDS) ? LOCALE_SSHORTTIME : LOCALE_STIMEFORMAT;
+ lcType |= LOCALE_NOUSEROVERRIDE;
+ int result = NewApis::GetLocaleInfoEx(gc.localeName->GetBuffer(), lcType, timeFormatNoUserOverride, LOCALE_NAME_MAX_LENGTH);
+ if(result != 0)
+ {
+ STRINGREF firstTimeFormat = (STRINGREF)gc.timeFormatsArray->GetAt(0);
+ if(wcscmp(timeFormatNoUserOverride, firstTimeFormat->GetBuffer())!=0)
+ {
+ gc.timeFormatsArray->SetAt(0, gc.timeFormatsArray->GetAt(1));
+ gc.timeFormatsArray->SetAt(1, firstTimeFormat);
+ }
+ }
+ }
+
+ }
+
+ HELPER_METHOD_FRAME_END();
+ return OBJECTREFToObject(gc.timeFormatsArray);
+}
+FCIMPLEND
+
+