1 package org.unicode.cldr.tool;
2 
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.PrintWriter;
6 import java.util.Locale;
7 import java.util.Map;
8 import java.util.Set;
9 import java.util.TreeMap;
10 import java.util.TreeSet;
11 import java.util.regex.Matcher;
12 
13 import org.unicode.cldr.draft.FileUtilities;
14 import org.unicode.cldr.util.CLDRFile;
15 import org.unicode.cldr.util.CLDRFile.WinningChoice;
16 import org.unicode.cldr.util.CLDRPaths;
17 import org.unicode.cldr.util.CldrUtility;
18 import org.unicode.cldr.util.Factory;
19 import org.unicode.cldr.util.Pair;
20 import org.unicode.cldr.util.PathUtilities;
21 import org.unicode.cldr.util.PatternCache;
22 import org.unicode.cldr.util.SimpleFactory;
23 import org.unicode.cldr.util.StandardCodes;
24 import org.unicode.cldr.util.TransliteratorUtilities;
25 import org.unicode.cldr.util.XMLFileReader;
26 
27 import com.ibm.icu.impl.Relation;
28 import com.ibm.icu.lang.UCharacter;
29 import com.ibm.icu.text.Collator;
30 import com.ibm.icu.text.DateFormatSymbols;
31 import com.ibm.icu.text.UnicodeSet;
32 import com.ibm.icu.util.ULocale;
33 
34 class ExtractMessages {
35     public static final UnicodeSet LATIN_SCRIPT = new UnicodeSet("[:script=latin:]").freeze();
36 
37     private static Matcher fileMatcher;
38 
39     public static PrintWriter output;
40 
41     public static boolean SKIPEQUALS = true;
42     public static boolean SKIPIFCLDR = true;
43     public static String DIR = CLDRPaths.GEN_DIRECTORY + "/../additions/";
44 
main(String[] args)45     public static void main(String[] args) throws IOException {
46         double startTime = System.currentTimeMillis();
47         output = FileUtilities.openUTF8Writer(DIR, "additions.txt");
48         int totalCount = 0;
49         Set<String> skipped = new TreeSet<>();
50 
51         try {
52             String sourceDirectory = getProperty("SOURCE", null);
53             if (sourceDirectory == null) {
54                 System.out.println("Need Source Directory! ");
55                 return;
56             }
57             fileMatcher = PatternCache.get(getProperty("FILE", ".*")).matcher("");
58 
59             SKIPIFCLDR = getProperty("SKIPIFCLDR", null) != null;
60 
61             boolean showMissing = true;
62 
63             File src = new File(sourceDirectory);
64 
65             XMLFileReader xfr = new XMLFileReader().setHandler(new EnglishHandler());
66             xfr.read(src + "/en.xmb", XMLFileReader.CONTENT_HANDLER
67                 | XMLFileReader.ERROR_HANDLER, false);
68 
69             for (File file : src.listFiles()) {
70                 if (file.isDirectory())
71                     continue;
72                 if (file.length() == 0)
73                     continue;
74                 String canonicalFile = PathUtilities.getNormalizedPathString(file);
75                 if (!canonicalFile.endsWith(".xtb")) {
76                     continue;
77                 }
78 
79                 String name = file.getName();
80                 name = name.substring(0, name.length() - 4);
81 
82                 if (!fileMatcher.reset(name).matches()) {
83                     continue;
84                 }
85                 System.out.println("* " + canonicalFile);
86 
87                 try {
88                     otherHandler.setLocale(name);
89                 } catch (RuntimeException e1) {
90                     System.out.println("Skipping, no CLDR locale file: " + name + "\t" + english.getName(name) + "\t"
91                         + e1.getClass().getName() + "\t" + e1.getMessage());
92                     skipped.add(name);
93                     continue;
94                 }
95 
96                 xfr = new XMLFileReader().setHandler(otherHandler);
97                 try {
98                     xfr.read(canonicalFile, XMLFileReader.CONTENT_HANDLER
99                         | XMLFileReader.ERROR_HANDLER, false);
100                 } catch (RuntimeException e) {
101                     System.out.println(e.getMessage());
102                     continue;
103                 }
104 
105                 // now write it out
106                 CLDRFile newFile = SimpleFactory.makeFile(otherHandler.getLocale());
107                 int itemCount = 0;
108                 for (DataHandler dataHandler : dataHandlers) {
109                     if (showMissing) {
110                         System.out.println("case " + dataHandler.type + ":");
111                         for (String value : dataHandler.missing) {
112                             System.out.println("addName(\"" + value + "\", \"XXX\", true);");
113                         }
114                     }
115 
116                     for (String id : dataHandler.id_to_value.keySet()) {
117                         Set<String> otherValue = dataHandler.id_to_value.getAll(id);
118                         if (otherValue == null || otherValue.size() == 0) continue;
119                         String cldrValue = dataHandler.id_to_cldrValue.get(id);
120                         int count = 0;
121                         for (String oValue : otherValue) {
122                             itemCount++;
123                             output.println(otherHandler.getLocale()
124                                 + "\t" + dataHandler.type
125                                 + "\t" + id
126                                 + "\t" + oValue
127                                 + (cldrValue == null ? "" : "\tcldr:\t" + cldrValue)
128                                 + (count == 0 ? "" : "\talt:\t" + String.valueOf(count)));
129                             newFile.add(dataHandler.getPath(id, count), oValue);
130                         }
131                     }
132                 }
133                 PrintWriter cldrOut = FileUtilities.openUTF8Writer(DIR, otherHandler.getLocale() + ".xml");
134                 newFile.write(cldrOut);
135                 cldrOut.close();
136 
137                 output.println();
138                 showMissing = false;
139                 output.flush();
140                 System.out.println("\titems: " + itemCount);
141                 totalCount += itemCount;
142             }
143 
144             for (String name : skipped) {
145                 System.out.println("\tSkipping, no CLDR locale file: " + name + "\t" + english.getName(name));
146             }
147             double deltaTime = System.currentTimeMillis() - startTime;
148             System.out.println("Elapsed: " + deltaTime / 1000.0 + " seconds");
149             System.out.println("\ttotal items: " + totalCount);
150         } finally {
151             output.close();
152         }
153     }
154 
getProperty(String key, String defaultValue)155     private static String getProperty(String key, String defaultValue) {
156         String fileRegex = System.getProperty(key);
157         if (fileRegex == null)
158             fileRegex = defaultValue;
159         System.out.println("-D" + key + "=" + fileRegex);
160         return fileRegex;
161     }
162 
163     private static Map<String, Pair<String, DataHandler>> numericId_Id = new TreeMap<>();
164     private static Matcher numericIdMatcher = PatternCache.get("\\[@id=\"([^\"]+)\"\\]").matcher("");
165     private static Factory cldrFactory = Factory.make(CLDRPaths.MAIN_DIRECTORY, ".*");
166     private static CLDRFile english = cldrFactory.make("en", true);
167 
168     private static class EnglishHandler extends XMLFileReader.SimpleHandler {
169 
170         @Override
handlePathValue(String path, String value)171         public void handlePathValue(String path, String value) {
172             for (DataHandler handler : dataHandlers) {
173                 if (handler.matches(path)) {
174                     // //messagebundle/msg[@id="1907015897505457162"][@seq="71982"][@desc="Andorra is a display name for a timezone"][@xml:space="default"]
175                     numericIdMatcher.reset(path).find();
176                     String id = numericIdMatcher.group(1);
177                     value = value.trim();
178                     if (value.length() == 0) return; // skip empties
179                     value = TransliteratorUtilities.fromXML.transliterate(value);
180                     String realID = handler.getCode(value);
181                     if (realID == null) {
182                         handler.missing.add(value);
183                         return;
184                     }
185                     numericId_Id.put(id, new Pair<>(realID, handler));
186                     // System.out.println(id + "\t" + path + "\t" + value);
187                 }
188             }
189         }
190     }
191 
192     public static Collator col = Collator.getInstance(ULocale.ENGLISH);
193     static {
194         col.setStrength(Collator.SECONDARY);
195     }
196 
197     private static OtherHandler otherHandler = new OtherHandler();
198 
199     private static class OtherHandler extends XMLFileReader.SimpleHandler {
200         private String locale;
201         private ULocale uLocale;
202         CLDRFile cldrFile;
203         boolean usesLatin;
204 
205         @Override
handlePathValue(String path, String value)206         public void handlePathValue(String path, String value) {
207             // //messagebundle/msg[@id="1907015897505457162"][@seq="71982"][@desc="Andorra is a display name for a timezone"][@xml:space="default"]
208             value = value.trim();
209             if (value.length() == 0) return; // skip empties
210 
211             numericIdMatcher.reset(path).find();
212             String numericId = numericIdMatcher.group(1);
213             Pair<String, DataHandler> id_handler = numericId_Id.get(numericId);
214             if (id_handler == null) return;
215             String id = id_handler.getFirst();
216             DataHandler dataHandler = id_handler.getSecond();
217 
218             if (!usesLatin && LATIN_SCRIPT.containsSome(value)) {
219                 // output.println(locale + "\tSkipping item with latin characters\t" + id + "\t" + value);
220                 return;
221             }
222 
223             // this should be reorganized to put more in the DataHandler, but for now...
224 
225             value = dataHandler.fixValue(uLocale, value);
226 
227             String cldrValue = dataHandler.getCldrValue(cldrFile, id);
228             if (cldrValue != null) {
229                 if (col.compare(cldrValue, value) == 0) {
230                     // System.out.println("Duplicate for " + id + "\t" + value);
231                     if (SKIPEQUALS) return;
232                 } else {
233                     if (SKIPIFCLDR) return;
234                     // output.println(locale + "\tDifferent value for\t" + id + "\t" + value + "\tcldr:\t" + cldrValue);
235                 }
236             }
237             dataHandler.addValues(id, value, cldrValue);
238         }
239 
setLocale(String locale)240         public void setLocale(String locale) {
241 
242             // skip en, fr_CA
243             // as, sa bad
244             // ku cldr-latin, g-arabic
245             // ml, my, pa, te has mixed english
246             // TODO move this into datahandler eventually
247             locale = fixLocale(locale);
248             this.locale = locale;
249             this.uLocale = new ULocale(locale);
250             String lang = uLocale.getLanguage();
251             if (locale.equals("fr_CA") || lang.equals("en")) {
252                 throw new RuntimeException("Skipping " + locale);
253             }
254             cldrFile = cldrFactory.make(locale, false);
255             UnicodeSet exemplars = cldrFile.getExemplarSet("", WinningChoice.WINNING);
256             usesLatin = exemplars != null && exemplars.containsSome(LATIN_SCRIPT);
257             for (DataHandler dataHandler : dataHandlers) {
258                 dataHandler.reset(cldrFile);
259             }
260         }
261 
getLocale()262         public String getLocale() {
263             return locale;
264         }
265     }
266 
267     static Map<String, String> fixLocaleMap = CldrUtility.asMap(new String[][] {
268         { "zh_CN", "zh" },
269         { "zh_TW", "zh_Hant" },
270         { "pt_BR", "pt" },
271         { "in", "id" },
272         { "iw", "he" },
273         { "jw", "jv" },
274         { "no", "nb" },
275         { "ku", "ku_Arab" },
276     });
277 
fixLocale(String locale)278     static private String fixLocale(String locale) {
279         locale = locale.replace('-', '_');
280         String newLocale = fixLocaleMap.get(locale);
281         if (newLocale != null) {
282             locale = newLocale;
283         }
284         return locale;
285     }
286 
287     /*
288      * Language
289      * -DXMLPATH=".*form of language.*"
290      *
291      * Country
292      * -DXMLPATH=".*the country or region.*"
293      *
294      * Currency
295      * -DXMLPATH=".*currency name.*"
296      *
297      * Month Long/Short
298      * -DXMLPATH=".*Name of the month of .*"
299      * -DXMLPATH=".*3 letter abbreviation for name of Month.*"
300      *
301      * Week Long/Short
302      * -DXMLPATH=".*day in week.*"
303      * -DXMLPATH=".*Short Version of .*"
304      *
305      * Timezone
306      * DXMLPATH=".*is a display name for a timezone.*"
307      */
308 
309     enum Type {
310         LANGUAGE, REGION, CURRENCY, MONTH, MONTHSHORT, DAY, DAYSHORT, TIMEZONE
311     }
312 
313     static StandardCodes sc = StandardCodes.make();
314     static DateFormatSymbols dfs = new DateFormatSymbols(ULocale.ENGLISH);
315 
316     static DataHandler[] dataHandlers = {
317         new DataHandler(Type.LANGUAGE, ".*form of language.*"),
318         new DataHandler(Type.REGION, ".*the country or region.*"),
319         new DataHandler(Type.CURRENCY, ".*currency name.*"),
320         new DataHandler(Type.MONTH, ".*Name of the month of .*"),
321         new DataHandler(Type.MONTHSHORT, ".*3 letter abbreviation for name of Month.*"),
322         new DataHandler(Type.DAY, ".*day in week.*"),
323         new DataHandler(Type.DAYSHORT, ".*Short Version of .*"),
324         new DataHandler(Type.TIMEZONE, ".*is a display name for a timezone.*"),
325     };
326 
327     enum CasingAction {
328         NONE, FORCE_TITLE, FORCE_LOWER
329     }
330 
331     static class DataHandler implements Comparable<DataHandler> {
332         // mostly stable
333         private Matcher matcher;
334         private Type type;
335         private Map<String, String> name_code = new TreeMap<>();
336         // private Map<String,String> code_name = new TreeMap();
337         private Set<String> missing = new TreeSet<>();
338 
339         // changes with each locale, must call reset
340         private Relation<String, String> id_to_value = Relation.of(new TreeMap<String, Set<String>>(), TreeSet.class);
341         private Map<String, String> id_to_cldrValue = new TreeMap<>();
342         private CasingAction forceCasing = CasingAction.NONE;
343 
reset(CLDRFile cldrFile)344         public void reset(CLDRFile cldrFile) {
345             id_to_value.clear();
346             id_to_cldrValue.clear();
347             forceCasing = CasingAction.NONE;
348             String key = null;
349             switch (type) {
350             case LANGUAGE:
351                 key = "en";
352                 break;
353             case REGION:
354                 key = "FR";
355                 break;
356             case CURRENCY:
357                 key = "GBP";
358                 break;
359             case MONTH:
360             case MONTHSHORT:
361                 key = "1";
362                 break;
363             case DAY:
364             case DAYSHORT:
365                 key = "mon";
366                 break;
367             case TIMEZONE:
368                 key = "America/New_York";
369                 break;
370             }
371             String sample = getCldrValue(cldrFile, key);
372             if (sample != null) {
373                 if (UCharacter.isLowerCase(sample.charAt(0))) {
374                     forceCasing = CasingAction.FORCE_LOWER;
375                 } else if (UCharacter.isUpperCase(sample.charAt(0))) {
376                     forceCasing = CasingAction.FORCE_TITLE;
377                 }
378             }
379         }
380 
fixValue(ULocale uLocale, String value)381         public String fixValue(ULocale uLocale, String value) {
382             value = TransliteratorUtilities.fromXML.transliterate(value);
383 
384             if (forceCasing == CasingAction.FORCE_LOWER) {
385                 if (!UCharacter.isLowerCase(value.charAt(0))) {
386                     value = UCharacter.toLowerCase(value);
387                 }
388             } else if (forceCasing == CasingAction.FORCE_TITLE) {
389                 if (!UCharacter.isUpperCase(value.charAt(0))) {
390                     value = UCharacter.toTitleCase(uLocale, value, null);
391                 }
392             }
393 
394             return value;
395         }
396 
addValues(String id, String value, String cldrValue)397         public void addValues(String id, String value, String cldrValue) {
398             id_to_value.put(id, value);
399             if (cldrValue != null) {
400                 id_to_cldrValue.put(id, cldrValue);
401             }
402         }
403 
addName(String name, String code, boolean skipMessage)404         public void addName(String name, String code, boolean skipMessage) {
405             // String old = code_name.get(code);
406             // if (old != null) {
407             // if (!skipMessage) {
408             // System.out.println("Name collision:\t" + code + "\tnew: " + name + "\tkeeping: " + old);
409             // }
410             // } else {
411             // }
412             // code_name.put(code, name);
413             name_code.put(name, code);
414         }
415 
DataHandler(Type type, String pattern)416         DataHandler(Type type, String pattern) {
417             this.type = type;
418             matcher = PatternCache.get(pattern).matcher("");
419             switch (type) {
420             case LANGUAGE:
421                 for (String code : sc.getAvailableCodes("language")) {
422                     String name = english.getName("language", code);
423                     if (name == null) {
424                         // System.out.println("Missing name for: " + code);
425                         continue;
426                     }
427                     addName(name, code.replace("-", "_"), false);
428                 }
429                 // add irregular names
430                 addName("English (US)", "en_US", true);
431                 addName("English (UK)", "en_GB", true);
432                 // addName("English (AU)", "en_AU/short");
433                 // addName("Portuguese (PT)", "pt_PT/short");
434                 // addName("Portuguese (BR)", "pt_BR/short");
435                 addName("Chinese (Simplified)", "zh_Hans", true);
436                 addName("Chinese (Traditional)", "zh_Hant", true);
437                 addName("Norwegian (Nynorsk)", "nn", true);
438                 addName("Portuguese (Portugal)", "pt_PT", true);
439                 addName("Portuguese (Brazil)", "pt_BR", true);
440                 addName("English (Australia)", "en_AU", true);
441                 addName("Scots Gaelic", "gd", true);
442                 addName("Frisian", "fy", true);
443                 addName("Sesotho", "st", true);
444                 addName("Kyrgyz", "ky", true);
445                 addName("Laothian", "lo", true);
446                 addName("Cambodian", "km", true);
447                 addName("Greenlandic", "kl", true);
448                 addName("Inupiak", "ik", true);
449                 addName("Volapuk", "vo", true);
450                 addName("Byelorussian", "be", true);
451                 addName("Faeroese", "fo", true);
452                 addName("Singhalese", "si", true);
453                 addName("Gaelic", "ga", true); // IRISH
454                 addName("Bhutani", "dz", true);
455                 addName("Setswana", "tn", true);
456                 addName("Siswati", "ss", true);
457                 addName("Sangro", "sg", true);
458                 // addName("Kirundi", "XXX"); // no ISO2 code
459                 // addName("Sudanese", "XXX"); // ???
460                 break;
461             case REGION:
462                 for (String code : sc.getAvailableCodes("territory")) {
463                     String name = english.getName("territory", code);
464                     if (name == null) {
465                         // System.out.println("Missing name for: " + code);
466                         continue;
467                     }
468                     addName(name, code, false);
469                 }
470                 // add irregular names
471                 addName("Bosnia and Herzegowina", "BA", true);
472                 addName("Congo", "CG", true);
473                 addName("Congo, Democratic Republic of the", "CD", true);
474                 addName("Congo, The Democratic Republic of the", "CD", true);
475                 addName("Cote D'ivoire", "CI", true);
476                 addName("Côte d'Ivoire", "CI", true);
477                 addName("Equitorial Guinea", "GQ", true);
478                 addName("French Quiana", "GF", true);
479                 addName("Heard and Mc Donald Islands", "HM", true);
480                 addName("Holy See (Vatican City State)", "VA", true);
481                 addName("Iran (Islamic Republic of)", "IR", true);
482                 addName("Korea, Democratic People's Republic of", "KP", true);
483                 addName("Korea, Republic of", "KR", true);
484                 addName("Libyan Arab Jamahiriya", "LY", true);
485                 addName("Lichtenstein", "LI", true);
486                 addName("Macao", "MO", true);
487                 addName("Micronesia, Federated States of", "FM", true);
488                 addName("Palestine", "PS", true);
489                 addName("Serbia and Montenegro", "CS", true);
490                 addName("Slovakia (Slovak Republic)", "SK", true);
491                 addName("São Tomé and Príncipe", "ST", true);
492                 addName("The Former Yugoslav Republic of Macedonia", "MK", true);
493                 addName("United States minor outlying islands", "UM", true);
494                 addName("Vatican City", "VA", true);
495                 addName("Virgin Islands, British", "VG", true);
496                 addName("Virgin Islands, U.S.", "VI", true);
497                 addName("Zaire", "CD", true);
498                 addName("Åland Islands", "AX", true);
499                 break;
500             case CURRENCY:
501                 for (String code : sc.getAvailableCodes("currency")) {
502                     String name = english.getName("currency", code);
503                     if (name == null) {
504                         // System.out.println("Missing name for: " + code);
505                         continue;
506                     }
507                     addName(name, code, false);
508                 }
509                 // add irregular names
510                 addName("Australian Dollars", "AUD", true);
511                 addName("Bolivian Boliviano", "BOB", true);
512                 addName("British Pounds Sterling", "GBP", true);
513                 addName("Bulgarian Lev", "BGN", true);
514                 addName("Canadian Dollars", "CAD", true);
515                 addName("Czech Koruna", "CZK", true);
516                 addName("Danish Kroner", "DKK", true);
517                 addName("Denmark Kroner", "DKK", true);
518                 addName("Deutsche Marks", "DEM", true);
519                 addName("Euros", "EUR", true);
520                 addName("French Franks", "FRF", true);
521                 addName("Hong Kong Dollars", "HKD", true);
522                 addName("Israeli Shekel", "ILS", true);
523                 addName("Lithuanian Litas", "LTL", true);
524                 addName("Mexico Peso", "MXN", true);
525                 addName("New Romanian Leu", "RON", true);
526                 addName("New Taiwan Dollar", "TWD", true);
527                 addName("New Zealand Dollars", "NZD", true);
528                 addName("Norway Kroner", "NOK", true);
529                 addName("Norwegian Kroner", "NOK", true);
530                 addName("Peruvian Nuevo Sol", "PEN", true);
531                 addName("Polish New Zloty", "PLN", true);
532                 addName("Polish NewZloty", "PLN", true);
533                 addName("Russian Rouble", "RUB", true);
534                 addName("Singapore Dollars", "SGD", true);
535                 addName("Slovenian Tolar", "SIT", true);
536                 addName("Sweden Kronor", "SEK", true);
537                 addName("Swedish Kronor", "SEK", true);
538                 addName("Swiss Francs", "CHF", true);
539                 addName("US Dollars", "USD", true);
540                 addName("United Arab EmiratesD irham", "AED", true);
541                 addName("Venezuela Bolivar", "VEB", true);
542                 addName("Yuan Renminbi", "CNY", true);
543                 break;
544             case TIMEZONE:
545                 for (String code : sc.getAvailableCodes("tzid")) {
546                     String[] parts = code.split("/");
547                     addName(parts[parts.length - 1].replace("_", " "), code, false);
548                 }
549                 // add irregular names
550                 addName("Alaska Time", "America/Anchorage", true);
551                 // addName("Atlantic Time", "XXX", true);
552                 // addName("Atlantic Time - Halifax", "America/Halifax", true);
553                 addName("Canary Islands", "Atlantic/Canary", true);
554                 // addName("Central European Time", "XXX", true);
555                 // addName("Central European Time - Madrid", "Europe/Madrid", true);
556                 // addName("Central Time", "America/Chicago", true);
557                 // addName("Central Time - Adelaide", "Australia/Adelaide", true);
558                 // addName("Central Time - Darwin", "Australia/Darwin", true);
559                 // addName("Central Time - Mexico City", "America/Mexico_City", true);
560                 // addName("Central Time - Mexico City, Monterrey", "America/Monterrey", true);
561                 // addName("Central Time - Regina", "America/Regina", true);
562                 // addName("Central Time - Sasketchewan", "XXX", true);
563                 // addName("Central Time - Winnipeg", "America/Winnipeg", true);
564                 // addName("China Time - Beijing", "XXX", true);
565                 addName("Dumont D'Urville", "Antarctica/DumontDUrville", true);
566                 addName("Easter Island", "Pacific/Easter", true);
567                 // addName("Eastern European Time", "XXX", true);
568                 // addName("Eastern Standard Time", "XXX", true);
569                 // addName("Eastern Time", "XXX", true);
570                 // addName("Eastern Time - Brisbane", "Australia/Brisbane", true);
571                 // addName("Eastern Time - Hobart", "Australia/Hobart", true);
572                 // addName("Eastern Time - Iqaluit", "America/Iqaluit", true);
573                 // addName("Eastern Time - Melbourne, Sydney", "XXX", true);
574                 // addName("Eastern Time - Montreal", "XXX", true);
575                 // addName("Eastern Time - Toronto", "XXX", true);
576                 // addName("GMT (no daylight saving)", "XXX", true);
577                 // addName("Greenwich Mean Time", "XXX", true);
578                 // addName("Hanoi", "XXX", true);
579                 // addName("Hawaii Time", "XXX", true);
580                 // addName("India Standard Time", "XXX", true);
581                 // addName("International Date Line West", "XXX", true);
582                 // addName("Japan Time", "XXX", true);
583                 // addName("Moscow+00", "XXX", true);
584                 // addName("Moscow+01 - Samara", "XXX", true);
585                 // addName("Moscow+02 - Yekaterinburg", "XXX", true);
586                 // addName("Moscow+03 - Omsk, Novosibirsk", "XXX", true);
587                 // addName("Moscow+04 - Krasnoyarsk", "XXX", true);
588                 // addName("Moscow+05 - Irkutsk", "XXX", true);
589                 // addName("Moscow+06 - Yakutsk", "XXX", true);
590                 // addName("Moscow+07 - Vladivostok, Sakhalin", "XXX", true);
591                 // addName("Moscow+07 - Yuzhno-Sakhalinsk", "XXX", true);
592                 // addName("Moscow+08 - Magadan", "XXX", true);
593                 // addName("Moscow+09 - Kamchatka, Anadyr", "XXX", true);
594                 // addName("Moscow+09 - Petropavlovsk-Kamchatskiy", "XXX", true);
595                 // addName("Moscow-01 - Kaliningrad", "XXX", true);
596                 // addName("Mountain Time", "XXX", true);
597                 // addName("Mountain Time - Arizona", "XXX", true);
598                 // addName("Mountain Time - Chihuahua, Mazatlan", "XXX", true);
599                 // addName("Mountain Time - Dawson Creek", "XXX", true);
600                 // addName("Mountain Time - Edmonton", "XXX", true);
601                 // addName("Mountain Time - Hermosillo", "XXX", true);
602                 // addName("Mountain Time - Yellowknife", "XXX", true);
603                 // addName("Newfoundland Time - St. Johns", "XXX", true);
604                 // addName("Pacific Time", "XXX", true);
605                 // addName("Pacific Time - Tijuana", "XXX", true);
606                 // addName("Pacific Time - Vancouver", "XXX", true);
607                 // addName("Pacific Time - Whitehorse", "XXX", true);
608                 addName("Salvador", "America/El_Salvador", true);
609                 addName("St. Kitts", "America/St_Kitts", true);
610                 addName("St. Lucia", "America/St_Lucia", true);
611                 addName("St. Thomas", "America/St_Thomas", true);
612                 addName("St. Vincent", "America/St_Vincent", true);
613                 // addName("Tel Aviv", "XXX", true);
614                 // addName("Western European Time", "XXX", true);
615                 // addName("Western European Time - Canary Islands", "XXX", true);
616                 // addName("Western European Time - Ceuta", "XXX", true);
617                 // addName("Western Time - Perth", "XXX", true);
618                 break;
619             case MONTH:
620             case MONTHSHORT:
621                 String[] names = type == Type.MONTH ? dfs.getMonths() : dfs.getShortMonths();
622                 for (int i = 0; i < names.length; ++i) {
623                     addName(names[i], String.valueOf(i + 1), true);
624                 }
625                 break;
626             case DAY:
627             case DAYSHORT:
628                 String[] names2 = type == Type.DAY ? dfs.getWeekdays() : dfs.getShortWeekdays();
629                 for (int i = 1; i < names2.length; ++i) {
630                     addName(names2[i], names2[i].substring(0, 3).toLowerCase(Locale.ENGLISH), true);
631                 }
632                 break;
633             default:
634                 // throw new IllegalArgumentException();
635                 break;
636             }
637         }
638 
getCldrValue(CLDRFile cldrFile, String id)639         public String getCldrValue(CLDRFile cldrFile, String id) {
640             String result = cldrFile.getStringValue(getPath(id));
641             // cldrFile.getName(CLDRFile.LANGUAGE_NAME, id, false);
642             if (result == null && type == Type.TIMEZONE) {
643                 String[] parts = id.split("/");
644                 result = parts[parts.length - 1].replace("_", " ");
645             }
646             return result;
647         }
648 
matches(String text)649         boolean matches(String text) {
650             return matcher.reset(text).matches();
651         }
652 
getCode(String value)653         String getCode(String value) {
654             return name_code.get(value);
655         }
656 
657         @Override
compareTo(DataHandler o)658         public int compareTo(DataHandler o) {
659             throw new IllegalArgumentException();
660         }
661 
getPath(String id, int count)662         String getPath(String id, int count) {
663             String result = getPath(id);
664             count += 650;
665             result += "[@alt=\"proposed-x" + count + "\"]";
666             result += "[@draft=\"provisional\"]";
667             return result;
668         }
669 
getPath(String id)670         String getPath(String id) {
671             switch (type) {
672             case LANGUAGE:
673                 return CLDRFile.getKey(CLDRFile.LANGUAGE_NAME, id);
674             case REGION:
675                 return CLDRFile.getKey(CLDRFile.TERRITORY_NAME, id);
676             case CURRENCY:
677                 return CLDRFile.getKey(CLDRFile.CURRENCY_NAME, id);
678             case TIMEZONE:
679                 return "//ldml/dates/timeZoneNames/zone[@type=\"$1\"]/exemplarCity".replace("$1", id);
680             case MONTH:
681                 return "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/months/monthContext[@type=\"format\"]/monthWidth[@type=\"wide\"]/month[@type=\"$1\"]"
682                     .replace("$1", id);
683             case MONTHSHORT:
684                 return "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/months/monthContext[@type=\"format\"]/monthWidth[@type=\"abbreviated\"]/month[@type=\"$1\"]"
685                     .replace("$1", id);
686             case DAY:
687                 return "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/days/dayContext[@type=\"format\"]/dayWidth[@type=\"wide\"]/day[@type=\"$1\"]"
688                     .replace("$1", id);
689             case DAYSHORT:
690                 return "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/days/dayContext[@type=\"format\"]/dayWidth[@type=\"abbreviated\"]/day[@type=\"$1\"]"
691                     .replace("$1", id);
692             }
693             return null;
694             //
695         }
696     }
697 }
698