bb6e8f900172e2f6774e5cf4d81797f93cfc973e
[platform/upstream/coreclr.git] / src / mscorlib / src / System / Globalization / CultureInfo.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 ////////////////////////////////////////////////////////////////////////////
6 //
7 //
8 //
9 //  Purpose:  This class represents the software preferences of a particular
10 //            culture or community.  It includes information such as the
11 //            language, writing system, and a calendar used by the culture
12 //            as well as methods for common operations such as printing
13 //            dates and sorting strings.
14 //
15 //
16 //
17 //  !!!! NOTE WHEN CHANGING THIS CLASS !!!!
18 //
19 //  If adding or removing members to this class, please update CultureInfoBaseObject
20 //  in ndp/clr/src/vm/object.h. Note, the "actual" layout of the class may be
21 //  different than the order in which members are declared. For instance, all
22 //  reference types will come first in the class before value types (like ints, bools, etc)
23 //  regardless of the order in which they are declared. The best way to see the
24 //  actual order of the class is to do a !dumpobj on an instance of the managed
25 //  object inside of the debugger.
26 //
27 ////////////////////////////////////////////////////////////////////////////
28
29 using System.Collections.Generic;
30 using System.Diagnostics;
31 using System.Threading;
32
33 namespace System.Globalization
34 {
35 #if CORECLR
36     using StringCultureInfoDictionary = Dictionary<string, CultureInfo>;
37     using StringLcidDictionary = Dictionary<int, CultureInfo>;
38     
39     using Lock = Object;
40 #else
41     using StringCultureInfoDictionary = LowLevelDictionary<string, CultureInfo>;
42     using StringLcidDictionary = LowLevelDictionary<int, CultureInfo>;
43 #endif
44
45     public partial class CultureInfo : IFormatProvider, ICloneable
46     {
47         //--------------------------------------------------------------------//
48         //                        Internal Information                        //
49         //--------------------------------------------------------------------//
50
51         // We use an RFC4646 type string to construct CultureInfo.
52         // This string is stored in _name and is authoritative.
53         // We use the _cultureData to get the data for our object
54
55         private bool _isReadOnly;
56         private CompareInfo compareInfo;
57         private TextInfo textInfo;
58         internal NumberFormatInfo numInfo;
59         internal DateTimeFormatInfo dateTimeInfo;
60         private Calendar calendar;
61         //
62         // The CultureData instance that we are going to read data from.
63         // For supported culture, this will be the CultureData instance that read data from mscorlib assembly.
64         // For customized culture, this will be the CultureData instance that read data from user customized culture binary file.
65         //
66         internal CultureData _cultureData;
67
68         internal bool _isInherited;
69
70         private CultureInfo _consoleFallbackCulture;
71
72         // Names are confusing.  Here are 3 names we have:
73         //
74         //  new CultureInfo()   _name          _nonSortName    _sortName
75         //      en-US           en-US           en-US           en-US
76         //      de-de_phoneb    de-DE_phoneb    de-DE           de-DE_phoneb
77         //      fj-fj (custom)  fj-FJ           fj-FJ           en-US (if specified sort is en-US)
78         //      en              en              
79         //
80         // Note that in Silverlight we ask the OS for the text and sort behavior, so the 
81         // textinfo and compareinfo names are the same as the name
82
83         // This has a de-DE, de-DE_phoneb or fj-FJ style name
84         internal string _name;
85
86         // This will hold the non sorting name to be returned from CultureInfo.Name property.
87         // This has a de-DE style name even for de-DE_phoneb type cultures
88         private string _nonSortName;
89
90         // This will hold the sorting name to be returned from CultureInfo.SortName property.
91         // This might be completely unrelated to the culture name if a custom culture.  Ie en-US for fj-FJ.
92         // Otherwise its the sort name, ie: de-DE or de-DE_phoneb
93         private string _sortName;
94
95         //--------------------------------------------------------------------//
96         //
97         // Static data members
98         //
99         //--------------------------------------------------------------------//
100
101         //Get the current user default culture.  This one is almost always used, so we create it by default.
102         private static volatile CultureInfo s_userDefaultCulture;
103
104         //The culture used in the user interface. This is mostly used to load correct localized resources.
105         private static volatile CultureInfo s_userDefaultUICulture;
106         //
107         // All of the following will be created on demand.
108         //
109
110         // WARNING: We allow diagnostic tools to directly inspect these three members (s_InvariantCultureInfo, s_DefaultThreadCurrentUICulture and s_DefaultThreadCurrentCulture)
111         // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. 
112         // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools. 
113         // Get in touch with the diagnostics team if you have questions.
114
115         //The Invariant culture;
116         private static volatile CultureInfo s_InvariantCultureInfo;
117
118         //These are defaults that we use if a thread has not opted into having an explicit culture
119         private static volatile CultureInfo s_DefaultThreadCurrentUICulture;
120         private static volatile CultureInfo s_DefaultThreadCurrentCulture;
121
122         internal static AsyncLocal<CultureInfo> s_asyncLocalCurrentCulture; 
123         internal static AsyncLocal<CultureInfo> s_asyncLocalCurrentUICulture;
124
125         internal static void AsyncLocalSetCurrentCulture(AsyncLocalValueChangedArgs<CultureInfo> args)
126         {
127             Thread.m_CurrentCulture = args.CurrentValue;
128         }
129
130         internal static void AsyncLocalSetCurrentUICulture(AsyncLocalValueChangedArgs<CultureInfo> args)
131         {
132             Thread.m_CurrentUICulture = args.CurrentValue;
133         }
134
135         private static readonly Lock _lock = new Lock();
136         private static volatile StringCultureInfoDictionary s_NameCachedCultures;
137         private static volatile StringLcidDictionary s_LcidCachedCultures;       
138
139         //The parent culture.
140         private CultureInfo _parent;
141
142         // LOCALE constants of interest to us internally and privately for LCID functions
143         // (ie: avoid using these and use names if possible)
144         internal const int LOCALE_NEUTRAL        = 0x0000;
145         private  const int LOCALE_USER_DEFAULT   = 0x0400;
146         private  const int LOCALE_SYSTEM_DEFAULT = 0x0800;
147         internal const int LOCALE_CUSTOM_UNSPECIFIED = 0x1000;
148         internal const int LOCALE_CUSTOM_DEFAULT  = 0x0c00;
149         internal const int LOCALE_INVARIANT       = 0x007F;
150
151         //
152         // The CultureData  instance that reads the data provided by our CultureData class.
153         //
154         // Using a field initializer rather than a static constructor so that the whole class can be lazy
155         // init.
156         private static readonly bool init = Init();
157         private static bool Init()
158         {
159             if (s_InvariantCultureInfo == null)
160             {
161                 CultureInfo temp = new CultureInfo("", false);
162                 temp._isReadOnly = true;
163                 s_InvariantCultureInfo = temp;
164             }
165
166             s_userDefaultCulture = GetUserDefaultCulture();
167             s_userDefaultUICulture = GetUserDefaultUILanguage();
168             return true;
169         }
170
171         ////////////////////////////////////////////////////////////////////////
172         //
173         //  CultureInfo Constructors
174         //
175         ////////////////////////////////////////////////////////////////////////
176
177
178         public CultureInfo(String name)
179             : this(name, true)
180         {
181         }
182
183
184         public CultureInfo(String name, bool useUserOverride)
185         {
186             if (name == null)
187             {
188                 throw new ArgumentNullException(nameof(name),
189                     SR.ArgumentNull_String);
190             }
191
192             InitializeFromName(name, useUserOverride);
193         }
194
195         private CultureInfo(CultureData cultureData)
196         {
197             Debug.Assert(cultureData != null);
198             _cultureData = cultureData;
199             _name = cultureData.CultureName;
200             _isInherited = false;
201         }
202
203         private static CultureInfo CreateCultureInfoNoThrow(string name, bool useUserOverride)
204         {
205             Debug.Assert(name != null);
206             CultureData cultureData = CultureData.GetCultureData(name, useUserOverride);
207             if (cultureData == null)
208             {
209                 return null;
210             }
211
212             return new CultureInfo(cultureData);
213         } 
214         public CultureInfo(int culture) : this(culture, true) 
215         {
216         }
217
218         public CultureInfo(int culture, bool useUserOverride)
219         {
220             // We don't check for other invalid LCIDS here...
221             if (culture < 0)
222             {
223                 throw new ArgumentOutOfRangeException(nameof(culture), SR.ArgumentOutOfRange_NeedPosNum);
224             }
225
226             InitializeFromCultureId(culture, useUserOverride);
227         }
228
229         private void InitializeFromCultureId(int culture, bool useUserOverride)
230         {
231             switch (culture)
232             {
233                 case LOCALE_CUSTOM_DEFAULT:
234                 case LOCALE_SYSTEM_DEFAULT:
235                 case LOCALE_NEUTRAL:
236                 case LOCALE_USER_DEFAULT:
237                 case LOCALE_CUSTOM_UNSPECIFIED:
238                     // Can't support unknown custom cultures and we do not support neutral or
239                     // non-custom user locales.
240                     throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported);
241
242                 default:
243                     // Now see if this LCID is supported in the system default CultureData table.
244                     _cultureData = CultureData.GetCultureData(culture, useUserOverride);
245                     break;
246             }
247             _isInherited = (this.GetType() != typeof(System.Globalization.CultureInfo));
248             _name = _cultureData.CultureName;
249         }
250
251         private void InitializeFromName(string name, bool useUserOverride)
252         {
253             // Get our data providing record
254             _cultureData = CultureData.GetCultureData(name, useUserOverride);
255
256             if (_cultureData == null)
257             {
258                 throw new CultureNotFoundException(nameof(name), name, SR.Argument_CultureNotSupported);
259             }
260
261             _name = _cultureData.CultureName;
262             _isInherited = (this.GetType() != typeof(System.Globalization.CultureInfo));
263         }
264
265         // Constructor called by SQL Server's special munged culture - creates a culture with
266         // a TextInfo and CompareInfo that come from a supplied alternate source. This object
267         // is ALWAYS read-only.
268         // Note that we really cannot use an LCID version of this override as the cached
269         // name we create for it has to include both names, and the logic for this is in
270         // the GetCultureInfo override *only*.
271         internal CultureInfo(String cultureName, String textAndCompareCultureName)
272         {
273             if (cultureName == null)
274             {
275                 throw new ArgumentNullException(nameof(cultureName),SR.ArgumentNull_String);
276             }
277
278             _cultureData = CultureData.GetCultureData(cultureName, false);
279             if (_cultureData == null)
280                 throw new CultureNotFoundException(nameof(cultureName), cultureName, SR.Argument_CultureNotSupported);
281             
282             _name = _cultureData.CultureName;
283
284             CultureInfo altCulture = GetCultureInfo(textAndCompareCultureName);
285             compareInfo = altCulture.CompareInfo;
286             textInfo = altCulture.TextInfo;
287         }
288
289         // We do this to try to return the system UI language and the default user languages
290         // This method will fallback if this fails (like Invariant)
291         //
292         // TODO: It would appear that this is only ever called with userOveride = true
293         // and this method only has one caller.  Can we fold it into the caller?
294         private static CultureInfo GetCultureByName(String name, bool userOverride)
295         {
296             CultureInfo ci = null;
297             // Try to get our culture
298             try
299             {
300                 ci = userOverride ? new CultureInfo(name) : CultureInfo.GetCultureInfo(name);
301             }
302             catch (ArgumentException)
303             {
304             }
305
306             if (ci == null)
307             {
308                 ci = InvariantCulture;
309             }
310
311             return ci;
312         }
313
314         //
315         // Return a specific culture.  A tad irrelevent now since we always return valid data
316         // for neutral locales.
317         //
318         // Note that there's interesting behavior that tries to find a smaller name, ala RFC4647,
319         // if we can't find a bigger name.  That doesn't help with things like "zh" though, so
320         // the approach is of questionable value
321         //
322         public static CultureInfo CreateSpecificCulture(String name)
323         {
324             CultureInfo culture;
325
326             try
327             {
328                 culture = new CultureInfo(name);
329             }
330             catch (ArgumentException)
331             {
332                 // When CultureInfo throws this exception, it may be because someone passed the form
333                 // like "az-az" because it came out of an http accept lang. We should try a little
334                 // parsing to perhaps fall back to "az" here and use *it* to create the neutral.
335
336                 int idx;
337
338                 culture = null;
339                 for (idx = 0; idx < name.Length; idx++)
340                 {
341                     if ('-' == name[idx])
342                     {
343                         try
344                         {
345                             culture = new CultureInfo(name.Substring(0, idx));
346                             break;
347                         }
348                         catch (ArgumentException)
349                         {
350                             // throw the original exception so the name in the string will be right
351                             throw;
352                         }
353                     }
354                 }
355
356                 if (culture == null)
357                 {
358                     // nothing to save here; throw the original exception
359                     throw;
360                 }
361             }
362
363             // In the most common case, they've given us a specific culture, so we'll just return that.
364             if (!(culture.IsNeutralCulture))
365             {
366                 return culture;
367             }
368
369             return (new CultureInfo(culture._cultureData.SSPECIFICCULTURE));
370         }
371
372         internal static bool VerifyCultureName(String cultureName, bool throwException)
373         {
374             // This function is used by ResourceManager.GetResourceFileName().
375             // ResourceManager searches for resource using CultureInfo.Name,
376             // so we should check against CultureInfo.Name.
377
378             for (int i = 0; i < cultureName.Length; i++)
379             {
380                 char c = cultureName[i];
381                 // TODO: Names can only be RFC4646 names (ie: a-zA-Z0-9) while this allows any unicode letter/digit
382                 if (Char.IsLetterOrDigit(c) || c == '-' || c == '_')
383                 {
384                     continue;
385                 }
386                 if (throwException)
387                 {
388                     throw new ArgumentException(SR.Format(SR.Argument_InvalidResourceCultureName, cultureName));
389                 }
390                 return false;
391             }
392             return true;
393         }
394
395         internal static bool VerifyCultureName(CultureInfo culture, bool throwException)
396         {
397             //If we have an instance of one of our CultureInfos, the user can't have changed the
398             //name and we know that all names are valid in files.
399             if (!culture._isInherited)
400             {
401                 return true;
402             }
403
404             return VerifyCultureName(culture.Name, throwException);
405         }
406
407         // We need to store the override from the culture data record.
408         private bool _useUserOverride;
409         
410         internal static CultureInfo GetCurrentUICultureNoAppX()
411         {
412             CultureInfo ci = GetUserDefaultCultureCacheOverride();
413             if (ci != null)
414             {
415                 return ci;
416             }
417
418             if (Thread.m_CurrentUICulture != null)
419             {
420                 return Thread.m_CurrentUICulture;
421             }
422
423             ci = s_DefaultThreadCurrentUICulture;
424             if (ci != null)
425             {
426                 return ci;
427             }
428
429             return UserDefaultUICulture;
430         }
431
432         internal static CultureInfo UserDefaultUICulture
433         {
434             get
435             {
436                 // if s_userDefaultUICulture == null means CultureInfo statics didn't get initialized yet. this can happen if there early static
437                 // method get executed which eventually hit the cultureInfo code while CultureInfo statics didn’t get chance to initialize
438                 if (s_userDefaultUICulture == null)
439                 {
440                     Init();
441                 }
442
443                 Debug.Assert(s_userDefaultUICulture != null);
444                 return s_userDefaultUICulture;
445             }
446         }
447
448         public static CultureInfo InstalledUICulture
449         {
450             get
451             {
452                 if (s_userDefaultCulture == null)
453                 {
454                     Init();
455                 }
456                 Debug.Assert(s_userDefaultCulture != null, "[CultureInfo.InstalledUICulture] s_userDefaultCulture != null");
457                 return s_userDefaultCulture;
458             }
459         }
460
461         public static CultureInfo DefaultThreadCurrentCulture
462         {
463             get { return s_DefaultThreadCurrentCulture; }
464             set
465             {
466                 // If you add pre-conditions to this method, check to see if you also need to
467                 // add them to Thread.CurrentCulture.set.
468
469                 s_DefaultThreadCurrentCulture = value;
470             }
471         }
472
473         public static CultureInfo DefaultThreadCurrentUICulture
474         {
475             get { return s_DefaultThreadCurrentUICulture; }
476             set
477             {
478                 //If they're trying to use a Culture with a name that we can't use in resource lookup,
479                 //don't even let them set it on the thread.
480
481                 // If you add more pre-conditions to this method, check to see if you also need to
482                 // add them to Thread.CurrentUICulture.set.
483
484                 if (value != null)
485                 {
486                     CultureInfo.VerifyCultureName(value, true);
487                 }
488
489                 s_DefaultThreadCurrentUICulture = value;
490             }
491         }
492
493         ////////////////////////////////////////////////////////////////////////
494         //
495         //  InvariantCulture
496         //
497         //  This instance provides methods, for example for casing and sorting,
498         //  that are independent of the system and current user settings.  It
499         //  should be used only by processes such as some system services that
500         //  require such invariant results (eg. file systems).  In general,
501         //  the results are not linguistically correct and do not match any
502         //  culture info.
503         //
504         ////////////////////////////////////////////////////////////////////////
505
506
507         public static CultureInfo InvariantCulture
508         {
509             get
510             {
511                 return (s_InvariantCultureInfo);
512             }
513         }
514
515
516         ////////////////////////////////////////////////////////////////////////
517         //
518         //  Parent
519         //
520         //  Return the parent CultureInfo for the current instance.
521         //
522         ////////////////////////////////////////////////////////////////////////
523
524         public virtual CultureInfo Parent
525         {
526             get
527             {
528                 if (null == _parent)
529                 {
530                     string parentName = _cultureData.SPARENT;
531
532                     if (String.IsNullOrEmpty(parentName))
533                     {
534                         _parent = InvariantCulture;
535                     }
536                     else
537                     {
538                         _parent = CreateCultureInfoNoThrow(parentName, _cultureData.UseUserOverride);
539                         if (_parent == null)
540                         {
541                             // For whatever reason our IPARENT or SPARENT wasn't correct, so use invariant
542                             // We can't allow ourselves to fail.  In case of custom cultures the parent of the
543                             // current custom culture isn't installed.
544                             _parent = InvariantCulture;
545                         }
546                     }
547                 }
548                 return _parent;
549             }
550         }
551
552         public virtual int LCID
553         {
554             get
555             {
556                 return (this._cultureData.ILANGUAGE);
557             }
558         }
559
560         public virtual int KeyboardLayoutId
561         {
562             get
563             {
564                 return _cultureData.IINPUTLANGUAGEHANDLE;
565             }
566         }
567
568         public static CultureInfo[] GetCultures(CultureTypes types)
569         {
570             // internally we treat UserCustomCultures as Supplementals but v2
571             // treats as Supplementals and Replacements
572             if ((types & CultureTypes.UserCustomCulture) == CultureTypes.UserCustomCulture)
573             {
574                 types |= CultureTypes.ReplacementCultures;
575             }
576             return (CultureData.GetCultures(types));
577         }
578
579         ////////////////////////////////////////////////////////////////////////
580         //
581         //  Name
582         //
583         //  Returns the full name of the CultureInfo. The name is in format like
584         //  "en-US"  This version does NOT include sort information in the name.
585         //
586         ////////////////////////////////////////////////////////////////////////
587         public virtual String Name
588         {
589             get
590             {
591                 // We return non sorting name here.
592                 if (_nonSortName == null)
593                 {
594                     _nonSortName = _cultureData.SNAME;
595                     if (_nonSortName == null)
596                     {
597                         _nonSortName = String.Empty;
598                     }
599                 }
600                 return _nonSortName;
601             }
602         }
603
604         // This one has the sort information (ie: de-DE_phoneb)
605         internal String SortName
606         {
607             get
608             {
609                 if (_sortName == null)
610                 {
611                     _sortName = _cultureData.SCOMPAREINFO;
612                 }
613
614                 return _sortName;
615             }
616         }
617
618         public string IetfLanguageTag
619         {
620             get
621             {
622                 // special case the compatibility cultures
623                 switch (this.Name)
624                 {
625                     case "zh-CHT":
626                         return "zh-Hant";
627                     case "zh-CHS":
628                         return "zh-Hans";
629                     default:
630                         return this.Name;
631                 }
632             }
633         }
634
635         ////////////////////////////////////////////////////////////////////////
636         //
637         //  DisplayName
638         //
639         //  Returns the full name of the CultureInfo in the localized language.
640         //  For example, if the localized language of the runtime is Spanish and the CultureInfo is
641         //  US English, "Ingles (Estados Unidos)" will be returned.
642         //
643         ////////////////////////////////////////////////////////////////////////
644         public virtual String DisplayName
645         {
646             get
647             {
648                 Debug.Assert(_name != null, "[CultureInfo.DisplayName] Always expect _name to be set");
649
650                 return _cultureData.SLOCALIZEDDISPLAYNAME;
651             }
652         }
653
654         ////////////////////////////////////////////////////////////////////////
655         //
656         //  GetNativeName
657         //
658         //  Returns the full name of the CultureInfo in the native language.
659         //  For example, if the CultureInfo is US English, "English
660         //  (United States)" will be returned.
661         //
662         ////////////////////////////////////////////////////////////////////////
663         public virtual String NativeName
664         {
665             get
666             {
667                 return (_cultureData.SNATIVEDISPLAYNAME);
668             }
669         }
670
671         ////////////////////////////////////////////////////////////////////////
672         //
673         //  GetEnglishName
674         //
675         //  Returns the full name of the CultureInfo in English.
676         //  For example, if the CultureInfo is US English, "English
677         //  (United States)" will be returned.
678         //
679         ////////////////////////////////////////////////////////////////////////
680         public virtual String EnglishName
681         {
682             get
683             {
684                 return (_cultureData.SENGDISPLAYNAME);
685             }
686         }
687
688         // ie: en
689         public virtual String TwoLetterISOLanguageName
690         {
691             get
692             {
693                 return (_cultureData.SISO639LANGNAME);
694             }
695         }
696
697         // ie: eng
698         public virtual String ThreeLetterISOLanguageName
699         {
700             get
701             {
702                 return _cultureData.SISO639LANGNAME2;
703             }
704         }
705
706         ////////////////////////////////////////////////////////////////////////
707         //
708         //  ThreeLetterWindowsLanguageName
709         //
710         //  Returns the 3 letter windows language name for the current instance.  eg: "ENU"
711         //  The ISO names are much preferred
712         //
713         ////////////////////////////////////////////////////////////////////////
714         public virtual String ThreeLetterWindowsLanguageName
715         {
716             get
717             {
718                 return _cultureData.SABBREVLANGNAME;
719             }
720         }
721
722         ////////////////////////////////////////////////////////////////////////
723         //
724         //  CompareInfo               Read-Only Property
725         //
726         //  Gets the CompareInfo for this culture.
727         //
728         ////////////////////////////////////////////////////////////////////////
729         public virtual CompareInfo CompareInfo
730         {
731             get
732             {
733                 if (this.compareInfo == null)
734                 {
735                     // Since CompareInfo's don't have any overrideable properties, get the CompareInfo from
736                     // the Non-Overridden CultureInfo so that we only create one CompareInfo per culture
737                     this.compareInfo = UseUserOverride
738                                         ? GetCultureInfo(this._name).CompareInfo
739                                         : new CompareInfo(this);
740                 }
741                 return (compareInfo);
742             }
743         }
744
745         ////////////////////////////////////////////////////////////////////////
746         //
747         //  TextInfo
748         //
749         //  Gets the TextInfo for this culture.
750         //
751         ////////////////////////////////////////////////////////////////////////
752         public virtual TextInfo TextInfo
753         {
754             get
755             {
756                 if (textInfo == null)
757                 {
758                     // Make a new textInfo
759                     TextInfo tempTextInfo = new TextInfo(_cultureData);
760                     tempTextInfo.SetReadOnlyState(_isReadOnly);
761                     textInfo = tempTextInfo;
762                 }
763                 return (textInfo);
764             }
765         }
766
767         ////////////////////////////////////////////////////////////////////////
768         //
769         //  Equals
770         //
771         //  Implements Object.Equals().  Returns a boolean indicating whether
772         //  or not object refers to the same CultureInfo as the current instance.
773         //
774         ////////////////////////////////////////////////////////////////////////
775
776
777         public override bool Equals(Object value)
778         {
779             if (Object.ReferenceEquals(this, value))
780                 return true;
781
782             CultureInfo that = value as CultureInfo;
783
784             if (that != null)
785             {
786                 // using CompareInfo to verify the data passed through the constructor
787                 // CultureInfo(String cultureName, String textAndCompareCultureName)
788
789                 return (this.Name.Equals(that.Name) && this.CompareInfo.Equals(that.CompareInfo));
790             }
791
792             return (false);
793         }
794
795
796         ////////////////////////////////////////////////////////////////////////
797         //
798         //  GetHashCode
799         //
800         //  Implements Object.GetHashCode().  Returns the hash code for the
801         //  CultureInfo.  The hash code is guaranteed to be the same for CultureInfo A
802         //  and B where A.Equals(B) is true.
803         //
804         ////////////////////////////////////////////////////////////////////////
805
806         public override int GetHashCode()
807         {
808             return (this.Name.GetHashCode() + this.CompareInfo.GetHashCode());
809         }
810
811
812         ////////////////////////////////////////////////////////////////////////
813         //
814         //  ToString
815         //
816         //  Implements Object.ToString().  Returns the name of the CultureInfo,
817         //  eg. "de-DE_phoneb", "en-US", or "fj-FJ".
818         //
819         ////////////////////////////////////////////////////////////////////////
820
821
822         public override String ToString()
823         {
824             return _name;
825         }
826
827
828         public virtual Object GetFormat(Type formatType)
829         {
830             if (formatType == typeof(NumberFormatInfo))
831                 return (NumberFormat);
832             if (formatType == typeof(DateTimeFormatInfo))
833                 return (DateTimeFormat);
834             return (null);
835         }
836
837         public virtual bool IsNeutralCulture
838         {
839             get
840             {
841                 return _cultureData.IsNeutralCulture;
842             }
843         }
844
845         public CultureTypes CultureTypes
846         {
847             get
848             {
849                 CultureTypes types = 0;
850
851                 if (_cultureData.IsNeutralCulture)
852                     types |= CultureTypes.NeutralCultures;
853                 else
854                     types |= CultureTypes.SpecificCultures;
855
856                 types |= _cultureData.IsWin32Installed ? CultureTypes.InstalledWin32Cultures : 0;
857
858                 // Disable  warning 618: System.Globalization.CultureTypes.FrameworkCultures' is obsolete
859 #pragma warning disable 618
860                 types |= _cultureData.IsFramework ? CultureTypes.FrameworkCultures : 0;
861
862 #pragma warning restore 618
863                 types |= _cultureData.IsSupplementalCustomCulture ? CultureTypes.UserCustomCulture : 0;
864                 types |= _cultureData.IsReplacementCulture ? CultureTypes.ReplacementCultures | CultureTypes.UserCustomCulture : 0;
865
866                 return types;
867             }
868         }
869
870         public virtual NumberFormatInfo NumberFormat
871         {
872             get
873             {
874                 if (numInfo == null)
875                 {
876                     NumberFormatInfo temp = new NumberFormatInfo(_cultureData);
877                     temp.isReadOnly = _isReadOnly;
878                     Interlocked.CompareExchange(ref numInfo, temp, null);
879                 }
880                 return (numInfo);
881             }
882             set
883             {
884                 if (value == null)
885                 {
886                     throw new ArgumentNullException(nameof(value), SR.ArgumentNull_Obj);
887                 }
888                 VerifyWritable();
889                 numInfo = value;
890             }
891         }
892
893         ////////////////////////////////////////////////////////////////////////
894         //
895         // GetDateTimeFormatInfo
896         //
897         // Create a DateTimeFormatInfo, and fill in the properties according to
898         // the CultureID.
899         //
900         ////////////////////////////////////////////////////////////////////////
901         public virtual DateTimeFormatInfo DateTimeFormat
902         {
903             get
904             {
905                 if (dateTimeInfo == null)
906                 {
907                     // Change the calendar of DTFI to the specified calendar of this CultureInfo.
908                     DateTimeFormatInfo temp = new DateTimeFormatInfo(_cultureData, this.Calendar);
909                     temp._isReadOnly = _isReadOnly;
910                     Interlocked.CompareExchange(ref dateTimeInfo, temp, null);
911                 }
912                 return (dateTimeInfo);
913             }
914
915             set
916             {
917                 if (value == null)
918                 {
919                     throw new ArgumentNullException(nameof(value), SR.ArgumentNull_Obj);
920                 }
921                 VerifyWritable();
922                 dateTimeInfo = value;
923             }
924         }
925
926         public void ClearCachedData()
927         {
928             Init(); // reset the default culture values
929
930             RegionInfo.s_currentRegionInfo = null;
931             #pragma warning disable 0618 // disable the obsolete warning 
932             TimeZone.ResetTimeZone();
933             #pragma warning restore 0618
934             TimeZoneInfo.ClearCachedData();
935             s_LcidCachedCultures = null;
936             s_NameCachedCultures = null;
937
938             CultureData.ClearCachedData();
939         }
940
941         /*=================================GetCalendarInstance==========================
942         **Action: Map a Win32 CALID to an instance of supported calendar.
943         **Returns: An instance of calendar.
944         **Arguments: calType    The Win32 CALID
945         **Exceptions:
946         **      Shouldn't throw exception since the calType value is from our data table or from Win32 registry.
947         **      If we are in trouble (like getting a weird value from Win32 registry), just return the GregorianCalendar.
948         ============================================================================*/
949         internal static Calendar GetCalendarInstance(CalendarId calType)
950         {
951             if (calType == CalendarId.GREGORIAN)
952             {
953                 return (new GregorianCalendar());
954             }
955             return GetCalendarInstanceRare(calType);
956         }
957
958         //This function exists as a shortcut to prevent us from loading all of the non-gregorian
959         //calendars unless they're required.
960         internal static Calendar GetCalendarInstanceRare(CalendarId calType)
961         {
962             Debug.Assert(calType != CalendarId.GREGORIAN, "calType!=CalendarId.GREGORIAN");
963
964             switch (calType)
965             {
966                 case CalendarId.GREGORIAN_US:               // Gregorian (U.S.) calendar
967                 case CalendarId.GREGORIAN_ME_FRENCH:        // Gregorian Middle East French calendar
968                 case CalendarId.GREGORIAN_ARABIC:           // Gregorian Arabic calendar
969                 case CalendarId.GREGORIAN_XLIT_ENGLISH:     // Gregorian Transliterated English calendar
970                 case CalendarId.GREGORIAN_XLIT_FRENCH:      // Gregorian Transliterated French calendar
971                     return (new GregorianCalendar((GregorianCalendarTypes)calType));
972                 case CalendarId.TAIWAN:                     // Taiwan Era calendar
973                     return (new TaiwanCalendar());
974                 case CalendarId.JAPAN:                      // Japanese Emperor Era calendar
975                     return (new JapaneseCalendar());
976                 case CalendarId.KOREA:                      // Korean Tangun Era calendar
977                     return (new KoreanCalendar());
978                 case CalendarId.THAI:                       // Thai calendar
979                     return (new ThaiBuddhistCalendar());
980                 case CalendarId.HIJRI:                      // Hijri (Arabic Lunar) calendar
981                     return (new HijriCalendar());
982                 case CalendarId.HEBREW:                     // Hebrew (Lunar) calendar
983                     return (new HebrewCalendar());
984                 case CalendarId.UMALQURA:
985                     return (new UmAlQuraCalendar());
986                 case CalendarId.PERSIAN:
987                     return (new PersianCalendar());
988             }
989             return (new GregorianCalendar());
990         }
991
992         /*=================================Calendar==========================
993         **Action: Return/set the default calendar used by this culture.
994         ** This value can be overridden by regional option if this is a current culture.
995         **Returns:
996         **Arguments:
997         **Exceptions:
998         **  ArgumentNull_Obj if the set value is null.
999         ============================================================================*/
1000         public virtual Calendar Calendar
1001         {
1002             get
1003             {
1004                 if (calendar == null)
1005                 {
1006                     Debug.Assert(_cultureData.CalendarIds.Length > 0, "_cultureData.CalendarIds.Length > 0");
1007                     // Get the default calendar for this culture.  Note that the value can be
1008                     // from registry if this is a user default culture.
1009                     Calendar newObj = _cultureData.DefaultCalendar;
1010
1011                     System.Threading.Interlocked.MemoryBarrier();
1012                     newObj.SetReadOnlyState(_isReadOnly);
1013                     calendar = newObj;
1014                 }
1015                 return (calendar);
1016             }
1017         }
1018
1019         /*=================================OptionCalendars==========================
1020         **Action: Return an array of the optional calendar for this culture.
1021         **Returns: an array of Calendar.
1022         **Arguments:
1023         **Exceptions:
1024         ============================================================================*/
1025
1026
1027         public virtual Calendar[] OptionalCalendars
1028         {
1029             get
1030             {
1031                 //
1032                 // This property always returns a new copy of the calendar array.
1033                 //
1034                 CalendarId[] calID = _cultureData.CalendarIds;
1035                 Calendar[] cals = new Calendar[calID.Length];
1036                 for (int i = 0; i < cals.Length; i++)
1037                 {
1038                     cals[i] = GetCalendarInstance(calID[i]);
1039                 }
1040                 return (cals);
1041             }
1042         }
1043
1044         public bool UseUserOverride
1045         {
1046             get
1047             {
1048                 return _cultureData.UseUserOverride;
1049             }
1050         }
1051
1052         public CultureInfo GetConsoleFallbackUICulture()
1053         {
1054             CultureInfo temp = _consoleFallbackCulture;
1055             if (temp == null)
1056             {
1057                 temp = CreateSpecificCulture(_cultureData.SCONSOLEFALLBACKNAME);
1058                 _isReadOnly = true;
1059                 _consoleFallbackCulture = temp;
1060             }
1061             return (temp);
1062         }
1063
1064         public virtual Object Clone()
1065         {
1066             CultureInfo ci = (CultureInfo)MemberwiseClone();
1067             ci._isReadOnly = false;
1068
1069             //If this is exactly our type, we can make certain optimizations so that we don't allocate NumberFormatInfo or DTFI unless
1070             //they've already been allocated.  If this is a derived type, we'll take a more generic codepath.
1071             if (!_isInherited)
1072             {
1073                 if (this.dateTimeInfo != null)
1074                 {
1075                     ci.dateTimeInfo = (DateTimeFormatInfo)this.dateTimeInfo.Clone();
1076                 }
1077                 if (this.numInfo != null)
1078                 {
1079                     ci.numInfo = (NumberFormatInfo)this.numInfo.Clone();
1080                 }
1081             }
1082             else
1083             {
1084                 ci.DateTimeFormat = (DateTimeFormatInfo)this.DateTimeFormat.Clone();
1085                 ci.NumberFormat = (NumberFormatInfo)this.NumberFormat.Clone();
1086             }
1087
1088             if (textInfo != null)
1089             {
1090                 ci.textInfo = (TextInfo)textInfo.Clone();
1091             }
1092
1093             if (calendar != null)
1094             {
1095                 ci.calendar = (Calendar)calendar.Clone();
1096             }
1097
1098             return (ci);
1099         }
1100
1101         public static CultureInfo ReadOnly(CultureInfo ci)
1102         {
1103             if (ci == null)
1104             {
1105                 throw new ArgumentNullException(nameof(ci));
1106             }
1107
1108             if (ci.IsReadOnly)
1109             {
1110                 return (ci);
1111             }
1112             CultureInfo newInfo = (CultureInfo)(ci.MemberwiseClone());
1113
1114             if (!ci.IsNeutralCulture)
1115             {
1116                 //If this is exactly our type, we can make certain optimizations so that we don't allocate NumberFormatInfo or DTFI unless
1117                 //they've already been allocated.  If this is a derived type, we'll take a more generic codepath.
1118                 if (!ci._isInherited)
1119                 {
1120                     if (ci.dateTimeInfo != null)
1121                     {
1122                         newInfo.dateTimeInfo = DateTimeFormatInfo.ReadOnly(ci.dateTimeInfo);
1123                     }
1124                     if (ci.numInfo != null)
1125                     {
1126                         newInfo.numInfo = NumberFormatInfo.ReadOnly(ci.numInfo);
1127                     }
1128                 }
1129                 else
1130                 {
1131                     newInfo.DateTimeFormat = DateTimeFormatInfo.ReadOnly(ci.DateTimeFormat);
1132                     newInfo.NumberFormat = NumberFormatInfo.ReadOnly(ci.NumberFormat);
1133                 }
1134             }
1135
1136             if (ci.textInfo != null)
1137             {
1138                 newInfo.textInfo = TextInfo.ReadOnly(ci.textInfo);
1139             }
1140
1141             if (ci.calendar != null)
1142             {
1143                 newInfo.calendar = Calendar.ReadOnly(ci.calendar);
1144             }
1145
1146             // Don't set the read-only flag too early.
1147             // We should set the read-only flag here.  Otherwise, info.DateTimeFormat will not be able to set.
1148             newInfo._isReadOnly = true;
1149
1150             return (newInfo);
1151         }
1152
1153
1154         public bool IsReadOnly
1155         {
1156             get
1157             {
1158                 return (_isReadOnly);
1159             }
1160         }
1161
1162         private void VerifyWritable()
1163         {
1164             if (_isReadOnly)
1165             {
1166                 throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
1167             }
1168         }
1169
1170         // For resource lookup, we consider a culture the invariant culture by name equality. 
1171         // We perform this check frequently during resource lookup, so adding a property for
1172         // improved readability.
1173         internal bool HasInvariantCultureName
1174         {
1175             get { return Name == CultureInfo.InvariantCulture.Name; }
1176         }
1177
1178         // Helper function both both overloads of GetCachedReadOnlyCulture.  If lcid is 0, we use the name.
1179         // If lcid is -1, use the altName and create one of those special SQL cultures.
1180         internal static CultureInfo GetCultureInfoHelper(int lcid, string name, string altName)
1181         {
1182             // retval is our return value.
1183             CultureInfo retval;
1184
1185             // Temporary hashtable for the names.
1186             StringCultureInfoDictionary tempNameHT = s_NameCachedCultures;
1187
1188             if (name != null)
1189             {
1190                 name = CultureData.AnsiToLower(name);
1191             }
1192
1193             if (altName != null)
1194             {
1195                 altName = CultureData.AnsiToLower(altName);
1196             }
1197
1198             // We expect the same result for both hashtables, but will test individually for added safety.
1199             if (tempNameHT == null)
1200             {
1201                 tempNameHT = new StringCultureInfoDictionary();
1202             }
1203             else
1204             {
1205                 // If we are called by name, check if the object exists in the hashtable.  If so, return it.
1206                 if (lcid == -1 || lcid == 0)
1207                 {
1208                     bool ret;
1209                     lock (_lock)
1210                     {
1211                         ret = tempNameHT.TryGetValue(lcid == 0 ? name : name + '\xfffd' + altName, out retval);
1212                     }
1213
1214                     if (ret && retval != null)
1215                     {
1216                         return retval;
1217                     }
1218                 }
1219             }
1220
1221             // Next, the Lcid table.
1222             StringLcidDictionary tempLcidHT = s_LcidCachedCultures;
1223
1224             if (tempLcidHT == null)
1225             {
1226                 // Case insensitive is not an issue here, save the constructor call.
1227                 tempLcidHT = new StringLcidDictionary();
1228             }
1229             else
1230             {
1231                 // If we were called by Lcid, check if the object exists in the table.  If so, return it.
1232                 if (lcid > 0)
1233                 {
1234                     bool ret;
1235                     lock (_lock)
1236                     {
1237                         ret = tempLcidHT.TryGetValue(lcid, out retval);
1238                     }
1239                     if (ret && retval != null)
1240                     {
1241                         return retval;
1242                     }
1243                 }
1244             }
1245
1246             // We now have two temporary hashtables and the desired object was not found.
1247             // We'll construct it.  We catch any exceptions from the constructor call and return null.
1248             try
1249             {
1250                 switch (lcid)
1251                 {
1252                     case -1:
1253                         // call the private constructor
1254                         retval = new CultureInfo(name, altName);
1255                         break;
1256
1257                     case 0:
1258                         retval = new CultureInfo(name, false);
1259                         break;
1260
1261                     default:
1262                         retval = new CultureInfo(lcid, false);
1263                         break;
1264                 }
1265             }
1266             catch (ArgumentException)
1267             {
1268                 return null;
1269             }
1270
1271             // Set it to read-only
1272             retval._isReadOnly = true;
1273
1274             if (lcid == -1)
1275             {
1276                 lock (_lock)
1277                 {
1278                     // This new culture will be added only to the name hash table.
1279                     tempNameHT[name + '\xfffd' + altName] = retval;
1280                 }
1281                 // when lcid == -1 then TextInfo object is already get created and we need to set it as read only.
1282                 retval.TextInfo.SetReadOnlyState(true);
1283             }
1284             else if (lcid == 0)
1285             {
1286                 // Remember our name (as constructed).  Do NOT use alternate sort name versions because
1287                 // we have internal state representing the sort.  (So someone would get the wrong cached version)
1288                 string newName = CultureData.AnsiToLower(retval._name);
1289
1290                 // We add this new culture info object to both tables.
1291                 lock (_lock)
1292                 {
1293                     tempNameHT[newName] = retval;
1294                 }
1295             } 
1296             else
1297             {
1298                 lock (_lock)
1299                 {
1300                     tempLcidHT[lcid] = retval;
1301                 }
1302             }
1303
1304             // Copy the two hashtables to the corresponding member variables.  This will potentially overwrite
1305             // new tables simultaneously created by a new thread, but maximizes thread safety.
1306             if (-1 != lcid)
1307             {
1308                 // Only when we modify the lcid hash table, is there a need to overwrite.
1309                 s_LcidCachedCultures = tempLcidHT;
1310             }
1311
1312             s_NameCachedCultures = tempNameHT;
1313
1314             // Finally, return our new CultureInfo object.
1315             return retval;
1316         }
1317
1318         // Gets a cached copy of the specified culture from an internal hashtable (or creates it
1319         // if not found).  (LCID version)... use named version
1320         public static CultureInfo GetCultureInfo(int culture)
1321         {
1322             // Must check for -1 now since the helper function uses the value to signal
1323             // the altCulture code path for SQL Server.
1324             // Also check for zero as this would fail trying to add as a key to the hash.
1325             if (culture <= 0) 
1326             {
1327                 throw new ArgumentOutOfRangeException(nameof(culture), SR.ArgumentOutOfRange_NeedPosNum);
1328             }
1329             CultureInfo retval = GetCultureInfoHelper(culture, null, null);
1330             if (null == retval)
1331             {
1332                 throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported);
1333             }
1334             return retval;
1335         }
1336
1337         // Gets a cached copy of the specified culture from an internal hashtable (or creates it
1338         // if not found).  (Named version)
1339         public static CultureInfo GetCultureInfo(string name)
1340         {
1341             // Make sure we have a valid, non-zero length string as name
1342             if (name == null)
1343             {
1344                 throw new ArgumentNullException(nameof(name));
1345             }
1346
1347             CultureInfo retval = GetCultureInfoHelper(0, name, null);
1348             if (retval == null)
1349             {
1350                 throw new CultureNotFoundException(
1351                     nameof(name), name, SR.Argument_CultureNotSupported);
1352             }
1353             return retval;
1354         }
1355
1356         // Gets a cached copy of the specified culture from an internal hashtable (or creates it
1357         // if not found).
1358         public static CultureInfo GetCultureInfo(string name, string altName)
1359         {
1360             // Make sure we have a valid, non-zero length string as name
1361             if (name == null)
1362             {
1363                 throw new ArgumentNullException(nameof(name));
1364             }
1365
1366             if (altName == null)
1367             {
1368                 throw new ArgumentNullException(nameof(altName));
1369             }
1370             
1371
1372             CultureInfo retval = GetCultureInfoHelper(-1, name, altName);
1373             if (retval == null)
1374             {
1375                 throw new CultureNotFoundException("name or altName",
1376                                         SR.Format(SR.Argument_OneOfCulturesNotSupported, name, altName));
1377             }
1378             return retval;
1379         }
1380
1381         // This function is deprecated, we don't like it
1382         public static CultureInfo GetCultureInfoByIetfLanguageTag(string name)
1383         {
1384             // Disallow old zh-CHT/zh-CHS names
1385             if (name == "zh-CHT" || name == "zh-CHS")
1386             {
1387                 throw new CultureNotFoundException(nameof(name), SR.Format(SR.Argument_CultureIetfNotSupported, name));
1388             }
1389
1390             CultureInfo ci = GetCultureInfo(name);
1391
1392             // Disallow alt sorts and es-es_TS
1393             if (ci.LCID > 0xffff || ci.LCID == 0x040a)
1394             {
1395                 throw new CultureNotFoundException(nameof(name), SR.Format(SR.Argument_CultureIetfNotSupported, name));
1396             }
1397
1398             return ci;
1399         }
1400     }
1401 }
1402