2004-05-27 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / corlib / System.Globalization / CultureInfo.cs
1 //
2 // System.Globalization.CultureInfo.cs
3 //
4 // Miguel de Icaza (miguel@ximian.com)
5 // Dick Porter (dick@ximian.com)
6 //
7 // (C) 2001, 2002, 2003 Ximian, Inc. (http://www.ximian.com)
8 //
9
10 using System.Collections;
11 using System.Threading;
12 using System.Runtime.CompilerServices;
13
14 namespace System.Globalization
15 {
16         [Serializable]
17         public class CultureInfo : ICloneable, IFormatProvider
18         {
19                 static CultureInfo invariant_culture_info;
20
21                 const int NumOptionalCalendars = 5;
22                 const int CalendarTypeMask = 0xFF;
23                 const int CalendarTypeBits = 24;
24
25                 bool m_isReadOnly;
26                 int  cultureID;
27                 [NonSerialized]
28                 int parent_lcid;
29                 [NonSerialized]
30                 int specific_lcid;
31                 [NonSerialized]
32                 int datetime_index;
33                 [NonSerialized]
34                 int number_index;
35                 bool m_useUserOverride;
36                 NumberFormatInfo numInfo;
37                 DateTimeFormatInfo dateTimeInfo;
38                 TextInfo textInfo;
39                 private string m_name;
40                 
41                 [NonSerialized]
42                 private string displayname;
43                 [NonSerialized]
44                 private string englishname;
45                 [NonSerialized]
46                 private string nativename;
47                 [NonSerialized]
48                 private string iso3lang;
49                 [NonSerialized]
50                 private string iso2lang;
51                 [NonSerialized]
52                 private string icu_name;
53                 [NonSerialized]
54                 private string win3lang;
55                 CompareInfo compareInfo;
56                 [NonSerialized]
57                 private unsafe int *calendar_data;
58                 [NonSerialized]
59                 private Calendar [] optional_calendars;
60                                 
61                 int m_dataItem; // MS.NET serializes this.
62                 
63                 // Deserialized instances will set this to false
64                 [NonSerialized]
65                 bool constructed;
66                 
67                 private static readonly string MSG_READONLY = "This instance is read only";
68                 
69                 static public CultureInfo InvariantCulture {
70                         get {
71                                 if (invariant_culture_info == null) {
72                                         lock (typeof (CultureInfo)) {
73                                                 if (invariant_culture_info == null) {
74                                                         invariant_culture_info = new CultureInfo (0x7f, false);
75                                                         invariant_culture_info.m_isReadOnly = true;
76                                                 }
77                                         }
78                                 }
79                                 
80                                 return(invariant_culture_info);
81                         }
82                 }
83
84                 public static CultureInfo CreateSpecificCulture (string name)
85                 {
86                         if (name == null) {
87                                 throw new ArgumentNullException ("name");
88                         }
89
90                         if (name == String.Empty)
91                                 return InvariantCulture;
92
93                         CultureInfo ci = new CultureInfo ();
94                         if (!ConstructInternalLocaleFromSpecificName (ci, name.ToLowerInvariant ()))
95                                 throw new ArgumentException ("Culture name " + name +
96                                                 " is not supported.", name);
97
98                         return ci;
99                 }
100
101                 public static CultureInfo CurrentCulture 
102                 {
103                         get {
104                                 return Thread.CurrentThread.CurrentCulture;
105                         }
106                 }
107
108                 public static CultureInfo CurrentUICulture 
109                 {
110                         get {
111                                 return Thread.CurrentThread.CurrentUICulture;
112                         }
113                 }
114
115                 internal static CultureInfo ConstructCurrentCulture ()
116                 {
117                         CultureInfo ci = new CultureInfo ();
118                         if (!ConstructInternalLocaleFromCurrentLocale (ci))
119                                 ci = InvariantCulture;
120                         return ci;
121                 }
122
123                 internal static CultureInfo ConstructCurrentUICulture ()
124                 {
125                         return ConstructCurrentCulture ();
126                 }
127
128                 public virtual int LCID {
129                         get {
130                                 return cultureID;
131                         }
132                 }
133
134                 public virtual string Name {
135                         get {
136                                 return(m_name);
137                         }
138                 }
139
140                 public virtual string NativeName
141                 {
142                         get {
143                                 if (!constructed) Construct ();
144                                 return(nativename);
145                         }
146                 }
147                 
148                 public virtual Calendar Calendar
149                 {
150                         get { return DateTimeFormat.Calendar; }
151                 }
152
153                 public virtual Calendar[] OptionalCalendars
154                 {
155                         get {
156                                 return optional_calendars;
157                         }
158                 }
159
160                 public virtual CultureInfo Parent
161                 {
162                         get {
163                                 if (cultureID == 0x7f)
164                                         return this;
165                                 if (!constructed) Construct ();
166                                 if (parent_lcid == cultureID)
167                                         return null;
168                                 return new CultureInfo (parent_lcid);
169                         }
170                 }
171
172                 public virtual TextInfo TextInfo
173                 {
174                         get {
175                                 if (textInfo == null) {
176                                         lock (this) {
177                                                 if(textInfo == null) {
178                                                         textInfo = new TextInfo (cultureID);
179                                                 }
180                                         }
181                                 }
182                                 
183                                 return(textInfo);
184                         }
185                 }
186
187                 public virtual string ThreeLetterISOLanguageName
188                 {
189                         get {
190                                 if (!constructed) Construct ();
191                                 return(iso3lang);
192                         }
193                 }
194
195                 public virtual string ThreeLetterWindowsLanguageName
196                 {
197                         get {
198                                 if (!constructed) Construct ();
199                                 return(win3lang);
200                         }
201                 }
202
203                 public virtual string TwoLetterISOLanguageName
204                 {
205                         get {
206                                 if (!constructed) Construct ();
207                                 return(iso2lang);
208                         }
209                 }
210
211                 public bool UseUserOverride
212                 {
213                         get {
214                                 return m_useUserOverride;
215                         }
216                 }
217
218                 internal string IcuName {
219                         get {
220                                 if (!constructed) Construct ();
221                                 return icu_name;
222                         }
223                 }
224
225                 public void ClearCachedData()
226                 {
227                         Thread.CurrentThread.CurrentCulture = null;
228                         Thread.CurrentThread.CurrentUICulture = null;
229                 }
230
231                 public virtual object Clone()
232                 {
233                         if (!constructed) Construct ();
234                         CultureInfo ci=(CultureInfo)MemberwiseClone ();
235                         ci.m_isReadOnly=false;
236                         return(ci);
237                 }
238
239                 public override bool Equals (object value)
240                 {
241                         CultureInfo b = value as CultureInfo;
242                         
243                         if (b != null)
244                                 return b.cultureID == cultureID;
245                         return false;
246                 }
247
248                 public static CultureInfo[] GetCultures(CultureTypes types)
249                 {
250                         bool neutral=((types & CultureTypes.NeutralCultures)!=0);
251                         bool specific=((types & CultureTypes.SpecificCultures)!=0);
252                         bool installed=((types & CultureTypes.InstalledWin32Cultures)!=0);  // TODO
253
254                         return internal_get_cultures (neutral, specific, installed);
255                 }
256
257                 public override int GetHashCode()
258                 {
259                         return cultureID;
260                 }
261
262                 public static CultureInfo ReadOnly(CultureInfo ci)
263                 {
264                         if(ci==null) {
265                                 throw new ArgumentNullException("ci");
266                         }
267
268                         if(ci.m_isReadOnly) {
269                                 return(ci);
270                         } else {
271                                 CultureInfo new_ci=(CultureInfo)ci.Clone ();
272                                 new_ci.m_isReadOnly=true;
273                                 return(new_ci);
274                         }
275                 }
276
277                 public override string ToString()
278                 {
279                         return(m_name);
280                 }
281                 
282                 public virtual CompareInfo CompareInfo
283                 {
284                         get {
285                                 if (!constructed) Construct ();
286                                 if(compareInfo==null) {
287                                         lock (this) {
288                                                 if(compareInfo==null) {
289                                                         compareInfo=new CompareInfo (this);
290                                                 }
291                                         }
292                                 }
293                                 
294                                 return(compareInfo);
295                         }
296                 }
297
298                 internal static bool IsIDNeutralCulture (int lcid)
299                 {
300                         bool ret;
301                         if (!internal_is_lcid_neutral (lcid, out ret))
302                                 throw new ArgumentException (String.Format ("Culture id 0x{:x4} is not supported.", lcid));
303                                 
304                         return ret;
305                 }
306
307                 public virtual bool IsNeutralCulture {
308                         get {
309                                 if (!constructed) Construct ();
310                                 return ((cultureID & 0xff00) == 0 || specific_lcid == 0);
311                         }
312                 }
313
314                 public virtual NumberFormatInfo NumberFormat {
315                         get {
316                                 if (!constructed) Construct ();
317                                 if (IsNeutralCulture && cultureID != 0x7f) {
318                                         throw new NotSupportedException ("Culture \"" + m_name + "\" is " +
319                                                         "a neutral culture. It can not be used in formatting " +
320                                                         "and parsing and therefore cannot be set as the thread's " +
321                                                         "current culture.");
322                                 }
323                                 if (numInfo == null){
324                                         lock (this){
325                                                 if (numInfo == null) {
326                                                         numInfo = new NumberFormatInfo ();
327                                                         construct_number_format ();
328                                                 }
329                                         }
330                                 }
331
332                                 return numInfo;
333                         }
334
335                         set {
336                                 if (!constructed) Construct ();
337                                 if (m_isReadOnly) throw new InvalidOperationException(MSG_READONLY);
338
339                                 if (value == null)
340                                         throw new ArgumentNullException ("NumberFormat");
341                                 
342                                 numInfo = value;
343                         }
344                 }
345
346                 public virtual DateTimeFormatInfo DateTimeFormat
347                 {
348                         get 
349                         {
350                                 if (!constructed) Construct ();
351                                 if (IsNeutralCulture && cultureID != 0x7f) {
352                                         throw new NotSupportedException ("Culture \"" + m_name + "\" is " +
353                                                         "a neutral culture. It can not be used in formatting " +
354                                                         "and parsing and therefore cannot be set as the thread's " +
355                                                         "current culture.");
356                                 }
357                                 if (dateTimeInfo == null)
358                                 {
359                                         lock (this)
360                                         {
361                                                 if (dateTimeInfo == null) {
362                                                         dateTimeInfo = new DateTimeFormatInfo();
363                                                         construct_datetime_format ();
364                                                         if (optional_calendars != null)
365                                                                 dateTimeInfo.Calendar = optional_calendars [0];
366                                                 }
367                                         }
368                                 }
369
370                                 return dateTimeInfo;
371                         }
372
373                         set 
374                         {
375                                 if (!constructed) Construct ();
376                                 if (m_isReadOnly) throw new InvalidOperationException(MSG_READONLY);
377
378                                 if (value == null)
379                                         throw new ArgumentNullException ("DateTimeFormat");
380                                 
381                                 dateTimeInfo = value;
382                         }
383                 }
384
385                 public virtual string DisplayName
386                 {
387                         get {
388                                 if (!constructed) Construct ();
389                                 return(displayname);
390                         }
391                 }
392
393                 public virtual string EnglishName
394                 {
395                         get {
396                                 if (!constructed) Construct ();
397                                 return(englishname);
398                         }
399                 }
400
401                 [MonoTODO]
402                 public static CultureInfo InstalledUICulture
403                 {
404                         get {
405                                 return(null);
406                         }
407                 }
408
409                 public bool IsReadOnly 
410                 {
411                         get {
412                                 return(m_isReadOnly);
413                         }
414                 }
415                 
416
417                 // 
418                 // IFormatProvider implementation
419                 //
420                 public virtual object GetFormat( Type formatType )
421                 {
422                         object format = null;
423
424                         if ( formatType == typeof(NumberFormatInfo) )
425                                 format = NumberFormat;
426                         else if ( formatType == typeof(DateTimeFormatInfo) )
427                                 format = DateTimeFormat;
428                         
429                         return format;
430                 }
431                 
432                 void Construct ()
433                 {
434                         construct_internal_locale_from_lcid (cultureID);
435                         constructed = true;
436                 }
437
438                 bool ConstructInternalLocaleFromName (string locale)
439                 {
440                         if (!construct_internal_locale_from_name (locale))
441                                 return false;
442                         ConstructCalendars ();
443                         return true;
444                 }
445
446                 bool ConstructInternalLocaleFromLcid (int lcid)
447                 {
448                         if (!construct_internal_locale_from_lcid (lcid))
449                                 return false;
450                         ConstructCalendars ();
451                         return true;
452                 }
453
454                 static bool ConstructInternalLocaleFromSpecificName (CultureInfo ci, string name)
455                 {
456                         if (!construct_internal_locale_from_specific_name (ci, name))
457                                 return false;
458                         ci.ConstructCalendars ();
459                         return true;
460                 }
461
462                 static bool ConstructInternalLocaleFromCurrentLocale (CultureInfo ci)
463                 {
464                         if (!construct_internal_locale_from_current_locale (ci))
465                                 return false;
466                         ci.ConstructCalendars ();
467                         return true;
468                 }
469
470                 static CultureInfo [] GetCultures (bool neutral, bool specific, bool installed)
471                 {
472                         CultureInfo [] cis = internal_get_cultures (neutral, specific, installed);
473                         foreach (CultureInfo ci in cis)
474                                 ci.ConstructCalendars ();
475                         return cis;
476                 }
477
478                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
479                 private extern bool construct_internal_locale_from_lcid (int lcid);
480
481                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
482                 private extern bool construct_internal_locale_from_name (string name);
483
484                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
485                 private extern static bool construct_internal_locale_from_specific_name (CultureInfo ci,
486                                 string name);
487
488                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
489                 private extern static bool construct_internal_locale_from_current_locale (CultureInfo ci);
490
491                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
492                 private extern static CultureInfo [] internal_get_cultures (bool neutral, bool specific, bool installed);
493
494                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
495                 private extern void construct_datetime_format ();
496
497                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
498                 private extern void construct_number_format ();
499
500                 // Returns false if the culture can not be found, sets is_neutral if it is
501                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
502                 private extern static bool internal_is_lcid_neutral (int lcid, out bool is_neutral);
503
504                 private void ConstructInvariant (bool use_user_override)
505                 {
506                         m_isReadOnly=false;
507                         cultureID=0x7f;
508                         this.m_useUserOverride=use_user_override;
509
510                         /* NumberFormatInfo defaults to the invariant data */
511                         numInfo=NumberFormatInfo.InvariantInfo;
512                         
513                         /* DateTimeFormatInfo defaults to the invariant data */
514                         dateTimeInfo=DateTimeFormatInfo.InvariantInfo;
515
516                         textInfo=new TextInfo ();
517
518                         m_name="";
519                         displayname="Invariant Language (Invariant Country)";
520                         englishname="Invariant Language (Invariant Country)";
521                         nativename="Invariant Language (Invariant Country)";
522                         iso3lang="IVL";
523                         iso2lang="iv";
524                         icu_name="en_US_POSIX";
525                         win3lang="IVL";
526                 }
527                 
528                 public CultureInfo (int culture, bool use_user_override)
529                 {
530                         if (culture < 0)
531                                 throw new ArgumentOutOfRangeException ("Positive number required.",
532                                                 "culture");
533
534                         constructed = true;
535                         
536                         if(culture==0x007f) {
537                                 /* Short circuit the invariant culture */
538                                 ConstructInvariant (use_user_override);
539                                 return;
540                         }
541
542                         if (!ConstructInternalLocaleFromLcid (culture))
543                                 throw new ArgumentException (
544                                         String.Format ("Culture ID {0} (0x{0:X4}) is not a " +
545                                                         "supported culture.", culture), "culture");
546                 }
547
548                 public CultureInfo (int culture) : this (culture, false) {}
549                 
550                 public CultureInfo (string name, bool use_user_override)
551                 {
552                         if (name == null)
553                                 throw new ArgumentNullException ();
554
555                         constructed = true;
556                         
557                         if(name=="") {
558                                 /* Short circuit the invariant culture */
559                                 ConstructInvariant (use_user_override);
560                                 return;
561                         }
562
563                         if (!ConstructInternalLocaleFromName (name.ToLowerInvariant ()))
564                                 throw new ArgumentException ("Culture name " + name +
565                                                 " is not supported.", "name");
566                         ConstructCalendars ();
567                 }
568
569                 public CultureInfo (string name) : this (name, false) {}
570
571                 // This is used when creating by specific name and creating by
572                 // current locale so we can initialize the object without
573                 // doing any member initialization
574                 private CultureInfo () { constructed = true; } 
575
576                 unsafe internal void ConstructCalendars ()
577                 {
578                         if (calendar_data == null)
579                                 return;
580
581                         optional_calendars = new Calendar [NumOptionalCalendars];
582
583                         for (int i=0; i<NumOptionalCalendars; i++) {
584                                 Calendar cal = null;
585                                 switch (*(calendar_data + i) & CalendarTypeMask >> CalendarTypeBits) {
586                                 case 0:
587                                         int gt = (*(calendar_data + i) & CalendarTypeMask);
588                                         GregorianCalendarTypes type = (GregorianCalendarTypes) gt;
589                                         cal = new GregorianCalendar (type);
590                                         break;
591                                 case 1:
592                                         cal = new HijriCalendar ();
593                                         break;
594                                 case 2:
595                                         cal = new ThaiBuddhistCalendar ();
596                                         break;
597                                 default:
598                                         throw new Exception ("invalid calendar type:  " + *(calendar_data + i));
599                                 }
600                                 optional_calendars [i] = cal;
601                         }
602                 }
603         }
604 }