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