2 * locales.c: Culture-sensitive handling
5 * Dick Porter (dick@ximian.com)
7 * (C) 2003 Ximian, Inc.
13 #include <mono/metadata/object.h>
14 #include <mono/metadata/appdomain.h>
15 #include <mono/metadata/exception.h>
16 #include <mono/metadata/locales.h>
20 static void set_field_by_name (MonoObject *obj, const guchar *fieldname,
23 MonoClassField *field;
25 field=mono_class_get_field_from_name (mono_object_class (obj),
27 mono_field_set_value (obj, field, value);
30 static gpointer get_field_by_name (MonoObject *obj, const guchar *fieldname)
32 MonoClassField *field;
35 field=mono_class_get_field_from_name (mono_object_class (obj),
37 mono_field_get_value (obj, field, &ret);
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>
49 static MonoString *monostring_from_UChars (const UChar *res_str,
55 int32_t ret, utf16_strlen;
57 utf16_strlen=u_strlen (res_str)*ucnv_getMaxCharSize (conv)+2;
58 utf16_str=(char *)g_malloc0 (sizeof(char)*utf16_strlen);
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
67 g_assert_not_reached ();
70 str=mono_string_from_utf16 ((gunichar2 *)utf16_str);
77 static UChar *monostring_to_UChars (const MonoString *str, UConverter *conv)
81 int32_t ret, dest_strlen;
83 /* Add 1 for the trailing NULL */
84 dest_strlen=mono_string_length (str)+1;
85 dest=(UChar *)g_malloc0 (sizeof(UChar)*dest_strlen);
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
97 g_assert_not_reached ();
103 static MonoString *monostring_from_resource_index (const UResourceBundle *bundle, UConverter *conv, int32_t idx)
105 const UChar *res_str;
110 res_str=ures_getStringByIndex (bundle, idx, &res_strlen, &ec);
115 return(monostring_from_UChars (res_str, conv));
118 static UResourceBundle *open_subbundle (const UResourceBundle *bundle,
119 const char *name, int32_t req_count)
121 UResourceBundle *subbundle;
126 subbundle=ures_getByKey (bundle, name, NULL, &ec);
128 /* Couldn't find the subbundle */
132 count=ures_countArrayItems (bundle, name, &ec);
134 /* Couldn't count the subbundle */
135 ures_close (subbundle);
139 if(count!=req_count) {
141 ures_close (subbundle);
148 static void set_array (MonoObject *obj, const guchar *fieldname,
149 const UResourceBundle *bundle, const char *resname,
150 int32_t req_count, UConverter *conv)
153 UResourceBundle *subbundle;
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);
161 for(i=0; i<req_count; i++) {
162 mono_array_set(arr, MonoString *, i, monostring_from_resource_index (subbundle, conv, i));
164 set_field_by_name (obj, fieldname, arr);
166 ures_close (subbundle);
171 static MonoObject *create_DateTimeFormat (const char *locale)
176 UResourceBundle *bundle, *subbundle;
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);
187 /* Plain "UTF-16" adds a BOM, which confuses other stuff */
188 conv=ucnv_open ("UTF16_PlatformEndian", &ec);
193 bundle=ures_open (NULL, locale, &ec);
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,
204 set_field_by_name (new_dtf, "_PMDesignator",
205 monostring_from_resource_index (subbundle,
208 ures_close (subbundle);
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.
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,
220 set_field_by_name (new_dtf, "_LongDatePattern",
221 monostring_from_resource_index (subbundle,
223 set_field_by_name (new_dtf, "_ShortTimePattern",
224 monostring_from_resource_index (subbundle,
226 set_field_by_name (new_dtf, "_LongTimePattern",
227 monostring_from_resource_index (subbundle,
230 /* RFC1123Pattern, SortableDateTimePattern and
231 * UniversalSortableDateTimePattern all seem to be
232 * constant, and all the same as the invariant default
236 ures_close (subbundle);
240 /* Not sure what to do with these yet, so leave them set to
241 * the invariant default
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);
249 /* Day names. Luckily both ICU and .net start Sunday at index 0 */
250 set_array (new_dtf, "_DayNames", bundle, "DayNames", 7, conv);
252 /* Abbreviated day names */
253 set_array (new_dtf, "_AbbreviatedDayNames", bundle,
254 "DayAbbreviations", 7, conv);
257 set_array (new_dtf, "_MonthNames", bundle, "MonthNames", 12, conv);
259 /* Abbreviated month names */
260 set_array (new_dtf, "_AbbreviatedMonthNames", bundle,
261 "MonthAbbreviations", 12, conv);
263 /* TODO: DayOfWeek _FirstDayOfWeek, Calendar _Calendar, CalendarWeekRule _CalendarWeekRule */
272 static char *mono_string_to_icu_locale (MonoString *locale)
275 char *passed_locale, *icu_locale=NULL;
276 int32_t loc_len, ret;
278 passed_locale=mono_string_to_utf8 (locale);
281 ret=uloc_getName (passed_locale, NULL, 0, &ec);
282 if(ec==U_BUFFER_OVERFLOW_ERROR) {
285 icu_locale=(char *)g_malloc0 (sizeof(char)*loc_len);
286 ret=uloc_getName (passed_locale, icu_locale, loc_len, &ec);
288 g_free (passed_locale);
293 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoObject *this, MonoString *locale)
300 int32_t str_len, ret;
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"));
312 conv=ucnv_open ("UTF16_PlatformEndian", &ec);
315 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
319 /* Fill in the static fields */
321 /* TODO: Calendar, CurrentCulture,
322 * CurrentUICulture, InstalledUICulture, NumberFormat,
323 * OptionalCalendars, Parent, TextInfo
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);
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));
336 ret=uloc_getDisplayName (icu_locale, uloc_getDefault (), ustr, str_len,
338 if(U_SUCCESS (ec) && ret<str_len) {
339 set_field_by_name (this, "displayname",
340 monostring_from_UChars (ustr, conv));
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));
349 set_field_by_name (this, "iso3lang",mono_string_new_wrapper (uloc_getISO3Language (icu_locale)));
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));
357 set_field_by_name (this, "datetime_format",
358 create_DateTimeFormat (icu_locale));
366 void ves_icall_System_Globalization_CultureInfo_construct_compareinfo (MonoObject *comp, MonoString *locale)
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"));
382 coll=ucol_open (icu_locale, &ec);
384 set_field_by_name (comp, "ICU_collator", &coll);
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.
395 * Don't bother to check the error returns when setting the
396 * attributes, as a failure here is hardly grounds to error out.
398 static void set_collator_options (UCollator *coll, gint32 options)
400 UErrorCode ec=U_ZERO_ERROR;
402 /* Set up the defaults */
403 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
405 ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_OFF, &ec);
407 /* Do this first so other options will override the quaternary
408 * level strength setting if necessary
410 if(!(options & CompareOptions_IgnoreKanaType)) {
411 ucol_setAttribute (coll, UCOL_HIRAGANA_QUATERNARY_MODE,
413 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_QUATERNARY, &ec);
416 /* Word sort, the default */
417 if(!(options & CompareOptions_StringSort)) {
418 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING,
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.)
425 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_TERTIARY, &ec);
428 if(options & CompareOptions_IgnoreCase) {
429 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_SECONDARY, &ec);
432 if(options & CompareOptions_IgnoreWidth) {
433 /* Kana width is a tertiary strength difference. This
434 * will totally break the !IgnoreKanaType option
436 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_SECONDARY, &ec);
439 if(options & CompareOptions_IgnoreNonSpace) {
440 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
441 /* We can still compare case even when just checking
444 if(!(options & CompareOptions_IgnoreCase) ||
445 !(options & CompareOptions_IgnoreWidth)) {
446 /* Not sure if CASE_LEVEL handles kana width
448 ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON,
453 if(options & CompareOptions_IgnoreSymbols) {
454 /* Don't know what to do here */
457 if(options == CompareOptions_Ordinal) {
462 gint32 ves_icall_System_Globalization_CompareInfo_internal_compare (MonoObject *this, MonoString *str1, MonoString *str2, gint32 options)
466 UChar *ustr1, *ustr2;
467 UCollationResult result;
472 coll=get_field_by_name (this, "ICU_collator");
478 conv=ucnv_open ("UTF16_PlatformEndian", &ec);
483 ustr1=monostring_to_UChars (str1, conv);
484 ustr2=monostring_to_UChars (str2, conv);
488 set_collator_options (coll, options);
490 result=ucol_strcoll (coll, ustr1, -1, ustr2, -1);
498 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoObject *this)
504 coll=get_field_by_name (this, "ICU_collator");
510 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoObject *this, MonoObject *key, MonoString *source, gint32 options)
522 coll=get_field_by_name (this, "ICU_collator");
524 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
529 conv=ucnv_open ("UTF16_PlatformEndian", &ec);
531 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
534 ustr=monostring_to_UChars (source, conv);
537 set_collator_options (coll, options);
539 keylen=ucol_getSortKey (coll, ustr, -1, NULL, 0);
540 keybuf=g_malloc (sizeof(char)* keylen);
541 ucol_getSortKey (coll, ustr, -1, keybuf, keylen);
543 arr=mono_array_new (mono_domain_get (), mono_defaults.byte_class,
545 for(i=0; i<keylen; i++) {
546 mono_array_set (arr, guint8, i, keybuf[i]);
549 set_field_by_name (key, "key", arr);
556 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoObject *this, MonoString *locale)
560 /* Always claim "unknown locale" if we don't have ICU (only
561 * called for non-invariant locales)
563 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "ArgumentException"));
566 void ves_icall_System_Globalization_CultureInfo_construct_compareinfo (MonoObject *comp, MonoString *locale)
568 /* Nothing to do here */
571 int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoObject *this, MonoString *str1, MonoString *str2, gint32 options)
575 /* Do a normal ascii string compare, as we only know the
576 * invariant locale if we dont have ICU
579 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
582 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoObject *this)
584 /* Nothing to do here */
587 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoObject *this, MonoObject *key, MonoString *source, gint32 options)
594 keylen=mono_string_length (source);
596 arr=mono_array_new (mono_domain_get (), mono_defaults.byte_class,
598 for(i=0; i<keylen; i++) {
599 mono_array_set (arr, guint8, i, mono_string_chars (source)[i]);
602 set_field_by_name (key, "key", arr);
605 #endif /* HAVE_ICU */