Merge pull request #3487 from marek-safar/rs-Threading
[mono.git] / mcs / class / corlib / System.Globalization / CultureInfo.cs
1 //
2 // System.Globalization.CultureInfo.cs
3 //
4 // Authors:
5 // Miguel de Icaza (miguel@ximian.com)
6 // Dick Porter (dick@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
8 //
9 // (C) 2001, 2002, 2003 Ximian, Inc. (http://www.ximian.com)
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
11 // Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System.Collections.Generic;
34 using System.Threading;
35 using System.Runtime.CompilerServices;
36 using System.Runtime.InteropServices;
37 using System.Diagnostics.Contracts;
38
39 namespace System.Globalization
40 {
41         [System.Runtime.InteropServices.ComVisible (true)]
42         [Serializable]
43         [StructLayout (LayoutKind.Sequential)]
44         public class CultureInfo : ICloneable, IFormatProvider
45         {
46                 static volatile CultureInfo invariant_culture_info = new CultureInfo (InvariantCultureId, false, true);
47                 static object shared_table_lock = new object ();
48                 static CultureInfo default_current_culture;
49
50 #pragma warning disable 169, 649
51                 bool m_isReadOnly;
52                 int  cultureID;
53                 [NonSerialized]
54                 int parent_lcid;
55                 [NonSerialized]
56                 int datetime_index;
57                 [NonSerialized]
58                 int number_index;
59                 [NonSerialized]
60                 int default_calendar_type;
61                 bool m_useUserOverride;
62                 internal volatile NumberFormatInfo numInfo;
63                 internal volatile DateTimeFormatInfo dateTimeInfo;
64                 volatile TextInfo textInfo;
65                 internal string m_name;
66                 
67                 [NonSerialized]
68                 private string englishname;
69                 [NonSerialized]
70                 private string nativename;
71                 [NonSerialized]
72                 private string iso3lang;
73                 [NonSerialized]
74                 private string iso2lang;
75                 [NonSerialized]
76                 private string win3lang;
77                 [NonSerialized]
78                 private string territory;
79                 [NonSerialized]
80                 string[] native_calendar_names;
81
82                 volatile CompareInfo compareInfo;
83                 [NonSerialized]
84                 private unsafe readonly void *textinfo_data;
85
86                 [StructLayout (LayoutKind.Sequential)]
87                 struct Data {
88                         public int ansi;
89                         public int ebcdic;
90                         public int mac;
91                         public int oem;
92                         public bool right_to_left;
93                         public byte list_sep;
94                 }
95
96                 int m_dataItem;         // MS.NET serializes this.
97 #pragma warning restore 169, 649
98
99                 Calendar calendar;
100
101                 [NonSerialized]
102                 CultureInfo parent_culture;
103
104                 // Deserialized instances will set this to false
105                 [NonSerialized]
106                 bool constructed;
107
108                 [NonSerialized]
109                 // Used by Thread.set_CurrentCulture
110                 internal byte[] cached_serialized_form;
111
112                 [NonSerialized]internal CultureData m_cultureData;
113                 [NonSerialized]internal bool m_isInherited;
114                 
115                 internal const int InvariantCultureId = 0x7F;
116                 const int CalendarTypeBits = 8;
117
118                 const string MSG_READONLY = "This instance is read only";
119
120                 static volatile CultureInfo s_DefaultThreadCurrentUICulture;
121                 static volatile CultureInfo s_DefaultThreadCurrentCulture;
122                 
123                 public static CultureInfo InvariantCulture {
124                         get {
125                                 return invariant_culture_info;
126                         }
127                 }
128
129                 public static CultureInfo CurrentCulture {
130                         get {
131                                 return Thread.CurrentThread.CurrentCulture;
132                         }
133                         set {
134                                 Thread.CurrentThread.CurrentCulture = value;
135                         }
136                 }
137
138                 public static CultureInfo CurrentUICulture { 
139                         get {
140                                 return Thread.CurrentThread.CurrentUICulture;
141                         }
142                         set {
143                                 Thread.CurrentThread.CurrentUICulture = value;
144                         }
145                 }
146
147                 internal static CultureInfo ConstructCurrentCulture ()
148                 {
149                         if (default_current_culture != null)
150                                 return default_current_culture;
151
152                         var locale_name = get_current_locale_name ();
153                         CultureInfo ci = null;
154
155                         if (locale_name != null) {
156                                 try {
157                                         ci = CreateSpecificCulture (locale_name);
158                                 } catch {
159                                 }
160                         }
161
162                         if (ci == null) {
163                                 ci = InvariantCulture;
164                         } else {
165                                 ci.m_isReadOnly = true;
166                                 ci.m_useUserOverride = true;
167                         }
168
169                         default_current_culture = ci;
170                         return ci;
171                 }
172
173                 internal static CultureInfo ConstructCurrentUICulture ()
174                 {
175                         return ConstructCurrentCulture ();
176                 }
177
178                 // it is used for RegionInfo.
179                 internal string Territory {
180                         get { return territory; }
181                 }
182
183 #if !MOBILE
184                 // FIXME: It is implemented, but would be hell slow.
185                 [ComVisible (false)]
186                 public CultureTypes CultureTypes {
187                         get {
188                                 CultureTypes ret = (CultureTypes) 0;
189                                 foreach (CultureTypes v in Enum.GetValues (typeof (CultureTypes)))
190                                         if (Array.IndexOf (GetCultures (v), this) >= 0)
191                                                 ret |= v;
192                                 return ret;
193                         }
194                 }
195
196                 [ComVisible (false)]
197                 public CultureInfo GetConsoleFallbackUICulture ()
198                 {
199                         // as documented in MSDN ...
200                         switch (Name) {
201                         case "ar": case "ar-BH": case "ar-EG": case "ar-IQ":
202                         case "ar-JO": case "ar-KW": case "ar-LB": case "ar-LY":
203                         case "ar-QA": case "ar-SA": case "ar-SY": case "ar-AE":
204                         case "ar-YE":
205                         case "dv": case "dv-MV":
206                         case "fa": case "fa-IR":
207                         case "gu": case "gu-IN":
208                         case "he": case "he-IL":
209                         case "hi": case "hi-IN":
210                         case "kn": case "kn-IN":
211                         case "kok": case "kok-IN":
212                         case "mr": case "mr-IN":
213                         case "pa": case "pa-IN":
214                         case "sa": case "sa-IN":
215                         case "syr": case "syr-SY":
216                         case "ta": case "ta-IN":
217                         case "te": case "te-IN":
218                         case "th": case "th-TH":
219                         case "ur": case "ur-PK":
220                         case "vi": case "vi-VN":
221                                 return GetCultureInfo ("en");
222                         case "ar-DZ": case "ar-MA": case "ar-TN":
223                                 return GetCultureInfo ("fr");
224                         }
225                         return (CultureTypes & CultureTypes.WindowsOnlyCultures) != 0 ? CultureInfo.InvariantCulture : this;
226                 }
227
228                 [ComVisible (false)]
229                 public string IetfLanguageTag {
230                         // There could be more consistent way to implement
231                         // it, but right now it works just fine with this...
232                         get {
233                                 switch (Name) {
234                                 case "zh-CHS":
235                                         return "zh-Hans";
236                                 case "zh-CHT":
237                                         return "zh-Hant";
238                                 default:
239                                         return Name;
240                                 }
241                         }
242                 }
243
244                 // For specific cultures it basically returns LCID.
245                 // For neutral cultures it is mapped to the default(?) specific
246                 // culture, where the LCID of the specific culture seems to be
247                 // n + 1024 by default. zh-CHS is the only exception which is 
248                 // mapped to 2052, not 1028 (zh-CHT is mapped to 1028 instead).
249                 // There are very few exceptions, here I simply list them here.
250                 // It is Windows-specific property anyways, so no worthy of
251                 // trying to do some complex things with locale-builder.
252                 [ComVisible (false)]
253                 public virtual int KeyboardLayoutId {
254                         get {
255                                 switch (LCID) {
256                                 case 4: // zh-CHS (neutral)
257                                         return 2052;
258                                 case 1034: // es-ES Spanish 2
259                                         return 3082;
260                                 case 31748: // zh-CHT (neutral)
261                                         return 1028;
262                                 case 31770: // sr (neutral)
263                                         return 2074;
264                                 default:
265                                         return LCID < 1024 ? LCID + 1024 : LCID;
266                                 }
267                         }
268                 }
269 #endif
270
271                 public virtual int LCID {
272                         get {
273                                 return cultureID;
274                         }
275                 }
276
277                 public virtual string Name {
278                         get {
279                                 return(m_name);
280                         }
281                 }
282
283                 public virtual string NativeName {
284                         get {
285                                 if (!constructed) Construct ();
286                                 return nativename;
287                         }
288                 }
289
290                 internal string NativeCalendarName {
291                         get {
292                                 if (!constructed) Construct ();
293                                 return native_calendar_names[(default_calendar_type >> CalendarTypeBits) - 1];
294                         }
295                 }
296                 
297                 public virtual Calendar Calendar {
298                         get {
299                                 if (calendar == null) {
300                                         if (!constructed) Construct ();
301                                         calendar = CreateCalendar (default_calendar_type);
302                                 }
303
304                                 return calendar;
305                         }
306                 }
307
308                 [MonoLimitation ("Optional calendars are not supported only default calendar is returned")]
309                 public virtual Calendar[] OptionalCalendars {
310                         get {
311                                 return new[] { Calendar };
312                         }
313                 }
314
315                 public virtual CultureInfo Parent
316                 {
317                         get {
318                                 if (parent_culture == null) {
319                                         if (!constructed)
320                                                 Construct ();
321                                         if (parent_lcid == cultureID) {
322                                                 //
323                                                 // Parent lcid is same but culture info is not for legacy zh culture
324                                                 //
325                                                 if (parent_lcid == 0x7C04 && EnglishName [EnglishName.Length - 1] == 'y')
326                                                         return parent_culture = new CultureInfo ("zh-Hant");
327                                                 else if (parent_lcid == 0x0004 && EnglishName [EnglishName.Length -1] == 'y')
328                                                         return parent_culture = new CultureInfo ("zh-Hans");
329                                                 return null;
330                                         }
331                                         
332                                         if (parent_lcid == InvariantCultureId)
333                                                 parent_culture = InvariantCulture;
334                                         else if (cultureID == InvariantCultureId)
335                                                 parent_culture = this;
336                                         else if (cultureID == 0x0404) {
337                                                 // zh-tw has parent id 0x7C04 which is in this case zh-cht and not zh-hant
338                                                 parent_culture = new CultureInfo ("zh-CHT");
339                                         } else
340                                                 parent_culture = new CultureInfo (parent_lcid);
341                                 }
342                                 return parent_culture;
343                         }
344                 }
345
346                 public virtual TextInfo TextInfo
347                 {
348                         get {
349                                 if (textInfo == null) {
350                                         if (!constructed) Construct ();
351                                         lock (this) {
352                                                 if(textInfo == null) {
353                                                         textInfo = CreateTextInfo (m_isReadOnly);
354                                                 }
355                                         }
356                                 }
357                                 
358                                 return(textInfo);
359                         }
360                 }
361
362                 public virtual string ThreeLetterISOLanguageName {
363                         get {
364                                 if (!constructed) Construct ();
365                                 return iso3lang;
366                         }
367                 }
368
369                 public virtual string ThreeLetterWindowsLanguageName
370                 {
371                         get {
372                                 if (!constructed) Construct ();
373                                 return(win3lang);
374                         }
375                 }
376
377                 public virtual string TwoLetterISOLanguageName {
378                         get {
379                                 if (!constructed) Construct ();
380                                 return(iso2lang);
381                         }
382                 }
383
384                 public bool UseUserOverride
385                 {
386                         get {
387                                 return m_useUserOverride;
388                         }
389                 }
390
391                 public void ClearCachedData()
392                 {
393                         lock (shared_table_lock) {
394                                 shared_by_number = null;
395                                 shared_by_name = null;
396                         }
397
398                         //
399                         // ClearCachedData method does not refresh the information in
400                         // the Thread.CurrentCulture property for existing threads
401                         //
402                         default_current_culture = null;
403
404                         RegionInfo.ClearCachedData ();
405                         TimeZone.ClearCachedData ();
406                         TimeZoneInfo.ClearCachedData ();
407                 }
408
409                 public virtual object Clone()
410                 {
411                         if (!constructed) Construct ();
412                         CultureInfo ci=(CultureInfo)MemberwiseClone ();
413                         ci.m_isReadOnly=false;
414                         ci.cached_serialized_form=null;
415                         if (!IsNeutralCulture) {
416                                 ci.NumberFormat = (NumberFormatInfo)NumberFormat.Clone ();
417                                 ci.DateTimeFormat = (DateTimeFormatInfo)DateTimeFormat.Clone ();
418                         }
419                         return(ci);
420                 }
421
422                 public override bool Equals (object value)
423                 {
424                         CultureInfo b = value as CultureInfo;
425                         return b != null && b.cultureID == cultureID && b.m_name == m_name;
426                 }
427
428                 public static CultureInfo[] GetCultures(CultureTypes types)
429                 {
430                         bool neutral=((types & CultureTypes.NeutralCultures)!=0);
431                         bool specific=((types & CultureTypes.SpecificCultures)!=0);
432                         bool installed=((types & CultureTypes.InstalledWin32Cultures)!=0);  // TODO
433
434                         CultureInfo [] infos = internal_get_cultures (neutral, specific, installed);
435                         // The runtime returns a NULL in the first position of the array when
436                         // 'neutral' is true. We fill it in with a clone of InvariantCulture
437                         // since it must not be read-only
438                         int i = 0;
439                         if (neutral && infos.Length > 0 && infos [0] == null) {
440                                 infos [i++] = (CultureInfo) InvariantCulture.Clone ();
441                         }
442
443                         for (; i < infos.Length; ++i) {
444                                 var ci = infos [i];
445                                 var ti = ci.GetTextInfoData ();
446                                 infos [i].m_cultureData = CultureData.GetCultureData (ci.m_name, false, ci.datetime_index, ci.CalendarType, ci.number_index, ci.iso2lang,
447                                         ti.ansi, ti.oem, ti.mac, ti.ebcdic, ti.right_to_left, ((char)ti.list_sep).ToString ());
448                         }
449
450                         return infos;
451                 }
452
453                 unsafe Data GetTextInfoData ()
454                 {
455                         return *(Data*) textinfo_data;
456                 }
457
458                 public override int GetHashCode ()
459                 {
460                         return cultureID.GetHashCode ();
461                 }
462
463                 public static CultureInfo ReadOnly(CultureInfo ci)
464                 {
465                         if(ci==null) {
466                                 throw new ArgumentNullException("ci");
467                         }
468
469                         if(ci.m_isReadOnly) {
470                                 return(ci);
471                         } else {
472                                 CultureInfo new_ci=(CultureInfo)ci.Clone ();
473                                 new_ci.m_isReadOnly=true;
474                                 if (new_ci.numInfo != null)
475                                         new_ci.numInfo = NumberFormatInfo.ReadOnly (new_ci.numInfo);
476                                 if (new_ci.dateTimeInfo != null)
477                                         new_ci.dateTimeInfo = DateTimeFormatInfo.ReadOnly (new_ci.dateTimeInfo);
478                                 if (new_ci.textInfo != null)
479                                         new_ci.textInfo = TextInfo.ReadOnly (new_ci.textInfo);
480                                 return(new_ci);
481                         }
482                 }
483
484                 public override string ToString()
485                 {
486                         return(m_name);
487                 }
488                 
489                 public virtual CompareInfo CompareInfo
490                 {
491                         get {
492                                 if(compareInfo==null) {
493                                         if (!constructed)
494                                                 Construct ();
495
496                                         lock (this) {
497                                                 if(compareInfo==null) {
498                                                         compareInfo=new CompareInfo (this);
499                                                 }
500                                         }
501                                 }
502                                 
503                                 return(compareInfo);
504                         }
505                 }
506
507                 public virtual bool IsNeutralCulture {
508                         get {
509                                 if (cultureID == InvariantCultureId)
510                                         return false;
511
512                                 if (!constructed) Construct ();
513                                 return territory == null;
514                         }
515                 }
516
517                 void CheckNeutral ()
518                 {
519                 }
520
521                 public virtual NumberFormatInfo NumberFormat {
522                         get {
523                                 if (numInfo == null) {
524                                         NumberFormatInfo temp = new NumberFormatInfo(this.m_cultureData);
525                                         temp.isReadOnly = m_isReadOnly;
526                                         numInfo = temp;
527                                 }
528
529                                 return numInfo;
530                         }
531
532                         set {
533                                 if (!constructed) Construct ();
534                                 if (m_isReadOnly) throw new InvalidOperationException(MSG_READONLY);
535
536                                 if (value == null)
537                                         throw new ArgumentNullException ("NumberFormat");
538                                 
539                                 numInfo = value;
540                         }
541                 }
542
543                 public virtual DateTimeFormatInfo DateTimeFormat {
544                         get {
545                                 if (dateTimeInfo != null)
546                                         return dateTimeInfo;
547
548                                 if (!constructed) Construct ();
549                                 CheckNeutral ();
550
551                                 var temp = new DateTimeFormatInfo (m_cultureData, Calendar);
552                                 temp.m_isReadOnly = m_isReadOnly;
553                                 System.Threading.Thread.MemoryBarrier();
554                                 dateTimeInfo = temp;
555                                 return dateTimeInfo;
556                         }
557
558                         set {
559                                 if (!constructed) Construct ();
560                                 if (m_isReadOnly) throw new InvalidOperationException(MSG_READONLY);
561
562                                 if (value == null)
563                                         throw new ArgumentNullException ("DateTimeFormat");
564                                 
565                                 dateTimeInfo = value;
566                         }
567                 }
568
569                 public virtual string DisplayName {
570                         get {
571                                 // Mono is not localized and will always return english name regardless of OS locale
572                                 return EnglishName;
573                         }
574                 }
575
576                 public virtual string EnglishName {
577                         get {
578                                 if (!constructed) Construct ();
579                                 return englishname;
580                         }
581                 }
582
583                 public static CultureInfo InstalledUICulture {
584                         get {
585                                 return ConstructCurrentCulture ();
586                         }
587                 }
588
589                 public bool IsReadOnly {
590                         get {
591                                 return m_isReadOnly;
592                         }
593                 }
594                 
595
596                 // 
597                 // IFormatProvider implementation
598                 //
599                 public virtual object GetFormat( Type formatType )
600                 {
601                         object format = null;
602
603                         if ( formatType == typeof(NumberFormatInfo) )
604                                 format = NumberFormat;
605                         else if ( formatType == typeof(DateTimeFormatInfo) )
606                                 format = DateTimeFormat;
607                         
608                         return format;
609                 }
610                 
611                 void Construct ()
612                 {
613                         construct_internal_locale_from_lcid (cultureID);
614                         constructed = true;
615                 }
616
617                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
618                 private extern bool construct_internal_locale_from_lcid (int lcid);
619
620                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
621                 private extern bool construct_internal_locale_from_name (string name);
622
623                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
624                 private extern static string get_current_locale_name ();
625
626                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
627                 private extern static CultureInfo [] internal_get_cultures (bool neutral, bool specific, bool installed);
628
629                 private void ConstructInvariant (bool read_only)
630                 {
631                         cultureID = InvariantCultureId;
632
633                         /* NumberFormatInfo defaults to the invariant data */
634                         numInfo=NumberFormatInfo.InvariantInfo;
635
636                         if (!read_only) {
637                                 numInfo = (NumberFormatInfo) numInfo.Clone ();
638                         }
639
640                         textInfo = TextInfo.Invariant;
641
642                         m_name=String.Empty;
643                         englishname=
644                         nativename="Invariant Language (Invariant Country)";
645                         iso3lang="IVL";
646                         iso2lang="iv";
647                         win3lang="IVL";
648                         default_calendar_type = 1 << CalendarTypeBits | (int) GregorianCalendarTypes.Localized;
649                 }
650
651                 private unsafe TextInfo CreateTextInfo (bool readOnly)
652                 {
653                         TextInfo tempTextInfo = new TextInfo (this.m_cultureData);
654                         tempTextInfo.SetReadOnlyState (readOnly);
655                         return tempTextInfo;
656                 }
657
658                 public CultureInfo (int culture) : this (culture, true) {}
659
660                 public CultureInfo (int culture, bool useUserOverride) :
661                         this (culture, useUserOverride, false) {}
662
663                 private CultureInfo (int culture, bool useUserOverride, bool read_only)
664                 {
665                         if (culture < 0)
666                                 throw new ArgumentOutOfRangeException ("culture", "Positive "
667                                         + "number required.");
668
669                         constructed = true;
670                         m_isReadOnly = read_only;
671                         m_useUserOverride = useUserOverride;
672
673                         if (culture == InvariantCultureId) {
674                                 /* Short circuit the invariant culture */
675                                 m_cultureData = CultureData.Invariant;
676                                 ConstructInvariant (read_only);
677                                 return;
678                         }
679
680                         if (!construct_internal_locale_from_lcid (culture)) {
681                                 //
682                                 // Be careful not to cause recursive CultureInfo initialization
683                                 //
684                                 var msg = string.Format (InvariantCulture, "Culture ID {0} (0x{1}) is not a supported culture.", culture.ToString (InvariantCulture), culture.ToString ("X4", InvariantCulture));
685                                 throw new CultureNotFoundException ("culture", msg);
686                         }
687
688                         var ti = GetTextInfoData ();
689                         m_cultureData = CultureData.GetCultureData (m_name, m_useUserOverride, datetime_index, CalendarType, number_index, iso2lang,
690                                 ti.ansi, ti.oem, ti.mac, ti.ebcdic, ti.right_to_left, ((char)ti.list_sep).ToString ());
691                 }
692
693                 public CultureInfo (string name) : this (name, true) {}
694
695                 public CultureInfo (string name, bool useUserOverride) :
696                         this (name, useUserOverride, false) {}
697
698                 private CultureInfo (string name, bool useUserOverride, bool read_only)
699                 {
700                         if (name == null)
701                                 throw new ArgumentNullException ("name");
702
703                         constructed = true;
704                         m_isReadOnly = read_only;
705                         m_useUserOverride = useUserOverride;
706                         m_isInherited = GetType() != typeof(System.Globalization.CultureInfo);
707
708                         if (name.Length == 0) {
709                                 /* Short circuit the invariant culture */
710                                 m_cultureData = CultureData.Invariant;
711                                 ConstructInvariant (read_only);
712                                 return;
713                         }
714
715                         if (!construct_internal_locale_from_name (name.ToLowerInvariant ())) {
716                                 throw CreateNotFoundException (name);
717                         }
718
719                         var ti = GetTextInfoData ();
720                         m_cultureData = CultureData.GetCultureData (m_name, useUserOverride, datetime_index, CalendarType, number_index, iso2lang,
721                                 ti.ansi, ti.oem, ti.mac, ti.ebcdic, ti.right_to_left, ((char)ti.list_sep).ToString ());
722                 }
723
724                 // This is used when creating by specific name and creating by
725                 // current locale so we can initialize the object without
726                 // doing any member initialization
727                 private CultureInfo () { constructed = true; }
728                 static Dictionary<int, CultureInfo> shared_by_number;
729                 static Dictionary<string, CultureInfo> shared_by_name;
730                 
731                 static void insert_into_shared_tables (CultureInfo c)
732                 {
733                         if (shared_by_number == null){
734                                 shared_by_number = new Dictionary<int, CultureInfo> ();
735                                 shared_by_name = new Dictionary<string, CultureInfo> ();
736                         }
737                         shared_by_number [c.cultureID] = c;
738                         shared_by_name [c.m_name] = c;
739                 }
740                 
741                 public static CultureInfo GetCultureInfo (int culture)
742                 {
743                         if (culture < 1)
744                                 throw new ArgumentOutOfRangeException ("culture", "Positive number required.");
745
746                         CultureInfo c;
747                         
748                         lock (shared_table_lock){
749                                 if (shared_by_number != null) {
750                                         if (shared_by_number.TryGetValue (culture, out c))
751                                                 return c;
752                                 }
753
754                                 c = new CultureInfo (culture, false, true);
755                                 insert_into_shared_tables (c);
756                                 return c;
757                         }
758                 }
759
760                 public static CultureInfo GetCultureInfo (string name)
761                 {
762                         if (name == null)
763                                 throw new ArgumentNullException ("name");
764
765                         CultureInfo c;
766                         lock (shared_table_lock){
767                                 if (shared_by_name != null){
768                                         if (shared_by_name.TryGetValue (name, out c))
769                                                 return c;
770                                 }
771                                 c = new CultureInfo (name, false, true);
772                                 insert_into_shared_tables (c);
773                                 return c;
774                         }
775                 }
776
777                 [MonoTODO ("Currently it ignores the altName parameter")]
778                 public static CultureInfo GetCultureInfo (string name, string altName) {
779                         if (name == null)
780                                 throw new ArgumentNullException ("null");
781                         if (altName == null)
782                                 throw new ArgumentNullException ("null");
783
784                         return GetCultureInfo (name);
785                 }
786
787                 public static CultureInfo GetCultureInfoByIetfLanguageTag (string name)
788                 {
789                         // There could be more consistent way to implement
790                         // it, but right now it works just fine with this...
791                         switch (name) {
792                         case "zh-Hans":
793                                 return GetCultureInfo ("zh-CHS");
794                         case "zh-Hant":
795                                 return GetCultureInfo ("zh-CHT");
796                         default:
797                                 return GetCultureInfo (name);
798                         }
799                 }
800
801                 // used in runtime (icall.c) to construct CultureInfo for
802                 // AssemblyName of assemblies
803                 internal static CultureInfo CreateCulture (string name, bool reference)
804                 {
805                         bool read_only;
806                         bool use_user_override;
807
808                         bool invariant = name.Length == 0;
809                         if (reference) {
810                                 use_user_override = invariant ? false : true;
811                                 read_only = false;
812                         } else {
813                                 read_only = false;
814                                 use_user_override = invariant ? false : true;
815                         }
816
817                         return new CultureInfo (name, use_user_override, read_only);
818                 }
819
820                 public static CultureInfo CreateSpecificCulture (string name)
821                 {
822                         if (name == null)
823                                 throw new ArgumentNullException ("name");
824
825                         if (name.Length == 0)
826                                 return InvariantCulture;
827
828                         var src_name = name;
829                         name = name.ToLowerInvariant ();
830                         CultureInfo ci = new CultureInfo ();
831
832                         if (!ci.construct_internal_locale_from_name (name)) {
833                                 int idx = name.Length - 1;
834                                 if (idx > 0) {
835                                         while ((idx = name.LastIndexOf ('-', idx - 1)) > 0) {
836                                                 if (ci.construct_internal_locale_from_name (name.Substring (0, idx)))
837                                                         break;
838                                         }
839                                 }
840
841                                 if (idx <= 0)
842                                         throw CreateNotFoundException (src_name);
843                         }
844
845                         if (ci.IsNeutralCulture)
846                                 ci = CreateSpecificCultureFromNeutral (ci.Name);
847
848                         var ti = ci.GetTextInfoData ();
849
850                         ci.m_cultureData = CultureData.GetCultureData (ci.m_name, false, ci.datetime_index, ci.CalendarType, ci.number_index, ci.iso2lang,
851                                 ti.ansi, ti.oem, ti.mac, ti.ebcdic, ti.right_to_left, ((char)ti.list_sep).ToString ());
852                         return ci;
853                 }
854
855                 //
856                 // Creates specific culture from neutral culture. Used by CreateSpecificCulture
857                 // only but using separate method we can delay switch underlying Dictionary
858                 // initialization
859                 //
860                 static CultureInfo CreateSpecificCultureFromNeutral (string name)
861                 {
862                         int id;
863
864                         //
865                         // For neutral cultures find predefined default specific culture
866                         //
867                         // Use managed switch because we need this for only some cultures
868                         // and the method is not used frequently
869                         //
870                         // TODO: We could optimize for cultures with single specific culture 
871                         //
872                         switch (name.ToLowerInvariant ()) {
873                         case "af": id = 1078; break;
874                         case "am": id = 1118; break;
875                         case "ar": id = 1025; break;
876                         case "arn": id = 1146; break;
877                         case "as": id = 1101; break;
878                         case "az": id = 1068; break;
879                         case "az-cyrl": id = 2092; break;
880                         case "az-latn": id = 1068; break;
881                         case "ba": id = 1133; break;
882                         case "be": id = 1059; break;
883                         case "bg": id = 1026; break;
884                         case "bn": id = 1093; break;
885                         case "bo": id = 1105; break;
886                         case "br": id = 1150; break;
887                         case "bs": id = 5146; break;
888                         case "bs-cyrl": id = 8218; break;
889                         case "bs-latn": id = 5146; break;
890                         case "ca": id = 1027; break;
891                         case "co": id = 1155; break;
892                         case "cs": id = 1029; break;
893                         case "cy": id = 1106; break;
894                         case "da": id = 1030; break;
895                         case "de": id = 1031; break;
896                         case "dsb": id = 2094; break;
897                         case "dv": id = 1125; break;
898                         case "el": id = 1032; break;
899                         case "en": id = 1033; break;
900                         case "es": id = 3082; break;
901                         case "et": id = 1061; break;
902                         case "eu": id = 1069; break;
903                         case "fa": id = 1065; break;
904                         case "fi": id = 1035; break;
905                         case "fil": id = 1124; break;
906                         case "fo": id = 1080; break;
907                         case "fr": id = 1036; break;
908                         case "fy": id = 1122; break;
909                         case "ga": id = 2108; break;
910                         case "gd": id = 1169; break;
911                         case "gl": id = 1110; break;
912                         case "gsw": id = 1156; break;
913                         case "gu": id = 1095; break;
914                         case "ha": id = 1128; break;
915                         case "ha-latn": id = 1128; break;
916                         case "he": id = 1037; break;
917                         case "hi": id = 1081; break;
918                         case "hr": id = 1050; break;
919                         case "hsb": id = 1070; break;
920                         case "hu": id = 1038; break;
921                         case "hy": id = 1067; break;
922                         case "id": id = 1057; break;
923                         case "ig": id = 1136; break;
924                         case "ii": id = 1144; break;
925                         case "is": id = 1039; break;
926                         case "it": id = 1040; break;
927                         case "iu": id = 2141; break;
928                         case "iu-cans": id = 1117; break;
929                         case "iu-latn": id = 2141; break;
930                         case "ja": id = 1041; break;
931                         case "ka": id = 1079; break;
932                         case "kk": id = 1087; break;
933                         case "kl": id = 1135; break;
934                         case "km": id = 1107; break;
935                         case "kn": id = 1099; break;
936                         case "ko": id = 1042; break;
937                         case "kok": id = 1111; break;
938                         case "ky": id = 1088; break;
939                         case "lb": id = 1134; break;
940                         case "lo": id = 1108; break;
941                         case "lt": id = 1063; break;
942                         case "lv": id = 1062; break;
943                         case "mi": id = 1153; break;
944                         case "mk": id = 1071; break;
945                         case "ml": id = 1100; break;
946                         case "mn": id = 1104; break;
947                         case "mn-cyrl": id = 1104; break;
948                         case "mn-mong": id = 2128; break;
949                         case "moh": id = 1148; break;
950                         case "mr": id = 1102; break;
951                         case "ms": id = 1086; break;
952                         case "mt": id = 1082; break;
953                         case "nb": id = 1044; break;
954                         case "ne": id = 1121; break;
955                         case "nl": id = 1043; break;
956                         case "nn": id = 2068; break;
957                         case "no": id = 1044; break;
958                         case "nso": id = 1132; break;
959                         case "oc": id = 1154; break;
960                         case "or": id = 1096; break;
961                         case "pa": id = 1094; break;
962                         case "pl": id = 1045; break;
963                         case "prs": id = 1164; break;
964                         case "ps": id = 1123; break;
965                         case "pt": id = 1046; break;
966                         case "qut": id = 1158; break;
967                         case "quz": id = 1131; break;
968                         case "rm": id = 1047; break;
969                         case "ro": id = 1048; break;
970                         case "ru": id = 1049; break;
971                         case "rw": id = 1159; break;
972                         case "sa": id = 1103; break;
973                         case "sah": id = 1157; break;
974                         case "se": id = 1083; break;
975                         case "si": id = 1115; break;
976                         case "sk": id = 1051; break;
977                         case "sl": id = 1060; break;
978                         case "sma": id = 7227; break;
979                         case "smj": id = 5179; break;
980                         case "smn": id = 9275; break;
981                         case "sms": id = 8251; break;
982                         case "sq": id = 1052; break;
983                         case "sr": id = 9242; break;
984                         case "sr-cyrl": id = 10266; break;
985                         case "sr-latn": id = 9242; break;
986                         case "sv": id = 1053; break;
987                         case "sw": id = 1089; break;
988                         case "syr": id = 1114; break;
989                         case "ta": id = 1097; break;
990                         case "te": id = 1098; break;
991                         case "tg": id = 1064; break;
992                         case "tg-cyrl": id = 1064; break;
993                         case "th": id = 1054; break;
994                         case "tk": id = 1090; break;
995                         case "tn": id = 1074; break;
996                         case "tr": id = 1055; break;
997                         case "tt": id = 1092; break;
998                         case "tzm": id = 2143; break;
999                         case "tzm-latn": id = 2143; break;
1000                         case "ug": id = 1152; break;
1001                         case "uk": id = 1058; break;
1002                         case "ur": id = 1056; break;
1003                         case "uz": id = 1091; break;
1004                         case "uz-cyrl": id = 2115; break;
1005                         case "uz-latn": id = 1091; break;
1006                         case "vi": id = 1066; break;
1007                         case "wo": id = 1160; break;
1008                         case "xh": id = 1076; break;
1009                         case "yo": id = 1130; break;
1010                         case "zh": id = 2052; break;
1011                         case "zh-chs":
1012                         case "zh-hans":
1013                                 id = 2052; break;
1014                         case "zh-cht":
1015                         case "zh-hant":
1016                                 id = 3076; break;
1017                         case "zu": id = 1077; break;
1018                         default:
1019                                 throw new NotImplementedException ("Mapping for neutral culture " + name);
1020                         }
1021
1022                         return new CultureInfo (id);
1023                 }
1024
1025                 internal int CalendarType {
1026                         get {
1027                                 switch (default_calendar_type >> CalendarTypeBits) {
1028                                 case 1:
1029                                         return Calendar.CAL_GREGORIAN;
1030                                 case 2:
1031                                         return Calendar.CAL_THAI;
1032                                 case 3:
1033                                         return Calendar.CAL_UMALQURA;
1034                                 case 4:
1035                                         return Calendar.CAL_HIJRI;
1036                                 default:
1037                                         throw new NotImplementedException ("CalendarType");
1038                                 }
1039                         }
1040                 }
1041
1042                 static Calendar CreateCalendar (int calendarType)
1043                 {
1044                         string name = null;
1045                         switch (calendarType >> CalendarTypeBits) {
1046                         case 1:
1047                                 GregorianCalendarTypes greg_type;
1048                                 greg_type = (GregorianCalendarTypes) (calendarType & 0xFF);
1049                                 return new GregorianCalendar (greg_type);
1050                         case 2:
1051                                 name = "System.Globalization.ThaiBuddhistCalendar";
1052                                 break;
1053                         case 3:
1054                                 name = "System.Globalization.UmAlQuraCalendar";
1055                                 break;
1056                         case 4:
1057                                 name = "System.Globalization.HijriCalendar";
1058                                 break;
1059                         default:
1060                                 throw new NotImplementedException ("Unknown calendar type: " + calendarType);
1061                         }
1062
1063                         Type type = Type.GetType (name, false);
1064                         if (type == null)
1065                                 return new GregorianCalendar (GregorianCalendarTypes.Localized); // return invariant calendar if not found
1066                         return (Calendar) Activator.CreateInstance (type);
1067                 }
1068
1069                 static Exception CreateNotFoundException (string name)
1070                 {
1071                         return new CultureNotFoundException ("name", "Culture name " + name + " is not supported.");
1072                 }
1073                 
1074                 public static CultureInfo DefaultThreadCurrentCulture {
1075                         get {
1076                                 return s_DefaultThreadCurrentCulture;
1077                         }
1078                         set {
1079                                 s_DefaultThreadCurrentCulture = value;
1080                         }
1081                 }
1082                 
1083                 public static CultureInfo DefaultThreadCurrentUICulture {
1084                         get {
1085                                 return s_DefaultThreadCurrentUICulture;
1086                         }
1087                         set {
1088                                 s_DefaultThreadCurrentUICulture = value;
1089                         }
1090                 }
1091
1092                 internal string SortName {
1093                         get {
1094                                 return m_name;
1095                         }
1096                 }
1097
1098                 internal static CultureInfo UserDefaultUICulture {
1099                         get {
1100                                 return ConstructCurrentUICulture ();
1101                         }
1102                 }
1103
1104                 internal static CultureInfo UserDefaultCulture {
1105                         get {
1106                                 return ConstructCurrentCulture ();
1107                         }
1108                 }
1109
1110
1111 #region reference sources
1112                 // TODO:
1113                 internal static readonly bool IsTaiwanSku;
1114
1115         //
1116         // CheckDomainSafetyObject throw if the object is customized object which cannot be attached to 
1117         // other object (like CultureInfo or DateTimeFormatInfo).
1118         //
1119
1120         internal static void CheckDomainSafetyObject(Object obj, Object container)
1121         {
1122             if (obj.GetType().Assembly != typeof(System.Globalization.CultureInfo).Assembly) {
1123                 
1124                 throw new InvalidOperationException(
1125                             String.Format(
1126                                 CultureInfo.CurrentCulture, 
1127                                 Environment.GetResourceString("InvalidOperation_SubclassedObject"), 
1128                                 obj.GetType(),
1129                                 container.GetType()));
1130             }
1131             Contract.EndContractBlock();
1132         }
1133
1134         // For resource lookup, we consider a culture the invariant culture by name equality.
1135         // We perform this check frequently during resource lookup, so adding a property for
1136         // improved readability.
1137         internal bool HasInvariantCultureName
1138         {
1139             get { return Name == CultureInfo.InvariantCulture.Name; }
1140         }
1141
1142         internal static bool VerifyCultureName(String cultureName, bool throwException)
1143         {
1144             // This function is used by ResourceManager.GetResourceFileName(). 
1145             // ResourceManager searches for resource using CultureInfo.Name,
1146             // so we should check against CultureInfo.Name.
1147
1148             for (int i=0; i<cultureName.Length; i++) {
1149                 char c = cultureName[i];
1150                 // 
1151
1152                 if (Char.IsLetterOrDigit(c) || c=='-' || c=='_') {
1153                     continue;
1154                 }
1155                 if (throwException) {
1156                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidResourceCultureName", cultureName));
1157                 }
1158                 return false;
1159             }
1160             return true;
1161         }
1162
1163         internal static bool VerifyCultureName(CultureInfo culture, bool throwException) {
1164             Contract.Assert(culture!=null, "[CultureInfo.VerifyCultureName]culture!=null");
1165
1166             //If we have an instance of one of our CultureInfos, the user can't have changed the
1167             //name and we know that all names are valid in files.
1168             if (!culture.m_isInherited) {
1169                 return true;
1170             }
1171
1172             return VerifyCultureName(culture.Name, throwException);
1173
1174         }
1175
1176 #endregion
1177         }
1178 }