2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / tools / locale-builder / Driver.cs
1 //
2 // Mono.Tools.LocalBuilder.Driver
3 //
4 // Author(s):
5 //  Jackson Harper (jackson@ximian.com)
6 //
7 // (C) 2004 Novell, Inc (http://www.novell.com)
8 //
9
10
11 using System;
12 using System.IO;
13 using System.Text;
14 using System.Xml;
15 using System.Xml.XPath;
16 using System.Collections;
17 using System.Globalization;
18 using System.Text.RegularExpressions;
19
20 namespace Mono.Tools.LocaleBuilder {
21
22         public class Driver {
23
24                 public static void Main (string [] args)
25                 {
26                         Driver d = new Driver ();
27                         ParseArgs (args, d);
28                         d.Run ();
29                 }
30
31                 private static void ParseArgs (string [] args, Driver d)
32                 {
33                         for (int i = 0; i < args.Length; i++) {
34                                 if (args [i] == "--lang" && i+1 < args.Length)
35                                         d.Lang = args [++i];
36                                 else if (args [i] == "--locales" && i+1 < args.Length)
37                                         d.Locales = args [++i];
38                                 else if (args [i] == "--header" && i + 1 < args.Length)
39                                         d.HeaderFileName = args [++i];
40                         }
41                 }
42
43                 private string lang;
44                 private string locales;
45                 private string header_name;
46                 private ArrayList cultures;
47                 private Hashtable langs;
48                 private Hashtable currency_types;
49                 
50                 // The lang is the language that display names will be displayed in
51                 public string Lang {
52                         get {
53                                 if (lang == null)
54                                         lang = "en";
55                                 return lang;
56                         }
57                         set { lang = value; }
58                 }
59
60                 public string Locales {
61                         get { return locales; }
62                         set { locales = value; }
63                 }
64
65                 public string HeaderFileName {
66                         get {
67                                 if (header_name == null)
68                                         return "culture-info-tables.h";
69                                 return header_name;
70                         }
71                         set { header_name = value; }
72                 }
73
74                 public void Run ()
75                 {
76                         Regex locales_regex = null;
77                         if (Locales != null)
78                                 locales_regex = new Regex (Locales);
79
80                         langs = new Hashtable ();
81                         cultures = new ArrayList ();
82
83                         LookupCurrencyTypes ();
84
85                         foreach (string file in Directory.GetFiles ("locales", "*.xml")) {
86                                 string fn = Path.GetFileNameWithoutExtension (file);
87                                 if (locales_regex == null || locales_regex.IsMatch (fn)) {
88                                         ParseLocale (fn);
89                                 }
90                         }
91
92                         /**
93                          * Dump each table individually. Using StringBuilders
94                          * because it is easier to debug, should switch to just
95                          * writing to streams eventually.
96                          */
97                         using (StreamWriter writer = new StreamWriter (HeaderFileName, false, new UTF8Encoding (false, true))) {
98
99                                 writer.WriteLine ();
100                                 writer.WriteLine ("/* This is a generated file. Do not edit. See tools/locale-builder. */");
101                                 writer.WriteLine ("#ifndef MONO_METADATA_CULTURE_INFO_TABLES");
102                                 writer.WriteLine ("#define MONO_METADATA_CULTURE_INFO_TABLES 1");
103                                 writer.WriteLine ("\n");
104
105                                 writer.WriteLine ("#define NUM_CULTURE_ENTRIES " + cultures.Count);
106                                 writer.WriteLine ("\n");
107
108                                 // Sort the cultures by lcid
109                                 cultures.Sort (new LcidComparer ());
110
111                                 StringBuilder builder = new StringBuilder ();
112                                 int row = 0;
113                                 int count = cultures.Count;
114                                 for (int i = 0; i < count; i++) {
115                                         CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
116                                         if (ci.DateTimeFormatEntry == null)
117                                                 continue;
118                                         ci.DateTimeFormatEntry.AppendTableRow (builder);
119                                         ci.DateTimeFormatEntry.Row = row++;
120                                         if (i + 1 < count)
121                                                 builder.Append (',');
122                                         builder.Append ('\n');
123                                 }
124
125                                 writer.WriteLine ("static const DateTimeFormatEntry datetime_format_entries [] = {");
126                                 writer.Write (builder);
127                                 writer.WriteLine ("};\n\n");
128                                 
129                                 builder = new StringBuilder ();
130                                 row = 0;
131                                 for (int i=0; i < count; i++) {
132                                         CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
133                                         if (ci.NumberFormatEntry == null)
134                                                 continue;
135                                         ci.NumberFormatEntry.AppendTableRow (builder);
136                                         ci.NumberFormatEntry.Row = row++;
137                                         if (i + 1 < count)
138                                                 builder.Append (',');
139                                         builder.Append ('\n');
140                                 }
141
142                                 writer.WriteLine ("static const NumberFormatEntry number_format_entries [] = {");
143                                 writer.Write (builder);
144                                 writer.WriteLine ("};\n\n");
145                                 
146                                 builder = new StringBuilder ();
147                                 row = 0;
148                                 for (int i = 0; i < count; i++) {
149                                         CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
150                                         ci.AppendTableRow (builder);
151                                         ci.Row = row++;
152                                         if (i + 1 < count)
153                                                 builder.Append (',');
154                                         builder.Append ('\n');
155                                 }
156                                 
157                                 writer.WriteLine ("static const CultureInfoEntry culture_entries [] = {");
158                                 writer.Write (builder);
159                                 writer.WriteLine ("};\n\n");
160
161                                 cultures.Sort (new NameComparer ()); // Sort based on name
162                                 builder = new StringBuilder ();
163                                 for (int i = 0; i < count; i++) {
164                                         CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
165                                         builder.Append ("\t{" + Entry.EncodeStringIdx (ci.Name.ToLower ()) + ", ");
166                                         builder.Append (ci.Row + "}");
167                                         if (i + 1 < count)
168                                                 builder.Append (',');
169                                         builder.Append ('\n');
170                                 }
171
172                                 writer.WriteLine ("static const CultureInfoNameEntry culture_name_entries [] = {");
173                                 writer.Write (builder);
174                                 writer.WriteLine ("};\n\n");
175
176                                 writer.WriteLine ("static const char locale_strings [] = {");
177                                 writer.Write (Entry.GetStrings ());
178                                 writer.WriteLine ("};\n\n");
179
180                                 writer.WriteLine ("#endif\n");
181                         }
182                 }
183
184                 private XPathDocument GetXPathDocument (string path)
185                 {
186                         XmlTextReader xtr = new XmlTextReader (path);
187                         xtr.XmlResolver = null;
188                         return new XPathDocument (xtr);
189                 }
190
191                 private bool ParseLang (string lang)
192                 {
193                         XPathDocument doc = GetXPathDocument (Path.Combine ("langs", lang + ".xml"));
194                         XPathNavigator nav = doc.CreateNavigator ();
195                         CultureInfoEntry ci = new CultureInfoEntry ();
196                         string lang_type, terr_type;
197
198 //                        ci.Name = lang; // TODO: might need to be mapped.
199
200                         lang_type = nav.Evaluate ("string (ldml/identity/language/@type)").ToString ();
201                         terr_type = nav.Evaluate ("string (ldml/identity/territory/@type)").ToString ();
202
203                         ci.Language = (lang_type == String.Empty ? null : lang_type);
204                         ci.Territory = (terr_type == String.Empty ? null : terr_type);
205
206                         if (!LookupLcids (ci))
207                                 return false;
208
209                         doc = GetXPathDocument (Path.Combine ("langs", Lang + ".xml"));
210                         nav = doc.CreateNavigator ();
211                         ci.DisplayName = LookupFullName (ci, nav);
212                         
213                         if (Lang == "en") {
214                                 ci.EnglishName = ci.DisplayName;
215                         } else {
216                                 doc = GetXPathDocument (Path.Combine ("langs", Lang + ".xml"));
217                                 nav = doc.CreateNavigator ();
218                                 ci.EnglishName = LookupFullName (ci, nav);
219                         }
220
221                         if (ci.Language == Lang) {
222                                 ci.NativeName = ci.DisplayName;
223                         } else {
224                                 doc = GetXPathDocument (Path.Combine ("langs", lang + ".xml"));
225                                 nav = doc.CreateNavigator ();
226                                 ci.NativeName = LookupFullName (ci, nav);
227                         }
228
229                         // Null these out because langs dont have them
230                         ci.DateTimeFormatEntry = null;
231                         ci.NumberFormatEntry = null;
232
233                         langs [lang] = ci;
234                         cultures.Add (ci);
235
236                         return true;
237                 }
238
239                 private void ParseLocale (string locale)
240                 {
241                         CultureInfoEntry ci;
242
243                         ci = LookupCulture (locale);
244
245                         if (ci == null)
246                                 return;
247
248                         if (langs [ci.Language] == null) {
249                                 if (!ParseLang (ci.Language)) // If we can't parse the lang we cant have the locale
250                                         return;
251                         }
252
253                         cultures.Add (ci);
254                 }
255
256                 private CultureInfoEntry LookupCulture (string locale)
257                 {
258                         XPathDocument doc = GetXPathDocument (Path.Combine ("locales", locale + ".xml"));
259                         XPathNavigator nav = doc.CreateNavigator ();
260                         CultureInfoEntry ci = new CultureInfoEntry ();
261                         string supp;
262
263 //                        ci.Name = locale; // TODO: Some of these need to be mapped.
264
265                         // First thing we do is get the lang-territory combo, lcid, and full names
266                         ci.Language = nav.Evaluate ("string (ldml/identity/language/@type)").ToString ();
267                         ci.Territory = nav.Evaluate ("string (ldml/identity/territory/@type)").ToString ();
268
269                         if (!LookupLcids (ci))
270                                 return null;
271                         LookupNames (ci);
272
273                         /**
274                          * Locale generation is done in six steps, first we
275                          * read the root file which is the base invariant data
276                          * then the supplemental root data, 
277                          * then the language file, the supplemental languages
278                          * file then the locale file, then the supplemental
279                          * locale file. Values in each descending file can
280                          * overwrite previous values.
281                          */
282                         doc = GetXPathDocument (Path.Combine ("langs", "root.xml"));
283                         nav = doc.CreateNavigator ();
284                         Lookup (nav, ci);
285
286                         doc = GetXPathDocument (Path.Combine ("supp", "root.xml"));
287                         nav = doc.CreateNavigator ();
288                         Lookup (nav, ci);
289
290                         doc = GetXPathDocument (Path.Combine ("langs", ci.Language + ".xml"));
291                         nav = doc.CreateNavigator ();
292                         Lookup (nav, ci);
293
294                         supp = Path.Combine ("supp", ci.Language + ".xml");
295                         if (File.Exists (supp)) {
296                                 doc = GetXPathDocument (supp);
297                                 nav = doc.CreateNavigator ();
298                                 Lookup (nav, ci);
299                         }
300                         
301                         doc = GetXPathDocument (Path.Combine ("locales", locale + ".xml"));
302                         nav = doc.CreateNavigator ();
303                         Lookup (nav, ci);
304
305                         supp = Path.Combine ("supp", locale + ".xml");
306                         if (File.Exists (supp)) {
307                                 doc = GetXPathDocument (supp);
308                                 nav = doc.CreateNavigator ();
309                                 Lookup (nav, ci);
310                         }
311
312                         return ci;
313                 }
314
315                 private void Lookup (XPathNavigator nav, CultureInfoEntry ci)
316                 {
317                         LookupDateTimeInfo (nav, ci);
318                         LookupNumberInfo (nav, ci);
319                 }
320
321                 private void LookupNames (CultureInfoEntry ci)
322                 {
323                         XPathDocument doc = GetXPathDocument (Path.Combine ("langs", Lang + ".xml"));
324                         XPathNavigator nav = doc.CreateNavigator ();
325
326                         ci.DisplayName = LookupFullName (ci, nav);
327                         
328                         if (Lang == "en") {
329                                 ci.EnglishName = ci.DisplayName;
330                         } else {
331                                 doc = GetXPathDocument (Path.Combine ("langs", "en.xml"));
332                                 nav = doc.CreateNavigator ();
333                                 ci.EnglishName = LookupFullName (ci, nav);
334                         }
335
336                         if (ci.Language == Lang) {
337                                 ci.NativeName = ci.DisplayName;
338                         } else {
339                                 doc = GetXPathDocument (Path.Combine ("langs", ci.Language + ".xml"));
340                                 nav = doc.CreateNavigator ();
341                                 ci.NativeName = LookupFullName (ci, nav);
342                         }
343                 }
344
345                 private void LookupDateTimeInfo (XPathNavigator nav, CultureInfoEntry ci)
346                 {
347                         /**
348                          * TODO: Does anyone have multiple calendars?
349                          */
350                         XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("ldml/dates/calendars/calendar");
351
352                         while (ni.MoveNext ()) {
353                                 DateTimeFormatEntry df = ci.DateTimeFormatEntry;
354                                 string cal_type = ni.Current.GetAttribute ("type", String.Empty);
355
356                                 if (cal_type != String.Empty)
357                                         df.CalendarType = cal_type;
358
359                                 XPathNodeIterator ni2 = (XPathNodeIterator) ni.Current.Evaluate ("optionalCalendars/calendar");
360                                 int opt_cal_count = 0;
361                                 while (ni2.MoveNext ()) {
362                                         int type;
363                                         string greg_type_str;
364                                         XPathNavigator df_nav = ni2.Current;
365                                         switch (df_nav.GetAttribute ("type", String.Empty)) {
366                                         case "Gregorian":
367                                                 type = 0;
368                                                 break;
369                                         case "Hijri":
370                                                 type = 0x01;
371                                                 break;
372                                         case "ThaiBuddhist":
373                                                 type = 0x02;
374                                                 break;
375                                         default:
376                                                 Console.WriteLine ("unknown calendar type:  " +
377                                                                 df_nav.GetAttribute ("type", String.Empty));
378                                                 continue;
379                                         }
380                                         type <<= 24;
381                                         greg_type_str = df_nav.GetAttribute ("greg_type", String.Empty);
382                                         if (greg_type_str != null && greg_type_str != String.Empty) {
383                                                 GregorianCalendarTypes greg_type = (GregorianCalendarTypes)
384                                                         Enum.Parse (typeof (GregorianCalendarTypes), greg_type_str);
385                                                 int greg_type_int = (int) greg_type;
386                                                 type |= greg_type_int;
387                                                 
388                                         }
389                                         Console.WriteLine ("Setting cal type: {0:X}  for   {1}", type, ci.Name);
390                                         ci.CalendarData [opt_cal_count++] = type;
391                                 }
392                                 
393                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("monthNames/month");
394                                 while (ni2.MoveNext ()) {
395                                         if (ni2.CurrentPosition == 1)
396                                                 df.MonthNames.Clear ();
397                                         df.MonthNames.Add (ni2.Current.Value);
398                                 }
399
400                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dayNames/day");
401                                 while (ni2.MoveNext ()) {
402                                         if (ni2.CurrentPosition == 1)
403                                                 df.DayNames.Clear ();
404                                         df.DayNames.Add (ni2.Current.Value);
405                                 }
406
407                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dayAbbr/day");
408                                 while (ni2.MoveNext ()) {
409                                         if (ni2.CurrentPosition == 1)
410                                                 df.AbbreviatedDayNames.Clear ();
411                                         df.AbbreviatedDayNames.Add (ni2.Current.Value);
412                                 }
413
414                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("monthAbbr/month");
415                                 while (ni2.MoveNext ()) {
416                                         if (ni2.CurrentPosition == 1)
417                                                 df.AbbreviatedMonthNames.Clear ();
418                                         df.AbbreviatedMonthNames.Add (ni2.Current.Value);
419                                 }
420
421                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dateFormats/dateFormatLength");
422                                 while (ni2.MoveNext ()) {
423                                         XPathNavigator df_nav = ni2.Current;
424                                         XPathNodeIterator p = df_nav.Select ("dateFormat/pattern");
425                                         string value = null;
426                                         if (p.MoveNext ())
427                                                 value = p.Current.Value;
428                                         XPathNodeIterator ext = null;
429                                         switch (df_nav.GetAttribute ("type", String.Empty)) {
430                                         case "full":
431                                                 if (value != null)
432                                                         ParseFullDateFormat (df, value);
433                                                 break;
434                                         case "long":
435                                                 if (value != null)
436                                                         df.LongDatePattern = value;
437                                                 ext = df_nav.Select ("extraPatterns/pattern");
438                                                 if (ext.MoveNext ()) {
439                                                         df.LongDatePatterns.Clear ();
440                                                         do {
441                                                                 df.LongDatePatterns.Add (ext.Current.Value);
442                                                         } while (ext.MoveNext ());
443                                                 }
444                                                 break;
445                                         case "short":
446                                                 if (value != null)
447                                                         df.ShortDatePattern = value;
448                                                 ext = df_nav.Select ("extraPatterns/pattern");
449                                                 if (ext.MoveNext ()) {
450                                                         df.ShortDatePatterns.Clear ();
451                                                         do {
452                                                                 df.ShortDatePatterns.Add (ext.Current.Value);
453                                                         } while (ext.MoveNext ());
454                                                 }
455                                                 break;
456                                         case "year_month":
457                                                 if (value != null)
458                                                         df.YearMonthPattern = value;
459                                                 break;
460                                         case "month_day":
461                                                 if (value != null)
462                                                         df.MonthDayPattern = value;
463                                                 break;
464                                         }
465                                 }
466
467                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("timeFormats/timeFormatLength");
468                                 while (ni2.MoveNext ()) {
469                                         XPathNavigator df_nav = ni2.Current;
470                                         XPathNodeIterator p = df_nav.Select ("timeFormat/pattern");
471                                         string value = null;
472                                         if (p.MoveNext ())
473                                                 value = p.Current.Value;
474                                         XPathNodeIterator ext = null;
475                                         switch (df_nav.GetAttribute ("type", String.Empty)) {
476                                         case "long":
477                                                 if (value != null)
478                                                         df.LongTimePattern = value.Replace ('a', 't');
479                                                 ext = df_nav.Select ("extraPatterns/pattern");
480                                                 if (ext.MoveNext ()) {
481                                                         df.LongTimePatterns.Clear ();
482                                                         do {
483                                                                 df.LongTimePatterns.Add (ext.Current.Value);
484                                                         } while (ext.MoveNext ());
485                                                 }
486                                                 break;
487                                         case "short":
488                                                 if (value != null)
489                                                         df.ShortTimePattern = value.Replace ('a', 't');
490                                                 ext = df_nav.Select ("extraPatterns/pattern");
491                                                 if (ext.MoveNext ()) {
492                                                         df.ShortTimePatterns.Clear ();
493                                                         do {
494                                                                 df.ShortTimePatterns.Add (ext.Current.Value);
495                                                         } while (ext.MoveNext ());
496                                                 }
497                                                 break;
498                                         }
499                                 }
500
501                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dateTimeFormats/dateTimeFormatLength/dateTimeFormat/pattern");
502                                 if (ni2.MoveNext ())
503                                         df.FullDateTimePattern = String.Format (ni2.Current.ToString (),
504                                                         df.LongTimePattern, df.LongDatePattern);
505
506                                 XPathNodeIterator am = ni.Current.SelectChildren ("am", "");
507                                 if (am.MoveNext ())
508                                         df.AMDesignator = am.Current.Value;
509                                 XPathNodeIterator pm = ni.Current.SelectChildren ("pm", "");
510                                 if (pm.MoveNext ())
511                                         df.PMDesignator = pm.Current.Value;
512 /*
513                                 string am = (string) ni.Current.Evaluate ("string(am)");
514                                 string pm = (string) ni.Current.Evaluate ("string(pm)");
515
516                                 if (am != String.Empty)
517                                         df.AMDesignator = am;
518                                 if (pm != String.Empty)
519                                         df.PMDesignator = pm;
520 */
521                         }
522
523                         string date_sep = (string) nav.Evaluate ("string(ldml/dates/symbols/dateSeparator)");
524                         string time_sep = (string) nav.Evaluate ("string(ldml/dates/symbols/timeSeparator)");
525
526                         if (date_sep != String.Empty)
527                                 ci.DateTimeFormatEntry.DateSeparator = date_sep;
528                         if (time_sep != String.Empty)
529                                 ci.DateTimeFormatEntry.TimeSeparator = time_sep;
530                 }
531
532                 private void LookupNumberInfo (XPathNavigator nav, CultureInfoEntry ci)
533                 {
534                         XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("ldml/numbers");
535
536                         while (ni.MoveNext ()) {
537                                 LookupNumberSymbols (ni.Current, ci);
538                                 LookupDecimalFormat (ni.Current, ci);
539                                 LookupPercentFormat (ni.Current, ci);
540                                 LookupCurrencyFormat (ni.Current, ci);
541                                 LookupCurrencySymbol (ni.Current, ci);
542                         }
543                 }
544
545                 private void LookupDecimalFormat (XPathNavigator nav, CultureInfoEntry ci)
546                 {
547                         string format = (string) nav.Evaluate ("string(decimalFormats/" +
548                                         "decimalFormatLength/decimalFormat/pattern)");
549
550                         if (format == String.Empty)
551                                 return;
552
553                         string [] part_one, part_two;
554                         string [] pos_neg = format.Split (new char [1] {';'}, 2);
555
556                         // Most of the patterns are common in positive and negative
557                         if (pos_neg.Length == 1)
558                                 pos_neg = new string [] {pos_neg [0], pos_neg [0]};
559
560                         if (pos_neg.Length == 2) {
561                                 
562                                 part_one = pos_neg [0].Split (new char [1] {'.'}, 2);
563                                 if (part_one.Length == 1)
564                                         part_one = new string [] {part_one [0], String.Empty};
565
566                                 if (part_one.Length == 2) {
567                                         // assumed same for both positive and negative
568                                         // decimal digit side
569                                         ci.NumberFormatEntry.NumberDecimalDigits = 0;                                   
570                                         for (int i = 0; i < part_one [1].Length; i++) {
571                                                 if (part_one [1][i] == '#') {
572                                                         ci.NumberFormatEntry.NumberDecimalDigits ++;
573                                                 } else
574                                                         break;                                                          }
575                                         // FIXME: This should be actually done by modifying culture xml files, but too many files to be modified.
576                                         if (ci.NumberFormatEntry.NumberDecimalDigits > 0)
577                                                 ci.NumberFormatEntry.NumberDecimalDigits --;
578
579                                         // decimal grouping side
580                                         part_two = part_one [0].Split (',');
581                                         if (part_two.Length > 1) {
582                                                 int len = part_two.Length - 1;
583                                                 ci.NumberFormatEntry.NumberGroupSizes = new int [len];
584                                                 for (int i = 0; i < len; i++) {
585                                                         string pat = part_two [i + 1];
586                                                         ci.NumberFormatEntry.NumberGroupSizes [i] = pat.Length;
587                                                 }
588                                         } else {
589                                                 ci.NumberFormatEntry.NumberGroupSizes = new int [1] { 3 };
590                                         }
591
592                                         if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith (")")) {
593                                                 ci.NumberFormatEntry.NumberNegativePattern = 0;
594                                         } else if (pos_neg [1].StartsWith ("- ")) {
595                                                 ci.NumberFormatEntry.NumberNegativePattern = 2;
596                                         } else if (pos_neg [1].StartsWith ("-")) {
597                                                 ci.NumberFormatEntry.NumberNegativePattern = 1;
598                                         } else if (pos_neg [1].EndsWith (" -")) {
599                                                 ci.NumberFormatEntry.NumberNegativePattern = 4;
600                                         } else if (pos_neg [1].EndsWith ("-")) {
601                                                 ci.NumberFormatEntry.NumberNegativePattern = 3;
602                                         } else {
603                                                 ci.NumberFormatEntry.NumberNegativePattern = 1;
604                                         }
605                                 }
606                         }
607                 }
608
609                 private void LookupPercentFormat (XPathNavigator nav, CultureInfoEntry ci)
610                 {
611                         string format = (string) nav.Evaluate ("string(percentFormats/" +
612                                         "percentFormatLength/percentFormat/pattern)");
613
614                         if (format == String.Empty)
615                                 return;
616
617                         string [] part_one, part_two;
618
619                         // we don't have percentNegativePattern in CLDR so 
620                         // the percentNegativePattern are just guesses
621                         if (format.StartsWith ("%")) {
622                                 ci.NumberFormatEntry.PercentPositivePattern = 2;
623                                 ci.NumberFormatEntry.PercentNegativePattern = 2;
624                                 format = format.Substring (1);
625                         } else if (format.EndsWith (" %")) {
626                                 ci.NumberFormatEntry.PercentPositivePattern = 0;
627                                 ci.NumberFormatEntry.PercentNegativePattern = 0;
628                                 format = format.Substring (0, format.Length - 2);
629                         } else if (format.EndsWith ("%")) {
630                                 ci.NumberFormatEntry.PercentPositivePattern = 1;
631                                 ci.NumberFormatEntry.PercentNegativePattern = 1;
632                                 format = format.Substring (0, format.Length - 1);
633                         } else {
634                                 ci.NumberFormatEntry.PercentPositivePattern = 0;
635                                 ci.NumberFormatEntry.PercentNegativePattern = 0;
636                         }
637
638                         part_one = format.Split (new char [1] {'.'}, 2);
639                         if (part_one.Length == 2) {
640                                 // assumed same for both positive and negative
641                                 // decimal digit side
642                                 ci.NumberFormatEntry.PercentDecimalDigits = 0;
643                                 for (int i = 0; i < part_one [1].Length; i++) {
644                                         if (part_one [1][i] == '#')
645                                                 ci.NumberFormatEntry.PercentDecimalDigits++;
646                                         else
647                                                 break;
648                                 }
649                         }
650
651                         if (part_one.Length > 0) {
652                                 // percent grouping side
653                                 part_two = part_one [0].Split (',');
654                                 if (part_two.Length > 1) {
655                                         int len = part_two.Length - 1;
656                                         ci.NumberFormatEntry.PercentGroupSizes = new int [len];
657                                         for (int i = 0; i < len; i++) {
658                                                 string pat = part_two [i + 1];
659                                                 if (pat [pat.Length -1] == '0')
660                                                         ci.NumberFormatEntry.PercentDecimalDigits = pat.Length - 1;
661                                                 ci.NumberFormatEntry.PercentGroupSizes [i] = pat.Length;
662                                         }
663                                 } else {
664                                         ci.NumberFormatEntry.PercentGroupSizes = new int [1] { 3 };
665                                         ci.NumberFormatEntry.PercentDecimalDigits = 2;
666                                 }
667                         }
668                 }
669
670                 private void LookupCurrencyFormat (XPathNavigator nav, CultureInfoEntry ci)
671                 {
672                         string format = (string) nav.Evaluate ("string(currencyFormats/" +
673                                         "currencyFormatLength/currencyFormat/pattern)");
674
675                         if (format == String.Empty)
676                                 return;
677
678                         string [] part_one, part_two;
679                         string [] pos_neg = format.Split (new char [1] {';'}, 2);
680         
681                         // Most of the patterns are common in positive and negative
682                         if (pos_neg.Length == 1)
683                                 pos_neg = new string [] {pos_neg [0], pos_neg [0]};
684
685                         if (pos_neg.Length == 2) {
686                                 part_one = pos_neg [0].Split (new char [1] {'.'}, 2);
687                                 if (part_one.Length == 1)
688                                         part_one = new string [] {part_one [0], String.Empty};
689                                 if (part_one.Length == 2) {
690                                         // assumed same for both positive and negative
691                                         // decimal digit side
692                                         ci.NumberFormatEntry.CurrencyDecimalDigits = 0;
693                                         for (int i = 0; i < part_one [1].Length; i++) {
694                                                 if (part_one [1][i] == '0')
695                                                         ci.NumberFormatEntry.CurrencyDecimalDigits++;
696                                                 else
697                                                         break;
698                                         }
699
700                                         // decimal grouping side
701                                         part_two = part_one [0].Split (',');
702                                         if (part_two.Length > 1) {
703                                                 int len = part_two.Length - 1;
704                                                 ci.NumberFormatEntry.CurrencyGroupSizes = new int [len];
705                                                 for (int i = 0; i < len; i++) {
706                                                         string pat = part_two [i + 1];
707                                                         ci.NumberFormatEntry.CurrencyGroupSizes [i] = pat.Length;
708                                                 }
709                                         } else {
710                                                 ci.NumberFormatEntry.CurrencyGroupSizes = new int [1] { 3 };
711                                         }
712
713                                         if (pos_neg [1].StartsWith ("(\u00a4 ") && pos_neg [1].EndsWith (")")) {
714                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 14;
715                                         } else if (pos_neg [1].StartsWith ("(\u00a4") && pos_neg [1].EndsWith (")")) {
716                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 0;
717                                         } else if (pos_neg [1].StartsWith ("\u00a4 ") && pos_neg [1].EndsWith ("-")) {
718                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 11;
719                                         } else if (pos_neg [1].StartsWith ("\u00a4") && pos_neg [1].EndsWith ("-")) {
720                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 3;
721                                         } else if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith (" \u00a4")) {
722                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 15;
723                                         } else if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith ("\u00a4")) {
724                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 4;
725                                         } else if (pos_neg [1].StartsWith ("-") && pos_neg [1].EndsWith (" \u00a4")) {
726                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 8;
727                                         } else if (pos_neg [1].StartsWith ("-") && pos_neg [1].EndsWith ("\u00a4")) {
728                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 5;
729                                         } else if (pos_neg [1].StartsWith ("-\u00a4 ")) {
730                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 9;
731                                         } else if (pos_neg [1].StartsWith ("-\u00a4")) {
732                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 1;
733                                         } else if (pos_neg [1].StartsWith ("\u00a4 -")) {
734                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 12;
735                                         } else if (pos_neg [1].StartsWith ("\u00a4-")) {
736                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 2;
737                                         } else if (pos_neg [1].EndsWith (" \u00a4-")) {
738                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 10;
739                                         } else if (pos_neg [1].EndsWith ("\u00a4-")) {
740                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 7;
741                                         } else if (pos_neg [1].EndsWith ("- \u00a4")) {
742                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 13;
743                                         } else if (pos_neg [1].EndsWith ("-\u00a4")) {
744                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 6;
745                                         } else {
746                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 0;
747                                         }
748                                         
749                                         if (pos_neg [0].StartsWith ("\u00a4 ")) {
750                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 2;
751                                         } else if (pos_neg [0].StartsWith ("\u00a4")) {
752                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 0;
753                                         } else if (pos_neg [0].EndsWith (" \u00a4")) {
754                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 3;
755                                         } else if (pos_neg [0].EndsWith ("\u00a4")) {
756                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 1; 
757                                         } else {
758                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 0;
759                                         }
760                                 }
761                         }
762                 }
763
764                 private void LookupNumberSymbols (XPathNavigator nav, CultureInfoEntry ci)
765                 {
766                         string dec = (string) nav.Evaluate ("string(symbols/decimal)");
767                         string group = (string) nav.Evaluate ("string(symbols/group)");
768                         string percent = (string) nav.Evaluate ("string(symbols/percentSign)");
769                         string positive = (string) nav.Evaluate ("string(symbols/plusSign)");
770                         string negative = (string) nav.Evaluate ("string(symbols/minusSign)");
771                         string per_mille = (string) nav.Evaluate ("string(symbols/perMille)");
772                         string infinity = (string) nav.Evaluate ("string(symbols/infinity)");
773                         string nan = (string) nav.Evaluate ("string(symbols/nan)");
774
775                         if (dec != String.Empty) {
776                                 ci.NumberFormatEntry.NumberDecimalSeparator = dec;
777                                 ci.NumberFormatEntry.PercentDecimalSeparator = dec;
778                                 ci.NumberFormatEntry.CurrencyDecimalSeparator = dec;
779                         }
780
781                         if (group != String.Empty) {
782                                 ci.NumberFormatEntry.NumberGroupSeparator = group;
783                                 ci.NumberFormatEntry.PercentGroupSeparator = group;
784                                 ci.NumberFormatEntry.CurrencyGroupSeparator = group;
785                         }
786
787                         if (percent != String.Empty)
788                                 ci.NumberFormatEntry.PercentSymbol = percent;
789                         if (positive != String.Empty)
790                                 ci.NumberFormatEntry.PositiveSign = positive;
791                         if (negative != String.Empty)
792                                 ci.NumberFormatEntry.NegativeSign = negative;
793                         if (per_mille != String.Empty)
794                                 ci.NumberFormatEntry.PerMilleSymbol = per_mille;
795                         if (infinity != String.Empty)
796                                 ci.NumberFormatEntry.PositiveInfinitySymbol = infinity;
797                         if (nan != String.Empty)
798                                 ci.NumberFormatEntry.NaNSymbol = nan;
799                 }
800
801                 private void LookupCurrencySymbol (XPathNavigator nav, CultureInfoEntry ci)
802                 {
803                         string type = currency_types [ci.Territory] as string;
804
805                         if (type == null) {
806                                 Console.WriteLine ("no currency type for:  " + ci.Territory);
807                                 return;
808                         }
809                         
810                         string cur = (string) nav.Evaluate ("string(currencies/currency [@type='" +
811                                         type + "']/symbol)");
812
813                         if (cur != String.Empty)
814                                 ci.NumberFormatEntry.CurrencySymbol = cur;
815                 }
816
817                 private bool LookupLcids (CultureInfoEntry ci)
818                 {
819                         XPathDocument doc = GetXPathDocument ("lcids.xml");
820                         XPathNavigator nav = doc.CreateNavigator ();
821                         string name = ci.Language;
822
823                         if (ci.Territory != null)
824                                 name += "-" + ci.Territory;
825
826                         XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("lcids/lcid[@name='"
827                                         + name + "']");
828                         if (!ni.MoveNext ()) {
829                                 string file;
830                                 if (ci.Territory != null) {
831                                         file = Path.Combine ("locales", ci.Language + "_" + ci.Territory + ".xml");
832                                         File.Delete (file);
833                                         Console.WriteLine ("deleting file:  " + file);
834                                 }
835                                 Console.WriteLine ("no lcid found for:  " + name);
836                                 return false;
837                         }
838
839                         string id = ni.Current.GetAttribute ("id", String.Empty);
840                         string parent = ni.Current.GetAttribute ("parent", String.Empty);
841                         string specific = ni.Current.GetAttribute ("specific", String.Empty);
842                         string iso2 = ni.Current.GetAttribute ("iso2", String.Empty);
843                         string iso3 = ni.Current.GetAttribute ("iso3", String.Empty);
844                         string win = ni.Current.GetAttribute ("win", String.Empty);
845                         string icu = ni.Current.GetAttribute ("icu_name", String.Empty);
846
847                         // lcids are in 0x<hex> format
848                         ci.Lcid = id;
849                         ci.ParentLcid = parent;
850                         ci.SpecificLcid = specific;
851                         ci.ISO2Lang = iso2;
852                         ci.ISO3Lang = iso3;
853                         ci.Win3Lang = win;
854                         ci.IcuName = icu;
855                         
856                         ci.TextInfoEntry = new TextInfoEntry (int.Parse (id.Substring (2), NumberStyles.HexNumber), GetXPathDocument ("textinfo.xml"));
857
858                         return true;
859                 }
860                 
861                 private string LookupFullName (CultureInfoEntry ci, XPathNavigator nav)
862                 {
863                         string pre = "ldml/localeDisplayNames/";
864                         string ret;
865
866                         ret = (string) nav.Evaluate ("string("+
867                                         pre + "languages/language[@type='" + ci.Language + "'])");
868
869                         if (ci.Territory == null)
870                                 return ret;
871                         ret += " (" + (string) nav.Evaluate ("string("+
872                                         pre + "territories/territory[@type='" + ci.Territory + "'])") + ")";
873
874                         return ret;
875                 }
876
877                 private void LookupCurrencyTypes ()
878                 {
879                         XPathDocument doc = GetXPathDocument ("supplementalData.xml");
880                         XPathNavigator nav = doc.CreateNavigator ();
881
882                         currency_types = new Hashtable ();
883
884                         XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("supplementalData/currencyData/region");
885                         while (ni.MoveNext ()) {
886                                 string territory = (string) ni.Current.GetAttribute ("iso3166", String.Empty);
887                                 string currency = (string) ni.Current.Evaluate ("string(currency/@iso4217)");
888                                 currency_types [territory] = currency;
889                         }
890                 }
891
892                 static string control_chars = "ghmsftz";
893
894                 // HACK: We are trying to build year_month and month_day patterns from the full pattern.
895                 private void ParseFullDateFormat (DateTimeFormatEntry df, string full)
896                 {
897                         
898                         string month_day = String.Empty;
899                         string year_month = String.Empty;
900                         bool in_month_data = false;
901                         bool in_year_data = false;
902                         int month_end = 0;
903                         int year_end = 0;
904                         bool inquote = false;
905                         
906                         for (int i = 0; i < full.Length; i++) {
907                                 char c = full [i];
908                                 if (!inquote && c == 'M') {
909                                         month_day += c;
910                                         year_month += c;
911                                         in_year_data = true;
912                                         in_month_data = true;
913                                         month_end = month_day.Length;
914                                         year_end = year_month.Length;
915                                 } else if (!inquote && Char.ToLower (c) == 'd') {
916                                         month_day += c;
917                                         in_month_data = true;
918                                         in_year_data = false;
919                                         month_end = month_day.Length;
920                                 } else if (!inquote && Char.ToLower (c) == 'y') {
921                                         year_month += c;
922                                         in_year_data = true;
923                                         in_month_data = false;
924                                         year_end = year_month.Length;
925                                 } else if (!inquote && control_chars.IndexOf (Char.ToLower (c)) >= 0) {
926                                         in_year_data = false;
927                                         in_month_data = false;
928                                 } else if (in_year_data || in_month_data) {
929                                         if (in_month_data)
930                                                 month_day += c;
931                                         if (in_year_data)
932                                                 year_month += c;
933                                 }
934
935                                 if (c == '\'') {
936                                         inquote = !inquote;
937                                 }
938                         }
939
940                         if (month_day != String.Empty) {
941                                 month_day = month_day.Substring (0, month_end);
942                                 df.MonthDayPattern = month_day;
943                         }
944                         if (year_month != String.Empty) {
945                                 year_month = year_month.Substring (0, year_end);
946                                 df.YearMonthPattern = year_month;
947                         }
948                 }
949
950                 private class LcidComparer : IComparer {
951
952                         public int Compare (object a, object b)
953                         {
954                                 CultureInfoEntry aa = (CultureInfoEntry) a;
955                                 CultureInfoEntry bb = (CultureInfoEntry) b;
956                         
957                                 return aa.Lcid.CompareTo (bb.Lcid);
958                         }                
959                 }
960
961                 private class NameComparer : IComparer {
962
963                         public int Compare (object a, object b)
964                         {
965                                 CultureInfoEntry aa = (CultureInfoEntry) a;
966                                 CultureInfoEntry bb = (CultureInfoEntry) b;
967
968                                 return aa.Name.ToLower ().CompareTo (bb.Name.ToLower ());
969                         }
970                 }
971         }
972 }
973
974