BindingFlags.Public needed here as Exception.HResult is now public in .NET 4.5. This...
[mono.git] / tools / locale-builder / Driver.cs
1 //
2 // Mono.Tools.LocalBuilder.Driver
3 //
4 // Author(s):
5 //  Jackson Harper (jackson@ximian.com)
6 //  Atsushi Enomoto (atsushi@ximian.com)
7 //
8 // (C) 2004-2005 Novell, Inc (http://www.novell.com)
9 //
10
11
12 using System;
13 using System.IO;
14 using System.Text;
15 using System.Xml;
16 using System.Xml.XPath;
17 using System.Collections;
18 using System.Globalization;
19 using System.Text.RegularExpressions;
20
21 namespace Mono.Tools.LocaleBuilder {
22
23         public class Driver {
24
25                 public static void Main (string [] args)
26                 {
27                         Driver d = new Driver ();
28                         ParseArgs (args, d);
29                         d.Run ();
30                 }
31
32                 private static void ParseArgs (string [] args, Driver d)
33                 {
34                         for (int i = 0; i < args.Length; i++) {
35                                 if (args [i] == "--lang" && i+1 < args.Length)
36                                         d.Lang = args [++i];
37                                 else if (args [i] == "--locales" && i+1 < args.Length)
38                                         d.Locales = args [++i];
39                                 else if (args [i] == "--header" && i + 1 < args.Length)
40                                         d.HeaderFileName = args [++i];
41                         }
42                 }
43
44                 private string lang;
45                 private string locales;
46                 private string header_name;
47                 private ArrayList cultures;
48                 private Hashtable langs;
49                 private Hashtable currency_types;
50                 private Hashtable regions;
51
52                 private XPathDocument lcids_doc;
53
54                 // The lang is the language that display names will be displayed in
55                 public string Lang {
56                         get {
57                                 if (lang == null)
58                                         lang = "en";
59                                 return lang;
60                         }
61                         set { lang = value; }
62                 }
63
64                 public string Locales {
65                         get { return locales; }
66                         set { locales = value; }
67                 }
68
69                 public string HeaderFileName {
70                         get {
71                                 if (header_name == null)
72                                         return "culture-info-tables.h";
73                                 return header_name;
74                         }
75                         set { header_name = value; }
76                 }
77
78                 public void Run ()
79                 {
80                         lcids_doc = GetXPathDocument ("lcids.xml");
81
82                         Regex locales_regex = null;
83                         if (Locales != null)
84                                 locales_regex = new Regex (Locales);
85
86                         langs = new Hashtable ();
87                         cultures = new ArrayList ();
88                         regions = new Hashtable ();
89
90                         LookupRegions ();
91
92                         LookupCurrencyTypes ();
93
94                         foreach (string file in Directory.GetFiles ("locales", "*.xml")) {
95                                 string fn = Path.GetFileNameWithoutExtension (file);
96                                 if (fn == "hy_AM")
97                                         continue; // see bug #75499
98                                 if (locales_regex == null || locales_regex.IsMatch (fn)) {
99                                         ParseLocale (fn);
100                                 }
101                         }
102
103                         /* FIXME: This is hacky.
104                          * Since there is only langs/zh.xml while there are
105                          * two "zh" languages (CHS and CHT), there should be
106                          * different language profiles and we are not likely
107                          * to add lang/* files. So here I just clone zh-CHS
108                          * as zh-CHT
109                          */
110                          foreach (CultureInfoEntry e in cultures) {
111                                 if (e.Name == "zh-CHS") {
112                                         CultureInfoEntry t =
113                                                 CultureInfoEntry.ShallowCopy (e);
114                                         t.Language = "zh-CHT";
115                                         LookupLcids (t, true);
116                                         cultures.Add (t);
117                                         break;
118                                 }
119                          }
120
121                         ArrayList regionList = new ArrayList (regions.Values);
122                         regionList.Sort (RegionComparer.Instance);
123                         int number = 0;
124                         foreach (RegionInfoEntry r in regionList)
125                                 r.RegionId = number++;
126
127                         foreach (CultureInfoEntry e in cultures) {
128                                 int lcid = int.Parse (e.Lcid.Substring (2),
129                                         NumberStyles.HexNumber);
130                                 int idx;
131                                 int start = e.Name.IndexOf ('-') + 1;
132                                 if (start == 0)
133                                         continue;
134                                 for (idx = start; idx < e.Name.Length; idx++)
135                                         if (!Char.IsLetter (e.Name [idx]))
136                                                 break;
137                                 if (start == idx) {
138                                         Console.Error.WriteLine ("Culture {0} {1} is not mappable to Region.", e.Lcid, e.Name);
139                                         continue;
140                                 }
141                                 string name = e.Name.Substring (start, idx - start);
142                                 RegionInfoEntry rm = null;
143                                 foreach (RegionInfoEntry r in regions.Values)
144                                         if (r.ISO2Name == name) {
145                                                 rm = r;
146                                                 break;
147                                         }
148                                 if (rm == null) {
149                                         Console.Error.WriteLine ("No definition for region {0}", name);
150                                         continue;
151                                 }
152                                 e.RegionId = rm.RegionId;
153                         }
154
155                         /**
156                          * Dump each table individually. Using StringBuilders
157                          * because it is easier to debug, should switch to just
158                          * writing to streams eventually.
159                          */
160                         using (StreamWriter writer = new StreamWriter (HeaderFileName, false, new UTF8Encoding (false, true))) {
161                                 writer.NewLine = "\n";
162                                 writer.WriteLine ();
163                                 writer.WriteLine ("/* This is a generated file. Do not edit. See tools/locale-builder. */");
164                                 writer.WriteLine ("#ifndef MONO_METADATA_CULTURE_INFO_TABLES");
165                                 writer.WriteLine ("#define MONO_METADATA_CULTURE_INFO_TABLES 1");
166                                 writer.WriteLine ("\n");
167
168                                 writer.WriteLine ("#define NUM_CULTURE_ENTRIES " + cultures.Count);
169                                 writer.WriteLine ("#define NUM_REGION_ENTRIES " + regionList.Count);
170                                 writer.WriteLine ("\n");
171
172                                 // Sort the cultures by lcid
173                                 cultures.Sort (new LcidComparer ());
174
175                                 StringBuilder builder = new StringBuilder ();
176                                 int row = 0;
177                                 int count = cultures.Count;
178                                 for (int i = 0; i < count; i++) {
179                                         CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
180                                         if (ci.DateTimeFormatEntry == null)
181                                                 continue;
182                                         ci.DateTimeFormatEntry.AppendTableRow (builder);
183                                         ci.DateTimeFormatEntry.Row = row++;
184                                         if (i + 1 < count)
185                                                 builder.Append (',');
186                                         builder.Append ('\n');
187                                 }
188
189                                 writer.WriteLine ("static const DateTimeFormatEntry datetime_format_entries [] = {");
190                                 writer.Write (builder);
191                                 writer.WriteLine ("};\n\n");
192                                 
193                                 builder = new StringBuilder ();
194                                 row = 0;
195                                 for (int i=0; i < count; i++) {
196                                         CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
197                                         if (ci.NumberFormatEntry == null)
198                                                 continue;
199                                         ci.NumberFormatEntry.AppendTableRow (builder);
200                                         ci.NumberFormatEntry.Row = row++;
201                                         if (i + 1 < count)
202                                                 builder.Append (',');
203                                         builder.Append ('\n');
204                                 }
205
206                                 writer.WriteLine ("static const NumberFormatEntry number_format_entries [] = {");
207                                 writer.Write (builder);
208                                 writer.WriteLine ("};\n\n");
209                                 
210                                 builder = new StringBuilder ();
211                                 row = 0;
212                                 for (int i = 0; i < count; i++) {
213                                         CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
214                                         ci.AppendTableRow (builder);
215                                         ci.Row = row++;
216                                         if (i + 1 < count)
217                                                 builder.Append (',');
218                                         builder.Append ('\n');
219                                 }
220                                 
221                                 writer.WriteLine ("static const CultureInfoEntry culture_entries [] = {");
222                                 writer.Write (builder);
223                                 writer.WriteLine ("};\n\n");
224
225                                 cultures.Sort (new NameComparer ()); // Sort based on name
226                                 builder = new StringBuilder ();
227                                 for (int i = 0; i < count; i++) {
228                                         CultureInfoEntry ci = (CultureInfoEntry) cultures [i];
229                                         builder.Append ("\t{" + Entry.EncodeStringIdx (ci.Name.ToLower ()) + ", ");
230                                         builder.Append (ci.Row + "}");
231                                         if (i + 1 < count)
232                                                 builder.Append (',');
233                                         builder.Append ('\n');
234                                 }
235
236                                 writer.WriteLine ("static const CultureInfoNameEntry culture_name_entries [] = {");
237                                 writer.Write (builder);
238                                 writer.WriteLine ("};\n\n");
239
240                                 builder = new StringBuilder ();
241                                 int rcount = 0;
242                                 foreach (RegionInfoEntry r in regionList) {
243                                         r.AppendTableRow (builder);
244                                         if (++rcount != regionList.Count)
245                                                 builder.Append (',');
246                                         builder.Append ('\n');
247                                 }
248                                 writer.WriteLine ("static const RegionInfoEntry region_entries [] = {");
249                                 writer.Write (builder);
250                                 writer.WriteLine ("};\n\n");
251
252                                 builder = new StringBuilder ();
253                                 rcount = 0;
254                                 foreach (RegionInfoEntry ri in regionList) {
255                                         builder.Append ("\t{" + Entry.EncodeStringIdx (ri.ISO2Name) + ", ");
256                                         builder.Append (ri.RegionId + "}");
257                                         if (++rcount < regionList.Count)
258                                                 builder.Append (',');
259                                         builder.Append ('\n');
260                                 }
261
262                                 writer.WriteLine ("static const RegionInfoNameEntry region_name_entries [] = {");
263                                 writer.Write (builder);
264                                 writer.WriteLine ("};\n\n");
265
266                                 writer.WriteLine ("static const char locale_strings [] = {");
267                                 writer.Write (Entry.GetStrings ());
268                                 writer.WriteLine ("};\n\n");
269
270                                 writer.WriteLine ("#endif\n");
271                         }
272                 }
273
274                 private XPathDocument GetXPathDocument (string path)
275                 {
276                         XmlTextReader xtr = null;
277                         try {
278                                 xtr = new XmlTextReader (path);
279                                 xtr.XmlResolver = null;
280                                 return new XPathDocument (xtr);
281                         } finally {
282                                 if (xtr != null)
283                                         xtr.Close ();
284                         }
285                 }
286
287                 private string GetShortName (string lang)
288                 {
289                         return lang == "zh-CHS" ? "zh" : lang;
290                 }
291
292                 private bool ParseLang (string lang)
293                 {
294                         XPathDocument doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
295                         XPathNavigator nav = doc.CreateNavigator ();
296                         CultureInfoEntry ci = LookupCulture (GetShortName (lang), true);
297                         string lang_type, terr_type;
298
299 //                        ci.Name = lang; // TODO: might need to be mapped.
300
301                         lang_type = nav.Evaluate ("string (ldml/identity/language/@type)").ToString ();
302                         terr_type = nav.Evaluate ("string (ldml/identity/territory/@type)").ToString ();
303
304                         ci.Language = (lang_type == String.Empty ? null : lang_type);
305                         ci.Territory = (terr_type == String.Empty ? null : terr_type);
306
307                         if (!LookupLcids (ci, true))
308                                 return false;
309
310                         doc = GetXPathDocument (Path.Combine ("langs", GetShortName (Lang) + ".xml"));
311                         nav = doc.CreateNavigator ();
312                         ci.DisplayName = LookupFullName (ci, nav);
313                         
314                         if (Lang == "en") {
315                                 ci.EnglishName = ci.DisplayName;
316                         } else {
317                                 doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
318                                 nav = doc.CreateNavigator ();
319                                 ci.EnglishName = LookupFullName (ci, nav);
320                         }
321
322                         if (ci.Language == Lang) {
323                                 ci.NativeName = ci.DisplayName;
324                         } else {
325                                 doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
326                                 nav = doc.CreateNavigator ();
327                                 ci.NativeName = LookupFullName (ci, nav);
328                         }
329
330                         langs [lang] = ci;
331                         cultures.Add (ci);
332
333                         return true;
334                 }
335
336                 private void ParseLocale (string locale)
337                 {
338                         CultureInfoEntry ci;
339
340                         ci = LookupCulture (locale);
341
342                         if (ci == null)
343                                 return;
344
345                         if (langs [GetLanguageFixed (ci)] == null) {
346                                 if (!ParseLang (GetLanguageFixed (ci))) // If we can't parse the lang we cant have the locale
347                                         return;
348                         }
349
350                         cultures.Add (ci);
351                 }
352
353                 private CultureInfoEntry LookupCulture (string locale)
354                 {
355                         return LookupCulture (locale, false);
356                 }
357                 private CultureInfoEntry LookupCulture (string locale, bool is_language)
358                 {
359                         string path = Path.Combine (is_language ? "langs" : "locales", locale + ".xml");
360                         if (!File.Exists (path))
361                                 return null;
362                         XPathDocument doc = GetXPathDocument (path);
363                         XPathNavigator nav = doc.CreateNavigator ();
364                         CultureInfoEntry ci = new CultureInfoEntry ();
365                         string supp;
366                         string loc;
367
368 //                        ci.Name = locale; // TODO: Some of these need to be mapped.
369
370                         // First thing we do is get the lang-territory combo, lcid, and full names
371                         ci.Language = nav.Evaluate ("string (ldml/identity/language/@type)").ToString ();
372                         ci.Territory = nav.Evaluate ("string (ldml/identity/territory/@type)").ToString ();
373
374                         if (!LookupLcids (ci, is_language))
375                                 return null;
376                         LookupNames (ci);
377
378                         /**
379                          * Locale generation is done in six steps, first we
380                          * read the root file which is the base invariant data
381                          * then the supplemental root data, 
382                          * then the language file, the supplemental languages
383                          * file then the locale file, then the supplemental
384                          * locale file. Values in each descending file can
385                          * overwrite previous values.
386                          */
387                         doc = GetXPathDocument (Path.Combine ("langs", "root.xml"));
388                         nav = doc.CreateNavigator ();
389                         Lookup (nav, ci);
390
391                         doc = GetXPathDocument (Path.Combine ("supp", "root.xml"));
392                         nav = doc.CreateNavigator ();
393                         Lookup (nav, ci);
394
395                         doc = GetXPathDocument (Path.Combine ("langs", GetShortName (GetLanguageFixed (ci)) + ".xml"));
396                         nav = doc.CreateNavigator ();
397                         Lookup (nav, ci);
398
399                         supp = Path.Combine ("supp", GetLanguageFixed (ci) + ".xml");
400                         if (File.Exists (supp)) {
401                                 doc = GetXPathDocument (supp);
402                                 nav = doc.CreateNavigator ();
403                                 Lookup (nav, ci);
404                         }
405                         
406                         loc = Path.Combine ("locales", locale + ".xml");
407                         if (File.Exists (loc)) {
408                             doc = GetXPathDocument (loc);
409                             nav = doc.CreateNavigator ();
410                             Lookup (nav, ci);
411                         }
412
413                         supp = Path.Combine ("supp", locale + ".xml");
414                         if (File.Exists (supp)) {
415                                 doc = GetXPathDocument (supp);
416                                 nav = doc.CreateNavigator ();
417                                 Lookup (nav, ci);
418                         }
419
420                         return ci;
421                 }
422
423                 private void Lookup (XPathNavigator nav, CultureInfoEntry ci)
424                 {
425                         LookupDateTimeInfo (nav, ci);
426                         LookupNumberInfo (nav, ci);
427                 }
428
429                 private string GetLanguageFixed (CultureInfoEntry ci)
430                 {
431                         // This is a hack, but without it nb-NO and nn-NO won't work.
432                         if (ci.Territory == "NO") {
433                                 switch (ci.Language) {
434                                 case "nb":
435                                 case "nn":
436                                         return "no";
437                                 }
438                         }
439                         return ci.Language;
440                 }
441
442                 private void LookupNames (CultureInfoEntry ci)
443                 {
444                         XPathDocument doc = GetXPathDocument (Path.Combine ("langs", GetShortName (Lang) + ".xml"));
445                         XPathNavigator nav = doc.CreateNavigator ();
446
447                         ci.DisplayName = LookupFullName (ci, nav);
448                         
449                         if (Lang == "en") {
450                                 ci.EnglishName = ci.DisplayName;
451                         } else {
452                                 doc = GetXPathDocument (Path.Combine ("langs", "en.xml"));
453                                 nav = doc.CreateNavigator ();
454                                 ci.EnglishName = LookupFullName (ci, nav);
455                         }
456
457                         if (ci.Language == Lang) {
458                                 ci.NativeName = ci.DisplayName;
459                         } else {
460                                 // FIXME: We use ci.Language here.
461                                 // This is nothing more than hack for nb-NO and nn-NO
462                                 // where Parent of them is nn (not nb or nn).
463                                 string lang = ci.Language;
464                                 doc = GetXPathDocument (Path.Combine ("langs", GetShortName (lang) + ".xml"));
465                                 nav = doc.CreateNavigator ();
466                                 ci.NativeName = LookupFullName (ci, nav);
467                         }
468                 }
469
470                 private void AddPattern (ArrayList al, string pattern)
471                 {
472                         if (!al.Contains (pattern))
473                                 al.Add (pattern);
474                 }
475
476                 private void LookupDateTimeInfo (XPathNavigator nav, CultureInfoEntry ci)
477                 {
478                         /**
479                          * TODO: Does anyone have multiple calendars?
480                          */
481                         XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("ldml/dates/calendars/calendar");
482
483                         while (ni.MoveNext ()) {
484                                 DateTimeFormatEntry df = ci.DateTimeFormatEntry;
485                                 string cal_type = ni.Current.GetAttribute ("type", String.Empty);
486
487                                 if (cal_type != String.Empty)
488                                         df.CalendarType = cal_type;
489
490                                 XPathNodeIterator ni2 = (XPathNodeIterator) ni.Current.Evaluate ("optionalCalendars/calendar");
491                                 int opt_cal_count = 0;
492                                 while (ni2.MoveNext ()) {
493                                         int type;
494                                         string greg_type_str;
495                                         XPathNavigator df_nav = ni2.Current;
496                                         switch (df_nav.GetAttribute ("type", String.Empty)) {
497                                         case "Gregorian":
498                                                 type = 0;
499                                                 break;
500                                         case "Hijri":
501                                                 type = 0x01;
502                                                 break;
503                                         case "ThaiBuddhist":
504                                                 type = 0x02;
505                                                 break;
506                                         default:
507                                                 Console.WriteLine ("unknown calendar type:  " +
508                                                                 df_nav.GetAttribute ("type", String.Empty));
509                                                 continue;
510                                         }
511                                         type <<= 24;
512                                         greg_type_str = df_nav.GetAttribute ("greg_type", String.Empty);
513                                         if (greg_type_str != null && greg_type_str != String.Empty) {
514                                                 GregorianCalendarTypes greg_type = (GregorianCalendarTypes)
515                                                         Enum.Parse (typeof (GregorianCalendarTypes), greg_type_str);
516                                                 int greg_type_int = (int) greg_type;
517                                                 type |= greg_type_int;
518                                                 
519                                         }
520                                         Console.WriteLine ("Setting cal type: {0:X}  for   {1}", type, ci.Name);
521                                         ci.CalendarData [opt_cal_count++] = type;
522                                 }
523                                 
524                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("monthNames/month");
525                                 while (ni2.MoveNext ()) {
526                                         if (ni2.CurrentPosition == 1)
527                                                 df.MonthNames.Clear ();
528                                         df.MonthNames.Add (ni2.Current.Value);
529                                 }
530                                 if (df.MonthNames.Count == 12)
531                                         df.MonthNames.Add (String.Empty);
532
533                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dayNames/day");
534                                 while (ni2.MoveNext ()) {
535                                         if (ni2.CurrentPosition == 1)
536                                                 df.DayNames.Clear ();
537                                         df.DayNames.Add (ni2.Current.Value);
538                                 }
539
540                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dayAbbr/day");
541                                 while (ni2.MoveNext ()) {
542                                         if (ni2.CurrentPosition == 1)
543                                                 df.AbbreviatedDayNames.Clear ();
544                                         df.AbbreviatedDayNames.Add (ni2.Current.Value);
545                                 }
546
547                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("monthAbbr/month");
548                                 while (ni2.MoveNext ()) {
549                                         if (ni2.CurrentPosition == 1)
550                                                 df.AbbreviatedMonthNames.Clear ();
551                                         df.AbbreviatedMonthNames.Add (ni2.Current.Value);
552                                 }
553                                 if (df.AbbreviatedMonthNames.Count == 12)
554                                         df.AbbreviatedMonthNames.Add (String.Empty);
555
556                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dateFormats/dateFormatLength");
557                                 while (ni2.MoveNext ()) {
558                                         XPathNavigator df_nav = ni2.Current;
559                                         XPathNodeIterator p = df_nav.Select ("dateFormat/pattern");
560                                         string value = null;
561                                         if (p.MoveNext ())
562                                                 value = p.Current.Value;
563                                         XPathNodeIterator ext = null;
564                                         switch (df_nav.GetAttribute ("type", String.Empty)) {
565                                         case "full":
566                                                 if (value != null)
567                                                         ParseFullDateFormat (df, value);
568                                                 break;
569                                         case "long":
570                                                 if (value != null)
571                                                         df.LongDatePattern = value;
572                                                 ext = df_nav.Select ("extraPatterns/pattern");
573                                                 if (ext.MoveNext ()) {
574                                                         df.LongDatePatterns.Clear ();
575                                                         AddPattern (df.LongDatePatterns, df.LongDatePattern);
576                                                         do {
577                                                                 df.LongDatePatterns.Add (ext.Current.Value);
578                                                         } while (ext.MoveNext ());
579                                                 }
580                                                 else
581                                                         AddPattern (df.LongDatePatterns, df.LongDatePattern);
582                                                 break;
583                                         case "short":
584                                                 if (value != null)
585                                                         df.ShortDatePattern = value;
586                                                 ext = df_nav.Select ("extraPatterns/pattern");
587                                                 if (ext.MoveNext ()) {
588                                                         df.ShortDatePatterns.Clear ();
589                                                         AddPattern (df.ShortDatePatterns, df.ShortDatePattern);
590                                                         do {
591                                                                 df.ShortDatePatterns.Add (ext.Current.Value);
592                                                         } while (ext.MoveNext ());
593                                                 }
594                                                 else
595                                                         AddPattern (df.ShortDatePatterns, df.ShortDatePattern);
596                                                 break;
597                                         case "year_month":
598                                                 if (value != null)
599                                                         df.YearMonthPattern = value;
600                                                 break;
601                                         case "month_day":
602                                                 if (value != null)
603                                                         df.MonthDayPattern = value;
604                                                 break;
605                                         }
606                                 }
607
608                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("timeFormats/timeFormatLength");
609                                 while (ni2.MoveNext ()) {
610                                         XPathNavigator df_nav = ni2.Current;
611                                         XPathNodeIterator p = df_nav.Select ("timeFormat/pattern");
612                                         string value = null;
613                                         if (p.MoveNext ())
614                                                 value = p.Current.Value;
615                                         XPathNodeIterator ext = null;
616                                         switch (df_nav.GetAttribute ("type", String.Empty)) {
617                                         case "long":
618                                                 if (value != null)
619                                                         df.LongTimePattern = value.Replace ('a', 't');
620                                                 ext = df_nav.Select ("extraPatterns/pattern");
621                                                 if (ext.MoveNext ()) {
622                                                         df.LongTimePatterns.Clear ();
623                                                         AddPattern (df.LongTimePatterns, df.LongTimePattern);
624                                                         do {
625                                                                 df.LongTimePatterns.Add (ext.Current.Value);
626                                                         } while (ext.MoveNext ());
627                                                 }
628                                                 else
629                                                         AddPattern (df.LongTimePatterns, df.LongTimePattern);
630                                                 break;
631                                         case "short":
632                                                 if (value != null)
633                                                         df.ShortTimePattern = value.Replace ('a', 't');
634                                                 ext = df_nav.Select ("extraPatterns/pattern");
635                                                 if (ext.MoveNext ()) {
636                                                         df.ShortTimePatterns.Clear ();
637                                                         AddPattern (df.ShortTimePatterns, df.ShortTimePattern);
638                                                         do {
639                                                                 df.ShortTimePatterns.Add (ext.Current.Value);
640                                                         } while (ext.MoveNext ());
641                                                 }
642                                                 else
643                                                         AddPattern (df.ShortTimePatterns, df.ShortTimePattern);
644                                                 break;
645                                         }
646                                 }
647
648                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate ("dateTimeFormats/dateTimeFormatLength/dateTimeFormat/pattern");
649                                 if (ni2.MoveNext ())
650                                         df.RawFullDateTimePattern = ni2.Current.ToString ();/*String.Format (ni2.Current.ToString (),
651                                                         df.LongTimePattern, df.LongDatePattern);*/
652
653                                 XPathNodeIterator am = ni.Current.SelectChildren ("am", "");
654                                 if (am.MoveNext ())
655                                         df.AMDesignator = am.Current.Value;
656                                 XPathNodeIterator pm = ni.Current.SelectChildren ("pm", "");
657                                 if (pm.MoveNext ())
658                                         df.PMDesignator = pm.Current.Value;
659 /*
660                                 string am = (string) ni.Current.Evaluate ("string(am)");
661                                 string pm = (string) ni.Current.Evaluate ("string(pm)");
662
663                                 if (am != String.Empty)
664                                         df.AMDesignator = am;
665                                 if (pm != String.Empty)
666                                         df.PMDesignator = pm;
667 */
668                                 ni2 = (XPathNodeIterator) ni.Current.Evaluate
669 ("week/firstDay");
670                                 if (ni2.MoveNext ()) {
671                                         XPathNavigator weekday_nav = ni2.Current;
672                                         switch (weekday_nav.GetAttribute ("day", String.Empty)) {
673                                         case "sun":
674                                                 df.FirstDayOfWeek = 0;
675                                                 break;
676                                         case "mon":
677                                                 df.FirstDayOfWeek = 1;
678                                                 break;
679                                         case "tue":
680                                                 df.FirstDayOfWeek = 2;
681                                                 break;
682                                         case "wed":
683                                                 df.FirstDayOfWeek = 3;
684                                                 break;
685                                         case "thu":
686                                                 df.FirstDayOfWeek = 4;
687                                                 break;
688                                         case "fri":
689                                                 df.FirstDayOfWeek = 5;
690                                                 break;
691                                         case "sat":
692                                                 df.FirstDayOfWeek = 6;
693                                                 break;
694                                         }
695                                 }
696                         }
697
698                         string date_sep = (string) nav.Evaluate ("string(ldml/dates/symbols/dateSeparator)");
699                         string time_sep = (string) nav.Evaluate ("string(ldml/dates/symbols/timeSeparator)");
700
701                         if (date_sep != String.Empty)
702                                 ci.DateTimeFormatEntry.DateSeparator = date_sep;
703                         if (time_sep != String.Empty)
704                                 ci.DateTimeFormatEntry.TimeSeparator = time_sep;
705                 }
706
707                 private void LookupNumberInfo (XPathNavigator nav, CultureInfoEntry ci)
708                 {
709                         XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("ldml/numbers");
710
711                         while (ni.MoveNext ()) {
712                                 LookupNumberSymbols (ni.Current, ci);
713                                 LookupDecimalFormat (ni.Current, ci);
714                                 LookupPercentFormat (ni.Current, ci);
715                                 LookupCurrencyFormat (ni.Current, ci);
716                                 LookupCurrencySymbol (ni.Current, ci);
717                         }
718                 }
719
720                 private void LookupDecimalFormat (XPathNavigator nav, CultureInfoEntry ci)
721                 {
722                         string format = (string) nav.Evaluate ("string(decimalFormats/" +
723                                         "decimalFormatLength/decimalFormat/pattern)");
724
725                         if (format == String.Empty)
726                                 return;
727
728                         string [] part_one, part_two;
729                         string [] pos_neg = format.Split (new char [1] {';'}, 2);
730
731                         // Most of the patterns are common in positive and negative
732                         if (pos_neg.Length == 1)
733                                 pos_neg = new string [] {pos_neg [0], pos_neg [0]};
734
735                         if (pos_neg.Length == 2) {
736                                 
737                                 part_one = pos_neg [0].Split (new char [1] {'.'}, 2);
738                                 if (part_one.Length == 1)
739                                         part_one = new string [] {part_one [0], String.Empty};
740
741                                 if (part_one.Length == 2) {
742                                         // assumed same for both positive and negative
743                                         // decimal digit side
744                                         ci.NumberFormatEntry.NumberDecimalDigits = 0;                                   
745                                         for (int i = 0; i < part_one [1].Length; i++) {
746                                                 if (part_one [1][i] == '#') {
747                                                         ci.NumberFormatEntry.NumberDecimalDigits ++;
748                                                 } else
749                                                         break;                                                          }
750                                         // FIXME: This should be actually done by modifying culture xml files, but too many files to be modified.
751                                         if (ci.NumberFormatEntry.NumberDecimalDigits > 0)
752                                                 ci.NumberFormatEntry.NumberDecimalDigits --;
753
754                                         // decimal grouping side
755                                         part_two = part_one [0].Split (',');
756                                         if (part_two.Length > 1) {
757                                                 int len = part_two.Length - 1;
758                                                 ci.NumberFormatEntry.NumberGroupSizes = new int [len];
759                                                 for (int i = 0; i < len; i++) {
760                                                         string pat = part_two [i + 1];
761                                                         ci.NumberFormatEntry.NumberGroupSizes [i] = pat.Length;
762                                                 }
763                                         } else {
764                                                 ci.NumberFormatEntry.NumberGroupSizes = new int [1] { 3 };
765                                         }
766
767                                         if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith (")")) {
768                                                 ci.NumberFormatEntry.NumberNegativePattern = 0;
769                                         } else if (pos_neg [1].StartsWith ("- ")) {
770                                                 ci.NumberFormatEntry.NumberNegativePattern = 2;
771                                         } else if (pos_neg [1].StartsWith ("-")) {
772                                                 ci.NumberFormatEntry.NumberNegativePattern = 1;
773                                         } else if (pos_neg [1].EndsWith (" -")) {
774                                                 ci.NumberFormatEntry.NumberNegativePattern = 4;
775                                         } else if (pos_neg [1].EndsWith ("-")) {
776                                                 ci.NumberFormatEntry.NumberNegativePattern = 3;
777                                         } else {
778                                                 ci.NumberFormatEntry.NumberNegativePattern = 1;
779                                         }
780                                 }
781                         }
782                 }
783
784                 private void LookupPercentFormat (XPathNavigator nav, CultureInfoEntry ci)
785                 {
786                         string format = (string) nav.Evaluate ("string(percentFormats/" +
787                                         "percentFormatLength/percentFormat/pattern)");
788
789                         if (format == String.Empty)
790                                 return;
791
792                         string [] part_one, part_two;
793
794                         // we don't have percentNegativePattern in CLDR so 
795                         // the percentNegativePattern are just guesses
796                         if (format.StartsWith ("%")) {
797                                 ci.NumberFormatEntry.PercentPositivePattern = 2;
798                                 ci.NumberFormatEntry.PercentNegativePattern = 2;
799                                 format = format.Substring (1);
800                         } else if (format.EndsWith (" %")) {
801                                 ci.NumberFormatEntry.PercentPositivePattern = 0;
802                                 ci.NumberFormatEntry.PercentNegativePattern = 0;
803                                 format = format.Substring (0, format.Length - 2);
804                         } else if (format.EndsWith ("%")) {
805                                 ci.NumberFormatEntry.PercentPositivePattern = 1;
806                                 ci.NumberFormatEntry.PercentNegativePattern = 1;
807                                 format = format.Substring (0, format.Length - 1);
808                         } else {
809                                 ci.NumberFormatEntry.PercentPositivePattern = 0;
810                                 ci.NumberFormatEntry.PercentNegativePattern = 0;
811                         }
812
813                         part_one = format.Split (new char [1] {'.'}, 2);
814                         if (part_one.Length == 2) {
815                                 // assumed same for both positive and negative
816                                 // decimal digit side
817                                 ci.NumberFormatEntry.PercentDecimalDigits = 0;
818                                 for (int i = 0; i < part_one [1].Length; i++) {
819                                         if (part_one [1][i] == '#')
820                                                 ci.NumberFormatEntry.PercentDecimalDigits++;
821                                         else
822                                                 break;
823                                 }
824                         }
825
826                         if (part_one.Length > 0) {
827                                 // percent grouping side
828                                 part_two = part_one [0].Split (',');
829                                 if (part_two.Length > 1) {
830                                         int len = part_two.Length - 1;
831                                         ci.NumberFormatEntry.PercentGroupSizes = new int [len];
832                                         for (int i = 0; i < len; i++) {
833                                                 string pat = part_two [i + 1];
834                                                 if (pat [pat.Length -1] == '0')
835                                                         ci.NumberFormatEntry.PercentDecimalDigits = pat.Length - 1;
836                                                 ci.NumberFormatEntry.PercentGroupSizes [i] = pat.Length;
837                                         }
838                                 } else {
839                                         ci.NumberFormatEntry.PercentGroupSizes = new int [1] { 3 };
840                                         ci.NumberFormatEntry.PercentDecimalDigits = 2;
841                                 }
842                         }
843                 }
844
845                 private void LookupCurrencyFormat (XPathNavigator nav, CultureInfoEntry ci)
846                 {
847                         string format = (string) nav.Evaluate ("string(currencyFormats/" +
848                                         "currencyFormatLength/currencyFormat/pattern)");
849
850                         if (format == String.Empty)
851                                 return;
852
853                         string [] part_one, part_two;
854                         string [] pos_neg = format.Split (new char [1] {';'}, 2);
855         
856                         // Most of the patterns are common in positive and negative
857                         if (pos_neg.Length == 1)
858                                 pos_neg = new string [] {pos_neg [0], pos_neg [0]};
859
860                         if (pos_neg.Length == 2) {
861                                 part_one = pos_neg [0].Split (new char [1] {'.'}, 2);
862                                 if (part_one.Length == 1)
863                                         part_one = new string [] {part_one [0], String.Empty};
864                                 if (part_one.Length == 2) {
865                                         // assumed same for both positive and negative
866                                         // decimal digit side
867                                         ci.NumberFormatEntry.CurrencyDecimalDigits = 0;
868                                         for (int i = 0; i < part_one [1].Length; i++) {
869                                                 if (part_one [1][i] == '0')
870                                                         ci.NumberFormatEntry.CurrencyDecimalDigits++;
871                                                 else
872                                                         break;
873                                         }
874
875                                         // decimal grouping side
876                                         part_two = part_one [0].Split (',');
877                                         if (part_two.Length > 1) {
878                                                 int len = part_two.Length - 1;
879                                                 ci.NumberFormatEntry.CurrencyGroupSizes = new int [len];
880                                                 for (int i = 0; i < len; i++) {
881                                                         string pat = part_two [i + 1];
882                                                         ci.NumberFormatEntry.CurrencyGroupSizes [i] = pat.Length;
883                                                 }
884                                         } else {
885                                                 ci.NumberFormatEntry.CurrencyGroupSizes = new int [1] { 3 };
886                                         }
887
888                                         if (pos_neg [1].StartsWith ("(\u00a4 ") && pos_neg [1].EndsWith (")")) {
889                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 14;
890                                         } else if (pos_neg [1].StartsWith ("(\u00a4") && pos_neg [1].EndsWith (")")) {
891                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 0;
892                                         } else if (pos_neg [1].StartsWith ("\u00a4 ") && pos_neg [1].EndsWith ("-")) {
893                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 11;
894                                         } else if (pos_neg [1].StartsWith ("\u00a4") && pos_neg [1].EndsWith ("-")) {
895                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 3;
896                                         } else if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith (" \u00a4")) {
897                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 15;
898                                         } else if (pos_neg [1].StartsWith ("(") && pos_neg [1].EndsWith ("\u00a4")) {
899                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 4;
900                                         } else if (pos_neg [1].StartsWith ("-") && pos_neg [1].EndsWith (" \u00a4")) {
901                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 8;
902                                         } else if (pos_neg [1].StartsWith ("-") && pos_neg [1].EndsWith ("\u00a4")) {
903                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 5;
904                                         } else if (pos_neg [1].StartsWith ("-\u00a4 ")) {
905                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 9;
906                                         } else if (pos_neg [1].StartsWith ("-\u00a4")) {
907                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 1;
908                                         } else if (pos_neg [1].StartsWith ("\u00a4 -")) {
909                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 12;
910                                         } else if (pos_neg [1].StartsWith ("\u00a4-")) {
911                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 2;
912                                         } else if (pos_neg [1].EndsWith (" \u00a4-")) {
913                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 10;
914                                         } else if (pos_neg [1].EndsWith ("\u00a4-")) {
915                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 7;
916                                         } else if (pos_neg [1].EndsWith ("- \u00a4")) {
917                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 13;
918                                         } else if (pos_neg [1].EndsWith ("-\u00a4")) {
919                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 6;
920                                         } else {
921                                                 ci.NumberFormatEntry.CurrencyNegativePattern = 0;
922                                         }
923                                         
924                                         if (pos_neg [0].StartsWith ("\u00a4 ")) {
925                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 2;
926                                         } else if (pos_neg [0].StartsWith ("\u00a4")) {
927                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 0;
928                                         } else if (pos_neg [0].EndsWith (" \u00a4")) {
929                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 3;
930                                         } else if (pos_neg [0].EndsWith ("\u00a4")) {
931                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 1; 
932                                         } else {
933                                                 ci.NumberFormatEntry.CurrencyPositivePattern = 0;
934                                         }
935                                 }
936                         }
937                 }
938
939                 private void LookupNumberSymbols (XPathNavigator nav, CultureInfoEntry ci)
940                 {
941                         string dec = (string) nav.Evaluate ("string(symbols/decimal)");
942                         string group = (string) nav.Evaluate ("string(symbols/group)");
943                         string percent = (string) nav.Evaluate ("string(symbols/percentSign)");
944                         string positive = (string) nav.Evaluate ("string(symbols/plusSign)");
945                         string negative = (string) nav.Evaluate ("string(symbols/minusSign)");
946                         string per_mille = (string) nav.Evaluate ("string(symbols/perMille)");
947                         string infinity = (string) nav.Evaluate ("string(symbols/infinity)");
948                         string nan = (string) nav.Evaluate ("string(symbols/nan)");
949
950                         if (dec != String.Empty) {
951                                 ci.NumberFormatEntry.NumberDecimalSeparator = dec;
952                                 ci.NumberFormatEntry.PercentDecimalSeparator = dec;
953                                 ci.NumberFormatEntry.CurrencyDecimalSeparator = dec;
954                         }
955
956                         if (group != String.Empty) {
957                                 ci.NumberFormatEntry.NumberGroupSeparator = group;
958                                 ci.NumberFormatEntry.PercentGroupSeparator = group;
959                                 ci.NumberFormatEntry.CurrencyGroupSeparator = group;
960                         }
961
962                         if (percent != String.Empty)
963                                 ci.NumberFormatEntry.PercentSymbol = percent;
964                         if (positive != String.Empty)
965                                 ci.NumberFormatEntry.PositiveSign = positive;
966                         if (negative != String.Empty)
967                                 ci.NumberFormatEntry.NegativeSign = negative;
968                         if (per_mille != String.Empty)
969                                 ci.NumberFormatEntry.PerMilleSymbol = per_mille;
970                         if (infinity != String.Empty)
971                                 ci.NumberFormatEntry.PositiveInfinitySymbol = infinity;
972                         if (nan != String.Empty)
973                                 ci.NumberFormatEntry.NaNSymbol = nan;
974                 }
975
976                 private void LookupCurrencySymbol (XPathNavigator nav, CultureInfoEntry ci)
977                 {
978                         string type = currency_types [ci.Territory] as string;
979
980                         if (type == null) {
981                                 Console.WriteLine ("no currency type for:  " + ci.Territory);
982                                 return;
983                         }
984                         
985                         string cur = (string) nav.Evaluate ("string(currencies/currency [@type='" +
986                                         type + "']/symbol)");
987
988                         if (cur != String.Empty)
989                                 ci.NumberFormatEntry.CurrencySymbol = cur;
990                 }
991
992                 private bool LookupLcids (CultureInfoEntry ci, bool lang)
993                 {
994                         XPathNavigator nav = lcids_doc.CreateNavigator ();
995                         string name = ci.Name;
996                         // Language name does not always consist of locale name.
997                         // (for zh-* it must be either zh-CHS or zh-CHT)
998                         string langName = GetLanguageFixed (ci);
999
1000 //                        if (ci.Territory != null)
1001 //                                name += "-" + ci.Territory;
1002
1003                         XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("lcids/lcid[@name='"
1004                                         + (lang ? langName : name) + "']");
1005                         if (!ni.MoveNext ()) {
1006                                 Console.WriteLine ("no lcid found for: {0} ({1}/{2})", name, ci.Language, ci.Territory);
1007                                 string file;
1008
1009                                 if (ci.Territory != null) {
1010                                         file = Path.Combine ("locales", ci.Language + "_" + ci.Territory + ".xml");
1011                                         Console.WriteLine ("deleting file:  " + file);
1012                                         File.Delete (file);
1013                                 }
1014
1015                                 return false;
1016                         }
1017
1018                         string id = ni.Current.GetAttribute ("id", String.Empty);
1019                         string parent = ni.Current.GetAttribute ("parent", String.Empty);
1020                         string specific = ni.Current.GetAttribute ("specific", String.Empty);
1021                         string iso2 = ni.Current.GetAttribute ("iso2", String.Empty);
1022                         string iso3 = ni.Current.GetAttribute ("iso3", String.Empty);
1023                         string win = ni.Current.GetAttribute ("win", String.Empty);
1024                         string icu = ni.Current.GetAttribute ("icu_name", String.Empty);
1025
1026                         // lcids are in 0x<hex> format
1027                         ci.Lcid = id;
1028                         ci.ParentLcid = parent;
1029                         ci.SpecificLcid = specific;
1030                         ci.ISO2Lang = iso2;
1031                         ci.ISO3Lang = iso3;
1032                         ci.Win3Lang = win;
1033                         ci.IcuName = icu;
1034                         
1035                         ci.TextInfoEntry = new TextInfoEntry (int.Parse (id.Substring (2), NumberStyles.HexNumber), GetXPathDocument ("textinfo.xml"));
1036
1037                         return true;
1038                 }
1039                 
1040                 private string LookupFullName (CultureInfoEntry ci, XPathNavigator nav)
1041                 {
1042                         string pre = "ldml/localeDisplayNames/";
1043                         string ret;
1044
1045                         // FIXME: We use ci.Language here.
1046                         // This is nothing more than hack for nb-NO or nn-NO
1047                         // where Parent of them is nn (not nb or nn).
1048                         ret = (string) nav.Evaluate ("string("+
1049                                         pre + "languages/language[@type='" + GetShortName (ci.Language) + "'])");
1050
1051                         if (ci.Territory == null)
1052                                 return ret;
1053                         ret += " (" + (string) nav.Evaluate ("string("+
1054                                         pre + "territories/territory[@type='" + ci.Territory + "'])") + ")";
1055
1056                         return ret;
1057                 }
1058
1059                 private void LookupRegions ()
1060                 {
1061                         XPathDocument doc = GetXPathDocument ("supplementalData.xml");
1062                         XPathNavigator nav = doc.CreateNavigator ();
1063                         XPathNodeIterator ni = nav.Select ("supplementalData/currencyData/region");
1064                         while (ni.MoveNext ()) {
1065                                 string territory = (string) ni.Current.GetAttribute ("iso3166", String.Empty);
1066                                 string currency = (string) ni.Current.Evaluate ("string(currency/@iso4217)");
1067                                 RegionInfoEntry region = new RegionInfoEntry ();
1068                                 region.ISO2Name = territory.ToUpper ();
1069                                 region.ISOCurrencySymbol = currency;
1070                                 regions [territory] = region;
1071                         }
1072
1073                         doc = GetXPathDocument ("langs/en.xml");
1074                         nav = doc.CreateNavigator ();
1075                         ni = nav.Select ("/ldml/localeDisplayNames/territories/territory");
1076                         while (ni.MoveNext ()) {
1077                                 RegionInfoEntry r = (RegionInfoEntry)
1078                                         regions [ni.Current.GetAttribute ("type", "")];
1079                                 if (r == null)
1080                                         continue;
1081                                 r.EnglishName = ni.Current.Value;
1082                         }
1083
1084                         Hashtable curNames = new Hashtable ();
1085                         ni = nav.Select ("/ldml/numbers/currencies/currency");
1086                         while (ni.MoveNext ())
1087                                 curNames [ni.Current.GetAttribute ("type", "")] =
1088                                         ni.Current.Evaluate ("string (displayName)");
1089
1090                         foreach (RegionInfoEntry r in regions.Values)
1091                                 r.CurrencyEnglishName =
1092                                         (string) curNames [r.ISOCurrencySymbol];
1093                 }
1094
1095                 private void LookupCurrencyTypes ()
1096                 {
1097                         XPathDocument doc = GetXPathDocument ("supplementalData.xml");
1098                         XPathNavigator nav = doc.CreateNavigator ();
1099
1100                         currency_types = new Hashtable ();
1101
1102                         XPathNodeIterator ni =(XPathNodeIterator) nav.Evaluate ("supplementalData/currencyData/region");
1103                         while (ni.MoveNext ()) {
1104                                 string territory = (string) ni.Current.GetAttribute ("iso3166", String.Empty);
1105                                 string currency = (string) ni.Current.Evaluate ("string(currency/@iso4217)");
1106                                 currency_types [territory] = currency;
1107                         }
1108                 }
1109
1110                 static string control_chars = "eghmsftz";
1111
1112                 // HACK: We are trying to build year_month and month_day patterns from the full pattern.
1113                 private void ParseFullDateFormat (DateTimeFormatEntry df, string full)
1114                 {
1115                         
1116                         string month_day = String.Empty;
1117                         string year_month = String.Empty;
1118                         bool in_month_data = false;
1119                         bool in_year_data = false;
1120                         int day_start = 0, day_end = 0;
1121                         int month_start = 0, month_end = 0;
1122                         int year_start = 0, year_end = 0;
1123                         bool inquote = false;
1124                         
1125                         for (int i = 0; i < full.Length; i++) {
1126                                 char c = full [i];
1127                                 if (!inquote && c == 'M') {
1128                                         month_day += c;
1129                                         year_month += c;
1130                                         in_year_data = true;
1131                                         in_month_data = true;
1132                                         if (month_start == 0)
1133                                                 month_start = i;
1134                                         month_end = i;
1135                                         year_end = year_month.Length;
1136                                 } else if (!inquote && Char.ToLower (c) == 'd') {
1137                                         month_day += c;
1138                                         in_month_data = true;
1139                                         in_year_data = false;
1140                                         if (day_start == 0)
1141                                                 day_start = i;
1142                                         day_end = i;
1143                                 } else if (!inquote && Char.ToLower (c) == 'y') {
1144                                         year_month += c;
1145                                         in_year_data = true;
1146                                         in_month_data = false;
1147                                         if (year_start == 0)
1148                                                 year_start = i;
1149                                         year_end = i;
1150                                 } else if (!inquote && control_chars.IndexOf (Char.ToLower (c)) >= 0) {
1151                                         in_year_data = false;
1152                                         in_month_data = false;
1153                                 } else if (in_year_data || in_month_data) {
1154                                         if (in_month_data)
1155                                                 month_day += c;
1156                                         if (in_year_data)
1157                                                 year_month += c;
1158                                 }
1159
1160                                 if (c == '\'') {
1161                                         inquote = !inquote;
1162                                 }
1163                         }
1164
1165                         if (month_day != String.Empty) {
1166                                 //month_day = month_day.Substring (0, month_end);
1167                                 df.MonthDayPattern = TrimPattern (month_day);
1168                         }
1169                         if (year_month != String.Empty) {
1170                                 //year_month = year_month.Substring (0, year_end);
1171                                 df.YearMonthPattern = TrimPattern (year_month);
1172                         }
1173                 }
1174
1175                 string TrimPattern (string p)
1176                 {
1177                         int idx = 0;
1178                         p = p.Trim ().TrimEnd (',');
1179                         idx = p.LastIndexOf ("' de '"); // spanish dates
1180                         if (idx > 0)
1181                                 p = p.Substring (0, idx);
1182                         idx = p.LastIndexOf ("' ta '"); // finnish
1183                         if (idx > 0)
1184                                 p = p.Substring (0, idx);
1185                         idx = p.LastIndexOf ("'ren'"); // euskara
1186                         if (idx > 0)
1187                                 p = p.Replace ("'ren'", "").Trim ();
1188                         idx = p.LastIndexOf ("'a'"); // estonian
1189                         if (idx > 0)
1190                                 p = p.Substring (0, idx);
1191
1192                         return p.Replace ("'ta '", "'ta'"); // finnish
1193                 }
1194
1195                 private class LcidComparer : IComparer {
1196
1197                         public int Compare (object a, object b)
1198                         {
1199                                 CultureInfoEntry aa = (CultureInfoEntry) a;
1200                                 CultureInfoEntry bb = (CultureInfoEntry) b;
1201                         
1202                                 return aa.Lcid.CompareTo (bb.Lcid);
1203                         }                
1204                 }
1205
1206                 private class NameComparer : IComparer {
1207
1208                         public int Compare (object a, object b)
1209                         {
1210                                 CultureInfoEntry aa = (CultureInfoEntry) a;
1211                                 CultureInfoEntry bb = (CultureInfoEntry) b;
1212
1213                                 return String.CompareOrdinal(aa.Name.ToLower (), bb.Name.ToLower ());
1214                         }
1215                 }
1216
1217                 class RegionComparer : IComparer
1218                 {
1219                         public static RegionComparer Instance = new RegionComparer ();
1220                         
1221                         public int Compare (object o1, object o2)
1222                         {
1223                                 RegionInfoEntry r1 = (RegionInfoEntry) o1;
1224                                 RegionInfoEntry r2 = (RegionInfoEntry) o2;
1225                                 return String.CompareOrdinal (
1226                                         r1.ISO2Name, r2.ISO2Name);
1227                         }
1228                 }
1229
1230                 class RegionLCIDMap
1231                 {
1232                         public RegionLCIDMap (int lcid, int regionId)
1233                         {
1234                                 LCID = lcid;
1235                                 RegionId = regionId;
1236                         }
1237
1238                         public int LCID;
1239                         public int RegionId;
1240                 }
1241         }
1242 }
1243
1244