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