New tests.
[mono.git] / mcs / class / corlib / System.Globalization / CultureInfo.cs
1 //
2 // System.Globalization.CultureInfo.cs
3 //
4 // Miguel de Icaza (miguel@ximian.com)
5 // Dick Porter (dick@ximian.com)
6 //
7 // (C) 2001, 2002, 2003 Ximian, Inc. (http://www.ximian.com)
8 //
9
10 //
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.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;
34 using System.Threading;
35 using System.Runtime.CompilerServices;
36 using System.Runtime.InteropServices;
37
38 namespace System.Globalization
39 {
40         [System.Runtime.InteropServices.ComVisible (true)]
41         [Serializable]
42         public class CultureInfo : ICloneable, IFormatProvider
43         {
44                 static volatile CultureInfo invariant_culture_info;
45                 static object shared_table_lock = new object ();
46                 internal static int BootstrapCultureID;
47
48                 const int NumOptionalCalendars = 5;
49                 const int GregorianTypeMask = 0x00FFFFFF;
50                 const int CalendarTypeBits = 24;
51
52 #pragma warning disable 169, 649
53                 bool m_isReadOnly;
54                 int  cultureID;
55                 [NonSerialized]
56                 int parent_lcid;
57                 [NonSerialized]
58                 int specific_lcid;
59                 [NonSerialized]
60                 int datetime_index;
61                 [NonSerialized]
62                 int number_index;
63                 bool m_useUserOverride;
64                 [NonSerialized]
65                 volatile NumberFormatInfo numInfo;
66                 volatile DateTimeFormatInfo dateTimeInfo;
67                 volatile TextInfo textInfo;
68                 private string m_name;
69                 
70                 [NonSerialized]
71                 private string displayname;
72                 [NonSerialized]
73                 private string englishname;
74                 [NonSerialized]
75                 private string nativename;
76                 [NonSerialized]
77                 private string iso3lang;
78                 [NonSerialized]
79                 private string iso2lang;
80                 [NonSerialized]
81                 private string icu_name;
82                 [NonSerialized]
83                 private string win3lang;
84                 [NonSerialized]
85                 private string territory;
86                 volatile CompareInfo compareInfo;
87                 [NonSerialized]
88                 private unsafe readonly int *calendar_data;
89                 [NonSerialized]
90                 private unsafe readonly void *textinfo_data;
91                 [NonSerialized]
92                 private Calendar [] optional_calendars;
93                 [NonSerialized]
94                 CultureInfo parent_culture;
95
96                 int m_dataItem;         // MS.NET serializes this.
97                 Calendar calendar;      // MS.NET serializes this.
98 #pragma warning restore 169, 649
99
100                 // Deserialized instances will set this to false
101                 [NonSerialized]
102                 bool constructed;
103
104                 [NonSerialized]
105                 // Used by Thread.set_CurrentCulture
106                 internal byte[] cached_serialized_form;
107                 
108                 const int InvariantCultureId = 0x7F;
109
110                 private static readonly string MSG_READONLY = "This instance is read only";
111                 
112                 static public CultureInfo InvariantCulture {
113                         get {
114                                 return invariant_culture_info;
115                         }
116                 }
117
118                 static CultureInfo ()
119                 {
120                         invariant_culture_info = new CultureInfo (InvariantCultureId, false, true);
121                 }
122                 
123                 public static CultureInfo CreateSpecificCulture (string name)
124                 {
125                         if (name == null) {
126                                 throw new ArgumentNullException ("name");
127                         }
128
129                         if (name == String.Empty)
130                                 return InvariantCulture;
131
132                         CultureInfo ci = new CultureInfo ();
133                         if (!ConstructInternalLocaleFromSpecificName (ci, name.ToLowerInvariant ()))
134                                 throw new ArgumentException ("Culture name " + name +
135                                                 " is not supported.", name);
136
137                         return ci;
138                 }
139
140                 public static CultureInfo CurrentCulture 
141                 {
142                         get {
143                                 return Thread.CurrentThread.CurrentCulture;
144                         }
145                 }
146
147                 public static CultureInfo CurrentUICulture 
148                 {
149                         get {
150                                 return Thread.CurrentThread.CurrentUICulture;
151                         }
152                 }
153
154                 internal static CultureInfo ConstructCurrentCulture ()
155                 {
156                         CultureInfo ci = new CultureInfo ();
157                         if (!ConstructInternalLocaleFromCurrentLocale (ci))
158                                 ci = InvariantCulture;
159                         BootstrapCultureID = ci.cultureID;
160                         return ci;
161                 }
162
163                 internal static CultureInfo ConstructCurrentUICulture ()
164                 {
165                         return ConstructCurrentCulture ();
166                 }
167
168                 // it is used for RegionInfo.
169                 internal string Territory {
170                         get { return territory; }
171                 }
172
173 #if !NET_2_1
174                 // FIXME: It is implemented, but would be hell slow.
175                 [ComVisible (false)]
176                 public CultureTypes CultureTypes {
177                         get {
178                                 CultureTypes ret = (CultureTypes) 0;
179                                 foreach (CultureTypes v in Enum.GetValues (typeof (CultureTypes)))
180                                         if (Array.IndexOf (GetCultures (v), this) >= 0)
181                                                 ret |= v;
182                                 return ret;
183                         }
184                 }
185
186                 [ComVisible (false)]
187                 public CultureInfo GetConsoleFallbackUICulture ()
188                 {
189                         // as documented in MSDN ...
190                         switch (Name) {
191                         case "ar": case "ar-BH": case "ar-EG": case "ar-IQ":
192                         case "ar-JO": case "ar-KW": case "ar-LB": case "ar-LY":
193                         case "ar-QA": case "ar-SA": case "ar-SY": case "ar-AE":
194                         case "ar-YE":
195                         case "dv": case "dv-MV":
196                         case "fa": case "fa-IR":
197                         case "gu": case "gu-IN":
198                         case "he": case "he-IL":
199                         case "hi": case "hi-IN":
200                         case "kn": case "kn-IN":
201                         case "kok": case "kok-IN":
202                         case "mr": case "mr-IN":
203                         case "pa": case "pa-IN":
204                         case "sa": case "sa-IN":
205                         case "syr": case "syr-SY":
206                         case "ta": case "ta-IN":
207                         case "te": case "te-IN":
208                         case "th": case "th-TH":
209                         case "ur": case "ur-PK":
210                         case "vi": case "vi-VN":
211                                 return GetCultureInfo ("en");
212                         case "ar-DZ": case "ar-MA": case "ar-TN":
213                                 return GetCultureInfo ("fr");
214                         }
215                         return (CultureTypes & CultureTypes.WindowsOnlyCultures) != 0 ? CultureInfo.InvariantCulture : this;
216                 }
217
218                 [ComVisible (false)]
219                 public string IetfLanguageTag {
220                         // There could be more consistent way to implement
221                         // it, but right now it works just fine with this...
222                         get {
223                                 switch (Name) {
224                                 case "zh-CHS":
225                                         return "zh-Hans";
226                                 case "zh-CHT":
227                                         return "zh-Hant";
228                                 default:
229                                         return Name;
230                                 }
231                         }
232                 }
233
234                 // For specific cultures it basically returns LCID.
235                 // For neutral cultures it is mapped to the default(?) specific
236                 // culture, where the LCID of the specific culture seems to be
237                 // n + 1024 by default. zh-CHS is the only exception which is 
238                 // mapped to 2052, not 1028 (zh-CHT is mapped to 1028 instead).
239                 // There are very few exceptions, here I simply list them here.
240                 // It is Windows-specific property anyways, so no worthy of
241                 // trying to do some complex things with locale-builder.
242                 [ComVisible (false)]
243                 public virtual int KeyboardLayoutId {
244                         get {
245                                 switch (LCID) {
246                                 case 4: // zh-CHS (neutral)
247                                         return 2052;
248                                 case 1034: // es-ES Spanish 2
249                                         return 3082;
250                                 case 31748: // zh-CHT (neutral)
251                                         return 1028;
252                                 case 31770: // sr (neutral)
253                                         return 2074;
254                                 default:
255                                         return LCID < 1024 ? LCID + 1024 : LCID;
256                                 }
257                         }
258                 }
259 #endif
260
261                 public virtual int LCID {
262                         get {
263                                 return cultureID;
264                         }
265                 }
266
267                 public virtual string Name {
268                         get {
269 #if MOONLIGHT
270                                 if (m_name == "zh-CHS")
271                                         return "zh-Hans";
272                                 if (m_name == "zh-CHT")
273                                         return "zh-Hant";
274 #endif
275                                 return(m_name);
276                         }
277                 }
278
279                 public virtual string NativeName
280                 {
281                         get {
282                                 if (!constructed) Construct ();
283                                 return(nativename);
284                         }
285                 }
286                 
287                 public virtual Calendar Calendar
288                 {
289                         get { return DateTimeFormat.Calendar; }
290                 }
291
292                 public virtual Calendar[] OptionalCalendars
293                 {
294                         get {
295                                 if (optional_calendars == null) {
296                                         lock (this) {
297                                                 if (optional_calendars == null)
298                                                         ConstructCalendars ();
299                                         }
300                                 }
301                                 return optional_calendars;
302                         }
303                 }
304
305                 public virtual CultureInfo Parent
306                 {
307                         get {
308                                 if (parent_culture == null) {
309                                         if (!constructed)
310                                                 Construct ();
311                                         if (parent_lcid == cultureID)
312                                                 return null;
313                                         
314                                         if (parent_lcid == InvariantCultureId)
315                                                 parent_culture = InvariantCulture;
316                                         else if (cultureID == InvariantCultureId)
317                                                 parent_culture = this;
318                                         else
319                                                 parent_culture = new CultureInfo (parent_lcid);
320                                 }
321                                 return parent_culture;
322                         }
323                 }
324
325                 public virtual TextInfo TextInfo
326                 {
327                         get {
328                                 if (textInfo == null) {
329                                         if (!constructed) Construct ();
330                                         lock (this) {
331                                                 if(textInfo == null) {
332                                                         textInfo = CreateTextInfo (m_isReadOnly);
333                                                 }
334                                         }
335                                 }
336                                 
337                                 return(textInfo);
338                         }
339                 }
340
341                 public virtual string ThreeLetterISOLanguageName
342                 {
343                         get {
344                                 if (!constructed) Construct ();
345                                 return(iso3lang);
346                         }
347                 }
348
349                 public virtual string ThreeLetterWindowsLanguageName
350                 {
351                         get {
352                                 if (!constructed) Construct ();
353                                 return(win3lang);
354                         }
355                 }
356
357                 public virtual string TwoLetterISOLanguageName
358                 {
359                         get {
360                                 if (!constructed) Construct ();
361                                 return(iso2lang);
362                         }
363                 }
364
365                 public bool UseUserOverride
366                 {
367                         get {
368                                 return m_useUserOverride;
369                         }
370                 }
371
372                 internal string IcuName {
373                         get {
374                                 if (!constructed) Construct ();
375                                 return icu_name;
376                         }
377                 }
378
379                 public void ClearCachedData()
380                 {
381                         Thread.CurrentThread.CurrentCulture = null;
382                         Thread.CurrentThread.CurrentUICulture = null;
383                 }
384
385                 public virtual object Clone()
386                 {
387                         if (!constructed) Construct ();
388                         CultureInfo ci=(CultureInfo)MemberwiseClone ();
389                         ci.m_isReadOnly=false;
390                         ci.cached_serialized_form=null;
391                         if (!IsNeutralCulture) {
392                                 ci.NumberFormat = (NumberFormatInfo)NumberFormat.Clone ();
393                                 ci.DateTimeFormat = (DateTimeFormatInfo)DateTimeFormat.Clone ();
394                         }
395                         return(ci);
396                 }
397
398                 public override bool Equals (object value)
399                 {
400                         CultureInfo b = value as CultureInfo;
401                         
402                         if (b != null)
403                                 return b.cultureID == cultureID;
404                         return false;
405                 }
406
407 #if !MOONLIGHT
408                 public static CultureInfo[] GetCultures(CultureTypes types)
409                 {
410                         bool neutral=((types & CultureTypes.NeutralCultures)!=0);
411                         bool specific=((types & CultureTypes.SpecificCultures)!=0);
412                         bool installed=((types & CultureTypes.InstalledWin32Cultures)!=0);  // TODO
413
414                         CultureInfo [] infos = internal_get_cultures (neutral, specific, installed);
415                         // The runtime returns a NULL in the first position of the array when
416                         // 'neutral' is true. We fill it in with a clone of InvariantCulture
417                         // since it must not be read-only
418                         if (neutral && infos.Length > 0 && infos [0] == null) {
419                                 infos [0] = (CultureInfo) InvariantCulture.Clone ();
420                         }
421
422                         return infos;
423                 }
424 #endif
425
426                 public override int GetHashCode()
427                 {
428                         return cultureID;
429                 }
430
431                 public static CultureInfo ReadOnly(CultureInfo ci)
432                 {
433                         if(ci==null) {
434                                 throw new ArgumentNullException("ci");
435                         }
436
437                         if(ci.m_isReadOnly) {
438                                 return(ci);
439                         } else {
440                                 CultureInfo new_ci=(CultureInfo)ci.Clone ();
441                                 new_ci.m_isReadOnly=true;
442                                 if (new_ci.numInfo != null)
443                                         new_ci.numInfo = NumberFormatInfo.ReadOnly (new_ci.numInfo);
444                                 if (new_ci.dateTimeInfo != null)
445                                         new_ci.dateTimeInfo = DateTimeFormatInfo.ReadOnly (new_ci.dateTimeInfo);
446                                 // TextInfo doesn't have a ReadOnly method in 1.1...
447                                 if (new_ci.textInfo != null)
448                                         new_ci.textInfo = TextInfo.ReadOnly (new_ci.textInfo);
449                                 return(new_ci);
450                         }
451                 }
452
453                 public override string ToString()
454                 {
455                         return(m_name);
456                 }
457                 
458                 public virtual CompareInfo CompareInfo
459                 {
460                         get {
461                                 if(compareInfo==null) {
462                                         if (!constructed)
463                                                 Construct ();
464
465                                         lock (this) {
466                                                 if(compareInfo==null) {
467                                                         compareInfo=new CompareInfo (this);
468                                                 }
469                                         }
470                                 }
471                                 
472                                 return(compareInfo);
473                         }
474                 }
475
476                 internal static bool IsIDNeutralCulture (int lcid)
477                 {
478                         bool ret;
479                         if (!internal_is_lcid_neutral (lcid, out ret))
480                                 throw new ArgumentException (String.Format ("Culture id 0x{:x4} is not supported.", lcid));
481                                 
482                         return ret;
483                 }
484
485                 public virtual bool IsNeutralCulture {
486                         get {
487                                 if (!constructed) Construct ();
488                                 if (cultureID == InvariantCultureId)
489                                         return false;
490
491                                 return ((cultureID & 0xff00) == 0 || specific_lcid == 0);
492                         }
493                 }
494
495                 internal void CheckNeutral ()
496                 {
497 #if !MOONLIGHT
498                         if (IsNeutralCulture) {
499                                 throw new NotSupportedException ("Culture \"" + m_name + "\" is " +
500                                                 "a neutral culture. It can not be used in formatting " +
501                                                 "and parsing and therefore cannot be set as the thread's " +
502                                                 "current culture.");
503                         }
504 #endif
505                 }
506
507                 public virtual NumberFormatInfo NumberFormat {
508                         get {
509                                 if (!constructed) Construct ();
510                                 CheckNeutral ();
511                                 if (numInfo == null){
512                                         lock (this){
513                                                 if (numInfo == null) {
514                                                         numInfo = new NumberFormatInfo (m_isReadOnly);
515                                                         construct_number_format ();
516                                                 }
517                                         }
518                                 }
519
520                                 return numInfo;
521                         }
522
523                         set {
524                                 if (!constructed) Construct ();
525                                 if (m_isReadOnly) throw new InvalidOperationException(MSG_READONLY);
526
527                                 if (value == null)
528                                         throw new ArgumentNullException ("NumberFormat");
529                                 
530                                 numInfo = value;
531                         }
532                 }
533
534                 public virtual DateTimeFormatInfo DateTimeFormat
535                 {
536                         get 
537                         {
538                                 if (!constructed) Construct ();
539                                 CheckNeutral ();
540                                 if (dateTimeInfo == null)
541                                 {
542                                         lock (this)
543                                         {
544                                                 if (dateTimeInfo == null) {
545                                                         dateTimeInfo = new DateTimeFormatInfo(m_isReadOnly);
546                                                         construct_datetime_format ();
547                                                         if (optional_calendars != null)
548                                                                 dateTimeInfo.Calendar = optional_calendars [0];
549                                                 }
550                                         }
551                                 }
552
553                                 return dateTimeInfo;
554                         }
555
556                         set 
557                         {
558                                 if (!constructed) Construct ();
559                                 if (m_isReadOnly) throw new InvalidOperationException(MSG_READONLY);
560
561                                 if (value == null)
562                                         throw new ArgumentNullException ("DateTimeFormat");
563                                 
564                                 dateTimeInfo = value;
565                         }
566                 }
567
568                 public virtual string DisplayName
569                 {
570                         get {
571                                 if (!constructed) Construct ();
572                                 return(displayname);
573                         }
574                 }
575
576                 public virtual string EnglishName
577                 {
578                         get {
579                                 if (!constructed) Construct ();
580                                 return(englishname);
581                         }
582                 }
583
584                 public static CultureInfo InstalledUICulture
585                 {
586                         get { return GetCultureInfo (BootstrapCultureID); }
587                 }
588                 public bool IsReadOnly 
589                 {
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                 bool ConstructInternalLocaleFromName (string locale)
618                 {
619                         // It is sort of hack to get those new pseudo-alias
620                         // culture names that are not supported in good old
621                         // Windows.
622 #if MOONLIGHT
623                         if (locale == "zh-chs" || locale == "zh-cht")
624                                 return false;
625 #endif
626                         switch (locale) {
627                         case "zh-hans":
628                                 locale = "zh-chs";
629                                 break;
630                         case "zh-hant":
631                                 locale = "zh-cht";
632                                 break;
633                         }
634
635                         if (!construct_internal_locale_from_name (locale))
636                                 return false;
637                         return true;
638                 }
639
640                 bool ConstructInternalLocaleFromLcid (int lcid)
641                 {
642                         if (!construct_internal_locale_from_lcid (lcid))
643                                 return false;
644                         return true;
645                 }
646
647                 static bool ConstructInternalLocaleFromSpecificName (CultureInfo ci, string name)
648                 {
649                         if (!construct_internal_locale_from_specific_name (ci, name))
650                                 return false;
651                         return true;
652                 }
653
654                 static bool ConstructInternalLocaleFromCurrentLocale (CultureInfo ci)
655                 {
656                         if (!construct_internal_locale_from_current_locale (ci))
657                                 return false;
658                         return true;
659                 }
660
661                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
662                 private extern bool construct_internal_locale_from_lcid (int lcid);
663
664                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
665                 private extern bool construct_internal_locale_from_name (string name);
666
667                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
668                 private extern static bool construct_internal_locale_from_specific_name (CultureInfo ci,
669                                 string name);
670
671                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
672                 private extern static bool construct_internal_locale_from_current_locale (CultureInfo ci);
673
674                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
675                 private extern static CultureInfo [] internal_get_cultures (bool neutral, bool specific, bool installed);
676
677                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
678                 private extern void construct_datetime_format ();
679
680                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
681                 private extern void construct_number_format ();
682
683                 // Returns false if the culture can not be found, sets is_neutral if it is
684                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
685                 private extern static bool internal_is_lcid_neutral (int lcid, out bool is_neutral);
686
687                 private void ConstructInvariant (bool read_only)
688                 {
689                         cultureID = InvariantCultureId;
690
691                         /* NumberFormatInfo defaults to the invariant data */
692                         numInfo=NumberFormatInfo.InvariantInfo;
693                         /* DateTimeFormatInfo defaults to the invariant data */
694                         dateTimeInfo=DateTimeFormatInfo.InvariantInfo;
695
696                         if (!read_only) {
697                                 numInfo = (NumberFormatInfo) numInfo.Clone ();
698                                 dateTimeInfo = (DateTimeFormatInfo) dateTimeInfo.Clone ();
699                         }
700
701                         textInfo = CreateTextInfo (read_only);
702
703                         m_name=String.Empty;
704                         displayname=
705                         englishname=
706                         nativename="Invariant Language (Invariant Country)";
707                         iso3lang="IVL";
708                         iso2lang="iv";
709                         icu_name="en_US_POSIX";
710                         win3lang="IVL";
711                 }
712
713                 private unsafe TextInfo CreateTextInfo (bool readOnly)
714                 {
715                         return new TextInfo (this, cultureID, this.textinfo_data, readOnly);
716                 }
717
718                 public CultureInfo (int culture) : this (culture, true) {}
719
720                 public CultureInfo (int culture, bool useUserOverride) :
721                         this (culture, useUserOverride, false) {}
722
723                 private CultureInfo (int culture, bool useUserOverride, bool read_only)
724                 {
725                         if (culture < 0)
726                                 throw new ArgumentOutOfRangeException ("culture", "Positive "
727                                         + "number required.");
728
729                         constructed = true;
730                         m_isReadOnly = read_only;
731                         m_useUserOverride = useUserOverride;
732
733                         if (culture == InvariantCultureId) {
734                                 /* Short circuit the invariant culture */
735                                 ConstructInvariant (read_only);
736                                 return;
737                         }
738
739                         if (!ConstructInternalLocaleFromLcid (culture))
740                                 throw new ArgumentException (
741                                         String.Format ("Culture ID {0} (0x{0:X4}) is not a " +
742                                                         "supported culture.", culture), "culture");
743                 }
744
745                 public CultureInfo (string name) : this (name, true) {}
746
747                 public CultureInfo (string name, bool useUserOverride) :
748                         this (name, useUserOverride, false) {}
749
750                 private CultureInfo (string name, bool useUserOverride, bool read_only)
751                 {
752                         if (name == null)
753                                 throw new ArgumentNullException ("name");
754
755                         constructed = true;
756                         m_isReadOnly = read_only;
757                         m_useUserOverride = useUserOverride;
758
759                         if (name.Length == 0) {
760                                 /* Short circuit the invariant culture */
761                                 ConstructInvariant (read_only);
762                                 return;
763                         }
764
765                         if (!ConstructInternalLocaleFromName (name.ToLowerInvariant ()))
766                                 throw new ArgumentException ("Culture name " + name +
767                                                 " is not supported.", "name");
768                 }
769
770                 // This is used when creating by specific name and creating by
771                 // current locale so we can initialize the object without
772                 // doing any member initialization
773                 private CultureInfo () { constructed = true; }
774                 static Hashtable shared_by_number, shared_by_name;
775                 
776                 static void insert_into_shared_tables (CultureInfo c)
777                 {
778                         if (shared_by_number == null){
779                                 shared_by_number = new Hashtable ();
780                                 shared_by_name = new Hashtable ();
781                         }
782                         shared_by_number [c.cultureID] = c;
783                         shared_by_name [c.m_name] = c;
784                 }
785                 
786                 public static CultureInfo GetCultureInfo (int culture)
787                 {
788                         CultureInfo c;
789                         
790                         lock (shared_table_lock){
791                                 if (shared_by_number != null){
792                                         c = shared_by_number [culture] as CultureInfo;
793
794                                         if (c != null)
795                                                 return (CultureInfo) c;
796                                 }
797                                 c = new CultureInfo (culture, false, true);
798                                 insert_into_shared_tables (c);
799                                 return c;
800                         }
801                 }
802
803                 public static CultureInfo GetCultureInfo (string name)
804                 {
805                         if (name == null)
806                                 throw new ArgumentNullException ("name");
807
808                         CultureInfo c;
809                         lock (shared_table_lock){
810                                 if (shared_by_name != null){
811                                         c = shared_by_name [name] as CultureInfo;
812
813                                         if (c != null)
814                                                 return (CultureInfo) c;
815                                 }
816                                 c = new CultureInfo (name, false, true);
817                                 insert_into_shared_tables (c);
818                                 return c;
819                         }
820                 }
821
822                 [MonoTODO ("Currently it ignores the altName parameter")]
823                 public static CultureInfo GetCultureInfo (string name, string altName) {
824                         if (name == null)
825                                 throw new ArgumentNullException ("null");
826                         if (altName == null)
827                                 throw new ArgumentNullException ("null");
828
829                         return GetCultureInfo (name);
830                 }
831
832                 public static CultureInfo GetCultureInfoByIetfLanguageTag (string name)
833                 {
834                         // There could be more consistent way to implement
835                         // it, but right now it works just fine with this...
836                         switch (name) {
837                         case "zh-Hans":
838                                 return GetCultureInfo ("zh-CHS");
839                         case "zh-Hant":
840                                 return GetCultureInfo ("zh-CHT");
841                         default:
842                                 return GetCultureInfo (name);
843                         }
844                 }
845
846                 // used in runtime (icall.c) to construct CultureInfo for
847                 // AssemblyName of assemblies
848                 internal static CultureInfo CreateCulture (string name, bool reference)
849                 {
850                         bool read_only;
851                         bool use_user_override;
852
853                         bool invariant = name.Length == 0;
854                         if (reference) {
855                                 use_user_override = invariant ? false : true;
856                                 read_only = false;
857                         } else {
858                                 read_only = false;
859                                 use_user_override = invariant ? false : true;
860                         }
861
862                         return new CultureInfo (name, use_user_override, read_only);
863                 }
864
865                 unsafe internal void ConstructCalendars ()
866                 {
867                         if (calendar_data == null) {
868                                 optional_calendars = new Calendar [] {new GregorianCalendar (GregorianCalendarTypes.Localized)};
869                                 return;
870                         }
871
872                         optional_calendars = new Calendar [NumOptionalCalendars];
873
874                         for (int i=0; i<NumOptionalCalendars; i++) {
875                                 Calendar cal = null;
876                                 int caldata = *(calendar_data + i);
877                                 int caltype = (caldata >> CalendarTypeBits);
878                                 switch (caltype) {
879                                 case 0:
880                                         GregorianCalendarTypes greg_type;
881                                         greg_type = (GregorianCalendarTypes) (caldata & GregorianTypeMask);
882                                         cal = new GregorianCalendar (greg_type);
883                                         break;
884                                 case 1:
885                                         cal = new HijriCalendar ();
886                                         break;
887                                 case 2:
888                                         cal = new ThaiBuddhistCalendar ();
889                                         break;
890                                 default:
891                                         throw new Exception ("invalid calendar type:  " + caldata);
892                                 }
893                                 optional_calendars [i] = cal;
894                         }
895                 }
896         }
897 }