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