New test.
[mono.git] / tools / locale-builder / Driver.cs
index 90d8e40a4d2f99b1ef2071308a21c60c3cd19831..3f851f17f59dd1042a3f881496f63ac0faf781c2 100644 (file)
@@ -3,8 +3,9 @@
 //
 // Author(s):
 //  Jackson Harper (jackson@ximian.com)
+//  Atsushi Enomoto (atsushi@ximian.com)
 //
-// (C) 2004 Novell, Inc (http://www.novell.com)
+// (C) 2004-2005 Novell, Inc (http://www.novell.com)
 //
 
 
@@ -46,7 +47,10 @@ namespace Mono.Tools.LocaleBuilder {
                 private ArrayList cultures;
                 private Hashtable langs;
                 private Hashtable currency_types;
-                
+                private Hashtable regions;
+
+               private XPathDocument lcids_doc;
+
                // The lang is the language that display names will be displayed in
                public string Lang {
                        get {
@@ -73,35 +77,96 @@ namespace Mono.Tools.LocaleBuilder {
 
                public void Run ()
                {
+                       lcids_doc = GetXPathDocument ("lcids.xml");
+
                        Regex locales_regex = null;
                        if (Locales != null)
                                locales_regex = new Regex (Locales);
 
                         langs = new Hashtable ();
                         cultures = new ArrayList ();
+                        regions = new Hashtable ();
+
+                       LookupRegions ();
 
                         LookupCurrencyTypes ();
 
                        foreach (string file in Directory.GetFiles ("locales", "*.xml")) {
                                string fn = Path.GetFileNameWithoutExtension (file);
+                               if (fn == "hy_AM")
+                                       continue; // see bug #75499
                                if (locales_regex == null || locales_regex.IsMatch (fn)) {
                                        ParseLocale (fn);
                                 }
                        }
 
+                       /* FIXME: This is hacky.
+                        * Since there is only langs/zh.xml while there are
+                        * two "zh" languages (CHS and CHT), there should be
+                        * different language profiles and we are not likely
+                        * to add lang/* files. So here I just clone zh-CHS
+                        * as zh-CHT
+                        */
+                        foreach (CultureInfoEntry e in cultures) {
+                               if (e.Name == "zh-CHS") {
+                                       CultureInfoEntry t =
+                                               CultureInfoEntry.ShallowCopy (e);
+                                       t.Language = "zh-CHT";
+                                       LookupLcids (t, true);
+                                       cultures.Add (t);
+                                       break;
+                               }
+                        }
+
+                       ArrayList regionList = new ArrayList (regions.Values);
+                       regionList.Sort (RegionComparer.Instance);
+                       int number = 0;
+                       foreach (RegionInfoEntry r in regionList)
+                               r.RegionId = number++;
+
+                       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;
+                               }
+                               e.RegionId = rm.RegionId;
+                       }
+
                         /**
                          * Dump each table individually. Using StringBuilders
                          * because it is easier to debug, should switch to just
                          * writing to streams eventually.
                          */
                         using (StreamWriter writer = new StreamWriter (HeaderFileName, false, new UTF8Encoding (false, true))) {
-
+                                writer.NewLine = "\n";
                                 writer.WriteLine ();
+                                writer.WriteLine ("/* This is a generated file. Do not edit. See tools/locale-builder. */");
                                 writer.WriteLine ("#ifndef MONO_METADATA_CULTURE_INFO_TABLES");
                                 writer.WriteLine ("#define MONO_METADATA_CULTURE_INFO_TABLES 1");
                                 writer.WriteLine ("\n");
 
                                 writer.WriteLine ("#define NUM_CULTURE_ENTRIES " + cultures.Count);
+                                writer.WriteLine ("#define NUM_REGION_ENTRIES " + regionList.Count);
                                 writer.WriteLine ("\n");
 
                                 // Sort the cultures by lcid
@@ -161,7 +226,7 @@ namespace Mono.Tools.LocaleBuilder {
                                 builder = new StringBuilder ();
                                 for (int i = 0; i < count; i++) {
                                         CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
-                                        builder.Append ("\t{\"" + ci.Name.ToLower () + "\", ");
+                                        builder.Append ("\t{" + Entry.EncodeStringIdx (ci.Name.ToLower ()) + ", ");
                                         builder.Append (ci.Row + "}");
                                         if (i + 1 < count)
                                                 builder.Append (',');
@@ -172,20 +237,61 @@ namespace Mono.Tools.LocaleBuilder {
                                 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");
+
+                                writer.WriteLine ("static const char locale_strings [] = {");
+                                writer.Write (Entry.GetStrings ());
+                                writer.WriteLine ("};\n\n");
+
                                 writer.WriteLine ("#endif\n");
                         }
                }
 
                private XPathDocument GetXPathDocument (string path)
                {
-                       XmlTextReader xtr = new XmlTextReader (path);
-                       xtr.XmlResolver = null;
-                       return new XPathDocument (xtr);
+                       XmlTextReader xtr = null;
+                       try {
+                               xtr = new XmlTextReader (path);
+                               xtr.XmlResolver = null;
+                               return new XPathDocument (xtr);
+                       } finally {
+                               if (xtr != null)
+                                       xtr.Close ();
+                       }
+               }
+
+               private string GetShortName (string lang)
+               {
+                       return lang == "zh-CHS" ? "zh" : lang;
                }
 
                 private bool ParseLang (string lang)
                {
-                        XPathDocument doc = GetXPathDocument (Path.Combine ("langs", lang + ".xml"));
+                        XPathDocument doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
                        XPathNavigator nav = doc.CreateNavigator ();
                         CultureInfoEntry ci = new CultureInfoEntry ();
                         string lang_type, terr_type;
@@ -198,17 +304,17 @@ namespace Mono.Tools.LocaleBuilder {
                         ci.Language = (lang_type == String.Empty ? null : lang_type);
                         ci.Territory = (terr_type == String.Empty ? null : terr_type);
 
-                       if (!LookupLcids (ci))
+                       if (!LookupLcids (ci, true))
                                 return false;
 
-                        doc = GetXPathDocument (Path.Combine ("langs", Lang + ".xml"));
+                        doc = GetXPathDocument (Path.Combine ("langs", GetShortName (Lang) + ".xml"));
                        nav = doc.CreateNavigator ();
                        ci.DisplayName = LookupFullName (ci, nav);
                         
                        if (Lang == "en") {
                                ci.EnglishName = ci.DisplayName;
                        } else {
-                               doc = GetXPathDocument (Path.Combine ("langs", Lang + ".xml"));
+                               doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
                                nav = doc.CreateNavigator ();
                                ci.EnglishName = LookupFullName (ci, nav);
                        }
@@ -216,7 +322,7 @@ namespace Mono.Tools.LocaleBuilder {
                        if (ci.Language == Lang) {
                                ci.NativeName = ci.DisplayName;
                        } else {
-                               doc = GetXPathDocument (Path.Combine ("langs", lang + ".xml"));
+                               doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
                                nav = doc.CreateNavigator ();
                                ci.NativeName = LookupFullName (ci, nav);
                        }
@@ -240,8 +346,8 @@ namespace Mono.Tools.LocaleBuilder {
                         if (ci == null)
                                 return;
 
-                        if (langs [ci.Language] == null) {
-                                if (!ParseLang (ci.Language)) // If we can't parse the lang we cant have the locale
+                        if (langs [GetLanguageFixed (ci)] == null) {
+                                if (!ParseLang (GetLanguageFixed (ci))) // If we can't parse the lang we cant have the locale
                                         return;
                         }
 
@@ -250,7 +356,10 @@ namespace Mono.Tools.LocaleBuilder {
 
                private CultureInfoEntry LookupCulture (string locale)
                {
-                        XPathDocument doc = GetXPathDocument (Path.Combine ("locales", locale + ".xml"));
+                       string path = Path.Combine ("locales", locale + ".xml");
+                       if (!File.Exists (path))
+                               return null;
+                        XPathDocument doc = GetXPathDocument (path);
                         XPathNavigator nav = doc.CreateNavigator ();
                        CultureInfoEntry ci = new CultureInfoEntry ();
                        string supp;
@@ -261,7 +370,7 @@ namespace Mono.Tools.LocaleBuilder {
                        ci.Language = nav.Evaluate ("string (ldml/identity/language/@type)").ToString ();
                        ci.Territory = nav.Evaluate ("string (ldml/identity/territory/@type)").ToString ();
 
-                        if (!LookupLcids (ci))
+                        if (!LookupLcids (ci, false))
                                 return null;
                        LookupNames (ci);
 
@@ -282,11 +391,11 @@ namespace Mono.Tools.LocaleBuilder {
                         nav = doc.CreateNavigator ();
                         Lookup (nav, ci);
 
-                       doc = GetXPathDocument (Path.Combine ("langs", ci.Language + ".xml"));
+                       doc = GetXPathDocument (Path.Combine ("langs", GetShortName (GetLanguageFixed (ci)) + ".xml"));
                        nav = doc.CreateNavigator ();
                        Lookup (nav, ci);
 
-                       supp = Path.Combine ("supp", ci.Language + ".xml");
+                       supp = Path.Combine ("supp", GetLanguageFixed (ci) + ".xml");
                        if (File.Exists (supp)) {
                                doc = GetXPathDocument (supp);
                                nav = doc.CreateNavigator ();
@@ -313,9 +422,22 @@ namespace Mono.Tools.LocaleBuilder {
                        LookupNumberInfo (nav, ci);
                }
 
+               private string GetLanguageFixed (CultureInfoEntry ci)
+               {
+                       // This is a hack, but without it nb-NO and nn-NO won't work.
+                       if (ci.Territory == "NO") {
+                               switch (ci.Language) {
+                               case "nb":
+                               case "nn":
+                                       return "no";
+                               }
+                       }
+                       return ci.Language;
+               }
+
                private void LookupNames (CultureInfoEntry ci)
                {
-                       XPathDocument doc = GetXPathDocument (Path.Combine ("langs", Lang + ".xml"));
+                       XPathDocument doc = GetXPathDocument (Path.Combine ("langs", GetShortName (Lang) + ".xml"));
                        XPathNavigator nav = doc.CreateNavigator ();
 
                        ci.DisplayName = LookupFullName (ci, nav);
@@ -331,12 +453,22 @@ namespace Mono.Tools.LocaleBuilder {
                        if (ci.Language == Lang) {
                                ci.NativeName = ci.DisplayName;
                        } else {
-                               doc = GetXPathDocument (Path.Combine ("langs", ci.Language + ".xml"));
+                               // FIXME: We use ci.Language here.
+                               // This is nothing more than hack for nb-NO and nn-NO
+                               // where Parent of them is nn (not nb or nn).
+                               string lang = ci.Language;
+                               doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
                                nav = doc.CreateNavigator ();
                                ci.NativeName = LookupFullName (ci, nav);
                        }
                }
 
+               private void AddPattern (ArrayList al, string pattern)
+               {
+                       if (!al.Contains (pattern))
+                               al.Add (pattern);
+               }
+
                private void LookupDateTimeInfo (XPathNavigator nav, CultureInfoEntry ci)
                {
                        /**
@@ -432,10 +564,13 @@ namespace Mono.Tools.LocaleBuilder {
                                                ext = df_nav.Select ("extraPatterns/pattern");
                                                if (ext.MoveNext ()) {
                                                        df.LongDatePatterns.Clear ();
+                                                       AddPattern (df.LongDatePatterns, df.LongDatePattern);
                                                        do {
                                                                df.LongDatePatterns.Add (ext.Current.Value);
                                                        } while (ext.MoveNext ());
                                                }
+                                               else
+                                                       AddPattern (df.LongDatePatterns, df.LongDatePattern);
                                                break;
                                        case "short":
                                                if (value != null)
@@ -443,10 +578,13 @@ namespace Mono.Tools.LocaleBuilder {
                                                ext = df_nav.Select ("extraPatterns/pattern");
                                                if (ext.MoveNext ()) {
                                                        df.ShortDatePatterns.Clear ();
+                                                       AddPattern (df.ShortDatePatterns, df.ShortDatePattern);
                                                        do {
                                                                df.ShortDatePatterns.Add (ext.Current.Value);
                                                        } while (ext.MoveNext ());
                                                }
+                                               else
+                                                       AddPattern (df.ShortDatePatterns, df.ShortDatePattern);
                                                break;
                                        case "year_month":
                                                if (value != null)
@@ -474,10 +612,13 @@ namespace Mono.Tools.LocaleBuilder {
                                                ext = df_nav.Select ("extraPatterns/pattern");
                                                if (ext.MoveNext ()) {
                                                        df.LongTimePatterns.Clear ();
+                                                       AddPattern (df.LongTimePatterns, df.LongTimePattern);
                                                        do {
                                                                df.LongTimePatterns.Add (ext.Current.Value);
                                                        } while (ext.MoveNext ());
                                                }
+                                               else
+                                                       AddPattern (df.LongTimePatterns, df.LongTimePattern);
                                                break;
                                        case "short":
                                                if (value != null)
@@ -485,10 +626,13 @@ namespace Mono.Tools.LocaleBuilder {
                                                ext = df_nav.Select ("extraPatterns/pattern");
                                                if (ext.MoveNext ()) {
                                                        df.ShortTimePatterns.Clear ();
+                                                       AddPattern (df.ShortTimePatterns, df.ShortTimePattern);
                                                        do {
                                                                df.ShortTimePatterns.Add (ext.Current.Value);
                                                        } while (ext.MoveNext ());
                                                }
+                                               else
+                                                       AddPattern (df.ShortTimePatterns, df.ShortTimePattern);
                                                break;
                                        }
                                }
@@ -809,25 +953,29 @@ namespace Mono.Tools.LocaleBuilder {
                                 ci.NumberFormatEntry.CurrencySymbol = cur;
                 }
 
-               private bool LookupLcids (CultureInfoEntry ci)
+               private bool LookupLcids (CultureInfoEntry ci, bool lang)
                {
-                       XPathDocument doc = GetXPathDocument ("lcids.xml");
-                       XPathNavigator nav = doc.CreateNavigator ();
-                       string name = ci.Language;
+                       XPathNavigator nav = lcids_doc.CreateNavigator ();
+                       string name = ci.Name;
+                       // Language name does not always consist of locale name.
+                       // (for zh-* it must be either zh-CHS or zh-CHT)
+                       string langName = GetLanguageFixed (ci);
 
-                        if (ci.Territory != null)
-                                name += "-" + ci.Territory;
+//                        if (ci.Territory != null)
+//                                name += "-" + ci.Territory;
 
                        XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("lcids/lcid[@name='"
-                                       + name + "']");
+                                       + (lang ? langName : name) + "']");
                        if (!ni.MoveNext ()) {
+                               Console.WriteLine ("no lcid found for: {0} ({1}/{2})", name, ci.Language, ci.Territory);
                                 string file;
+
                                 if (ci.Territory != null) {
                                         file = Path.Combine ("locales", ci.Language + "_" + ci.Territory + ".xml");
-                                        File.Delete (file);
                                         Console.WriteLine ("deleting file:  " + file);
+                                        File.Delete (file);
                                 }
-                               Console.WriteLine ("no lcid found for:  " + name);
+
                                return false;
                        }
 
@@ -847,6 +995,8 @@ namespace Mono.Tools.LocaleBuilder {
                         ci.ISO3Lang = iso3;
                         ci.Win3Lang = win;
                         ci.IcuName = icu;
+                       
+                       ci.TextInfoEntry = new TextInfoEntry (int.Parse (id.Substring (2), NumberStyles.HexNumber), GetXPathDocument ("textinfo.xml"));
 
                         return true;
                }
@@ -856,8 +1006,11 @@ namespace Mono.Tools.LocaleBuilder {
                        string pre = "ldml/localeDisplayNames/";
                        string ret;
 
+                       // FIXME: We use ci.Language here.
+                       // This is nothing more than hack for nb-NO or nn-NO
+                       // where Parent of them is nn (not nb or nn).
                        ret = (string) nav.Evaluate ("string("+
-                                       pre + "languages/language[@type='" + ci.Language + "'])");
+                                       pre + "languages/language[@type='" + GetShortName (ci.Language) + "'])");
 
                        if (ci.Territory == null)
                                return ret;
@@ -867,6 +1020,42 @@ namespace Mono.Tools.LocaleBuilder {
                        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");
@@ -961,6 +1150,31 @@ namespace Mono.Tools.LocaleBuilder {
                                 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;
+               }
         }
 }