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