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