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