2005-06-07 Gonzalo Paniagua Javier <gonzalo@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 volatile CultureInfo invariant_culture_info;
43
44                 const int NumOptionalCalendars = 5;
45                 const int GregorianTypeMask = 0x00FFFFFF;
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                 volatile NumberFormatInfo numInfo;
60                 volatile DateTimeFormatInfo dateTimeInfo;
61                 volatile 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                 volatile CompareInfo compareInfo;
79                 [NonSerialized]
80                 private unsafe readonly int *calendar_data;
81                 [NonSerialized]
82                 private unsafe readonly void *textinfo_data;
83                 [NonSerialized]
84                 private Calendar [] optional_calendars;
85                                 
86                 int m_dataItem;         // MS.NET serializes this.
87                 Calendar calendar;      // MS.NET serializes this.
88                 
89                 // Deserialized instances will set this to false
90                 [NonSerialized]
91                 bool constructed;
92                 
93                 private static readonly string MSG_READONLY = "This instance is read only";
94                 
95                 static public CultureInfo InvariantCulture {
96                         get {
97                                 return invariant_culture_info;
98                         }
99                 }
100
101                 static CultureInfo ()
102                 {
103                         invariant_culture_info = new CultureInfo (0x7f, false);
104                         invariant_culture_info.m_isReadOnly = true;
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 unsafe virtual TextInfo TextInfo
196                 {
197                         get {
198                                 if (textInfo == null) {
199                                         lock (this) {
200                                                 if(textInfo == null) {
201                                                         textInfo = new TextInfo (this, cultureID, textinfo_data);
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                         ci.NumberFormat = (NumberFormatInfo)NumberFormat.Clone ();
260                         ci.DateTimeFormat = (DateTimeFormatInfo)DateTimeFormat.Clone ();
261                         return(ci);
262                 }
263
264                 public override bool Equals (object value)
265                 {
266                         CultureInfo b = value as CultureInfo;
267                         
268                         if (b != null)
269                                 return b.cultureID == cultureID;
270                         return false;
271                 }
272
273                 public static CultureInfo[] GetCultures(CultureTypes types)
274                 {
275                         bool neutral=((types & CultureTypes.NeutralCultures)!=0);
276                         bool specific=((types & CultureTypes.SpecificCultures)!=0);
277                         bool installed=((types & CultureTypes.InstalledWin32Cultures)!=0);  // TODO
278
279                         CultureInfo [] infos = internal_get_cultures (neutral, specific, installed);
280                         // The runtime returns a NULL in the first position of the array when
281                         // 'neutral' is true. We fill it in with the InvariantCulture.
282                         if (neutral && infos.Length > 0 && infos [0] == null)
283                                 infos [0] = InvariantCulture;
284
285                         return infos;
286                 }
287
288                 public override int GetHashCode()
289                 {
290                         return cultureID;
291                 }
292
293                 public static CultureInfo ReadOnly(CultureInfo ci)
294                 {
295                         if(ci==null) {
296                                 throw new ArgumentNullException("ci");
297                         }
298
299                         if(ci.m_isReadOnly) {
300                                 return(ci);
301                         } else {
302                                 CultureInfo new_ci=(CultureInfo)ci.Clone ();
303                                 new_ci.m_isReadOnly=true;
304                                 return(new_ci);
305                         }
306                 }
307
308                 public override string ToString()
309                 {
310                         return(m_name);
311                 }
312                 
313                 public virtual CompareInfo CompareInfo
314                 {
315                         get {
316                                 if (!constructed) Construct ();
317                                 if(compareInfo==null) {
318                                         lock (this) {
319                                                 if(compareInfo==null) {
320                                                         compareInfo=new CompareInfo (this);
321                                                 }
322                                         }
323                                 }
324                                 
325                                 return(compareInfo);
326                         }
327                 }
328
329                 internal static bool IsIDNeutralCulture (int lcid)
330                 {
331                         bool ret;
332                         if (!internal_is_lcid_neutral (lcid, out ret))
333                                 throw new ArgumentException (String.Format ("Culture id 0x{:x4} is not supported.", lcid));
334                                 
335                         return ret;
336                 }
337
338                 public virtual bool IsNeutralCulture {
339                         get {
340                                 if (!constructed) Construct ();
341                                 if (cultureID == 0x7f)
342                                         return false;
343
344                                 return ((cultureID & 0xff00) == 0 || specific_lcid == 0);
345                         }
346                 }
347
348                 internal void CheckNeutral ()
349                 {
350                         if (IsNeutralCulture) {
351                                 throw new NotSupportedException ("Culture \"" + m_name + "\" is " +
352                                                 "a neutral culture. It can not be used in formatting " +
353                                                 "and parsing and therefore cannot be set as the thread's " +
354                                                 "current culture.");
355                         }
356                 }
357
358                 public virtual NumberFormatInfo NumberFormat {
359                         get {
360                                 if (!constructed) Construct ();
361                                 CheckNeutral ();
362                                 if (numInfo == null){
363                                         lock (this){
364                                                 if (numInfo == null) {
365                                                         numInfo = new NumberFormatInfo ();
366                                                         construct_number_format ();
367                                                 }
368                                         }
369                                 }
370
371                                 return numInfo;
372                         }
373
374                         set {
375                                 if (!constructed) Construct ();
376                                 if (m_isReadOnly) throw new InvalidOperationException(MSG_READONLY);
377
378                                 if (value == null)
379                                         throw new ArgumentNullException ("NumberFormat");
380                                 
381                                 numInfo = value;
382                         }
383                 }
384
385                 public virtual DateTimeFormatInfo DateTimeFormat
386                 {
387                         get 
388                         {
389                                 if (!constructed) Construct ();
390                                 CheckNeutral ();
391                                 if (dateTimeInfo == null)
392                                 {
393                                         lock (this)
394                                         {
395                                                 if (dateTimeInfo == null) {
396                                                         dateTimeInfo = new DateTimeFormatInfo();
397                                                         construct_datetime_format ();
398                                                         if (optional_calendars != null)
399                                                                 dateTimeInfo.Calendar = optional_calendars [0];
400                                                 }
401                                         }
402                                 }
403
404                                 return dateTimeInfo;
405                         }
406
407                         set 
408                         {
409                                 if (!constructed) Construct ();
410                                 if (m_isReadOnly) throw new InvalidOperationException(MSG_READONLY);
411
412                                 if (value == null)
413                                         throw new ArgumentNullException ("DateTimeFormat");
414                                 
415                                 dateTimeInfo = value;
416                         }
417                 }
418
419                 public virtual string DisplayName
420                 {
421                         get {
422                                 if (!constructed) Construct ();
423                                 return(displayname);
424                         }
425                 }
426
427                 public virtual string EnglishName
428                 {
429                         get {
430                                 if (!constructed) Construct ();
431                                 return(englishname);
432                         }
433                 }
434
435                 [MonoTODO]
436                 public static CultureInfo InstalledUICulture
437                 {
438                         get {
439                                 return(null);
440                         }
441                 }
442
443                 public bool IsReadOnly 
444                 {
445                         get {
446                                 return(m_isReadOnly);
447                         }
448                 }
449                 
450
451                 // 
452                 // IFormatProvider implementation
453                 //
454                 public virtual object GetFormat( Type formatType )
455                 {
456                         object format = null;
457
458                         if ( formatType == typeof(NumberFormatInfo) )
459                                 format = NumberFormat;
460                         else if ( formatType == typeof(DateTimeFormatInfo) )
461                                 format = DateTimeFormat;
462                         
463                         return format;
464                 }
465                 
466                 void Construct ()
467                 {
468                         construct_internal_locale_from_lcid (cultureID);
469                         constructed = true;
470                 }
471
472                 bool ConstructInternalLocaleFromName (string locale)
473                 {
474                         if (!construct_internal_locale_from_name (locale))
475                                 return false;
476                         ConstructCalendars ();
477                         return true;
478                 }
479
480                 bool ConstructInternalLocaleFromLcid (int lcid)
481                 {
482                         if (!construct_internal_locale_from_lcid (lcid))
483                                 return false;
484                         ConstructCalendars ();
485                         return true;
486                 }
487
488                 static bool ConstructInternalLocaleFromSpecificName (CultureInfo ci, string name)
489                 {
490                         if (!construct_internal_locale_from_specific_name (ci, name))
491                                 return false;
492                         ci.ConstructCalendars ();
493                         return true;
494                 }
495
496                 static bool ConstructInternalLocaleFromCurrentLocale (CultureInfo ci)
497                 {
498                         if (!construct_internal_locale_from_current_locale (ci))
499                                 return false;
500                         ci.ConstructCalendars ();
501                         return true;
502                 }
503
504                 static CultureInfo [] GetCultures (bool neutral, bool specific, bool installed)
505                 {
506                         CultureInfo [] cis = internal_get_cultures (neutral, specific, installed);
507
508                         // The runtime returns a NULL in the first position of the array when
509                         // 'neutral' is true. We fill it in with the InvariantCulture.
510                         if (neutral && cis.Length > 0 && cis [0] == null)
511                                 cis [0] = InvariantCulture;
512
513                         foreach (CultureInfo ci in cis)
514                                 ci.ConstructCalendars ();
515
516                         return cis;
517                 }
518
519                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
520                 private extern bool construct_internal_locale_from_lcid (int lcid);
521
522                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
523                 private extern bool construct_internal_locale_from_name (string name);
524
525                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
526                 private extern static bool construct_internal_locale_from_specific_name (CultureInfo ci,
527                                 string name);
528
529                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
530                 private extern static bool construct_internal_locale_from_current_locale (CultureInfo ci);
531
532                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
533                 private extern static CultureInfo [] internal_get_cultures (bool neutral, bool specific, bool installed);
534
535                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
536                 private extern void construct_datetime_format ();
537
538                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
539                 private extern void construct_number_format ();
540
541                 // Returns false if the culture can not be found, sets is_neutral if it is
542                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
543                 private extern static bool internal_is_lcid_neutral (int lcid, out bool is_neutral);
544
545                 private unsafe void ConstructInvariant (bool use_user_override)
546                 {
547                         m_isReadOnly=false;
548                         cultureID=0x7f;
549                         this.m_useUserOverride=use_user_override;
550
551                         /* NumberFormatInfo defaults to the invariant data */
552                         numInfo=NumberFormatInfo.InvariantInfo;
553                         
554                         /* DateTimeFormatInfo defaults to the invariant data */
555                         dateTimeInfo=DateTimeFormatInfo.InvariantInfo;
556
557                         textInfo=new TextInfo (this, cultureID, this.textinfo_data);
558
559                         m_name="";
560                         displayname="Invariant Language (Invariant Country)";
561                         englishname="Invariant Language (Invariant Country)";
562                         nativename="Invariant Language (Invariant Country)";
563                         iso3lang="IVL";
564                         iso2lang="iv";
565                         icu_name="en_US_POSIX";
566                         win3lang="IVL";
567                 }
568                 
569                 public CultureInfo (int culture, bool use_user_override)
570                 {
571                         if (culture < 0)
572                                 throw new ArgumentOutOfRangeException ("Positive number required.",
573                                                 "culture");
574
575                         constructed = true;
576                         
577                         if(culture==0x007f) {
578                                 /* Short circuit the invariant culture */
579                                 ConstructInvariant (use_user_override);
580                                 return;
581                         }
582
583                         if (!ConstructInternalLocaleFromLcid (culture))
584                                 throw new ArgumentException (
585                                         String.Format ("Culture ID {0} (0x{0:X4}) is not a " +
586                                                         "supported culture.", culture), "culture");
587                 }
588
589                 public CultureInfo (int culture) : this (culture, false) {}
590                 
591                 public CultureInfo (string name, bool use_user_override)
592                 {
593                         if (name == null)
594                                 throw new ArgumentNullException ();
595
596                         constructed = true;
597                         
598                         if(name=="") {
599                                 /* Short circuit the invariant culture */
600                                 ConstructInvariant (use_user_override);
601                                 return;
602                         }
603
604                         if (!ConstructInternalLocaleFromName (name.ToLowerInvariant ()))
605                                 throw new ArgumentException ("Culture name " + name +
606                                                 " is not supported.", "name");
607                         ConstructCalendars ();
608                 }
609
610                 public CultureInfo (string name) : this (name, true) {}
611
612                 // This is used when creating by specific name and creating by
613                 // current locale so we can initialize the object without
614                 // doing any member initialization
615                 private CultureInfo () { constructed = true; } 
616
617                 unsafe internal void ConstructCalendars ()
618                 {
619                         if (calendar_data == null)
620                                 return;
621
622                         optional_calendars = new Calendar [NumOptionalCalendars];
623
624                         for (int i=0; i<NumOptionalCalendars; i++) {
625                                 Calendar cal = null;
626                                 int caldata = *(calendar_data + i);
627                                 int caltype = (caldata >> CalendarTypeBits);
628                                 switch (caltype) {
629                                 case 0:
630                                         GregorianCalendarTypes greg_type;
631                                         greg_type = (GregorianCalendarTypes) (caldata & GregorianTypeMask);
632                                         cal = new GregorianCalendar (greg_type);
633                                         break;
634                                 case 1:
635                                         cal = new HijriCalendar ();
636                                         break;
637                                 case 2:
638                                         cal = new ThaiBuddhistCalendar ();
639                                         break;
640                                 default:
641                                         throw new Exception ("invalid calendar type:  " + caldata);
642                                 }
643                                 optional_calendars [i] = cal;
644                         }
645                 }
646         }
647 }