Merge pull request #8544 from sivarv/moveRegFix
[platform/upstream/coreclr.git] / src / mscorlib / src / System / Globalization / CultureData.cs
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 namespace System.Globalization
6 {
7
8     using System;
9     using System.Collections;
10     using System.Collections.Generic;
11     using System.Text;
12     using System.Threading;
13     using System.Runtime.CompilerServices;
14     using System.Runtime.InteropServices;
15     using System.Runtime.Versioning;
16     using System.Diagnostics.Contracts;
17     using System.Security;
18
19     //
20     // List of culture data
21     // Note the we cache overrides.
22     // Note that localized names (resource names) aren't available from here.
23     //
24
25     //
26     // Our names are a tad confusing.
27     //
28     // sWindowsName -- The name that windows thinks this culture is, ie:
29     //                            en-US if you pass in en-US
30     //                            de-DE_phoneb if you pass in de-DE_phoneb
31     //                            fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
32     //                            fj if you pass in fj (neutral, post-Windows 7 machine)
33     //
34     // sRealName -- The name you used to construct the culture, in pretty form
35     //                       en-US if you pass in EN-us
36     //                       en if you pass in en
37     //                       de-DE_phoneb if you pass in de-DE_phoneb
38     //
39     // sSpecificCulture -- The specific culture for this culture
40     //                             en-US for en-US
41     //                             en-US for en
42     //                             de-DE_phoneb for alt sort
43     //                             fj-FJ for fj (neutral)
44     //
45     // sName -- The IETF name of this culture (ie: no sort info, could be neutral)
46     //                en-US if you pass in en-US
47     //                en if you pass in en
48     //                de-DE if you pass in de-DE_phoneb
49     //
50
51     // StructLayout is needed here otherwise compiler can re-arrange the fields.
52     // We have to keep this in-sync with the definition in comnlsinfo.h
53     //
54     // WARNING WARNING WARNING
55     //
56     // WARNING: Anything changed here also needs to be updated on the native side (object.h see type CultureDataBaseObject)
57     // WARNING: The type loader will rearrange class member offsets so the mscorwks!CultureDataBaseObject
58     // WARNING: must be manually structured to match the true loaded class layout
59     //
60     [FriendAccessAllowed]
61     internal class CultureData
62     {
63         const int undef = -1;
64
65         // Override flag
66         private String sRealName; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
67         private String sWindowsName; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
68
69         // Identity
70         private String sName; // locale name (ie: en-us, NO sort info, but could be neutral)
71         private String sParent; // Parent name (which may be a custom locale/culture)
72         private String sLocalizedDisplayName; // Localized pretty name for this locale
73         private String sEnglishDisplayName; // English pretty name for this locale
74         private String sNativeDisplayName; // Native pretty name for this locale
75         private String sSpecificCulture; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US form if neutral, sort name if sort
76
77         // Language
78         private String sISO639Language; // ISO 639 Language Name
79         private String sLocalizedLanguage; // Localized name for this language
80         private String sEnglishLanguage; // English name for this language
81         private String sNativeLanguage; // Native name of this language
82
83         // Region
84         private String sRegionName; // (RegionInfo)
85         private int iGeoId = undef; // GeoId
86         private String sLocalizedCountry; // localized country name
87         private String sEnglishCountry; // english country name (RegionInfo)
88         private String sNativeCountry; // native country name
89         private String sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US
90
91         // Numbers
92         private String sPositiveSign; // (user can override) positive sign
93         private String sNegativeSign; // (user can override) negative sign
94         private String[] saNativeDigits; // (user can override) native characters for digits 0-9
95         // (nfi populates these 5, don't have to be = undef)
96         private int iDigitSubstitution; // (user can override) Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused)
97         private int iLeadingZeros; // (user can override) leading zeros 0 = no leading zeros, 1 = leading zeros
98         private int iDigits; // (user can override) number of fractional digits
99         private int iNegativeNumber; // (user can override) negative number format
100         private int[] waGrouping; // (user can override) grouping of digits
101         private String sDecimalSeparator; // (user can override) decimal separator
102         private String sThousandSeparator; // (user can override) thousands separator
103         private String sNaN; // Not a Number
104         private String sPositiveInfinity; // + Infinity
105         private String sNegativeInfinity; // - Infinity
106
107         // Percent
108         private int iNegativePercent = undef; // Negative Percent (0-3)
109         private int iPositivePercent = undef; // Positive Percent (0-11)
110         private String sPercent; // Percent (%) symbol
111         private String sPerMille; // PerMille (‰) symbol
112
113         // Currency
114         private String sCurrency; // (user can override) local monetary symbol
115         private String sIntlMonetarySymbol; // international monetary symbol (RegionInfo)
116         private String sEnglishCurrency; // English name for this currency
117         private String sNativeCurrency; // Native name for this currency
118         // (nfi populates these 4, don't have to be = undef)
119         private int iCurrencyDigits; // (user can override) # local monetary fractional digits
120         private int iCurrency; // (user can override) positive currency format
121         private int iNegativeCurrency; // (user can override) negative currency format
122         private int[] waMonetaryGrouping; // (user can override) monetary grouping of digits
123         private String sMonetaryDecimal; // (user can override) monetary decimal separator
124         private String sMonetaryThousand; // (user can override) monetary thousands separator
125
126         // Misc
127         private int iMeasure = undef; // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
128         private String sListSeparator; // (user can override) list separator
129         //        private int    iPaperSize               ; // default paper size (RegionInfo)
130
131         // Time
132         private String sAM1159; // (user can override) AM designator
133         private String sPM2359; // (user can override) PM designator
134         private String sTimeSeparator;
135         private volatile String[] saLongTimes; // (user can override) time format
136         private volatile String[] saShortTimes; // short time format
137         private volatile String[] saDurationFormats; // time duration format
138
139         // Calendar specific data
140         private int iFirstDayOfWeek = undef; // (user can override) first day of week (gregorian really)
141         private int iFirstWeekOfYear = undef; // (user can override) first week of year (gregorian really)
142         private volatile int[] waCalendars; // all available calendar type(s).  The first one is the default calendar
143
144         // Store for specific data about each calendar
145         private CalendarData[] calendars; // Store for specific calendar data
146
147         // Text information
148         private int iReadingLayout = undef; // Reading layout data
149         // 0 - Left to right (eg en-US)
150         // 1 - Right to left (eg arabic locales)
151         // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
152         // 3 - Vertical top to bottom with columns proceeding to the right
153
154         private String sTextInfo; // Text info name to use for custom
155         private String sCompareInfo; // Compare info name (including sorting key) to use if custom
156         private String sScripts; // Typical Scripts for this locale (latn;cyrl; etc)
157
158         private int iDefaultAnsiCodePage = undef; // default ansi code page ID (ACP)
159         private int iDefaultOemCodePage = undef; // default oem code page ID (OCP or OEM)
160         private int iDefaultMacCodePage = undef; // default macintosh code page
161         private int iDefaultEbcdicCodePage = undef; // default EBCDIC code page
162
163         // These are desktop only, not coreclr
164         private int    iLanguage; // locale ID (0409) - NO sort information
165         private String sAbbrevLang; // abbreviated language name (Windows Language Name) ex: ENU
166         private String sAbbrevCountry; // abbreviated country name (RegionInfo) (Windows Region Name) ex: USA
167         private String sISO639Language2; // 3 char ISO 639 lang name 2 ex: eng
168         private String sISO3166CountryName2; // 3 char ISO 3166 country name 2 2(RegionInfo) ex: USA (ISO)
169         private int    iInputLanguageHandle=undef;// input language handle
170         private String sConsoleFallbackName; // The culture name for the console fallback UI culture
171         private String sKeyboardsToInstall; // Keyboard installation string.
172         private String fontSignature; // Font signature (16 WORDS)
173
174         // The bools all need to be in one spot
175         private bool bUseOverrides; // use user overrides?
176         private bool bNeutral; // Flags for the culture (ie: neutral or not right now)
177         private bool bWin32Installed; // Flags indicate if the culture is Win32 installed
178         private bool bFramework; // Flags for indicate if the culture is one of Whidbey cultures
179
180         // Region Name to Culture Name mapping table
181         // (In future would be nice to be in registry or something)
182
183         //Using a property so we avoid creating the dictionary untill we need it
184         private static Dictionary<string, string> RegionNames
185         {
186             get 
187             {
188                 if (s_RegionNames == null)
189                 {
190                     var regionNames = new Dictionary<string, string> {
191                         { "029", "en-029" },
192                         { "AE",  "ar-AE" },
193                         { "AF",  "prs-AF" },
194                         { "AL",  "sq-AL" },
195                         { "AM",  "hy-AM" },
196                         { "AR",  "es-AR" },
197                         { "AT",  "de-AT" },
198                         { "AU",  "en-AU" },
199                         { "AZ",  "az-Cyrl-AZ" },
200                         { "BA",  "bs-Latn-BA" },
201                         { "BD",  "bn-BD" },
202                         { "BE",  "nl-BE" },
203                         { "BG",  "bg-BG" },
204                         { "BH",  "ar-BH" },
205                         { "BN",  "ms-BN" },
206                         { "BO",  "es-BO" },
207                         { "BR",  "pt-BR" },
208                         { "BY",  "be-BY" },
209                         { "BZ",  "en-BZ" },
210                         { "CA",  "en-CA" },
211                         { "CH",  "it-CH" },
212                         { "CL",  "es-CL" },
213                         { "CN",  "zh-CN" },
214                         { "CO",  "es-CO" },
215                         { "CR",  "es-CR" },
216                         { "CS",  "sr-Cyrl-CS" },
217                         { "CZ",  "cs-CZ" },
218                         { "DE",  "de-DE" },
219                         { "DK",  "da-DK" },
220                         { "DO",  "es-DO" },
221                         { "DZ",  "ar-DZ" },
222                         { "EC",  "es-EC" },
223                         { "EE",  "et-EE" },
224                         { "EG",  "ar-EG" },
225                         { "ES",  "es-ES" },
226                         { "ET",  "am-ET" },
227                         { "FI",  "fi-FI" },
228                         { "FO",  "fo-FO" },
229                         { "FR",  "fr-FR" },
230                         { "GB",  "en-GB" },
231                         { "GE",  "ka-GE" },
232                         { "GL",  "kl-GL" },
233                         { "GR",  "el-GR" },
234                         { "GT",  "es-GT" },
235                         { "HK",  "zh-HK" },
236                         { "HN",  "es-HN" },
237                         { "HR",  "hr-HR" },
238                         { "HU",  "hu-HU" },
239                         { "ID",  "id-ID" },
240                         { "IE",  "en-IE" },
241                         { "IL",  "he-IL" },
242                         { "IN",  "hi-IN" },
243                         { "IQ",  "ar-IQ" },
244                         { "IR",  "fa-IR" },
245                         { "IS",  "is-IS" },
246                         { "IT",  "it-IT" },
247                         { "IV",  "" },
248                         { "JM",  "en-JM" },
249                         { "JO",  "ar-JO" },
250                         { "JP",  "ja-JP" },
251                         { "KE",  "sw-KE" },
252                         { "KG",  "ky-KG" },
253                         { "KH",  "km-KH" },
254                         { "KR",  "ko-KR" },
255                         { "KW",  "ar-KW" },
256                         { "KZ",  "kk-KZ" },
257                         { "LA",  "lo-LA" },
258                         { "LB",  "ar-LB" },
259                         { "LI",  "de-LI" },
260                         { "LK",  "si-LK" },
261                         { "LT",  "lt-LT" },
262                         { "LU",  "lb-LU" },
263                         { "LV",  "lv-LV" },
264                         { "LY",  "ar-LY" },
265                         { "MA",  "ar-MA" },
266                         { "MC",  "fr-MC" },
267                         { "ME",  "sr-Latn-ME" },
268                         { "MK",  "mk-MK" },
269                         { "MN",  "mn-MN" },
270                         { "MO",  "zh-MO" },
271                         { "MT",  "mt-MT" },
272                         { "MV",  "dv-MV" },
273                         { "MX",  "es-MX" },
274                         { "MY",  "ms-MY" },
275                         { "NG",  "ig-NG" },
276                         { "NI",  "es-NI" },
277                         { "NL",  "nl-NL" },
278                         { "NO",  "nn-NO" },
279                         { "NP",  "ne-NP" },
280                         { "NZ",  "en-NZ" },
281                         { "OM",  "ar-OM" },
282                         { "PA",  "es-PA" },
283                         { "PE",  "es-PE" },
284                         { "PH",  "en-PH" },
285                         { "PK",  "ur-PK" },
286                         { "PL",  "pl-PL" },
287                         { "PR",  "es-PR" },
288                         { "PT",  "pt-PT" },
289                         { "PY",  "es-PY" },
290                         { "QA",  "ar-QA" },
291                         { "RO",  "ro-RO" },
292                         { "RS",  "sr-Latn-RS" },
293                         { "RU",  "ru-RU" },
294                         { "RW",  "rw-RW" },
295                         { "SA",  "ar-SA" },
296                         { "SE",  "sv-SE" },
297                         { "SG",  "zh-SG" },
298                         { "SI",  "sl-SI" },
299                         { "SK",  "sk-SK" },
300                         { "SN",  "wo-SN" },
301                         { "SV",  "es-SV" },
302                         { "SY",  "ar-SY" },
303                         { "TH",  "th-TH" },
304                         { "TJ",  "tg-Cyrl-TJ" },
305                         { "TM",  "tk-TM" },
306                         { "TN",  "ar-TN" },
307                         { "TR",  "tr-TR" },
308                         { "TT",  "en-TT" },
309                         { "TW",  "zh-TW" },
310                         { "UA",  "uk-UA" },
311                         { "US",  "en-US" },
312                         { "UY",  "es-UY" },
313                         { "UZ",  "uz-Cyrl-UZ" },
314                         { "VE",  "es-VE" },
315                         { "VN",  "vi-VN" },
316                         { "YE",  "ar-YE" },
317                         { "ZA",  "af-ZA" },
318                         { "ZW",  "en-ZW" }
319                     };
320                     s_RegionNames = regionNames;
321                 }
322                 return s_RegionNames;
323             }
324         }
325         private volatile static Dictionary<string, string> s_RegionNames;
326
327         /////////////////////////////////////////////////////////////////////////
328         // Build our invariant information
329         //
330         // We need an invariant instance, which we build hard-coded
331         /////////////////////////////////////////////////////////////////////////
332         internal static CultureData Invariant  
333         {
334             get 
335             {
336                 if (s_Invariant == null)
337                 {
338                     // Make a new culturedata
339                     CultureData invariant = new CultureData();
340
341                     // Call the native code to get the value of bWin32Installed.
342                     // For versions <= Vista, we set this to false for compatibility with v2.
343                     // For Windows 7, the flag is true.
344                     invariant.bUseOverrides = false;
345                     invariant.sRealName = "";
346
347                     // Ask the native code to fill it out for us, we only need the field IsWin32Installed
348                     nativeInitCultureData(invariant);
349
350                     // Basics
351                     // Note that we override the resources since this IS NOT supposed to change (by definition)
352                     invariant.bUseOverrides = false;
353                     invariant.sRealName = "";                     // Name you passed in (ie: en-US, en, or de-DE_phoneb)
354                     invariant.sWindowsName = "";                     // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
355
356                     // Identity
357                     invariant.sName = "";                     // locale name (ie: en-us)
358                     invariant.sParent = "";                     // Parent name (which may be a custom locale/culture)
359                     invariant.bNeutral = false;                   // Flags for the culture (ie: neutral or not right now)
360                     // Don't set invariant.bWin32Installed, we used nativeInitCultureData for that.
361                     invariant.bFramework = true;
362
363                     invariant.sEnglishDisplayName = "Invariant Language (Invariant Country)"; // English pretty name for this locale
364                     invariant.sNativeDisplayName = "Invariant Language (Invariant Country)";  // Native pretty name for this locale
365                     invariant.sSpecificCulture = "";                     // The culture name to be used in CultureInfo.CreateSpecificCulture()
366
367                     // Language
368                     invariant.sISO639Language = "iv";                   // ISO 639 Language Name
369                     invariant.sLocalizedLanguage = "Invariant Language";   // Display name for this Language
370                     invariant.sEnglishLanguage = "Invariant Language";   // English name for this language
371                     invariant.sNativeLanguage = "Invariant Language";   // Native name of this language
372
373                     // Region
374                     invariant.sRegionName = "IV";                   // (RegionInfo)
375                     // Unused for now:
376                     //            invariant.iCountry              =1;                      // country code (RegionInfo)
377                     invariant.iGeoId = 244;                    // GeoId (Windows Only)
378                     invariant.sEnglishCountry = "Invariant Country";    // english country name (RegionInfo)
379                     invariant.sNativeCountry = "Invariant Country";    // native country name (Windows Only)
380                     invariant.sISO3166CountryName = "IV";                   // (RegionInfo), ie: US
381
382                     // Numbers
383                     invariant.sPositiveSign = "+";                    // positive sign
384                     invariant.sNegativeSign = "-";                    // negative sign
385                     invariant.saNativeDigits = new String[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; // native characters for digits 0-9
386                     invariant.iDigitSubstitution = 1;                      // Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused) (Windows Only)
387                     invariant.iLeadingZeros = 1;                      // leading zeros 0=no leading zeros, 1=leading zeros
388                     invariant.iDigits = 2;                      // number of fractional digits
389                     invariant.iNegativeNumber = 1;                      // negative number format
390                     invariant.waGrouping = new int[] { 3 };          // grouping of digits
391                     invariant.sDecimalSeparator = ".";                    // decimal separator
392                     invariant.sThousandSeparator = ",";                    // thousands separator
393                     invariant.sNaN = "NaN";                  // Not a Number
394                     invariant.sPositiveInfinity = "Infinity";             // + Infinity
395                     invariant.sNegativeInfinity = "-Infinity";            // - Infinity
396
397                     // Percent
398                     invariant.iNegativePercent = 0;                      // Negative Percent (0-3)
399                     invariant.iPositivePercent = 0;                      // Positive Percent (0-11)
400                     invariant.sPercent = "%";                    // Percent (%) symbol
401                     invariant.sPerMille = "\x2030";               // PerMille(‰) symbol
402
403                     // Currency
404                     invariant.sCurrency = "\x00a4";                // local monetary symbol "¤: for international monetary symbol
405                     invariant.sIntlMonetarySymbol = "XDR";                  // international monetary symbol (RegionInfo)
406                     invariant.sEnglishCurrency = "International Monetary Fund"; // English name for this currency (Windows Only)
407                     invariant.sNativeCurrency = "International Monetary Fund"; // Native name for this currency (Windows Only)
408                     invariant.iCurrencyDigits = 2;                      // # local monetary fractional digits
409                     invariant.iCurrency = 0;                      // positive currency format
410                     invariant.iNegativeCurrency = 0;                      // negative currency format
411                     invariant.waMonetaryGrouping = new int[] { 3 };          // monetary grouping of digits
412                     invariant.sMonetaryDecimal = ".";                    // monetary decimal separator
413                     invariant.sMonetaryThousand = ",";                    // monetary thousands separator
414
415                     // Misc
416                     invariant.iMeasure = 0;                      // system of measurement 0=metric, 1=US (RegionInfo)
417                     invariant.sListSeparator = ",";                    // list separator
418                     // Unused for now:
419                     //            invariant.iPaperSize            =9;                      // default paper size (RegionInfo)
420                     //            invariant.waFontSignature       ="\x0002\x0000\x0000\x0000\x0000\x0000\x0000\x8000\x0001\x0000\x0000\x8000\x0001\x0000\x0000\x8000"; // Font signature (16 WORDS) (Windows Only)
421
422                     // Time
423                     invariant.sAM1159 = "AM";                   // AM designator
424                     invariant.sPM2359 = "PM";                   // PM designator
425                     invariant.saLongTimes = new String[] { "HH:mm:ss" };                             // time format
426                     invariant.saShortTimes = new String[] { "HH:mm", "hh:mm tt", "H:mm", "h:mm tt" }; // short time format
427                     invariant.saDurationFormats = new String[] { "HH:mm:ss" };                             // time duration format
428
429                     // Calendar specific data
430                     invariant.iFirstDayOfWeek = 0;                      // first day of week
431                     invariant.iFirstWeekOfYear = 0;                      // first week of year
432                     invariant.waCalendars = new int[] { (int)CalendarId.GREGORIAN };       // all available calendar type(s).  The first one is the default calendar
433
434                     // Store for specific data about each calendar
435                     invariant.calendars = new CalendarData[CalendarData.MAX_CALENDARS];
436                     invariant.calendars[0] = CalendarData.Invariant;
437
438                     // Text information
439                     invariant.iReadingLayout = 0;                      // Reading Layout = RTL
440
441                     invariant.sTextInfo = "";                     // Text info name to use for custom
442                     invariant.sCompareInfo = "";                     // Compare info name (including sorting key) to use if custom
443                     invariant.sScripts = "Latn;";                // Typical Scripts for this locale (latn,cyrl, etc)
444
445                     invariant.iLanguage = 0x007f;                 // locale ID (0409) - NO sort information
446                     invariant.iDefaultAnsiCodePage = 1252;                   // default ansi code page ID (ACP)
447                     invariant.iDefaultOemCodePage = 437;                    // default oem code page ID (OCP or OEM)
448                     invariant.iDefaultMacCodePage = 10000;                  // default macintosh code page
449                     invariant.iDefaultEbcdicCodePage = 037;                    // default EBCDIC code page
450             invariant.sAbbrevLang = "IVL";                     // abbreviated language name (Windows Language Name)
451             invariant.sAbbrevCountry = "IVC";                  // abbreviated country name (RegionInfo) (Windows Region Name)
452                     invariant.sISO639Language2 = "ivl";                  // 3 char ISO 639 lang name 2
453                     invariant.sISO3166CountryName2 = "ivc";                  // 3 char ISO 3166 country name 2 2(RegionInfo)
454                     invariant.iInputLanguageHandle = 0x007f;                 // input language handle
455                     invariant.sConsoleFallbackName = "";                     // The culture name for the console fallback UI culture
456                     invariant.sKeyboardsToInstall = "0409:00000409";        // Keyboard installation string.
457                     // Remember it
458                     s_Invariant = invariant;
459                 }
460                 return s_Invariant;
461             }
462         }
463         private volatile static CultureData s_Invariant;
464
465         ///////////////
466         // Constructors //
467         ///////////////
468         // Cache of cultures we've already looked up
469         private static volatile Dictionary<String, CultureData> s_cachedCultures;
470
471         [FriendAccessAllowed]
472         internal static CultureData GetCultureData(String cultureName, bool useUserOverride)
473         {
474             // First do a shortcut for Invariant
475             if (String.IsNullOrEmpty(cultureName))
476             {
477                 return CultureData.Invariant;
478             }
479
480             // Try the hash table first
481             String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
482             Dictionary<String, CultureData> tempHashTable = s_cachedCultures;
483             if (tempHashTable == null)
484             {
485                 // No table yet, make a new one
486                 tempHashTable = new Dictionary<String, CultureData>();
487             }
488             else
489             {
490                 // Check the hash table
491                 CultureData retVal;
492                 lock (((ICollection)tempHashTable).SyncRoot)
493                 {
494                     tempHashTable.TryGetValue(hashName, out retVal);
495                 }
496                 if (retVal != null)
497                 {
498                     return retVal;
499                 }
500             }
501
502             // Not found in the hash table, need to see if we can build one that works for us
503             CultureData culture = CreateCultureData(cultureName, useUserOverride);
504             if (culture == null)
505             {
506                 return null;
507             }
508
509             // Found one, add it to the cache
510             lock (((ICollection)tempHashTable).SyncRoot)
511             {
512                 tempHashTable[hashName] = culture;
513             }
514
515             // Copy the hashtable to the corresponding member variables.  This will potentially overwrite
516             // new tables simultaneously created by a new thread, but maximizes thread safety.
517             s_cachedCultures = tempHashTable;
518
519             return culture;
520         }
521
522         private static CultureData CreateCultureData(string cultureName, bool useUserOverride)
523         {
524             CultureData culture = new CultureData();
525             culture.bUseOverrides = useUserOverride;
526             culture.sRealName = cultureName;
527
528             // Ask native code if that one's real
529             if (culture.InitCultureData() == false)
530             {
531                 return null;
532             }
533
534             return culture;
535         }
536
537         private bool InitCultureData()
538         {
539             if (nativeInitCultureData(this) == false)
540             {
541                 return false;
542             }
543             return true;
544         }
545
546         // Cache of regions we've already looked up
547         private static volatile Dictionary<String, CultureData> s_cachedRegions;
548
549         [System.Security.SecurityCritical]  // auto-generated
550         internal static CultureData GetCultureDataForRegion(String cultureName, bool useUserOverride)
551         {
552             // First do a shortcut for Invariant
553             if (String.IsNullOrEmpty(cultureName))
554             {
555                 return CultureData.Invariant;
556             }
557
558             //
559             // First check if GetCultureData() can find it (ie: its a real culture)
560             //
561             CultureData retVal = GetCultureData(cultureName, useUserOverride);
562             if (retVal != null && (retVal.IsNeutralCulture == false)) return retVal;
563
564             //
565             // Not a specific culture, perhaps it's region-only name
566             // (Remember this isn't a core clr path where that's not supported)
567             //
568
569             // If it was neutral remember that so that RegionInfo() can throw the right exception
570             CultureData neutral = retVal;
571
572             // Try the hash table next
573             String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
574             Dictionary<String, CultureData> tempHashTable = s_cachedRegions;
575             if (tempHashTable == null)
576             {
577                 // No table yet, make a new one
578                 tempHashTable = new Dictionary<String, CultureData>();
579             }
580             else
581             {
582                 // Check the hash table
583                 lock (((ICollection)tempHashTable).SyncRoot)
584                 {
585                     tempHashTable.TryGetValue(hashName, out retVal);
586                 }
587                 if (retVal != null)
588                 {
589                     return retVal;
590                 }
591             }
592
593             //
594             // Not found in the hash table, look it up the hard way
595             //
596
597             // If not a valid mapping from the registry we'll have to try the hard coded table
598             if (retVal == null || (retVal.IsNeutralCulture == true))
599             {
600                 // Not a valid mapping, try the hard coded table
601                 if (RegionNames.ContainsKey(cultureName))
602                 {
603                     // Make sure we can get culture data for it
604                     retVal = GetCultureData(RegionNames[cultureName], useUserOverride);
605                 }
606             }
607
608             // If not found in the hard coded table we'll have to find a culture that works for us
609             if (retVal == null || (retVal.IsNeutralCulture == true))
610             {
611                 // Not found in the hard coded table, need to see if we can find a culture that works for us
612                 // Not a real culture name, see if it matches a region name
613                 // (we just return the first culture we match)
614                 CultureInfo[] specifics = SpecificCultures;
615                 for (int i = 0; i < specifics.Length; i++)
616                 {
617                     if (String.Compare(specifics[i].m_cultureData.SREGIONNAME, cultureName, StringComparison.OrdinalIgnoreCase) == 0)
618                     {
619                         // Matched, use this culture
620                         retVal = specifics[i].m_cultureData;
621                         break;
622                     }
623                 }
624             }
625
626             // If we found one we can use, then cash it for next time
627             if (retVal != null && (retVal.IsNeutralCulture == false))
628             {
629                 // first add it to the cache
630                 lock (((ICollection)tempHashTable).SyncRoot)
631                 {
632                     tempHashTable[hashName] = retVal;
633                 }
634
635                 // Copy the hashtable to the corresponding member variables.  This will potentially overwrite
636                 // new tables simultaneously created by a new thread, but maximizes thread safety.
637                 s_cachedRegions = tempHashTable;
638             }
639             else
640             {
641                 // Unable to find a matching culture/region, return null or neutral
642                 // (regionInfo throws a more specific exception on neutrals)
643                 retVal = neutral;
644             }
645
646             // Return the found culture to use, null, or the neutral culture.
647             return retVal;
648         }
649
650 #if FEATURE_USE_LCID
651         // Obtain locale name from LCID
652         // NOTE: This will get neutral names, unlike the OS API
653         [System.Security.SecuritySafeCritical]  // auto-generated
654         [MethodImplAttribute(MethodImplOptions.InternalCall)]
655         internal static extern String LCIDToLocaleName(int lcid);
656
657         // We'd rather people use the named version since this doesn't allow custom locales
658         internal static CultureData GetCultureData(int culture, bool bUseUserOverride)
659         {
660             String localeName = null;
661             CultureData retVal = null;
662
663             if (localeName == null)
664             {
665                 // Convert the lcid to a name, then use that
666                 // Note that this'll return neutral names (unlike Vista native API)
667                 localeName = LCIDToLocaleName(culture);
668             }
669
670             // If its not valid, then throw
671             if (String.IsNullOrEmpty(localeName))
672             {
673                 // Could be valid for Invariant
674                 if (culture == 0x007f)
675                     return Invariant;
676             }
677             else
678             {
679                 // Valid name, use it
680                 retVal = GetCultureData(localeName, bUseUserOverride);
681             }
682
683             // If not successful, throw
684             if (retVal == null)
685                 throw new CultureNotFoundException(
686                     nameof(culture), culture, Environment.GetResourceString("Argument_CultureNotSupported"));
687
688             // Return the one we found
689             return retVal;
690         }
691 #endif
692
693         // Clear our internal caches
694         internal static void ClearCachedData()
695         {
696             s_cachedCultures = null;
697             s_cachedRegions = null;
698             s_replacementCultureNames = null;
699         }
700
701         [System.Security.SecuritySafeCritical]  // auto-generated
702         internal static CultureInfo[] GetCultures(CultureTypes types)
703         {
704             // Disable  warning 618: System.Globalization.CultureTypes.FrameworkCultures' is obsolete
705 #pragma warning disable 618
706             // Validate flags
707             if ((int)types <= 0 || ((int)types & (int)~(CultureTypes.NeutralCultures | CultureTypes.SpecificCultures |
708                                                             CultureTypes.InstalledWin32Cultures | CultureTypes.UserCustomCulture |
709                                                             CultureTypes.ReplacementCultures | CultureTypes.WindowsOnlyCultures |
710                                                             CultureTypes.FrameworkCultures)) != 0)
711             {
712                 throw new ArgumentOutOfRangeException(
713                                 nameof(types),
714                                 String.Format(
715                                     CultureInfo.CurrentCulture,
716                                     Environment.GetResourceString("ArgumentOutOfRange_Range"), CultureTypes.NeutralCultures, CultureTypes.FrameworkCultures));
717             }
718
719             //
720             // CHANGE FROM Whidbey
721             //
722             // We have deprecated CultureTypes.FrameworkCultures.
723             // When this enum is used, we will enumerate Whidbey framework cultures (for compatibility).
724             //
725
726             // We have deprecated CultureTypes.WindowsOnlyCultures.
727             // When this enum is used, we will return an empty array for this enum.
728             if ((types & CultureTypes.WindowsOnlyCultures) != 0)
729             {
730                 // Remove the enum as it is an no-op.
731                 types &= (~CultureTypes.WindowsOnlyCultures);
732             }
733
734             String[] cultureNames = null;
735
736             //
737             // Call nativeEnumCultureNames() to get a string array of culture names based on the specified
738             // enumeration type.
739             //
740             // nativeEnumCultureNames is a QCall.  We need to use a reference to return the string array
741             // allocated from the QCall.  That ref has to be wrapped as object handle.
742             // See vm\qcall.h for details in QCall.
743             //
744
745             if (nativeEnumCultureNames((int)types, JitHelpers.GetObjectHandleOnStack(ref cultureNames)) == 0)
746             {
747                 return new CultureInfo[0];
748             }
749
750             int arrayLength = cultureNames.Length;
751
752             CultureInfo[] cultures = new CultureInfo[arrayLength];
753
754             for (int i = 0; i < cultureNames.Length; i++)
755             {
756                 cultures[i] = new CultureInfo(cultureNames[i]);
757             }
758 #pragma warning restore 618
759
760             return cultures;
761         }
762
763         internal static volatile CultureInfo[] specificCultures;
764
765         private static CultureInfo[] SpecificCultures
766         {
767             get
768             {
769                 if (specificCultures == null)
770                     specificCultures = GetCultures(CultureTypes.SpecificCultures);
771
772                 return specificCultures;
773             }
774         }
775
776         internal bool IsReplacementCulture
777         {
778             get
779             {
780                 return IsReplacementCultureName(this.SNAME);
781             }
782         }
783
784         internal static volatile String[] s_replacementCultureNames;
785
786         ////////////////////////////////////////////////////////////////////////
787         //
788         // Cache for the known replacement cultures.
789         // This is used by CultureInfo.CultureType to check if a culture is a
790         // replacement culture.
791         //
792         ////////////////////////////////////////////////////////////////////////
793
794
795         [System.Security.SecuritySafeCritical]  // auto-generated
796         private static bool IsReplacementCultureName(String name)
797         {
798             Contract.Assert(name != null, "IsReplacementCultureName(): name should not be null");
799             String[] replacementCultureNames = s_replacementCultureNames;
800             if (replacementCultureNames == null)
801             {
802                 if (nativeEnumCultureNames((int)CultureTypes.ReplacementCultures, JitHelpers.GetObjectHandleOnStack(ref replacementCultureNames)) == 0)
803                 {
804                     return false;
805                 }
806
807                 // Even if we don't have any replacement cultures, the returned replacementCultureNames will still an empty string array, not null.
808                 Contract.Assert(name != null, "IsReplacementCultureName(): replacementCultureNames should not be null");
809                 Array.Sort(replacementCultureNames);
810                 s_replacementCultureNames = replacementCultureNames;
811             }
812             return Array.BinarySearch(replacementCultureNames, name) >= 0;
813         }
814
815         ////////////////////////////////////////////////////////////////////////
816         //
817         //  All the accessors
818         //
819         //  Accessors for our data object items
820         //
821         ////////////////////////////////////////////////////////////////////////
822
823         ///////////
824         // Identity //
825         ///////////
826
827         // The real name used to construct the locale (ie: de-DE_phoneb)
828         internal String CultureName
829         {
830             get
831             {
832                 Contract.Assert(this.sRealName != null, "[CultureData.CultureName] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
833                 // since windows doesn't know about zh-CHS and zh-CHT,
834                 // we leave sRealName == zh-Hanx but we still need to
835                 // pretend that it was zh-CHX.
836                 switch (this.sName)
837                 {
838                     case "zh-CHS":
839                     case "zh-CHT":
840                         return this.sName;
841                 }
842                 return this.sRealName;
843             }
844         }
845
846         // Are overrides enabled?
847         internal bool UseUserOverride
848         {
849             get
850             {
851                 return this.bUseOverrides;
852             }
853         }
854
855         // locale name (ie: de-DE, NO sort information)
856         internal String SNAME
857         {
858             get
859             {
860                 //                Contract.Assert(this.sName != null,
861                 //                    "[CultureData.SNAME] Expected this.sName to be populated by COMNlsInfo::nativeInitCultureData already");
862                 if (this.sName == null)
863                 {
864                     this.sName = String.Empty;
865                 }
866                 return this.sName;
867             }
868         }
869
870         // Parent name (which may be a custom locale/culture)
871         internal String SPARENT
872         {
873             [System.Security.SecurityCritical]  // auto-generated
874             get
875             {
876                 if (this.sParent == null)
877                 {
878                     // Ask using the real name, so that we get parents of neutrals
879                     this.sParent = DoGetLocaleInfo(this.sRealName, LOCALE_SPARENT);
880                 }
881                 return this.sParent;
882             }
883         }
884
885         // Localized pretty name for this locale (ie: Inglis (estados Unitos))
886         internal String SLOCALIZEDDISPLAYNAME
887         {
888             [System.Security.SecurityCritical]  // auto-generated
889             get
890             {
891                 if (this.sLocalizedDisplayName == null)
892                 {
893                     // If it hasn't been found (Windows 8 and up), fallback to the system
894                     if (String.IsNullOrEmpty(this.sLocalizedDisplayName))
895                     {
896                         // If its neutral use the language name
897                         if (this.IsNeutralCulture)
898                         {
899                             this.sLocalizedDisplayName = this.SLOCALIZEDLANGUAGE;
900                         }
901                         else
902                         {
903                             // We have to make the neutral distinction in case the OS returns a specific name
904                             if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name))
905                             {
906                                 this.sLocalizedDisplayName = DoGetLocaleInfo(LOCALE_SLOCALIZEDDISPLAYNAME);
907                             }
908                             if (String.IsNullOrEmpty(this.sLocalizedDisplayName))
909                             {
910                                 this.sLocalizedDisplayName = this.SNATIVEDISPLAYNAME;
911                             }
912                         }
913                     }
914                 }
915                 return this.sLocalizedDisplayName;
916             }
917         }
918
919         // English pretty name for this locale (ie: English (United States))
920         internal String SENGDISPLAYNAME
921         {
922             [System.Security.SecurityCritical]  // auto-generated
923             get
924             {
925                 if (this.sEnglishDisplayName == null)
926                 {
927                     // If its neutral use the language name
928                     if (this.IsNeutralCulture)
929                     {
930                         this.sEnglishDisplayName = this.SENGLISHLANGUAGE;
931                     }
932                     else
933                     {
934                         this.sEnglishDisplayName = DoGetLocaleInfo(LOCALE_SENGLISHDISPLAYNAME);
935
936                         // if it isn't found build one:
937                         if (String.IsNullOrEmpty(this.sEnglishDisplayName))
938                         {
939                             // Our existing names mostly look like:
940                             // "English" + "United States" -> "English (United States)"
941                             // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
942                             if (this.SENGLISHLANGUAGE.EndsWith(')'))
943                             {
944                                 // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
945                                 this.sEnglishDisplayName =
946                                     this.SENGLISHLANGUAGE.Substring(0, this.sEnglishLanguage.Length - 1) +
947                                     ", " + this.SENGCOUNTRY + ")";
948                             }
949                             else
950                             {
951                                 // "English" + "United States" -> "English (United States)"
952                                 this.sEnglishDisplayName = this.SENGLISHLANGUAGE + " (" + this.SENGCOUNTRY + ")";
953                             }
954                         }
955                     }
956                 }
957                 return this.sEnglishDisplayName;
958             }
959         }
960
961         // Native pretty name for this locale (ie: Deutsch (Deutschland))
962         internal String SNATIVEDISPLAYNAME
963         {
964             [System.Security.SecurityCritical]  // auto-generated
965             get
966             {
967                 if (this.sNativeDisplayName == null)
968                 {
969                     // If its neutral use the language name
970                     if (this.IsNeutralCulture)
971                     {
972                         this.sNativeDisplayName = this.SNATIVELANGUAGE;
973                     }
974                     else
975                     {
976                         this.sNativeDisplayName = DoGetLocaleInfo(LOCALE_SNATIVEDISPLAYNAME);
977
978                         // if it isn't found build one:
979                         if (String.IsNullOrEmpty(this.sNativeDisplayName))
980                         {
981                             // These should primarily be "Deutsch (Deutschland)" type names
982                             this.sNativeDisplayName = this.SNATIVELANGUAGE + " (" + this.SNATIVECOUNTRY + ")";
983                         }
984                     }
985                 }
986                 return this.sNativeDisplayName;
987             }
988         }
989
990         // The culture name to be used in CultureInfo.CreateSpecificCulture()
991         internal String SSPECIFICCULTURE
992         {
993             get
994             {
995                 // This got populated when ComNlsInfo::nativeInitCultureData told us we had a culture
996                 Contract.Assert(this.sSpecificCulture != null, "[CultureData.SSPECIFICCULTURE] Expected this.sSpecificCulture to be populated by COMNlsInfo::nativeInitCultureData already");
997                 return this.sSpecificCulture;
998             }
999         }
1000
1001         /////////////
1002         // Language //
1003         /////////////
1004
1005         // iso 639 language name, ie: en
1006         internal String SISO639LANGNAME
1007         {
1008             [System.Security.SecurityCritical]  // auto-generated
1009             get
1010             {
1011                 if (this.sISO639Language == null)
1012                 {
1013                     this.sISO639Language = DoGetLocaleInfo(LOCALE_SISO639LANGNAME);
1014                 }
1015                 return this.sISO639Language;
1016             }
1017         }
1018
1019         // iso 639 language name, ie: eng
1020         internal String SISO639LANGNAME2
1021         {
1022             [System.Security.SecurityCritical]  // auto-generated
1023             get
1024             {
1025                 if (this.sISO639Language2 == null)
1026                 {
1027                     this.sISO639Language2 = DoGetLocaleInfo(LOCALE_SISO639LANGNAME2);
1028                 }
1029                 return this.sISO639Language2;
1030             }
1031         }
1032
1033         // abbreviated windows language name (ie: enu) (non-standard, avoid this)
1034         internal String SABBREVLANGNAME
1035         {
1036             [System.Security.SecurityCritical]  // auto-generated
1037             get
1038             {
1039                 if (this.sAbbrevLang == null)
1040                 {
1041                     this.sAbbrevLang = DoGetLocaleInfo(LOCALE_SABBREVLANGNAME);
1042                 }
1043                 return this.sAbbrevLang;
1044             }
1045         }
1046
1047         // Localized name for this language (Windows Only) ie: Inglis
1048         // This is only valid for Windows 8 and higher neutrals:
1049         internal String SLOCALIZEDLANGUAGE
1050         {
1051             [System.Security.SecurityCritical]  // auto-generated
1052             get
1053             {
1054                 if (this.sLocalizedLanguage == null)
1055                 {
1056                     if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name))
1057                     {
1058                         this.sLocalizedLanguage = DoGetLocaleInfo(LOCALE_SLOCALIZEDLANGUAGENAME);
1059                     }
1060                     // Some OS's might not have this resource or LCTYPE
1061                     if (String.IsNullOrEmpty(this.sLocalizedLanguage))
1062                     {
1063                         this.sLocalizedLanguage = SNATIVELANGUAGE;
1064                     }
1065                 }
1066
1067                 return this.sLocalizedLanguage;
1068             }
1069         }
1070
1071         // English name for this language (Windows Only) ie: German
1072         internal String SENGLISHLANGUAGE
1073         {
1074             [System.Security.SecurityCritical]  // auto-generated
1075             get
1076             {
1077                 if (this.sEnglishLanguage == null)
1078                 {
1079                     this.sEnglishLanguage = DoGetLocaleInfo(LOCALE_SENGLISHLANGUAGENAME);
1080                 }
1081                 return this.sEnglishLanguage;
1082             }
1083         }
1084
1085         // Native name of this language (Windows Only) ie: Deutsch
1086         internal String SNATIVELANGUAGE
1087         {
1088             [System.Security.SecurityCritical]  // auto-generated
1089             get
1090             {
1091                 if (this.sNativeLanguage == null)
1092                 {
1093                     {
1094                         this.sNativeLanguage = DoGetLocaleInfo(LOCALE_SNATIVELANGUAGENAME);
1095                     }
1096                 }
1097                 return this.sNativeLanguage;
1098             }
1099         }
1100
1101         ///////////
1102         // Region //
1103         ///////////
1104
1105         // region name (eg US)
1106         internal String SREGIONNAME
1107         {
1108             [System.Security.SecurityCritical]  // auto-generated
1109             get
1110             {
1111                 if (this.sRegionName == null)
1112                 {
1113                     this.sRegionName = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME);
1114                 }
1115                 return this.sRegionName;
1116             }
1117         }
1118
1119         // (user can override) country code (RegionInfo)
1120         internal int ICOUNTRY
1121         {
1122             get
1123             {
1124                 return DoGetLocaleInfoInt(LOCALE_ICOUNTRY);
1125             }
1126         }
1127
1128         // GeoId
1129         internal int IGEOID
1130         {
1131             get
1132             {
1133                 if (this.iGeoId == undef)
1134                 {
1135                     this.iGeoId = DoGetLocaleInfoInt(LOCALE_IGEOID);
1136                 }
1137                 return this.iGeoId;
1138             }
1139         }
1140
1141         // localized name for the country
1142         internal string SLOCALIZEDCOUNTRY
1143         {
1144             [System.Security.SecurityCritical]  // auto-generated
1145             get
1146             {
1147                 if (this.sLocalizedCountry == null)
1148                 {
1149                     // If it hasn't been found (Windows 8 and up), fallback to the system
1150                     if (String.IsNullOrEmpty(this.sLocalizedCountry))
1151                     {
1152                         // We have to make the neutral distinction in case the OS returns a specific name
1153                         if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name))
1154                         {
1155                             this.sLocalizedCountry = DoGetLocaleInfo(LOCALE_SLOCALIZEDCOUNTRYNAME);
1156                         }
1157                         if (String.IsNullOrEmpty(this.sLocalizedDisplayName))
1158                         {
1159                             this.sLocalizedCountry = SNATIVECOUNTRY;
1160                         }
1161                     }
1162                 }
1163                 return this.sLocalizedCountry;
1164             }
1165         }
1166
1167         // english country name (RegionInfo) ie: Germany
1168         internal String SENGCOUNTRY
1169         {
1170             [System.Security.SecurityCritical]  // auto-generated
1171             get
1172             {
1173                 if (this.sEnglishCountry == null)
1174                 {
1175                     this.sEnglishCountry = DoGetLocaleInfo(LOCALE_SENGLISHCOUNTRYNAME);
1176                 }
1177                 return this.sEnglishCountry;
1178             }
1179         }
1180
1181         // native country name (RegionInfo) ie: Deutschland
1182         internal String SNATIVECOUNTRY
1183         {
1184             [System.Security.SecurityCritical]  // auto-generated
1185             get
1186             {
1187                 if (this.sNativeCountry == null)
1188                 {
1189                     this.sNativeCountry = DoGetLocaleInfo(LOCALE_SNATIVECOUNTRYNAME);
1190                 }
1191                 return this.sNativeCountry;
1192             }
1193         }
1194
1195         // ISO 3166 Country Name
1196         internal String SISO3166CTRYNAME
1197         {
1198             [System.Security.SecurityCritical]  // auto-generated
1199             get
1200             {
1201                 if (this.sISO3166CountryName == null)
1202                 {
1203                     this.sISO3166CountryName = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME);
1204                 }
1205                 return this.sISO3166CountryName;
1206             }
1207         }
1208
1209         // ISO 3166 Country Name
1210         internal String SISO3166CTRYNAME2
1211         {
1212             [System.Security.SecurityCritical]  // auto-generated
1213             get
1214             {
1215                 if (this.sISO3166CountryName2 == null)
1216                 {
1217                     this.sISO3166CountryName2 = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME2);
1218                 }
1219                 return this.sISO3166CountryName2;
1220             }
1221         }
1222
1223         // abbreviated Country Name (windows version, non-standard, avoid)
1224         internal String SABBREVCTRYNAME
1225         {
1226             [System.Security.SecurityCritical]  // auto-generated
1227             get
1228             {
1229                 if (this.sAbbrevCountry == null)
1230                 {
1231                     this.sAbbrevCountry = DoGetLocaleInfo(LOCALE_SABBREVCTRYNAME);
1232                 }
1233                 return this.sAbbrevCountry;
1234             }
1235         }
1236
1237         // Default Country
1238         private int IDEFAULTCOUNTRY
1239         {
1240             get
1241             {
1242                 return DoGetLocaleInfoInt(LOCALE_IDEFAULTCOUNTRY);
1243             }
1244         }
1245
1246         // Console fallback name (ie: locale to use for console apps for unicode-only locales)
1247         internal int IINPUTLANGUAGEHANDLE
1248         {
1249             get
1250             {
1251                 if (this.iInputLanguageHandle == undef)
1252                 {
1253                     if (IsSupplementalCustomCulture)
1254                     {
1255                         this.iInputLanguageHandle = 0x0409;
1256                     }
1257                     else
1258                     {
1259                         // Input Language is same as LCID for built-in cultures
1260                         this.iInputLanguageHandle = this.ILANGUAGE;
1261                     }
1262                 }
1263                 return this.iInputLanguageHandle;
1264             }
1265         }
1266
1267         // Console fallback name (ie: locale to use for console apps for unicode-only locales)
1268         internal String SCONSOLEFALLBACKNAME
1269         {
1270             [System.Security.SecurityCritical]  // auto-generated
1271             get
1272             {
1273                 if (this.sConsoleFallbackName == null)
1274                 {
1275                     string consoleFallbackName = DoGetLocaleInfo(LOCALE_SCONSOLEFALLBACKNAME);
1276                     if (consoleFallbackName == "es-ES_tradnl")
1277                     {
1278                         consoleFallbackName = "es-ES";
1279                     }
1280                     this.sConsoleFallbackName = consoleFallbackName;
1281                 }
1282                 return this.sConsoleFallbackName;
1283             }
1284         }
1285
1286         /////////////
1287         // Numbers //
1288         ////////////
1289
1290         //                internal String sPositiveSign            ; // (user can override) positive sign
1291         //                internal String sNegativeSign            ; // (user can override) negative sign
1292         //                internal String[] saNativeDigits         ; // (user can override) native characters for digits 0-9
1293         //                internal int iDigitSubstitution       ; // (user can override) Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused) (Windows Only)
1294         //                internal int iDigits                  ; // (user can override) number of fractional digits
1295         //                internal int iNegativeNumber          ; // (user can override) negative number format
1296
1297         // Leading zeroes
1298         private bool ILEADINGZEROS
1299         {
1300             get
1301             {
1302                 return (DoGetLocaleInfoInt(LOCALE_ILZERO) == 1);
1303             }
1304         }
1305
1306
1307         // (user can override) grouping of digits
1308         internal int[] WAGROUPING
1309         {
1310             [System.Security.SecurityCritical]  // auto-generated
1311             get
1312             {
1313                 if (this.waGrouping == null || UseUserOverride)
1314                 {
1315                     this.waGrouping = ConvertWin32GroupString(DoGetLocaleInfo(LOCALE_SGROUPING));
1316                 }
1317                 return this.waGrouping;
1318             }
1319         }
1320
1321
1322         //                internal String sDecimalSeparator        ; // (user can override) decimal separator
1323         //                internal String sThousandSeparator       ; // (user can override) thousands separator
1324
1325         // Not a Number
1326         internal String SNAN
1327         {
1328             [System.Security.SecurityCritical]  // auto-generated
1329             get
1330             {
1331                 if (this.sNaN == null)
1332                 {
1333                     this.sNaN = DoGetLocaleInfo(LOCALE_SNAN);
1334                 }
1335                 return this.sNaN;
1336             }
1337         }
1338
1339         // + Infinity
1340         internal String SPOSINFINITY
1341         {
1342             [System.Security.SecurityCritical]  // auto-generated
1343             get
1344             {
1345                 if (this.sPositiveInfinity == null)
1346                 {
1347                     this.sPositiveInfinity = DoGetLocaleInfo(LOCALE_SPOSINFINITY);
1348                 }
1349                 return this.sPositiveInfinity;
1350             }
1351         }
1352
1353         // - Infinity
1354         internal String SNEGINFINITY
1355         {
1356             [System.Security.SecurityCritical]  // auto-generated
1357             get
1358             {
1359                 if (this.sNegativeInfinity == null)
1360                 {
1361                     this.sNegativeInfinity = DoGetLocaleInfo(LOCALE_SNEGINFINITY);
1362                 }
1363                 return this.sNegativeInfinity;
1364             }
1365         }
1366
1367
1368         ////////////
1369         // Percent //
1370         ///////////
1371
1372         // Negative Percent (0-3)
1373         internal int INEGATIVEPERCENT
1374         {
1375             get
1376             {
1377                 if (this.iNegativePercent == undef)
1378                 {
1379                     // Note that <= Windows Vista this is synthesized by native code
1380                     this.iNegativePercent = DoGetLocaleInfoInt(LOCALE_INEGATIVEPERCENT);
1381                 }
1382                 return this.iNegativePercent;
1383             }
1384         }
1385
1386         // Positive Percent (0-11)
1387         internal int IPOSITIVEPERCENT
1388         {
1389             get
1390             {
1391                 if (this.iPositivePercent == undef)
1392                 {
1393                     // Note that <= Windows Vista this is synthesized by native code
1394                     this.iPositivePercent = DoGetLocaleInfoInt(LOCALE_IPOSITIVEPERCENT);
1395                 }
1396                 return this.iPositivePercent;
1397             }
1398         }
1399
1400         // Percent (%) symbol
1401         internal String SPERCENT
1402         {
1403             [System.Security.SecurityCritical]  // auto-generated
1404             get
1405             {
1406                 if (this.sPercent == null)
1407                 {
1408                     // Note that <= Windows Vista this is synthesized by native code
1409                     this.sPercent = DoGetLocaleInfo(LOCALE_SPERCENT);
1410                 }
1411                 return this.sPercent;
1412             }
1413         }
1414
1415         // PerMille (‰) symbol
1416         internal String SPERMILLE
1417         {
1418             [System.Security.SecurityCritical]  // auto-generated
1419             get
1420             {
1421                 if (this.sPerMille == null)
1422                 {
1423                     // Note that <= Windows Vista this is synthesized by native code
1424                     this.sPerMille = DoGetLocaleInfo(LOCALE_SPERMILLE);
1425                 }
1426                 return this.sPerMille;
1427             }
1428         }
1429
1430         /////////////
1431         // Currency //
1432         /////////////
1433
1434         // (user can override) local monetary symbol, eg: $
1435         internal String SCURRENCY
1436         {
1437             [System.Security.SecurityCritical]  // auto-generated
1438             get
1439             {
1440                 if (this.sCurrency == null || UseUserOverride)
1441                 {
1442                     this.sCurrency = DoGetLocaleInfo(LOCALE_SCURRENCY);
1443                 }
1444                 return this.sCurrency;
1445             }
1446         }
1447
1448         // international monetary symbol (RegionInfo), eg: USD
1449         internal String SINTLSYMBOL
1450         {
1451             [System.Security.SecurityCritical]  // auto-generated
1452             get
1453             {
1454                 if (this.sIntlMonetarySymbol == null)
1455                 {
1456                     this.sIntlMonetarySymbol = DoGetLocaleInfo(LOCALE_SINTLSYMBOL);
1457                 }
1458                 return this.sIntlMonetarySymbol;
1459             }
1460         }
1461
1462         // English name for this currency (RegionInfo), eg: US Dollar
1463         internal String SENGLISHCURRENCY
1464         {
1465             [System.Security.SecurityCritical]  // auto-generated
1466             get
1467             {
1468                 if (this.sEnglishCurrency == null)
1469                 {
1470                     this.sEnglishCurrency = DoGetLocaleInfo(LOCALE_SENGCURRNAME);
1471                 }
1472                 return this.sEnglishCurrency;
1473             }
1474         }
1475
1476         // Native name for this currency (RegionInfo), eg: Schweiz Frank
1477         internal String SNATIVECURRENCY
1478         {
1479             [System.Security.SecurityCritical]  // auto-generated
1480             get
1481             {
1482                 if (this.sNativeCurrency == null)
1483                 {
1484                     this.sNativeCurrency = DoGetLocaleInfo(LOCALE_SNATIVECURRNAME);
1485                 }
1486                 return this.sNativeCurrency;
1487             }
1488         }
1489
1490         //                internal int iCurrencyDigits          ; // (user can override) # local monetary fractional digits
1491         //                internal int iCurrency                ; // (user can override) positive currency format
1492         //                internal int iNegativeCurrency        ; // (user can override) negative currency format
1493
1494         // (user can override) monetary grouping of digits
1495         internal int[] WAMONGROUPING
1496         {
1497             [System.Security.SecurityCritical]  // auto-generated
1498             get
1499             {
1500                 if (this.waMonetaryGrouping == null || UseUserOverride)
1501                 {
1502                     this.waMonetaryGrouping = ConvertWin32GroupString(DoGetLocaleInfo(LOCALE_SMONGROUPING));
1503                 }
1504                 return this.waMonetaryGrouping;
1505             }
1506         }
1507
1508         //                internal String sMonetaryDecimal         ; // (user can override) monetary decimal separator
1509         //                internal String sMonetaryThousand        ; // (user can override) monetary thousands separator
1510
1511         /////////
1512         // Misc //
1513         /////////
1514
1515         // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
1516         internal int IMEASURE
1517         {
1518             get
1519             {
1520                 if (this.iMeasure == undef || UseUserOverride)
1521                 {
1522                     this.iMeasure = DoGetLocaleInfoInt(LOCALE_IMEASURE);
1523                 }
1524                 return this.iMeasure;
1525             }
1526         }
1527
1528         // (user can override) list Separator
1529         internal String SLIST
1530         {
1531             [System.Security.SecurityCritical]  // auto-generated
1532             get
1533             {
1534                 if (this.sListSeparator == null || UseUserOverride)
1535                 {
1536                     this.sListSeparator = DoGetLocaleInfo(LOCALE_SLIST);
1537                 }
1538                 return this.sListSeparator;
1539             }
1540         }
1541
1542         // Paper size
1543         private int IPAPERSIZE
1544         {
1545             get
1546             {
1547                 return DoGetLocaleInfoInt(LOCALE_IPAPERSIZE);
1548             }
1549         }
1550
1551         ////////////////////////////
1552         // Calendar/Time (Gregorian) //
1553         ////////////////////////////
1554
1555         // (user can override) AM designator
1556         internal String SAM1159
1557         {
1558             [System.Security.SecurityCritical]  // auto-generated
1559             get
1560             {
1561                 if (this.sAM1159 == null || UseUserOverride)
1562                 {
1563                     this.sAM1159 = DoGetLocaleInfo(LOCALE_S1159);
1564                 }
1565                 return this.sAM1159;
1566             }
1567         }
1568
1569         // (user can override) PM designator
1570         internal String SPM2359
1571         {
1572             [System.Security.SecurityCritical]  // auto-generated
1573             get
1574             {
1575                 if (this.sPM2359 == null || UseUserOverride)
1576                 {
1577                     this.sPM2359 = DoGetLocaleInfo(LOCALE_S2359);
1578                 }
1579                 return this.sPM2359;
1580             }
1581         }
1582
1583         // (user can override) time format
1584         internal String[] LongTimes
1585         {
1586             get
1587             {
1588                 if (this.saLongTimes == null || UseUserOverride)
1589                 {
1590                     String[] longTimes = DoEnumTimeFormats();
1591                     if (longTimes == null || longTimes.Length == 0)
1592                     {
1593                         this.saLongTimes = Invariant.saLongTimes;
1594                     }
1595                     else
1596                     {
1597                         this.saLongTimes = longTimes;
1598                     }
1599                 }
1600                 return this.saLongTimes;
1601             }
1602         }
1603
1604         // short time format
1605         // Short times (derived from long times format)
1606         internal String[] ShortTimes
1607         {
1608             get
1609             {
1610                 if (this.saShortTimes == null || UseUserOverride)
1611                 {
1612                     // Try to get the short times from the OS/culture.dll
1613                     String[] shortTimes = DoEnumShortTimeFormats();
1614
1615                     if (shortTimes == null || shortTimes.Length == 0)
1616                     {
1617                         //
1618                         // If we couldn't find short times, then compute them from long times
1619                         // (eg: CORECLR on < Win7 OS & fallback for missing culture.dll)
1620                         //
1621                         shortTimes = DeriveShortTimesFromLong();
1622                     }
1623
1624                     // Found short times, use them
1625                     this.saShortTimes = shortTimes;
1626                 }
1627                 return this.saShortTimes;
1628             }
1629         }
1630
1631         private string[] DeriveShortTimesFromLong()
1632         {
1633             // Our logic is to look for h,H,m,s,t.  If we find an s, then we check the string
1634             // between it and the previous marker, if any.  If its a short, unescaped separator,
1635             // then we don't retain that part.
1636             // We then check after the ss and remove anything before the next h,H,m,t...
1637             string[] shortTimes = new string[LongTimes.Length];
1638
1639             for (int i = 0; i < LongTimes.Length; i++)
1640             {
1641                 shortTimes[i] = StripSecondsFromPattern(LongTimes[i]);
1642             }
1643             return shortTimes;
1644         }
1645
1646         private static string StripSecondsFromPattern(string time)
1647         {
1648             bool bEscape = false;
1649             int iLastToken = -1;
1650
1651             // Find the seconds
1652             for (int j = 0; j < time.Length; j++)
1653             {
1654                 // Change escape mode?
1655                 if (time[j] == '\'')
1656                 {
1657                     // Continue
1658                     bEscape = !bEscape;
1659                     continue;
1660                 }
1661
1662                 // See if there was a single \
1663                 if (time[j] == '\\')
1664                 {
1665                     // Skip next char
1666                     j++;
1667                     continue;
1668                 }
1669
1670                 if (bEscape)
1671                 {
1672                     continue;
1673                 }
1674
1675                 switch (time[j])
1676                 {
1677                     // Check for seconds
1678                     case 's':
1679                         // Found seconds, see if there was something unescaped and short between
1680                         // the last marker and the seconds.  Windows says separator can be a
1681                         // maximum of three characters (without null)
1682                         // If 1st or last characters were ', then ignore it
1683                         if ((j - iLastToken) <= 4 && (j - iLastToken) > 1 &&
1684                             (time[iLastToken + 1] != '\'') &&
1685                             (time[j - 1] != '\''))
1686                         {
1687                             // There was something there we want to remember
1688                             if (iLastToken >= 0)
1689                             {
1690                                 j = iLastToken + 1;
1691                             }
1692                         }
1693
1694                         bool containsSpace;
1695                         int endIndex = GetIndexOfNextTokenAfterSeconds(time, j, out containsSpace);
1696                         StringBuilder sb = new StringBuilder(time.Substring(0, j));
1697                         if (containsSpace)
1698                         {
1699                             sb.Append(' ');
1700                         }
1701                         sb.Append(time.Substring(endIndex));
1702                         time = sb.ToString();
1703                         break;
1704                     case 'm':
1705                     case 'H':
1706                     case 'h':
1707                         iLastToken = j;
1708                         break;
1709                 }
1710             }
1711             return time;
1712         }
1713
1714         private static int GetIndexOfNextTokenAfterSeconds(string time, int index, out bool containsSpace)
1715         {
1716             bool bEscape = false;
1717             containsSpace = false;
1718             for (; index < time.Length; index++)
1719             {
1720                 switch (time[index])
1721                 {
1722                     case '\'':
1723                         bEscape = !bEscape;
1724                         continue;
1725                     case '\\':
1726                         index++;
1727                         if (time[index] == ' ')
1728                         {
1729                             containsSpace = true;
1730                         }
1731                         continue;
1732                     case ' ':
1733                         containsSpace = true;
1734                         break;
1735                     case 't':
1736                     case 'm':
1737                     case 'H':
1738                     case 'h':
1739                         if (bEscape)
1740                         {
1741                             continue;
1742                         }
1743                         return index;
1744                 }
1745             }
1746             containsSpace = false;
1747             return index;
1748         }
1749
1750         // time duration format
1751         internal String[] SADURATION
1752         {
1753             [System.Security.SecurityCritical]  // auto-generated
1754             get
1755             {
1756                 if (this.saDurationFormats == null)
1757                 {
1758                     String durationFormat = DoGetLocaleInfo(LOCALE_SDURATION);
1759                     this.saDurationFormats = new String[] { ReescapeWin32String(durationFormat) };
1760                 }
1761                 return this.saDurationFormats;
1762             }
1763         }
1764
1765         // (user can override) first day of week
1766         internal int IFIRSTDAYOFWEEK
1767         {
1768             get
1769             {
1770                 if (this.iFirstDayOfWeek == undef || UseUserOverride)
1771                 {
1772                     // Have to convert it from windows to .Net formats
1773                     this.iFirstDayOfWeek = ConvertFirstDayOfWeekMonToSun(DoGetLocaleInfoInt(LOCALE_IFIRSTDAYOFWEEK));
1774                 }
1775                 return this.iFirstDayOfWeek;
1776             }
1777         }
1778
1779         // (user can override) first week of year
1780         internal int IFIRSTWEEKOFYEAR
1781         {
1782             get
1783             {
1784                 if (this.iFirstWeekOfYear == undef || UseUserOverride)
1785                 {
1786                     this.iFirstWeekOfYear = DoGetLocaleInfoInt(LOCALE_IFIRSTWEEKOFYEAR);
1787                 }
1788                 return this.iFirstWeekOfYear;
1789             }
1790         }
1791
1792         // (user can override default only) short date format
1793         internal String[] ShortDates(int calendarId)
1794         {
1795             return GetCalendar(calendarId).saShortDates;
1796         }
1797
1798         // (user can override default only) long date format
1799         internal String[] LongDates(int calendarId)
1800         {
1801             return GetCalendar(calendarId).saLongDates;
1802         }
1803
1804         // (user can override) date year/month format.
1805         internal String[] YearMonths(int calendarId)
1806         {
1807             return GetCalendar(calendarId).saYearMonths;
1808         }
1809
1810         // day names
1811         internal string[] DayNames(int calendarId)
1812         {
1813             return GetCalendar(calendarId).saDayNames;
1814         }
1815
1816         // abbreviated day names
1817         internal string[] AbbreviatedDayNames(int calendarId)
1818         {
1819             // Get abbreviated day names for this calendar from the OS if necessary
1820             return GetCalendar(calendarId).saAbbrevDayNames;
1821         }
1822
1823         // The super short day names
1824         internal string[] SuperShortDayNames(int calendarId)
1825         {
1826             return GetCalendar(calendarId).saSuperShortDayNames;
1827         }
1828
1829         // month names
1830         internal string[] MonthNames(int calendarId)
1831         {
1832             return GetCalendar(calendarId).saMonthNames;
1833         }
1834
1835         // Genitive month names
1836         internal string[] GenitiveMonthNames(int calendarId)
1837         {
1838             return GetCalendar(calendarId).saMonthGenitiveNames;
1839         }
1840
1841         // month names
1842         internal string[] AbbreviatedMonthNames(int calendarId)
1843         {
1844             return GetCalendar(calendarId).saAbbrevMonthNames;
1845         }
1846
1847         // Genitive month names
1848         internal string[] AbbreviatedGenitiveMonthNames(int calendarId)
1849         {
1850             return GetCalendar(calendarId).saAbbrevMonthGenitiveNames;
1851         }
1852
1853         // Leap year month names
1854         // Note: This only applies to Hebrew, and it basically adds a "1" to the 6th month name
1855         // the non-leap names skip the 7th name in the normal month name array
1856         internal string[] LeapYearMonthNames(int calendarId)
1857         {
1858             return GetCalendar(calendarId).saLeapYearMonthNames;
1859         }
1860
1861         // month/day format (single string, no override)
1862         internal String MonthDay(int calendarId)
1863         {
1864             return GetCalendar(calendarId).sMonthDay;
1865         }
1866
1867
1868
1869         /////////////
1870         // Calendars //
1871         /////////////
1872
1873         // all available calendar type(s), The first one is the default calendar.
1874         internal int[] CalendarIds
1875         {
1876             get
1877             {
1878                 if (this.waCalendars == null)
1879                 {
1880                     // We pass in an array of ints, and native side fills it up with count calendars.
1881                     // We then have to copy that list to a new array of the right size.
1882                     // Default calendar should be first
1883                     int[] calendarInts = new int[23];
1884                     Contract.Assert(this.sWindowsName != null, "[CultureData.CalendarIds] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
1885                     int count = CalendarData.nativeGetCalendars(this.sWindowsName, this.bUseOverrides, calendarInts);
1886
1887                     // See if we had a calendar to add.
1888                     if (count == 0)
1889                     {
1890                         // Failed for some reason, just grab Gregorian from Invariant
1891                         this.waCalendars = Invariant.waCalendars;
1892                     }
1893                     else
1894                     {
1895                         // The OS may not return calendar 4 for zh-TW, but we've always allowed it.
1896                         if (this.sWindowsName == "zh-TW")
1897                         {
1898                             bool found = false;
1899
1900                             // Do we need to insert calendar 4?
1901                             for (int i = 0; i < count; i++)
1902                             {
1903                                 // Stop if we found calendar four
1904                                 if (calendarInts[i] == Calendar.CAL_TAIWAN)
1905                                 {
1906                                     found = true;
1907                                     break;
1908                                 }
1909                             }
1910
1911                             // If not found then insert it
1912                             if (!found)
1913                             {
1914                                 // Insert it as the 2nd calendar
1915                                 count++;
1916                                 // Copy them from the 2nd position to the end, -1 for skipping 1st, -1 for one being added.
1917                                 Array.Copy(calendarInts, 1, calendarInts, 2, 23 - 1 - 1);
1918                                 calendarInts[1] = Calendar.CAL_TAIWAN;
1919                             }
1920                         }
1921
1922                         // It worked, remember the list
1923                         int[] temp = new int[count];
1924                         Array.Copy(calendarInts, temp, count);
1925
1926                         // Want 1st calendar to be default
1927                         // Prior to Vista the enumeration didn't have default calendar first
1928                         // Only a coreclr concern, culture.dll does the right thing.
1929 #if FEATURE_CORECLR
1930                         if (temp.Length > 1)
1931                         {
1932                             int i = DoGetLocaleInfoInt(LOCALE_ICALENDARTYPE);
1933                             if (temp[1] == i)
1934                             {
1935                                 temp[1] = temp[0];
1936                                 temp[0] = i;
1937                             }
1938                         }
1939 #endif
1940
1941                         this.waCalendars = temp;
1942                     }
1943                 }
1944
1945                 return this.waCalendars;
1946             }
1947         }
1948
1949         // Native calendar names.  index of optional calendar - 1, empty if no optional calendar at that number
1950         internal String CalendarName(int calendarId)
1951         {
1952             // Get the calendar
1953             return GetCalendar(calendarId).sNativeName;
1954         }
1955
1956         internal CalendarData GetCalendar(int calendarId)
1957         {
1958             Contract.Assert(calendarId > 0 && calendarId <= CalendarData.MAX_CALENDARS,
1959                 "[CultureData.GetCalendar] Expect calendarId to be in a valid range");
1960
1961             // arrays are 0 based, calendarIds are 1 based
1962             int calendarIndex = calendarId - 1;
1963
1964             // Have to have calendars
1965             if (calendars == null)
1966             {
1967                 calendars = new CalendarData[CalendarData.MAX_CALENDARS];
1968             }
1969
1970             // we need the following local variable to avoid returning null
1971             // when another thread creates a new array of CalendarData (above)
1972             // right after we insert the newly created CalendarData (below)
1973             CalendarData calendarData = calendars[calendarIndex];
1974             // Make sure that calendar has data
1975             if (calendarData == null || UseUserOverride)
1976             {
1977                 Contract.Assert(this.sWindowsName != null, "[CultureData.GetCalendar] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
1978                 calendarData = new CalendarData(this.sWindowsName, calendarId, this.UseUserOverride);
1979                 calendars[calendarIndex] = calendarData;
1980             }
1981
1982             return calendarData;
1983         }
1984
1985         internal int CurrentEra(int calendarId)
1986         {
1987             return GetCalendar(calendarId).iCurrentEra;
1988         }
1989
1990         ///////////////////
1991         // Text Information //
1992         ///////////////////
1993
1994         // IsRightToLeft
1995         internal bool IsRightToLeft
1996         {
1997             get
1998             {
1999                 // Returns one of the following 4 reading layout values:
2000                 // 0 - Left to right (eg en-US)
2001                 // 1 - Right to left (eg arabic locales)
2002                 // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
2003                 // 3 - Vertical top to bottom with columns proceeding to the right
2004                 return (this.IREADINGLAYOUT == 1);
2005             }
2006         }
2007
2008         // IREADINGLAYOUT
2009         // Returns one of the following 4 reading layout values:
2010         // 0 - Left to right (eg en-US)
2011         // 1 - Right to left (eg arabic locales)
2012         // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
2013         // 3 - Vertical top to bottom with columns proceeding to the right
2014         //
2015         // If exposed as a public API, we'd have an enum with those 4 values
2016         private int IREADINGLAYOUT
2017         {
2018             get
2019             {
2020                 if (this.iReadingLayout == undef)
2021                 {
2022                     Contract.Assert(this.sRealName != null, "[CultureData.IsRightToLeft] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
2023                     this.iReadingLayout = DoGetLocaleInfoInt(LOCALE_IREADINGLAYOUT);
2024                 }
2025
2026                 return (this.iReadingLayout);
2027             }
2028         }
2029
2030         // The TextInfo name never includes that alternate sort and is always specific
2031         // For customs, it uses the SortLocale (since the textinfo is not exposed in Win7)
2032         // en -> en-US
2033         // en-US -> en-US
2034         // fj (custom neutral) -> en-US (assuming that en-US is the sort locale for fj)
2035         // fj_FJ (custom specific) -> en-US (assuming that en-US is the sort locale for fj-FJ)
2036         // es-ES_tradnl -> es-ES
2037         internal String STEXTINFO               // Text info name to use for text information
2038         {
2039             [System.Security.SecuritySafeCritical]
2040             get
2041             {
2042                 if (this.sTextInfo == null)
2043                 {
2044                     // LOCALE_SSORTLOCALE is broken in Win7 for Alt sorts.
2045                     // It is also not supported downlevel without culture.dll.
2046                     if (IsNeutralCulture || IsSupplementalCustomCulture)
2047                     {
2048                         string sortLocale = DoGetLocaleInfo(LOCALE_SSORTLOCALE);
2049                         this.sTextInfo = GetCultureData(sortLocale, bUseOverrides).SNAME;
2050                     }
2051
2052                     if (this.sTextInfo == null)
2053                     {
2054                         this.sTextInfo = this.SNAME; // removes alternate sort
2055                     }
2056                 }
2057
2058                 return this.sTextInfo;
2059             }
2060         }
2061
2062         // Compare info name (including sorting key) to use if custom
2063         internal String SCOMPAREINFO
2064         {
2065             [System.Security.SecuritySafeCritical]
2066             get
2067             {
2068                 if (this.sCompareInfo == null)
2069                 {
2070                     // LOCALE_SSORTLOCALE is broken in Win7 for Alt sorts.
2071                     // It is also not supported downlevel without culture.dll.
2072                     // We really only need it for the custom locale case though
2073                     // since for all other cases, it is the same as sWindowsName
2074                     if (IsSupplementalCustomCulture)
2075                     {
2076                         this.sCompareInfo = DoGetLocaleInfo(LOCALE_SSORTLOCALE);
2077                     }
2078
2079                     if (this.sCompareInfo == null)
2080                     {
2081                         this.sCompareInfo = this.sWindowsName;
2082                     }
2083                 }
2084
2085                 return this.sCompareInfo;
2086             }
2087         }
2088
2089         internal bool IsSupplementalCustomCulture
2090         {
2091             get
2092             {
2093                 return IsCustomCultureId(this.ILANGUAGE);
2094             }
2095         }
2096
2097         // Typical Scripts for this locale (latn;cyrl; etc)
2098
2099         private String SSCRIPTS
2100         {
2101             [System.Security.SecuritySafeCritical]  // auto-generated
2102             get
2103             {
2104                 if (this.sScripts == null)
2105                 {
2106                     this.sScripts = DoGetLocaleInfo(LOCALE_SSCRIPTS);
2107                 }
2108                 return this.sScripts;
2109             }
2110         }
2111
2112         private String SOPENTYPELANGUAGETAG
2113         {
2114             [System.Security.SecuritySafeCritical]  // auto-generated
2115             get
2116             {
2117                 return DoGetLocaleInfo(LOCALE_SOPENTYPELANGUAGETAG);
2118             }
2119         }
2120
2121         private String FONTSIGNATURE
2122         {
2123             [System.Security.SecuritySafeCritical]  // auto-generated
2124             get
2125             {
2126                 if (this.fontSignature == null)
2127                 {
2128                     this.fontSignature = DoGetLocaleInfo(LOCALE_FONTSIGNATURE);
2129                 }
2130                 return this.fontSignature;
2131             }
2132         }
2133
2134         private String SKEYBOARDSTOINSTALL
2135         {
2136             [System.Security.SecuritySafeCritical]  // auto-generated
2137             get
2138             {
2139                 return DoGetLocaleInfo(LOCALE_SKEYBOARDSTOINSTALL);
2140             }
2141         }
2142
2143
2144         internal int IDEFAULTANSICODEPAGE   // default ansi code page ID (ACP)
2145         {
2146             get
2147             {
2148                 if (this.iDefaultAnsiCodePage == undef)
2149                 {
2150                     this.iDefaultAnsiCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTANSICODEPAGE);
2151                 }
2152                 return this.iDefaultAnsiCodePage;
2153             }
2154         }
2155
2156         internal int IDEFAULTOEMCODEPAGE   // default oem code page ID (OCP or OEM)
2157         {
2158             get
2159             {
2160                 if (this.iDefaultOemCodePage == undef)
2161                 {
2162                     this.iDefaultOemCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTCODEPAGE);
2163                 }
2164                 return this.iDefaultOemCodePage;
2165             }
2166         }
2167
2168         internal int IDEFAULTMACCODEPAGE   // default macintosh code page
2169         {
2170             get
2171             {
2172                 if (this.iDefaultMacCodePage == undef)
2173                 {
2174                     this.iDefaultMacCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTMACCODEPAGE);
2175                 }
2176                 return this.iDefaultMacCodePage;
2177             }
2178         }
2179
2180         internal int IDEFAULTEBCDICCODEPAGE   // default EBCDIC code page
2181         {
2182             get
2183             {
2184                 if (this.iDefaultEbcdicCodePage == undef)
2185                 {
2186                     this.iDefaultEbcdicCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTEBCDICCODEPAGE);
2187                 }
2188                 return this.iDefaultEbcdicCodePage;
2189             }
2190         }
2191
2192         // Obtain locale name from LCID
2193         // NOTE: This will get neutral names, unlike the OS API
2194         [System.Security.SecuritySafeCritical]  // auto-generated
2195         [MethodImplAttribute(MethodImplOptions.InternalCall)]
2196         internal static extern int LocaleNameToLCID(String localeName);
2197
2198         // These are desktop only, not coreclr
2199         // locale ID (0409), including sort information
2200         internal int ILANGUAGE
2201         {
2202             get
2203             {
2204                 if (this.iLanguage == 0)
2205                 {
2206                     Contract.Assert(this.sRealName != null, "[CultureData.ILANGUAGE] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already");
2207                     this.iLanguage = LocaleNameToLCID(this.sRealName);
2208                 }
2209                 return this.iLanguage;
2210             }
2211         }
2212
2213         internal bool IsWin32Installed
2214         {
2215             get { return this.bWin32Installed; }
2216         }
2217
2218         internal bool IsFramework
2219         {
2220             get { return this.bFramework; }
2221         }
2222
2223         ////////////////////
2224         // Derived properties //
2225         ////////////////////
2226
2227         internal bool IsNeutralCulture
2228         {
2229             get
2230             {
2231                 // NlsInfo::nativeInitCultureData told us if we're neutral or not
2232                 return this.bNeutral;
2233             }
2234         }
2235
2236         internal bool IsInvariantCulture
2237         {
2238             get
2239             {
2240                 return String.IsNullOrEmpty(this.SNAME);
2241             }
2242         }
2243
2244         // Get an instance of our default calendar
2245         internal Calendar DefaultCalendar
2246         {
2247             get
2248             {
2249                 int defaultCalId = DoGetLocaleInfoInt(LOCALE_ICALENDARTYPE);
2250                 if (defaultCalId == 0)
2251                 {
2252                     defaultCalId = this.CalendarIds[0];
2253                 }
2254
2255                 return CultureInfo.GetCalendarInstance(defaultCalId);
2256             }
2257         }
2258
2259         // All of our era names
2260         internal String[] EraNames(int calendarId)
2261         {
2262             Contract.Assert(calendarId > 0, "[CultureData.saEraNames] Expected Calendar.ID > 0");
2263
2264             return this.GetCalendar(calendarId).saEraNames;
2265         }
2266
2267         internal String[] AbbrevEraNames(int calendarId)
2268         {
2269             Contract.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
2270
2271             return this.GetCalendar(calendarId).saAbbrevEraNames;
2272         }
2273
2274         internal String[] AbbreviatedEnglishEraNames(int calendarId)
2275         {
2276             Contract.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0");
2277
2278             return this.GetCalendar(calendarId).saAbbrevEnglishEraNames;
2279         }
2280
2281         // String array DEFAULTS
2282         // Note: GetDTFIOverrideValues does the user overrides for these, so we don't have to.
2283
2284
2285         // Time separator (derived from time format)
2286         internal String TimeSeparator
2287         {
2288             [System.Security.SecuritySafeCritical]  // auto-generated
2289             get
2290             {
2291                 if (sTimeSeparator == null || UseUserOverride)
2292                 {
2293                     string longTimeFormat = ReescapeWin32String(DoGetLocaleInfo(LOCALE_STIMEFORMAT));
2294                     if (String.IsNullOrEmpty(longTimeFormat))
2295                     {
2296                         longTimeFormat = LongTimes[0];
2297                     }
2298
2299                     // Compute STIME from time format
2300                     sTimeSeparator = GetTimeSeparator(longTimeFormat);
2301                 }
2302                 return sTimeSeparator;
2303             }
2304         }
2305
2306         // Date separator (derived from short date format)
2307         internal String DateSeparator(int calendarId)
2308         {
2309             return GetDateSeparator(ShortDates(calendarId)[0]);
2310         }
2311
2312         //////////////////////////////////////
2313         // Helper Functions to get derived properties //
2314         //////////////////////////////////////
2315
2316         ////////////////////////////////////////////////////////////////////////////
2317         //
2318         // Unescape a NLS style quote string
2319         //
2320         // This removes single quotes:
2321         //      'fred' -> fred
2322         //      'fred -> fred
2323         //      fred' -> fred
2324         //      fred's -> freds
2325         //
2326         // This removes the first \ of escaped characters:
2327         //      fred\'s -> fred's
2328         //      a\\b -> a\b
2329         //      a\b -> ab
2330         //
2331         // We don't build the stringbuilder unless we find a ' or a \.  If we find a ' or a \, we
2332         // always build a stringbuilder because we need to remove the ' or \.
2333         //
2334         ////////////////////////////////////////////////////////////////////////////
2335         static private String UnescapeNlsString(String str, int start, int end)
2336         {
2337             Contract.Requires(str != null);
2338             Contract.Requires(start >= 0);
2339             Contract.Requires(end >= 0);
2340             StringBuilder result = null;
2341
2342             for (int i = start; i < str.Length && i <= end; i++)
2343             {
2344                 switch (str[i])
2345                 {
2346                     case '\'':
2347                         if (result == null)
2348                         {
2349                             result = new StringBuilder(str, start, i - start, str.Length);
2350                         }
2351                         break;
2352                     case '\\':
2353                         if (result == null)
2354                         {
2355                             result = new StringBuilder(str, start, i - start, str.Length);
2356                         }
2357                         ++i;
2358                         if (i < str.Length)
2359                         {
2360                             result.Append(str[i]);
2361                         }
2362                         break;
2363                     default:
2364                         if (result != null)
2365                         {
2366                             result.Append(str[i]);
2367                         }
2368                         break;
2369                 }
2370             }
2371
2372             if (result == null)
2373                 return (str.Substring(start, end - start + 1));
2374
2375             return (result.ToString());
2376         }
2377
2378         ////////////////////////////////////////////////////////////////////////////
2379         //
2380         // Reescape a Win32 style quote string as a NLS+ style quoted string
2381         //
2382         // This is also the escaping style used by custom culture data files
2383         //
2384         // NLS+ uses \ to escape the next character, whether in a quoted string or
2385         // not, so we always have to change \ to \\.
2386         //
2387         // NLS+ uses \' to escape a quote inside a quoted string so we have to change
2388         // '' to \' (if inside a quoted string)
2389         //
2390         // We don't build the stringbuilder unless we find something to change
2391         ////////////////////////////////////////////////////////////////////////////
2392         static internal String ReescapeWin32String(String str)
2393         {
2394             // If we don't have data, then don't try anything
2395             if (str == null)
2396                 return null;
2397
2398             StringBuilder result = null;
2399
2400             bool inQuote = false;
2401             for (int i = 0; i < str.Length; i++)
2402             {
2403                 // Look for quote
2404                 if (str[i] == '\'')
2405                 {
2406                     // Already in quote?
2407                     if (inQuote)
2408                     {
2409                         // See another single quote.  Is this '' of 'fred''s' or '''', or is it an ending quote?
2410                         if (i + 1 < str.Length && str[i + 1] == '\'')
2411                         {
2412                             // Found another ', so we have ''.  Need to add \' instead.
2413                             // 1st make sure we have our stringbuilder
2414                             if (result == null)
2415                                 result = new StringBuilder(str, 0, i, str.Length * 2);
2416
2417                             // Append a \' and keep going (so we don't turn off quote mode)
2418                             result.Append("\\'");
2419                             i++;
2420                             continue;
2421                         }
2422
2423                         // Turning off quote mode, fall through to add it
2424                         inQuote = false;
2425                     }
2426                     else
2427                     {
2428                         // Found beginning quote, fall through to add it
2429                         inQuote = true;
2430                     }
2431                 }
2432                 // Is there a single \ character?
2433                 else if (str[i] == '\\')
2434                 {
2435                     // Found a \, need to change it to \\
2436                     // 1st make sure we have our stringbuilder
2437                     if (result == null)
2438                         result = new StringBuilder(str, 0, i, str.Length * 2);
2439
2440                     // Append our \\ to the string & continue
2441                     result.Append("\\\\");
2442                     continue;
2443                 }
2444
2445                 // If we have a builder we need to add our character
2446                 if (result != null)
2447                     result.Append(str[i]);
2448             }
2449
2450             // Unchanged string? , just return input string
2451             if (result == null)
2452                 return str;
2453
2454             // String changed, need to use the builder
2455             return result.ToString();
2456         }
2457
2458         static internal String[] ReescapeWin32Strings(String[] array)
2459         {
2460             if (array != null)
2461             {
2462                 for (int i = 0; i < array.Length; i++)
2463                 {
2464                     array[i] = ReescapeWin32String(array[i]);
2465                 }
2466             }
2467
2468             return array;
2469         }
2470
2471         // NOTE: this method is used through reflection by System.Globalization.CultureXmlParser.ReadDateElement()
2472         // and breaking changes here will not show up at build time, only at run time.
2473         static private String GetTimeSeparator(String format)
2474         {
2475             // Time format separator (ie: : in 12:39:00)
2476             //
2477             // We calculate this from the provided time format
2478             //
2479
2480             //
2481             //  Find the time separator so that we can pretend we know STIME.
2482             //
2483             return GetSeparator(format, "Hhms");
2484         }
2485
2486         // NOTE: this method is used through reflection by System.Globalization.CultureXmlParser.ReadDateElement()
2487         // and breaking changes here will not show up at build time, only at run time.
2488         static private String GetDateSeparator(String format)
2489         {
2490             // Date format separator (ie: / in 9/1/03)
2491             //
2492             // We calculate this from the provided short date
2493             //
2494
2495             //
2496             //  Find the date separator so that we can pretend we know SDATE.
2497             //
2498             return GetSeparator(format, "dyM");
2499         }
2500
2501         private static string GetSeparator(string format, string timeParts)
2502         {
2503             int index = IndexOfTimePart(format, 0, timeParts);
2504
2505             if (index != -1)
2506             {
2507                 // Found a time part, find out when it changes
2508                 char cTimePart = format[index];
2509
2510                 do
2511                 {
2512                     index++;
2513                 } while (index < format.Length && format[index] == cTimePart);
2514
2515                 int separatorStart = index;
2516
2517                 // Now we need to find the end of the separator
2518                 if (separatorStart < format.Length)
2519                 {
2520                     int separatorEnd = IndexOfTimePart(format, separatorStart, timeParts);
2521                     if (separatorEnd != -1)
2522                     {
2523                         // From [separatorStart, count) is our string, except we need to unescape
2524                         return UnescapeNlsString(format, separatorStart, separatorEnd - 1);
2525                     }
2526                 }
2527             }
2528
2529             return String.Empty;
2530         }
2531
2532         private static int IndexOfTimePart(string format, int startIndex, string timeParts)
2533         {
2534             Contract.Assert(startIndex >= 0, "startIndex cannot be negative");
2535             Contract.Assert(timeParts.IndexOfAny(new char[] { '\'', '\\' }) == -1, "timeParts cannot include quote characters");
2536             bool inQuote = false;
2537             for (int i = startIndex; i < format.Length; ++i)
2538             {
2539                 // See if we have a time Part
2540                 if (!inQuote && timeParts.IndexOf(format[i]) != -1)
2541                 {
2542                     return i;
2543                 }
2544                 switch (format[i])
2545                 {
2546                     case '\\':
2547                         if (i + 1 < format.Length)
2548                         {
2549                             ++i;
2550                             switch (format[i])
2551                             {
2552                                 case '\'':
2553                                 case '\\':
2554                                     break;
2555                                 default:
2556                                     --i; //backup since we will move over this next
2557                                     break;
2558                             }
2559                         }
2560                         break;
2561                     case '\'':
2562                         inQuote = !inQuote;
2563                         break;
2564                 }
2565             }
2566
2567             return -1;
2568         }
2569
2570         [System.Security.SecurityCritical]
2571         string DoGetLocaleInfo(uint lctype)
2572         {
2573             Contract.Assert(this.sWindowsName != null, "[CultureData.DoGetLocaleInfo] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2574             return DoGetLocaleInfo(this.sWindowsName, lctype);
2575         }
2576
2577         // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the
2578         // "windows" name, which can be specific for downlevel (< windows 7) os's.
2579         [System.Security.SecurityCritical]  // auto-generated
2580         string DoGetLocaleInfo(string localeName, uint lctype)
2581         {
2582             // Fix lctype if we don't want overrides
2583             if (!UseUserOverride)
2584             {
2585                 lctype |= LOCALE_NOUSEROVERRIDE;
2586             }
2587
2588             // Ask OS for data
2589             Contract.Assert(localeName != null, "[CultureData.DoGetLocaleInfo] Expected localeName to be not be null");
2590             string result = CultureInfo.nativeGetLocaleInfoEx(localeName, lctype);
2591             if (result == null)
2592             {
2593                 // Failed, just use empty string
2594                 result = String.Empty;
2595             }
2596
2597             return result;
2598         }
2599
2600         int DoGetLocaleInfoInt(uint lctype)
2601         {
2602             // Fix lctype if we don't want overrides
2603             if (!UseUserOverride)
2604             {
2605                 lctype |= LOCALE_NOUSEROVERRIDE;
2606             }
2607
2608             // Ask OS for data, note that we presume it returns success, so we have to know that
2609             // sWindowsName is valid before calling.
2610             Contract.Assert(this.sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2611             int result = CultureInfo.nativeGetLocaleInfoExInt(this.sWindowsName, lctype);
2612
2613             return result;
2614         }
2615
2616         String[] DoEnumTimeFormats()
2617         {
2618             // Note that this gets overrides for us all the time
2619             Contract.Assert(this.sWindowsName != null, "[CultureData.DoEnumTimeFormats] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2620             String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(this.sWindowsName, 0, UseUserOverride));
2621
2622             return result;
2623         }
2624
2625         String[] DoEnumShortTimeFormats()
2626         {
2627             // Note that this gets overrides for us all the time
2628             Contract.Assert(this.sWindowsName != null, "[CultureData.DoEnumShortTimeFormats] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2629             String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(this.sWindowsName, TIME_NOSECONDS, UseUserOverride));
2630
2631             return result;
2632         }
2633
2634         /////////////////
2635         // Static Helpers //
2636         ////////////////
2637         internal static bool IsCustomCultureId(int cultureId)
2638         {
2639             if (cultureId == CultureInfo.LOCALE_CUSTOM_DEFAULT || cultureId == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED)
2640                 return true;
2641
2642             return false;
2643         }
2644
2645         ////////////////////////////////////////////////////////////////////////////
2646         //
2647         // Parameters:
2648         //      calendarValueOnly   Retrieve the values which are affected by the calendar change of DTFI.
2649         //                          This will cause values like longTimePattern not be retrieved since it is
2650         //                          not affected by the Calendar property in DTFI.
2651         //
2652         ////////////////////////////////////////////////////////////////////////////
2653         [System.Security.SecurityCritical]  // auto-generated
2654         internal void GetNFIValues(NumberFormatInfo nfi)
2655         {
2656             if (this.IsInvariantCulture)
2657             {
2658                 nfi.positiveSign = this.sPositiveSign;
2659                 nfi.negativeSign = this.sNegativeSign;
2660
2661                 nfi.nativeDigits = this.saNativeDigits;
2662                 nfi.digitSubstitution = this.iDigitSubstitution;
2663
2664                 nfi.numberGroupSeparator = this.sThousandSeparator;
2665                 nfi.numberDecimalSeparator = this.sDecimalSeparator;
2666                 nfi.numberDecimalDigits = this.iDigits;
2667                 nfi.numberNegativePattern = this.iNegativeNumber;
2668
2669                 nfi.currencySymbol = this.sCurrency;
2670                 nfi.currencyGroupSeparator = this.sMonetaryThousand;
2671                 nfi.currencyDecimalSeparator = this.sMonetaryDecimal;
2672                 nfi.currencyDecimalDigits = this.iCurrencyDigits;
2673                 nfi.currencyNegativePattern = this.iNegativeCurrency;
2674                 nfi.currencyPositivePattern = this.iCurrency;
2675             }
2676             else
2677             {
2678                 //
2679                 // We don't have information for the following four.  All cultures use
2680                 // the same value of the number formatting values.
2681                 //
2682                 // PercentDecimalDigits
2683                 // PercentDecimalSeparator
2684                 // PercentGroupSize
2685                 // PercentGroupSeparator
2686                 //
2687
2688                 //
2689                 // Ask native side for our data.
2690                 //
2691                 Contract.Assert(this.sWindowsName != null, "[CultureData.GetNFIValues] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
2692                 CultureData.nativeGetNumberFormatInfoValues(this.sWindowsName, nfi, UseUserOverride);
2693             }
2694
2695
2696             //
2697             // Gather additional data
2698             //
2699             nfi.numberGroupSizes = this.WAGROUPING;
2700             nfi.currencyGroupSizes = this.WAMONGROUPING;
2701
2702             // prefer the cached value since these do not have user overrides
2703             nfi.percentNegativePattern = this.INEGATIVEPERCENT;
2704             nfi.percentPositivePattern = this.IPOSITIVEPERCENT;
2705             nfi.percentSymbol = this.SPERCENT;
2706             nfi.perMilleSymbol = this.SPERMILLE;
2707
2708             nfi.negativeInfinitySymbol = this.SNEGINFINITY;
2709             nfi.positiveInfinitySymbol = this.SPOSINFINITY;
2710             nfi.nanSymbol = this.SNAN;
2711
2712             //
2713             // We don't have percent values, so use the number values
2714             //
2715             nfi.percentDecimalDigits = nfi.numberDecimalDigits;
2716             nfi.percentDecimalSeparator = nfi.numberDecimalSeparator;
2717             nfi.percentGroupSizes = nfi.numberGroupSizes;
2718             nfi.percentGroupSeparator = nfi.numberGroupSeparator;
2719
2720             //
2721             // Clean up a few odd values
2722             //
2723
2724             // Windows usually returns an empty positive sign, but we like it to be "+"
2725             if (nfi.positiveSign == null || nfi.positiveSign.Length == 0) nfi.positiveSign = "+";
2726
2727             //Special case for Italian.  The currency decimal separator in the control panel is the empty string. When the user
2728             //specifies C4 as the currency format, this results in the number apparently getting multiplied by 10000 because the
2729             //decimal point doesn't show up.  We'll just workaround this here because our default currency format will never use nfi.
2730             if (nfi.currencyDecimalSeparator == null || nfi.currencyDecimalSeparator.Length == 0)
2731             {
2732                 nfi.currencyDecimalSeparator = nfi.numberDecimalSeparator;
2733             }
2734         }
2735
2736         static private int ConvertFirstDayOfWeekMonToSun(int iTemp)
2737         {
2738             // Convert Mon-Sun to Sun-Sat format
2739             iTemp++;
2740             if (iTemp > 6)
2741             {
2742                 // Wrap Sunday and convert invalid data to Sunday
2743                 iTemp = 0;
2744             }
2745             return iTemp;
2746         }
2747
2748         // Helper
2749         // This is ONLY used for caching names and shouldn't be used for anything else
2750         internal static string AnsiToLower(string testString)
2751         {
2752             StringBuilder sb = new StringBuilder(testString.Length);
2753
2754             for (int ich = 0; ich < testString.Length; ich++)
2755             {
2756                 char ch = testString[ich];
2757                 sb.Append(ch <= 'Z' && ch >= 'A' ? (char)(ch - 'A' + 'a') : ch);
2758             }
2759
2760             return (sb.ToString());
2761         }
2762
2763         // If we get a group from windows, then its in 3;0 format with the 0 backwards
2764         // of how NLS+ uses it (ie: if the string has a 0, then the int[] shouldn't and vice versa)
2765         // EXCEPT in the case where the list only contains 0 in which NLS and NLS+ have the same meaning.
2766         static private int[] ConvertWin32GroupString(String win32Str)
2767         {
2768             // None of these cases make any sense
2769             if (win32Str == null || win32Str.Length == 0)
2770             {
2771                 return (new int[] { 3 });
2772             }
2773
2774             if (win32Str[0] == '0')
2775             {
2776                 return (new int[] { 0 });
2777             }
2778
2779             // Since its in n;n;n;n;n format, we can always get the length quickly
2780             int[] values;
2781             if (win32Str[win32Str.Length - 1] == '0')
2782             {
2783                 // Trailing 0 gets dropped. 1;0 -> 1
2784                 values = new int[(win32Str.Length / 2)];
2785             }
2786             else
2787             {
2788                 // Need extra space for trailing zero 1 -> 1;0
2789                 values = new int[(win32Str.Length / 2) + 2];
2790                 values[values.Length - 1] = 0;
2791             }
2792
2793             int i;
2794             int j;
2795             for (i = 0, j = 0; i < win32Str.Length && j < values.Length; i += 2, j++)
2796             {
2797                 // Note that this # shouldn't ever be zero, 'cause 0 is only at end
2798                 // But we'll test because its registry that could be anything
2799                 if (win32Str[i] < '1' || win32Str[i] > '9')
2800                     return new int[] { 3 };
2801
2802                 values[j] = (int)(win32Str[i] - '0');
2803             }
2804
2805             return (values);
2806         }
2807
2808         // LCTYPES for GetLocaleInfo
2809         private const uint LOCALE_NOUSEROVERRIDE = 0x80000000;   // do not use user overrides
2810         private const uint LOCALE_RETURN_NUMBER = 0x20000000;   // return number instead of string
2811
2812         // Modifier for genitive names
2813         private const uint LOCALE_RETURN_GENITIVE_NAMES = 0x10000000;   //Flag to return the Genitive forms of month names
2814
2815         //
2816         //  The following LCTypes are mutually exclusive in that they may NOT
2817         //  be used in combination with each other.
2818         //
2819
2820         //
2821         // These are the various forms of the name of the locale:
2822         //
2823         private const uint LOCALE_SLOCALIZEDDISPLAYNAME = 0x00000002;   // localized name of locale, eg "German (Germany)" in UI language
2824         private const uint LOCALE_SENGLISHDISPLAYNAME = 0x00000072;   // Display name (language + country usually) in English, eg "German (Germany)"
2825         private const uint LOCALE_SNATIVEDISPLAYNAME = 0x00000073;   // Display name in native locale language, eg "Deutsch (Deutschland)
2826
2827         private const uint LOCALE_SLOCALIZEDLANGUAGENAME = 0x0000006f;   // Language Display Name for a language, eg "German" in UI language
2828         private const uint LOCALE_SENGLISHLANGUAGENAME = 0x00001001;   // English name of language, eg "German"
2829         private const uint LOCALE_SNATIVELANGUAGENAME = 0x00000004;   // native name of language, eg "Deutsch"
2830
2831         private const uint LOCALE_SLOCALIZEDCOUNTRYNAME = 0x00000006;   // localized name of country, eg "Germany" in UI language
2832         private const uint LOCALE_SENGLISHCOUNTRYNAME = 0x00001002;   // English name of country, eg "Germany"
2833         private const uint LOCALE_SNATIVECOUNTRYNAME = 0x00000008;   // native name of country, eg "Deutschland"
2834
2835
2836         //        private const uint LOCALE_ILANGUAGE              =0x00000001;   // language id // Don't use, use NewApis::LocaleNameToLCID instead (GetLocaleInfo doesn't return neutrals)
2837
2838         //        private const uint LOCALE_SLANGUAGE              =LOCALE_SLOCALIZEDDISPLAYNAME;   // localized name of language (use LOCALE_SLOCALIZEDDISPLAYNAME instead)
2839         //        private const uint LOCALE_SENGLANGUAGE           =LOCALE_SENGLISHLANGUAGENAME;   // English name of language (use LOCALE_SENGLISHLANGUAGENAME instead)
2840         private const uint LOCALE_SABBREVLANGNAME = 0x00000003;   // abbreviated language name
2841         //        private const uint LOCALE_SNATIVELANGNAME        =LOCALE_SNATIVELANGUAGENAME;   // native name of language (use LOCALE_SNATIVELANGUAGENAME instead)
2842
2843         private const uint LOCALE_ICOUNTRY = 0x00000005;   // country code
2844         //        private const uint LOCALE_SCOUNTRY               =LOCALE_SLOCALIZEDCOUNTRYNAME;   // localized name of country (use LOCALE_SLOCALIZEDCOUNTRYNAME instead)
2845         //        private const uint LOCALE_SENGCOUNTRY            =LOCALE_SENGLISHCOUNTRYNAME;   // English name of country (use LOCALE_SENGLISHCOUNTRYNAME instead)
2846         private const uint LOCALE_SABBREVCTRYNAME = 0x00000007;   // abbreviated country name
2847         //        private const uint LOCALE_SNATIVECTRYNAME        =LOCALE_SNATIVECOUNTRYNAME;   // native name of country ( use LOCALE_SNATIVECOUNTRYNAME instead)
2848         private const uint LOCALE_IGEOID = 0x0000005B;   // geographical location id
2849
2850         private const uint LOCALE_IDEFAULTLANGUAGE = 0x00000009;   // default language id
2851         private const uint LOCALE_IDEFAULTCOUNTRY = 0x0000000A;   // default country code
2852         private const uint LOCALE_IDEFAULTCODEPAGE = 0x0000000B;   // default oem code page
2853         private const uint LOCALE_IDEFAULTANSICODEPAGE = 0x00001004;   // default ansi code page
2854         private const uint LOCALE_IDEFAULTMACCODEPAGE = 0x00001011;   // default mac code page
2855
2856         private const uint LOCALE_SLIST = 0x0000000C;   // list item separator
2857         private const uint LOCALE_IMEASURE = 0x0000000D;   // 0 = metric, 1 = US
2858
2859         private const uint LOCALE_SDECIMAL = 0x0000000E;   // decimal separator
2860         private const uint LOCALE_STHOUSAND = 0x0000000F;   // thousand separator
2861         private const uint LOCALE_SGROUPING = 0x00000010;   // digit grouping
2862         private const uint LOCALE_IDIGITS = 0x00000011;   // number of fractional digits
2863         private const uint LOCALE_ILZERO = 0x00000012;   // leading zeros for decimal
2864         private const uint LOCALE_INEGNUMBER = 0x00001010;   // negative number mode
2865         private const uint LOCALE_SNATIVEDIGITS = 0x00000013;   // native digits for 0-9
2866
2867         private const uint LOCALE_SCURRENCY = 0x00000014;   // local monetary symbol
2868         private const uint LOCALE_SINTLSYMBOL = 0x00000015;   // uintl monetary symbol
2869         private const uint LOCALE_SMONDECIMALSEP = 0x00000016;   // monetary decimal separator
2870         private const uint LOCALE_SMONTHOUSANDSEP = 0x00000017;   // monetary thousand separator
2871         private const uint LOCALE_SMONGROUPING = 0x00000018;   // monetary grouping
2872         private const uint LOCALE_ICURRDIGITS = 0x00000019;   // # local monetary digits
2873         private const uint LOCALE_IINTLCURRDIGITS = 0x0000001A;   // # uintl monetary digits
2874         private const uint LOCALE_ICURRENCY = 0x0000001B;   // positive currency mode
2875         private const uint LOCALE_INEGCURR = 0x0000001C;   // negative currency mode
2876
2877         private const uint LOCALE_SDATE = 0x0000001D;   // date separator (derived from LOCALE_SSHORTDATE, use that instead)
2878         private const uint LOCALE_STIME = 0x0000001E;   // time separator (derived from LOCALE_STIMEFORMAT, use that instead)
2879         private const uint LOCALE_SSHORTDATE = 0x0000001F;   // short date format string
2880         private const uint LOCALE_SLONGDATE = 0x00000020;   // long date format string
2881         private const uint LOCALE_STIMEFORMAT = 0x00001003;   // time format string
2882         private const uint LOCALE_IDATE = 0x00000021;   // short date format ordering (derived from LOCALE_SSHORTDATE, use that instead)
2883         private const uint LOCALE_ILDATE = 0x00000022;   // long date format ordering (derived from LOCALE_SLONGDATE, use that instead)
2884         private const uint LOCALE_ITIME = 0x00000023;   // time format specifier (derived from LOCALE_STIMEFORMAT, use that instead)
2885         private const uint LOCALE_ITIMEMARKPOSN = 0x00001005;   // time marker position (derived from LOCALE_STIMEFORMAT, use that instead)
2886         private const uint LOCALE_ICENTURY = 0x00000024;   // century format specifier (short date, LOCALE_SSHORTDATE is preferred)
2887         private const uint LOCALE_ITLZERO = 0x00000025;   // leading zeros in time field (derived from LOCALE_STIMEFORMAT, use that instead)
2888         private const uint LOCALE_IDAYLZERO = 0x00000026;   // leading zeros in day field (short date, LOCALE_SSHORTDATE is preferred)
2889         private const uint LOCALE_IMONLZERO = 0x00000027;   // leading zeros in month field (short date, LOCALE_SSHORTDATE is preferred)
2890         private const uint LOCALE_S1159 = 0x00000028;   // AM designator
2891         private const uint LOCALE_S2359 = 0x00000029;   // PM designator
2892
2893         private const uint LOCALE_ICALENDARTYPE = 0x00001009;   // type of calendar specifier
2894         private const uint LOCALE_IOPTIONALCALENDAR = 0x0000100B;   // additional calendar types specifier
2895         private const uint LOCALE_IFIRSTDAYOFWEEK = 0x0000100C;   // first day of week specifier
2896         private const uint LOCALE_IFIRSTWEEKOFYEAR = 0x0000100D;   // first week of year specifier
2897
2898         private const uint LOCALE_SDAYNAME1 = 0x0000002A;   // long name for Monday
2899         private const uint LOCALE_SDAYNAME2 = 0x0000002B;   // long name for Tuesday
2900         private const uint LOCALE_SDAYNAME3 = 0x0000002C;   // long name for Wednesday
2901         private const uint LOCALE_SDAYNAME4 = 0x0000002D;   // long name for Thursday
2902         private const uint LOCALE_SDAYNAME5 = 0x0000002E;   // long name for Friday
2903         private const uint LOCALE_SDAYNAME6 = 0x0000002F;   // long name for Saturday
2904         private const uint LOCALE_SDAYNAME7 = 0x00000030;   // long name for Sunday
2905         private const uint LOCALE_SABBREVDAYNAME1 = 0x00000031;   // abbreviated name for Monday
2906         private const uint LOCALE_SABBREVDAYNAME2 = 0x00000032;   // abbreviated name for Tuesday
2907         private const uint LOCALE_SABBREVDAYNAME3 = 0x00000033;   // abbreviated name for Wednesday
2908         private const uint LOCALE_SABBREVDAYNAME4 = 0x00000034;   // abbreviated name for Thursday
2909         private const uint LOCALE_SABBREVDAYNAME5 = 0x00000035;   // abbreviated name for Friday
2910         private const uint LOCALE_SABBREVDAYNAME6 = 0x00000036;   // abbreviated name for Saturday
2911         private const uint LOCALE_SABBREVDAYNAME7 = 0x00000037;   // abbreviated name for Sunday
2912         private const uint LOCALE_SMONTHNAME1 = 0x00000038;   // long name for January
2913         private const uint LOCALE_SMONTHNAME2 = 0x00000039;   // long name for February
2914         private const uint LOCALE_SMONTHNAME3 = 0x0000003A;   // long name for March
2915         private const uint LOCALE_SMONTHNAME4 = 0x0000003B;   // long name for April
2916         private const uint LOCALE_SMONTHNAME5 = 0x0000003C;   // long name for May
2917         private const uint LOCALE_SMONTHNAME6 = 0x0000003D;   // long name for June
2918         private const uint LOCALE_SMONTHNAME7 = 0x0000003E;   // long name for July
2919         private const uint LOCALE_SMONTHNAME8 = 0x0000003F;   // long name for August
2920         private const uint LOCALE_SMONTHNAME9 = 0x00000040;   // long name for September
2921         private const uint LOCALE_SMONTHNAME10 = 0x00000041;   // long name for October
2922         private const uint LOCALE_SMONTHNAME11 = 0x00000042;   // long name for November
2923         private const uint LOCALE_SMONTHNAME12 = 0x00000043;   // long name for December
2924         private const uint LOCALE_SMONTHNAME13 = 0x0000100E;   // long name for 13th month (if exists)
2925         private const uint LOCALE_SABBREVMONTHNAME1 = 0x00000044;   // abbreviated name for January
2926         private const uint LOCALE_SABBREVMONTHNAME2 = 0x00000045;   // abbreviated name for February
2927         private const uint LOCALE_SABBREVMONTHNAME3 = 0x00000046;   // abbreviated name for March
2928         private const uint LOCALE_SABBREVMONTHNAME4 = 0x00000047;   // abbreviated name for April
2929         private const uint LOCALE_SABBREVMONTHNAME5 = 0x00000048;   // abbreviated name for May
2930         private const uint LOCALE_SABBREVMONTHNAME6 = 0x00000049;   // abbreviated name for June
2931         private const uint LOCALE_SABBREVMONTHNAME7 = 0x0000004A;   // abbreviated name for July
2932         private const uint LOCALE_SABBREVMONTHNAME8 = 0x0000004B;   // abbreviated name for August
2933         private const uint LOCALE_SABBREVMONTHNAME9 = 0x0000004C;   // abbreviated name for September
2934         private const uint LOCALE_SABBREVMONTHNAME10 = 0x0000004D;   // abbreviated name for October
2935         private const uint LOCALE_SABBREVMONTHNAME11 = 0x0000004E;   // abbreviated name for November
2936         private const uint LOCALE_SABBREVMONTHNAME12 = 0x0000004F;   // abbreviated name for December
2937         private const uint LOCALE_SABBREVMONTHNAME13 = 0x0000100F;   // abbreviated name for 13th month (if exists)
2938
2939         private const uint LOCALE_SPOSITIVESIGN = 0x00000050;   // positive sign
2940         private const uint LOCALE_SNEGATIVESIGN = 0x00000051;   // negative sign
2941         private const uint LOCALE_IPOSSIGNPOSN = 0x00000052;   // positive sign position (derived from INEGCURR)
2942         private const uint LOCALE_INEGSIGNPOSN = 0x00000053;   // negative sign position (derived from INEGCURR)
2943         private const uint LOCALE_IPOSSYMPRECEDES = 0x00000054;   // mon sym precedes pos amt (derived from ICURRENCY)
2944         private const uint LOCALE_IPOSSEPBYSPACE = 0x00000055;   // mon sym sep by space from pos amt (derived from ICURRENCY)
2945         private const uint LOCALE_INEGSYMPRECEDES = 0x00000056;   // mon sym precedes neg amt (derived from INEGCURR)
2946         private const uint LOCALE_INEGSEPBYSPACE = 0x00000057;   // mon sym sep by space from neg amt (derived from INEGCURR)
2947
2948         private const uint LOCALE_FONTSIGNATURE = 0x00000058;   // font signature
2949         private const uint LOCALE_SISO639LANGNAME = 0x00000059;   // ISO abbreviated language name
2950         private const uint LOCALE_SISO3166CTRYNAME = 0x0000005A;   // ISO abbreviated country name
2951
2952         private const uint LOCALE_IDEFAULTEBCDICCODEPAGE = 0x00001012;   // default ebcdic code page
2953         private const uint LOCALE_IPAPERSIZE = 0x0000100A;   // 1 = letter, 5 = legal, 8 = a3, 9 = a4
2954         private const uint LOCALE_SENGCURRNAME = 0x00001007;   // english name of currency
2955         private const uint LOCALE_SNATIVECURRNAME = 0x00001008;   // native name of currency
2956         private const uint LOCALE_SYEARMONTH = 0x00001006;   // year month format string
2957         private const uint LOCALE_SSORTNAME = 0x00001013;   // sort name
2958         private const uint LOCALE_IDIGITSUBSTITUTION = 0x00001014;   // 0 = context, 1 = none, 2 = national
2959
2960         private const uint LOCALE_SNAME = 0x0000005c;   // locale name (with sort info) (ie: de-DE_phoneb)
2961         private const uint LOCALE_SDURATION = 0x0000005d;   // time duration format
2962         private const uint LOCALE_SKEYBOARDSTOINSTALL = 0x0000005e;   // (windows only) keyboards to install
2963         private const uint LOCALE_SSHORTESTDAYNAME1 = 0x00000060;   // Shortest day name for Monday
2964         private const uint LOCALE_SSHORTESTDAYNAME2 = 0x00000061;   // Shortest day name for Tuesday
2965         private const uint LOCALE_SSHORTESTDAYNAME3 = 0x00000062;   // Shortest day name for Wednesday
2966         private const uint LOCALE_SSHORTESTDAYNAME4 = 0x00000063;   // Shortest day name for Thursday
2967         private const uint LOCALE_SSHORTESTDAYNAME5 = 0x00000064;   // Shortest day name for Friday
2968         private const uint LOCALE_SSHORTESTDAYNAME6 = 0x00000065;   // Shortest day name for Saturday
2969         private const uint LOCALE_SSHORTESTDAYNAME7 = 0x00000066;   // Shortest day name for Sunday
2970         private const uint LOCALE_SISO639LANGNAME2 = 0x00000067;   // 3 character ISO abbreviated language name
2971         private const uint LOCALE_SISO3166CTRYNAME2 = 0x00000068;   // 3 character ISO country name
2972         private const uint LOCALE_SNAN = 0x00000069;   // Not a Number
2973         private const uint LOCALE_SPOSINFINITY = 0x0000006a;   // + Infinity
2974         private const uint LOCALE_SNEGINFINITY = 0x0000006b;   // - Infinity
2975         private const uint LOCALE_SSCRIPTS = 0x0000006c;   // Typical scripts in the locale
2976         private const uint LOCALE_SPARENT = 0x0000006d;   // Fallback name for resources
2977         private const uint LOCALE_SCONSOLEFALLBACKNAME = 0x0000006e;   // Fallback name for within the console
2978         //        private const uint LOCALE_SLANGDISPLAYNAME       =LOCALE_SLOCALIZEDLANGUAGENAME;   // Language Display Name for a language (use LOCALE_SLOCALIZEDLANGUAGENAME instead)
2979
2980         // Windows 7 LCTYPES
2981         private const uint LOCALE_IREADINGLAYOUT = 0x00000070;   // Returns one of the following 4 reading layout values:
2982         // 0 - Left to right (eg en-US)
2983         // 1 - Right to left (eg arabic locales)
2984         // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
2985         // 3 - Vertical top to bottom with columns proceeding to the right
2986         private const uint LOCALE_INEUTRAL = 0x00000071;   // Returns 0 for specific cultures, 1 for neutral cultures.
2987         private const uint LOCALE_INEGATIVEPERCENT = 0x00000074;   // Returns 0-11 for the negative percent format
2988         private const uint LOCALE_IPOSITIVEPERCENT = 0x00000075;   // Returns 0-3 for the positive percent formatIPOSITIVEPERCENT
2989         private const uint LOCALE_SPERCENT = 0x00000076;   // Returns the percent symbol
2990         private const uint LOCALE_SPERMILLE = 0x00000077;   // Returns the permille (U+2030) symbol
2991         private const uint LOCALE_SMONTHDAY = 0x00000078;   // Returns the preferred month/day format
2992         private const uint LOCALE_SSHORTTIME = 0x00000079;   // Returns the preferred short time format (ie: no seconds, just h:mm)
2993         private const uint LOCALE_SOPENTYPELANGUAGETAG = 0x0000007a;   // Open type language tag, eg: "latn" or "dflt"
2994         private const uint LOCALE_SSORTLOCALE = 0x0000007b;   // Name of locale to use for sorting/collation/casing behavior.
2995
2996         // Time formats enumerations
2997         internal const uint TIME_NOSECONDS = 0x00000002;   // Don't use seconds (get short time format for enumtimeformats on win7+)
2998
2999         // Get our initial minimal culture data (name, parent, etc.)
3000         [System.Security.SecuritySafeCritical]  // auto-generated
3001         [MethodImplAttribute(MethodImplOptions.InternalCall)]
3002         internal static extern bool nativeInitCultureData(CultureData cultureData);
3003
3004         // Grab the NumberFormatInfo data
3005         [System.Security.SecuritySafeCritical]  // auto-generated
3006         [MethodImplAttribute(MethodImplOptions.InternalCall)]
3007         internal static extern bool nativeGetNumberFormatInfoValues(String localeName, NumberFormatInfo nfi, bool useUserOverride);
3008
3009         [System.Security.SecuritySafeCritical]  // auto-generated
3010         [MethodImplAttribute(MethodImplOptions.InternalCall)]
3011         private static extern String[] nativeEnumTimeFormats(String localeName, uint dwFlags, bool useUserOverride);
3012
3013         [System.Security.SecurityCritical]  // auto-generated
3014         [SuppressUnmanagedCodeSecurityAttribute()]
3015         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
3016         internal static extern int nativeEnumCultureNames(int cultureTypes, ObjectHandleOnStack retStringArray);
3017     }
3018 }