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