Hi, I noticed that currently our RegionInfo (sys.globalization) support is hard-coded and pretty insufficient. So I reimplemented it like what we do for CultureInfo i.e. create region table from CLDR via locale-builder.
Actually I mostly reused locale-builder so it just requires attached patchs. The resulting culture-info-table.h will become like 175 KB where it currently is about 140 KB. Any comments are appreciated :) Atsushi Eno
Index: object-internals.h =================================================================== --- object-internals.h (revision 48380) +++ object-internals.h (working copy) @@ -373,6 +373,18 @@ typedef struct { MonoObject obj; + gint32 region_id; + MonoString *iso2name; + MonoString *iso3name; + MonoString *win3name; + MonoString *english_name; + MonoString *currency_symbol; + MonoString *iso_currency_symbol; + MonoString *currency_english_name; +} MonoRegionInfo; + +typedef struct { + MonoObject obj; MonoString *str; gint32 options; MonoArray *key; Index: locales.c =================================================================== --- locales.c (revision 48380) +++ locales.c (working copy) @@ -45,6 +45,8 @@ static const CultureInfoEntry* culture_info_entry_from_lcid (int lcid); +static const RegionInfoEntry* region_info_entry_from_lcid (int lcid); + static int culture_lcid_locator (const void *a, const void *b) { @@ -55,6 +57,15 @@ } static int +region_lcid_locator (const void *a, const void *b) +{ + const int *lcid = a; + const RegionLCIDMap *bb = b; + + return *lcid - bb->lcid; +} + +static int culture_name_locator (const void *a, const void *b) { const char *aa = a; @@ -66,6 +77,18 @@ return ret; } +static int +region_name_locator (const void *a, const void *b) +{ + const char *aa = a; + const RegionInfoNameEntry *bb = b; + int ret; + + ret = strcmp (aa, idx2string (bb->name)); + + return ret; +} + static MonoArray* create_group_sizes_array (const gint *gs, gint ml) { @@ -236,6 +259,23 @@ return TRUE; } +static MonoBoolean +construct_region (MonoRegionInfo *this, const RegionInfoEntry *ri) +{ + MonoDomain *domain = mono_domain_get (); + + this->region_id = ri->region_id; + this->iso2name = mono_string_new (domain, idx2string (ri->iso2name)); + this->iso3name = mono_string_new (domain, idx2string (ri->iso3name)); + this->win3name = mono_string_new (domain, idx2string (ri->win3name)); + this->english_name = mono_string_new (domain, idx2string (ri->english_name)); + this->currency_symbol = mono_string_new (domain, idx2string (ri->currency_symbol)); + this->iso_currency_symbol = mono_string_new (domain, idx2string (ri->iso_currency_symbol)); + this->currency_english_name = mono_string_new (domain, idx2string (ri->currency_english_name)); + + return TRUE; +} + static gboolean construct_culture_from_specific_name (MonoCultureInfo *ci, gchar *name) { @@ -259,6 +299,25 @@ return construct_culture (ci, entry); } +static gboolean +construct_region_from_specific_name (MonoRegionInfo *ri, gchar *name) +{ + const RegionInfoEntry *entry; + const RegionInfoNameEntry *ne; + + MONO_ARCH_SAVE_REGS; + + ne = bsearch (name, region_name_entries, NUM_REGION_ENTRIES, + sizeof (RegionInfoNameEntry), region_name_locator); + + if (ne == NULL) + return FALSE; + + entry = ®ion_entries [ne->region_entry_index]; + + return construct_region (ri, entry); +} + static const CultureInfoEntry* culture_info_entry_from_lcid (int lcid) { @@ -271,6 +330,25 @@ return ci; } +static const RegionInfoEntry* +region_info_entry_from_lcid (int lcid) +{ + const RegionInfoEntry *entry; + const RegionLCIDMap *ne; + + MONO_ARCH_SAVE_REGS; + + ne = bsearch (&lcid, region_lcid_map, NUM_REGION_LCID_MAP, + sizeof (RegionLCIDMap), region_lcid_locator); + + if (ne == NULL) + return FALSE; + + entry = ®ion_entries [ne->region_entry_index]; + + return entry; +} + /* * The following two methods are modified from the ICU source code. (http://oss.software.ibm.com/icu) * Copyright (c) 1995-2003 International Business Machines Corporation and others @@ -434,6 +512,44 @@ return ret; } +MonoBoolean +ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_lcid (MonoRegionInfo *this, + gint lcid) +{ + const RegionInfoEntry *ri; + + MONO_ARCH_SAVE_REGS; + + ri = region_info_entry_from_lcid (lcid); + if(ri == NULL) + return FALSE; + + return construct_region (this, ri); +} + +MonoBoolean +ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_name (MonoRegionInfo *this, + MonoString *name) +{ + const RegionInfoNameEntry *ne; + char *n; + + MONO_ARCH_SAVE_REGS; + + n = mono_string_to_utf8 (name); + ne = bsearch (n, region_name_entries, NUM_REGION_ENTRIES, + sizeof (RegionInfoNameEntry), region_name_locator); + + if (ne == NULL) { + /*g_print ("ne (%s) is null\n", n);*/ + g_free (n); + return FALSE; + } + g_free (n); + + return construct_region (this, ®ion_entries [ne->region_entry_index]); +} + MonoArray* ves_icall_System_Globalization_CultureInfo_internal_get_cultures (MonoBoolean neutral, MonoBoolean specific, MonoBoolean installed) @@ -511,6 +627,7 @@ return TRUE; } + #ifdef HAVE_ICU #include <unicode/utypes.h> Index: locales.h =================================================================== --- locales.h (revision 48380) +++ locales.h (working copy) @@ -38,6 +38,11 @@ extern void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale); extern int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options); extern void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this); +extern MonoBoolean +ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_lcid (MonoRegionInfo *this, gint lcid); +extern MonoBoolean +ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_name (MonoRegionInfo *this, + MonoString *name); extern void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this, MonoSortKey *key, MonoString *source, gint32 options); extern int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first); extern int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first); Index: culture-info.h =================================================================== --- culture-info.h (revision 48380) +++ culture-info.h (working copy) @@ -115,5 +115,28 @@ gint16 culture_entry_index; } CultureInfoNameEntry; +typedef struct { + gint16 region_id; + /* gint8 measurement_system; // 0:metric 1:US 2:UK */ + /* gint16 geo_id; */ + const stridx_t iso2name; + const stridx_t iso3name; + const stridx_t win3name; + const stridx_t english_name; + const stridx_t currency_symbol; + const stridx_t iso_currency_symbol; + const stridx_t currency_english_name; +} RegionInfoEntry; + +typedef struct { + const stridx_t name; + gint16 region_entry_index; +} RegionInfoNameEntry; + +typedef struct { + gint16 lcid; + gint16 region_entry_index; +} RegionLCIDMap; + #endif Index: icall.c =================================================================== --- icall.c (revision 48380) +++ icall.c (working copy) @@ -6333,6 +6333,11 @@ {"internal_is_lcid_neutral", ves_icall_System_Globalization_CultureInfo_internal_is_lcid_neutral} }; +static const IcallEntry regioninfo_icalls [] = { + {"construct_internal_region_from_lcid", ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_lcid}, + {"construct_internal_region_from_name", ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_name} +}; + static const IcallEntry compareinfo_icalls [] = { {"assign_sortkey(object,string,System.Globalization.CompareOptions)", ves_icall_System_Globalization_CompareInfo_assign_sortkey}, {"construct_compareinfo(string)", ves_icall_System_Globalization_CompareInfo_construct_compareinfo}, @@ -7044,6 +7049,7 @@ {"System.GC", gc_icalls, G_N_ELEMENTS (gc_icalls)}, {"System.Globalization.CompareInfo", compareinfo_icalls, G_N_ELEMENTS (compareinfo_icalls)}, {"System.Globalization.CultureInfo", cultureinfo_icalls, G_N_ELEMENTS (cultureinfo_icalls)}, + {"System.Globalization.RegionInfo", regioninfo_icalls, G_N_ELEMENTS (regioninfo_icalls)}, {"System.IO.FAMWatcher", famwatcher_icalls, G_N_ELEMENTS (famwatcher_icalls)}, {"System.IO.FileSystemWatcher", filewatcher_icalls, G_N_ELEMENTS (filewatcher_icalls)}, {"System.IO.MonoIO", monoio_icalls, G_N_ELEMENTS (monoio_icalls)},
Index: RegionInfoEntry.cs =================================================================== --- RegionInfoEntry.cs (revision 0) +++ RegionInfoEntry.cs (revision 0) @@ -0,0 +1,64 @@ +// +// Mono.Tools.LocaleBuilder.RegionInfoEntry +// +// Author(s): +// Atsushi Enomoto <[EMAIL PROTECTED]> +// +// (C) 2005, Novell, Inc (http://www.novell.com) +// + + +using System; +using System.Text; +using System.Collections; + +namespace Mono.Tools.LocaleBuilder +{ + public class RegionInfoEntry : Entry + { + public int RegionId; // numbered by alphabetical order of ISO2Name. + // public byte MeasurementSystem; + // public int GeoId; + public string ISO2Name = String.Empty; // supplementalData.xml + public string ISO3Name = String.Empty; + public string Win3Name = String.Empty; + public string EnglishName = String.Empty; // langs/en.xml + public string CurrencySymbol = String.Empty; + public string ISOCurrencySymbol = String.Empty; // supplementalData.xml + public string CurrencyEnglishName = String.Empty; // langs/en.xml + + // NativeName and CurrencyNativeName are language dependent. + + public void AppendTableRow (StringBuilder builder) + { + builder.Append ("\t{"); + builder.Append (RegionId); + builder.Append (','); + // builder.Append (MeasurementSystem); + // builder.Append (','); + builder.Append (EncodeStringIdx (ISO2Name)); + builder.Append (','); + builder.Append (EncodeStringIdx (ISO3Name)); + builder.Append (','); + builder.Append (EncodeStringIdx (Win3Name)); + builder.Append (','); + builder.Append (EncodeStringIdx (EnglishName)); + builder.Append (','); + builder.Append (EncodeStringIdx (CurrencySymbol)); + builder.Append (','); + builder.Append (EncodeStringIdx (ISOCurrencySymbol)); + builder.Append (','); + builder.Append (EncodeStringIdx (CurrencyEnglishName)); + builder.Append ('}'); + } + + public override string ToString () + { + StringBuilder builder = new StringBuilder (); + AppendTableRow (builder); + return builder.ToString (); + } + } +} + + Index: Driver.cs =================================================================== --- Driver.cs (revision 48341) +++ Driver.cs (working copy) @@ -3,8 +3,9 @@ // // Author(s): // Jackson Harper ([EMAIL PROTECTED]) +// Atsushi Enomoto ([EMAIL PROTECTED]) // -// (C) 2004 Novell, Inc (http://www.novell.com) +// (C) 2004-2005 Novell, Inc (http://www.novell.com) // @@ -46,7 +47,8 @@ private ArrayList cultures; private Hashtable langs; private Hashtable currency_types; - + private Hashtable regions; + // The lang is the language that display names will be displayed in public string Lang { get { @@ -79,7 +81,10 @@ langs = new Hashtable (); cultures = new ArrayList (); + regions = new Hashtable (); + LookupRegions (); + LookupCurrencyTypes (); foreach (string file in Directory.GetFiles ("locales", "*.xml")) { @@ -109,7 +114,42 @@ } } + ArrayList regionList = new ArrayList (regions.Values); + regionList.Sort (RegionComparer.Instance); + int number = 0; + foreach (RegionInfoEntry r in regionList) + r.RegionId = number++; + ArrayList regionMap = new ArrayList (); + + foreach (CultureInfoEntry e in cultures) { + int lcid = int.Parse (e.Lcid.Substring (2), + NumberStyles.HexNumber); + int idx; + int start = e.Name.IndexOf ('-') + 1; + if (start == 0) + continue; + for (idx = start; idx < e.Name.Length; idx++) + if (!Char.IsLetter (e.Name [idx])) + break; + if (start == idx) { + Console.Error.WriteLine ("Culture {0} {1} is not mappable to Region.", e.Lcid, e.Name); + continue; + } + string name = e.Name.Substring (start, idx - start); + RegionInfoEntry rm = null; + foreach (RegionInfoEntry r in regions.Values) + if (r.ISO2Name == name) { + rm = r; + break; + } + if (rm == null) { + Console.Error.WriteLine ("No definition for region {0}", name); + continue; + } + regionMap.Add (new RegionLCIDMap (lcid, rm.RegionId)); + } + /** * Dump each table individually. Using StringBuilders * because it is easier to debug, should switch to just @@ -124,6 +164,8 @@ writer.WriteLine ("\n"); writer.WriteLine ("#define NUM_CULTURE_ENTRIES " + cultures.Count); + writer.WriteLine ("#define NUM_REGION_ENTRIES " + regionList.Count); + writer.WriteLine ("#define NUM_REGION_LCID_MAP " + regionMap.Count); writer.WriteLine ("\n"); // Sort the cultures by lcid @@ -194,6 +236,47 @@ writer.Write (builder); writer.WriteLine ("};\n\n"); + builder = new StringBuilder (); + int rcount = 0; + foreach (RegionInfoEntry r in regionList) { + r.AppendTableRow (builder); + if (++rcount != regionList.Count) + builder.Append (','); + builder.Append ('\n'); + } + writer.WriteLine ("static const RegionInfoEntry region_entries [] = {"); + writer.Write (builder); + writer.WriteLine ("};\n\n"); + + builder = new StringBuilder (); + rcount = 0; + foreach (RegionInfoEntry ri in regionList) { + builder.Append ("\t{" + Entry.EncodeStringIdx (ri.ISO2Name) + ", "); + builder.Append (ri.RegionId + "}"); + if (++rcount < regionList.Count) + builder.Append (','); + builder.Append ('\n'); + } + + writer.WriteLine ("static const RegionInfoNameEntry region_name_entries [] = {"); + writer.Write (builder); + writer.WriteLine ("};\n\n"); + + builder = new StringBuilder (); + for (int i = 0; i < regionMap.Count; i++) { + RegionLCIDMap map = (RegionLCIDMap) regionMap [i]; + builder.Append ("\t{" + map.LCID + ", "); + builder.Append (map.RegionId + "}"); + if (i + 1 < regionMap.Count) + builder.Append (','); + builder.Append ('\n'); + } + + writer.WriteLine ("static const RegionLCIDMap region_lcid_map [] = {"); + writer.Write (builder); + writer.WriteLine ("};\n\n"); + + writer.WriteLine ("static const char locale_strings [] = {"); writer.Write (Entry.GetStrings ()); writer.WriteLine ("};\n\n"); @@ -915,6 +998,42 @@ return ret; } + private void LookupRegions () + { + XPathDocument doc = GetXPathDocument ("supplementalData.xml"); + XPathNavigator nav = doc.CreateNavigator (); + XPathNodeIterator ni = nav.Select ("supplementalData/currencyData/region"); + while (ni.MoveNext ()) { + string territory = (string) ni.Current.GetAttribute ("iso3166", String.Empty); + string currency = (string) ni.Current.Evaluate ("string(currency/@iso4217)"); + RegionInfoEntry region = new RegionInfoEntry (); + region.ISO2Name = territory.ToUpper (); + region.ISOCurrencySymbol = currency; + regions [territory] = region; + } + + doc = GetXPathDocument ("langs/en.xml"); + nav = doc.CreateNavigator (); + ni = nav.Select ("/ldml/localeDisplayNames/territories/territory"); + while (ni.MoveNext ()) { + RegionInfoEntry r = (RegionInfoEntry) + regions [ni.Current.GetAttribute ("type", "")]; + if (r == null) + continue; + r.EnglishName = ni.Current.Value; + } + + Hashtable curNames = new Hashtable (); + ni = nav.Select ("/ldml/numbers/currencies/currency"); + while (ni.MoveNext ()) + curNames [ni.Current.GetAttribute ("type", "")] = + ni.Current.Evaluate ("string (displayName)"); + + foreach (RegionInfoEntry r in regions.Values) + r.CurrencyEnglishName = + (string) curNames [r.ISOCurrencySymbol]; + } + private void LookupCurrencyTypes () { XPathDocument doc = GetXPathDocument ("supplementalData.xml"); @@ -1009,6 +1128,31 @@ return aa.Name.ToLower ().CompareTo (bb.Name.ToLower ()); } } + + class RegionComparer : IComparer + { + public static RegionComparer Instance = new RegionComparer (); + + public int Compare (object o1, object o2) + { + RegionInfoEntry r1 = (RegionInfoEntry) o1; + RegionInfoEntry r2 = (RegionInfoEntry) o2; + return String.CompareOrdinal ( + r1.ISO2Name, r2.ISO2Name); + } + } + + class RegionLCIDMap + { + public RegionLCIDMap (int lcid, int regionId) + { + LCID = lcid; + RegionId = regionId; + } + + public int LCID; + public int RegionId; + } } } Index: Makefile.am =================================================================== --- Makefile.am (revision 48341) +++ Makefile.am (working copy) @@ -1,6 +1,6 @@ MCS = mcs -RUNTIME = mono +RUNTIME = mono --debug MCSFLAGS = -debug+ # To build a reduced mono runtime with support only for some locales, # run: # make minimal @@ -22,6 +22,7 @@ CultureInfoEntry.cs \ DateTimeFormatEntry.cs \ NumberFormatEntry.cs \ + RegionInfoEntry.cs \ TextInfoEntry.cs \ Entry.cs
Index: RegionInfo.cs =================================================================== --- RegionInfo.cs (revision 48291) +++ RegionInfo.cs (working copy) @@ -22,10 +22,170 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System.Globalization; +using System.Runtime.CompilerServices; namespace System.Globalization { +#if true [Serializable] + public class RegionInfo + { + static object forLock = new object (); + static RegionInfo currentRegion; + + // This property is not synchronized with CurrentCulture, so + // we need to use bootstrap CurrentCulture LCID. + public static RegionInfo CurrentRegion { + get { + if (currentRegion == null) { + CultureInfo ci = CultureInfo.CurrentCulture; + // If current culture is invariant then region is not available. + if (ci == null || CultureInfo.BootstrapCultureID == 0x7F) + return null; + lock (forLock) { + // make sure to fill BootstrapCultureID. + currentRegion = new RegionInfo (CultureInfo.BootstrapCultureID); + } + } + return currentRegion; + } + } + + int regionId; + string iso2Name; + string iso3Name; + string win3Name; + string englishName; + string currencySymbol; + string isoCurrencySymbol; + string currencyEnglishName; + + public RegionInfo (int lcid) + { + if (!ConstructInternalRegionFromLcid (lcid)) + throw new ArgumentException ( + String.Format ("Region ID {0} (0x{0:X4}) is not a " + + "supported region.", lcid), "lcid"); + } + + public RegionInfo (string name) + { + if (name == null) + throw new ArgumentNullException (); + + if (!ConstructInternalRegionFromName (name.ToUpperInvariant ())) + throw new ArgumentException ("Region name " + name + + " is not supported.", "name"); + } + + bool ConstructInternalRegionFromName (string locale) + { + if (!construct_internal_region_from_name (locale)) + return false; + return true; + } + + bool ConstructInternalRegionFromLcid (int lcid) + { + if (!construct_internal_region_from_lcid (lcid)) + return false; + return true; + } + + [MethodImplAttribute (MethodImplOptions.InternalCall)] + private extern bool construct_internal_region_from_lcid (int lcid); + + [MethodImplAttribute (MethodImplOptions.InternalCall)] + private extern bool construct_internal_region_from_name (string name); + +#if NET_2_0 + public virtual string CurrencyEnglishName { + get { return currencyEnglishName; } + } +#endif + + public virtual string CurrencySymbol { + get { return currencySymbol; } + } + + [MonoTODO] + public virtual string DisplayName { + get { return englishName; } + } + + public virtual string EnglishName { + get { return englishName; } + } + + public virtual bool IsMetric { + get { + switch (iso2Name) { + case "US": + case "UK": + return false; + default: + return true; + } + } + } + + public virtual string ISOCurrencySymbol { + get { return isoCurrencySymbol; } + } + +#if NET_2_0 + [MonoTODO] + public virtual string NativeName { + get { return DisplayName; } + } + + [MonoTODO] + public virtual string CurrencyNativeName { + get { throw new NotImplementedException (); } + } +#endif + + public virtual string Name { + get { return iso2Name; } + } + + public virtual string ThreeLetterISORegionName { + get { return iso3Name; } + } + + public virtual string ThreeLetterWindowsRegionName { + get { return win3Name; } + } + + public virtual string TwoLetterISORegionName { + get { return iso2Name; } + } + + // + // methods + +#if NET_2_0 +#else + public override bool Equals (object value) + { + RegionInfo other = value as RegionInfo; + return other != null && regionId == other.regionId; + } + + public override int GetHashCode () { + return regionId; + } +#endif + + public override string ToString () + { + return Name; + } + } + +#else + + [Serializable] [MonoTODO ("This class should be implemented from scratch.")] public class RegionInfo { int NLS_id; @@ -1767,5 +1927,5 @@ } } - +#endif }
_______________________________________________ Mono-devel-list mailing list Mono-devel-list@lists.ximian.com http://lists.ximian.com/mailman/listinfo/mono-devel-list