2003-11-24 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / metadata / locales.c
1 /*
2  * locales.c: Culture-sensitive handling
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2003 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12
13 #include <mono/metadata/object.h>
14 #include <mono/metadata/appdomain.h>
15 #include <mono/metadata/exception.h>
16 #include <mono/metadata/locales.h>
17
18 #define DEBUG
19
20 static void set_field_by_name (MonoObject *obj, const guchar *fieldname,
21                                gpointer value)
22 {
23         MonoClassField *field;
24
25         field=mono_class_get_field_from_name (mono_object_class (obj),
26                                               fieldname);
27         mono_field_set_value (obj, field, value);
28 }
29
30 static gpointer get_field_by_name (MonoObject *obj, const guchar *fieldname)
31 {
32         MonoClassField *field;
33         gpointer ret;
34         
35         field=mono_class_get_field_from_name (mono_object_class (obj),
36                                               fieldname);
37         mono_field_get_value (obj, field, &ret);
38         return(ret);
39 }
40
41 #ifdef HAVE_ICU
42
43 #include <unicode/utypes.h>
44 #include <unicode/ustring.h>
45 #include <unicode/ures.h>
46 #include <unicode/ucnv.h>
47 #include <unicode/ucol.h>
48
49 static MonoString *monostring_from_UChars (const UChar *res_str,
50                                            UConverter *conv)
51 {
52         MonoString *str;
53         UErrorCode ec;
54         char *utf16_str;
55         int32_t ret, utf16_strlen;
56         
57         utf16_strlen=u_strlen (res_str)*ucnv_getMaxCharSize (conv)+2;
58         utf16_str=(char *)g_malloc0 (sizeof(char)*utf16_strlen);
59         
60         ec=U_ZERO_ERROR;
61         ret=ucnv_fromUChars (conv, utf16_str, utf16_strlen, res_str, -1, &ec);
62         if(ec==U_BUFFER_OVERFLOW_ERROR ||
63            ec==U_STRING_NOT_TERMINATED_WARNING) {
64                 /* This should never happen, cos we gave ourselves the
65                  * maximum length needed above
66                  */
67                 g_assert_not_reached ();
68         }
69         
70         str=mono_string_from_utf16 ((gunichar2 *)utf16_str);
71         
72         g_free (utf16_str);
73         
74         return(str);
75 }
76
77 static UChar *monostring_to_UChars (const MonoString *str, UConverter *conv)
78 {
79         UErrorCode ec;
80         UChar *dest;
81         int32_t ret, dest_strlen;
82         
83         /* Add 1 for the trailing NULL */
84         dest_strlen=mono_string_length (str)+1;
85         dest=(UChar *)g_malloc0 (sizeof(UChar)*dest_strlen);
86         
87         ec=U_ZERO_ERROR;
88         /* mono_string_length()*2 because its counting bytes not chars */
89         ret=ucnv_toUChars (conv, dest, dest_strlen,
90                            (const char *)mono_string_chars (str),
91                            mono_string_length (str)*2, &ec);
92         if(ec==U_BUFFER_OVERFLOW_ERROR ||
93            ec==U_STRING_NOT_TERMINATED_WARNING) {
94                 /* This should never happen, cos we gave ourselves the
95                  * length needed above
96                  */
97                 g_assert_not_reached ();
98         }
99         
100         return(dest);
101 }
102
103 static MonoString *monostring_from_resource_index (const UResourceBundle *bundle, UConverter *conv, int32_t idx)
104 {
105         const UChar *res_str;
106         int32_t res_strlen;
107         UErrorCode ec;
108         
109         ec=U_ZERO_ERROR;
110         res_str=ures_getStringByIndex (bundle, idx, &res_strlen, &ec);
111         if(U_FAILURE (ec)) {
112                 return(NULL);
113         }
114
115         return(monostring_from_UChars (res_str, conv));
116 }
117
118 static UResourceBundle *open_subbundle (const UResourceBundle *bundle,
119                                         const char *name, int32_t req_count)
120 {
121         UResourceBundle *subbundle;
122         UErrorCode ec;
123         int32_t count;
124         
125         ec=U_ZERO_ERROR;
126         subbundle=ures_getByKey (bundle, name, NULL, &ec);
127         if(U_FAILURE (ec)) {
128                 /* Couldn't find the subbundle */
129                 return(NULL);
130         }
131         
132         count=ures_countArrayItems (bundle, name, &ec);
133         if(U_FAILURE (ec)) {
134                 /* Couldn't count the subbundle */
135                 ures_close (subbundle);
136                 return(NULL);
137         }
138         
139         if(count!=req_count) {
140                 /* Bummer */
141                 ures_close (subbundle);
142                 return(NULL);
143         }
144
145         return(subbundle);
146 }
147
148 static void set_array (MonoObject *obj, const guchar *fieldname,
149                        const UResourceBundle *bundle, const char *resname,
150                        int32_t req_count, UConverter *conv)
151 {
152         MonoArray *arr;
153         UResourceBundle *subbundle;
154         int i;
155         
156         subbundle=open_subbundle (bundle, resname, req_count);
157         if(subbundle!=NULL) {
158                 arr=mono_array_new(mono_domain_get (),
159                                    mono_defaults.string_class, req_count);
160                 
161                 for(i=0; i<req_count; i++) {
162                         mono_array_set(arr, MonoString *, i, monostring_from_resource_index (subbundle, conv, i));
163                 }
164                 set_field_by_name (obj, fieldname, arr);
165
166                 ures_close (subbundle);
167         }
168 }
169
170
171 static MonoObject *create_DateTimeFormat (const char *locale)
172 {
173         MonoObject *new_dtf;
174         MonoClass *class;
175         UConverter *conv;
176         UResourceBundle *bundle, *subbundle;
177         UErrorCode ec;
178         
179         class=mono_class_from_name (mono_defaults.corlib,
180                                     "System.Globalization",
181                                     "DateTimeFormatInfo");
182         new_dtf=mono_object_new (mono_domain_get (), class);
183         mono_runtime_object_init (new_dtf);
184         
185         ec=U_ZERO_ERROR;
186
187         /* Plain "UTF-16" adds a BOM, which confuses other stuff */
188         conv=ucnv_open ("UTF16_PlatformEndian", &ec);
189         if(U_FAILURE (ec)) {
190                 goto error0;
191         }
192         
193         bundle=ures_open (NULL, locale, &ec);
194         if(U_FAILURE (ec)) {
195                 goto error1;
196         }
197         
198         /* AM/PM markers */
199         subbundle=open_subbundle (bundle, "AmPmMarkers", 2);
200         if(subbundle!=NULL) {
201                 set_field_by_name (new_dtf, "_AMDesignator",
202                                    monostring_from_resource_index (subbundle,
203                                                                    conv, 0));
204                 set_field_by_name (new_dtf, "_PMDesignator",
205                                    monostring_from_resource_index (subbundle,
206                                                                    conv, 1));
207                 
208                 ures_close (subbundle);
209         }
210         
211         /* Date/Time patterns.  Don't set FullDateTimePattern.  As it
212          * seems to always default to LongDatePattern + " " +
213          * LongTimePattern, let the property accessor deal with it.
214          */
215         subbundle=open_subbundle (bundle, "DateTimePatterns", 9);
216         if(subbundle!=NULL) {
217                 set_field_by_name (new_dtf, "_ShortDatePattern",
218                                    monostring_from_resource_index (subbundle,
219                                                                    conv, 7));
220                 set_field_by_name (new_dtf, "_LongDatePattern",
221                                    monostring_from_resource_index (subbundle,
222                                                                    conv, 5));
223                 set_field_by_name (new_dtf, "_ShortTimePattern",
224                                    monostring_from_resource_index (subbundle,
225                                                                    conv, 3));
226                 set_field_by_name (new_dtf, "_LongTimePattern",
227                                    monostring_from_resource_index (subbundle,
228                                                                    conv, 2));
229
230                 /* RFC1123Pattern, SortableDateTimePattern and
231                  * UniversalSortableDateTimePattern all seem to be
232                  * constant, and all the same as the invariant default
233                  * set in the ctor
234                  */
235         
236                 ures_close (subbundle);
237         }
238         
239 #if 0
240         /* Not sure what to do with these yet, so leave them set to
241          * the invariant default
242          */
243         set_field_string (new_dtf, "_DateSeparator", str);
244         set_field_string (new_dtf, "_TimeSeparator", str);
245         set_field_string (new_dtf, "_MonthDayPattern", str);
246         set_field_string (new_dtf, "_YearMonthPattern", str);
247 #endif
248
249         /* Day names.  Luckily both ICU and .net start Sunday at index 0 */
250         set_array (new_dtf, "_DayNames", bundle, "DayNames", 7, conv);
251
252         /* Abbreviated day names */
253         set_array (new_dtf, "_AbbreviatedDayNames", bundle,
254                    "DayAbbreviations", 7, conv);
255
256         /* Month names */
257         set_array (new_dtf, "_MonthNames", bundle, "MonthNames", 12, conv);
258         
259         /* Abbreviated month names */
260         set_array (new_dtf, "_AbbreviatedMonthNames", bundle,
261                    "MonthAbbreviations", 12, conv);
262
263         /* TODO: DayOfWeek _FirstDayOfWeek, Calendar _Calendar, CalendarWeekRule _CalendarWeekRule */
264
265         ures_close (bundle);
266 error1:
267         ucnv_close (conv);
268 error0:
269         return(new_dtf);
270 }
271
272 static char *mono_string_to_icu_locale (MonoString *locale)
273 {
274         UErrorCode ec;
275         char *passed_locale, *icu_locale=NULL;
276         int32_t loc_len, ret;
277
278         passed_locale=mono_string_to_utf8 (locale);
279         
280         ec=U_ZERO_ERROR;
281         ret=uloc_getName (passed_locale, NULL, 0, &ec);
282         if(ec==U_BUFFER_OVERFLOW_ERROR) {
283                 ec=U_ZERO_ERROR;
284                 loc_len=ret+1;
285                 icu_locale=(char *)g_malloc0 (sizeof(char)*loc_len);
286                 ret=uloc_getName (passed_locale, icu_locale, loc_len, &ec);
287         }
288         g_free (passed_locale);
289         
290         return(icu_locale);
291 }
292
293 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoObject *this, MonoString *locale)
294 {
295         UConverter *conv;
296         UChar *ustr;
297         char *str;
298         UErrorCode ec;
299         char *icu_locale;
300         int32_t str_len, ret;
301         
302         MONO_ARCH_SAVE_REGS;
303         
304         icu_locale=mono_string_to_icu_locale (locale);
305         if(icu_locale==NULL) {
306                 /* Something went wrong */
307                 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
308                 return;
309         }
310         
311         ec=U_ZERO_ERROR;
312         conv=ucnv_open ("UTF16_PlatformEndian", &ec);
313         if(U_FAILURE (ec)) {
314                 g_free (icu_locale);
315                 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
316                 return;
317         }
318         
319         /* Fill in the static fields */
320
321         /* TODO: Calendar, CurrentCulture,
322          * CurrentUICulture, InstalledUICulture, NumberFormat,
323          * OptionalCalendars, Parent, TextInfo
324          */
325
326         str_len=256;    /* Should be big enough for anything */
327         str=(char *)g_malloc0 (sizeof(char)*str_len);
328         ustr=(UChar *)g_malloc0 (sizeof(UChar)*str_len);
329         
330         ret=uloc_getDisplayName (icu_locale, "en", ustr, str_len, &ec);
331         if(U_SUCCESS (ec) && ret<str_len) {
332                 set_field_by_name (this, "englishname",
333                                    monostring_from_UChars (ustr, conv));
334         }
335         
336         ret=uloc_getDisplayName (icu_locale, uloc_getDefault (), ustr, str_len,
337                                  &ec);
338         if(U_SUCCESS (ec) && ret<str_len) {
339                 set_field_by_name (this, "displayname",
340                                    monostring_from_UChars (ustr, conv));
341         }
342         
343         ret=uloc_getDisplayName (icu_locale, icu_locale, ustr, str_len, &ec);
344         if(U_SUCCESS (ec) && ret<str_len) {
345                 set_field_by_name (this, "nativename",
346                                    monostring_from_UChars (ustr, conv));
347         }
348
349         set_field_by_name (this, "iso3lang",mono_string_new_wrapper (uloc_getISO3Language (icu_locale)));
350
351         ret=uloc_getLanguage (icu_locale, str, str_len, &ec);
352         if(U_SUCCESS (ec) && ret<str_len) {
353                 set_field_by_name (this, "iso2lang",
354                                    mono_string_new_wrapper (str));
355         }
356
357         set_field_by_name (this, "datetime_format",
358                            create_DateTimeFormat (icu_locale));
359         
360         g_free (str);
361         g_free (ustr);
362         g_free (icu_locale);
363         ucnv_close (conv);
364 }
365
366 void ves_icall_System_Globalization_CultureInfo_construct_compareinfo (MonoObject *comp, MonoString *locale)
367 {
368         UCollator *coll;
369         UErrorCode ec;
370         char *icu_locale;
371         
372         MONO_ARCH_SAVE_REGS;
373         
374         icu_locale=mono_string_to_icu_locale (locale);
375         if(icu_locale==NULL) {
376                 /* Something went wrong */
377                 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
378                 return;
379         }
380
381         ec=U_ZERO_ERROR;
382         coll=ucol_open (icu_locale, &ec);
383         if(U_SUCCESS (ec)) {
384                 set_field_by_name (comp, "ICU_collator", &coll);
385         }
386
387         g_free (icu_locale);
388 }
389
390 /* Set up the collator to reflect the options required.  Some of these
391  * options clash, as they adjust the collator strength level.  Try to
392  * make later checks reduce the strength level, and attempt to take
393  * previous options into account.
394  *
395  * Don't bother to check the error returns when setting the
396  * attributes, as a failure here is hardly grounds to error out.
397  */
398 static void set_collator_options (UCollator *coll, gint32 options)
399 {
400         UErrorCode ec=U_ZERO_ERROR;
401         
402         /* Set up the defaults */
403         ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
404                            &ec);
405         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_OFF, &ec);
406         
407         /* Do this first so other options will override the quaternary
408          * level strength setting if necessary
409          */
410         if(!(options & CompareOptions_IgnoreKanaType)) {
411                 ucol_setAttribute (coll, UCOL_HIRAGANA_QUATERNARY_MODE,
412                                    UCOL_ON, &ec);
413                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_QUATERNARY, &ec);
414         }
415
416         /* Word sort, the default */
417         if(!(options & CompareOptions_StringSort)) {
418                 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING,
419                                    UCOL_SHIFTED, &ec);
420                 /* Tertiary strength is the default, but it might have
421                  * been set to quaternary above.  (We don't want that
422                  * here, because that will order all the punctuation
423                  * first instead of just ignoring it.)
424                  */
425                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_TERTIARY, &ec);
426         }
427
428         if(options & CompareOptions_IgnoreCase) {
429                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_SECONDARY, &ec);
430         }
431
432         if(options & CompareOptions_IgnoreWidth) {
433                 /* Kana width is a tertiary strength difference.  This
434                  * will totally break the !IgnoreKanaType option
435                  */
436                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_SECONDARY, &ec);
437         }
438                 
439         if(options & CompareOptions_IgnoreNonSpace) {
440                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
441                 /* We can still compare case even when just checking
442                  * primary strength
443                  */
444                 if(!(options & CompareOptions_IgnoreCase) ||
445                    !(options & CompareOptions_IgnoreWidth)) {
446                         /* Not sure if CASE_LEVEL handles kana width
447                          */
448                         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON,
449                                            &ec);
450                 }
451         }
452
453         if(options & CompareOptions_IgnoreSymbols) {
454                 /* Don't know what to do here */
455         }
456
457         if(options == CompareOptions_Ordinal) {
458                 /* TODO */
459         }
460 }
461
462 gint32 ves_icall_System_Globalization_CompareInfo_internal_compare (MonoObject *this, MonoString *str1, MonoString *str2, gint32 options)
463 {
464         UConverter *conv;
465         UCollator *coll;
466         UChar *ustr1, *ustr2;
467         UCollationResult result;
468         UErrorCode ec;
469         
470         MONO_ARCH_SAVE_REGS;
471         
472         coll=get_field_by_name (this, "ICU_collator");
473         if(coll==NULL) {
474                 return(0);
475         }
476         
477         ec=U_ZERO_ERROR;
478         conv=ucnv_open ("UTF16_PlatformEndian", &ec);
479         if(U_FAILURE (ec)) {
480                 return(0);
481         }
482         
483         ustr1=monostring_to_UChars (str1, conv);
484         ustr2=monostring_to_UChars (str2, conv);
485         
486         ucnv_close (conv);
487         
488         set_collator_options (coll, options);
489                         
490         result=ucol_strcoll (coll, ustr1, -1, ustr2, -1);
491         
492         g_free (ustr1);
493         g_free (ustr2);
494         
495         return(result);
496 }
497
498 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoObject *this)
499 {
500         UCollator *coll;
501         
502         MONO_ARCH_SAVE_REGS;
503         
504         coll=get_field_by_name (this, "ICU_collator");
505         if(coll!=NULL) {
506                 ucol_close (coll);
507         }
508 }
509
510 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoObject *this, MonoObject *key, MonoString *source, gint32 options)
511 {
512         UCollator *coll;
513         UConverter *conv;
514         UChar *ustr;
515         UErrorCode ec;
516         MonoArray *arr;
517         char *keybuf;
518         int32_t keylen, i;
519         
520         MONO_ARCH_SAVE_REGS;
521         
522         coll=get_field_by_name (this, "ICU_collator");
523         if(coll==NULL) {
524                 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
525                 return;
526         }
527         
528         ec=U_ZERO_ERROR;
529         conv=ucnv_open ("UTF16_PlatformEndian", &ec);
530         if(U_FAILURE (ec)) {
531                 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
532                 return;
533         }
534         ustr=monostring_to_UChars (source, conv);
535         ucnv_close (conv);
536         
537         set_collator_options (coll, options);
538
539         keylen=ucol_getSortKey (coll, ustr, -1, NULL, 0);
540         keybuf=g_malloc (sizeof(char)* keylen);
541         ucol_getSortKey (coll, ustr, -1, keybuf, keylen);
542         
543         arr=mono_array_new (mono_domain_get (), mono_defaults.byte_class,
544                             keylen);
545         for(i=0; i<keylen; i++) {
546                 mono_array_set (arr, guint8, i, keybuf[i]);
547         }
548         
549         set_field_by_name (key, "key", arr);
550
551         g_free (ustr);
552         g_free (keybuf);
553 }
554
555 #else /* HAVE_ICU */
556 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoObject *this, MonoString *locale)
557 {
558         MONO_ARCH_SAVE_REGS;
559         
560         /* Always claim "unknown locale" if we don't have ICU (only
561          * called for non-invariant locales)
562          */
563         mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "ArgumentException"));
564 }
565
566 void ves_icall_System_Globalization_CultureInfo_construct_compareinfo (MonoObject *comp, MonoString *locale)
567 {
568         /* Nothing to do here */
569 }
570
571 int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoObject *this, MonoString *str1, MonoString *str2, gint32 options)
572 {
573         MONO_ARCH_SAVE_REGS;
574         
575         /* Do a normal ascii string compare, as we only know the
576          * invariant locale if we dont have ICU
577          */
578         /* TODO */
579         mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
580 }
581
582 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoObject *this)
583 {
584         /* Nothing to do here */
585 }
586
587 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoObject *this, MonoObject *key, MonoString *source, gint32 options)
588 {
589         MonoArray *arr;
590         gint32 keylen, i;
591
592         MONO_ARCH_SAVE_REGS;
593         
594         keylen=mono_string_length (source);
595         
596         arr=mono_array_new (mono_domain_get (), mono_defaults.byte_class,
597                             keylen);
598         for(i=0; i<keylen; i++) {
599                 mono_array_set (arr, guint8, i, mono_string_chars (source)[i]);
600         }
601         
602         set_field_by_name (key, "key", arr);
603 }
604
605 #endif /* HAVE_ICU */