• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.unicode.cldr.icu;
2 
3 import java.io.File;
4 import java.io.FilenameFilter;
5 import java.io.IOException;
6 import java.util.ArrayList;
7 import java.util.Collection;
8 import java.util.Locale;
9 import java.util.Map;
10 import java.util.Map.Entry;
11 import java.util.TreeMap;
12 
13 import org.unicode.cldr.icu.ICUResourceWriter.Resource;
14 import org.unicode.cldr.icu.ICUResourceWriter.ResourceAlias;
15 import org.unicode.cldr.icu.ICUResourceWriter.ResourceString;
16 import org.unicode.cldr.icu.ICUResourceWriter.ResourceTable;
17 import org.unicode.cldr.util.LDMLUtilities;
18 import org.w3c.dom.Document;
19 import org.w3c.dom.Node;
20 
21 public class KeyTypeDataConverter {
22     private final ICULog log;
23     private final String bcp47Dir;
24     private final String[] externalTypeKeys;
25     private Collection<Document> documents;
26 
27     private static final String SOURCE_INFO = "common/bcp47/*.xml";
28     private static final String EXTERNAL_TYPES_SUFFIX = "Types";
29 
30     private static final boolean DEBUG = false;
31 
KeyTypeDataConverter(ICULog log, String bcp47Dir, String[] externalTypeKeys)32     public KeyTypeDataConverter(ICULog log, String bcp47Dir, String[] externalTypeKeys) {
33         this.log = log;
34         this.bcp47Dir = bcp47Dir;
35         this.externalTypeKeys = (externalTypeKeys == null) ? new String[0] : externalTypeKeys;
36     }
37 
convert(ICUWriter writer)38     public void convert(ICUWriter writer) {
39         // creating key mapping data
40         Map<String, String> keyMap = new TreeMap<String, String>();
41         for (Document doc : getDocuments()) {
42             addKeyMap(keyMap, doc);
43         }
44 
45         if (DEBUG) {
46             log.log("Key mappings ---------------------------------------------");
47             dumpMap(keyMap);
48         }
49 
50         // creating type mapping data
51         Map<String, Map<String, String>> typeMaps = new TreeMap<String, Map<String, String>>();
52         Map<String, Map<String, String>> typeAliases = new TreeMap<String, Map<String, String>>();
53         for (Document doc : getDocuments()) {
54             addTypeMaps(typeMaps, typeAliases, doc);
55         }
56 
57         if (DEBUG) {
58             for (Map.Entry<String, Map<String, String>> e : typeMaps.entrySet()) {
59                 log.log("\n\n\nType mappings for " + e.getKey() + " ---------------------------------------------");
60                 dumpMap(e.getValue());
61             }
62 
63             for (Map.Entry<String, Map<String, String>> e : typeAliases.entrySet()) {
64                 log.log("\n\n\nAlias mappings for " + e.getKey() + " ---------------------------------------------");
65                 dumpMap(e.getValue());
66             }
67         }
68 
69         // write out keyTypeData.txt
70         Resource cur;
71 
72         ResourceTable keyTypeDataRes = new ResourceTable();
73         keyTypeDataRes.name = LDMLBCP47Constants.KEYTYPEDATA;
74         keyTypeDataRes.annotation = ResourceTable.NO_FALLBACK;
75 
76         // keyMap
77         ResourceTable keyMapRes = new ResourceTable();
78         keyMapRes.name = LDMLBCP47Constants.KEYMAP;
79         keyTypeDataRes.first = keyMapRes;
80 
81         cur = null;
82         for (Entry<String, String> keyMapItem : keyMap.entrySet()) {
83             ResourceString keyRes = new ResourceString();
84             keyRes.name = keyMapItem.getKey();
85             keyRes.val = keyMapItem.getValue();
86 
87             if (cur == null) {
88                 keyMapRes.first = keyRes;
89             } else {
90                 cur.next = keyRes;
91             }
92             cur = keyRes;
93         }
94 
95         // typeMap
96         Resource typeMapRes = createTypeMapResource(typeMaps, null);
97         keyMapRes.next = typeMapRes;
98 
99         // typeAlias
100         Resource typeAliasRes = createTypeAliasResource(typeAliases, null);
101         typeMapRes.next = typeAliasRes;
102 
103         writer.writeResource(keyTypeDataRes, SOURCE_INFO);
104 
105         // externalized type/alias map data
106         for (String key : externalTypeKeys) {
107             ResourceTable extTypeDataRes = new ResourceTable();
108             extTypeDataRes.name = key + EXTERNAL_TYPES_SUFFIX;
109             extTypeDataRes.annotation = ResourceTable.NO_FALLBACK;
110 
111             // typeMap
112             Resource extTypeMapRes = createTypeMapResource(typeMaps, key);
113             extTypeDataRes.first = extTypeMapRes;
114 
115             // typeAlias
116             Resource extTypeAliasRes = createTypeAliasResource(typeAliases, key);
117             extTypeMapRes.next = extTypeAliasRes;
118 
119             writer.writeResource(extTypeDataRes, SOURCE_INFO);
120         }
121 
122     }
123 
getDocuments()124     private Collection<Document> getDocuments() {
125         if (documents != null) {
126             return documents;
127         }
128 
129         documents = new ArrayList<Document>();
130 
131         FilenameFilter filter = new FilenameFilter() {
132             public boolean accept(File dir, String name) {
133                 if (name.endsWith(".xml")) {
134                     return true;
135                 }
136                 return false;
137             }
138         };
139 
140         File dir = new File(bcp47Dir);
141         String[] files = dir.list(filter);
142         if (files == null) {
143             String canonicalPath;
144             try {
145                 canonicalPath = dir.getCanonicalPath();
146             } catch (IOException e) {
147                 canonicalPath = e.getMessage();
148             }
149             log.error("BCP47 files are missing " + canonicalPath);
150             System.exit(-1);
151         }
152 
153         String dirPath = dir.getAbsolutePath();
154         for (String fileName : files) {
155             try {
156                 log.info("Parsing document " + fileName);
157                 String filePath = dirPath + File.separator + fileName;
158                 Document doc = LDMLUtilities.parse(filePath, false);
159                 documents.add(doc);
160             } catch (Throwable se) {
161                 log.error("Parsing: " + fileName + " " + se.toString(), se);
162                 System.exit(1);
163             }
164         }
165         return documents;
166     }
167 
addKeyMap(Map<String, String> keyMap, Document root)168     private static void addKeyMap(Map<String, String> keyMap, Document root) {
169         for (Node node = root.getFirstChild(); node != null; node = node.getNextSibling()) {
170             if (node.getNodeType() != Node.ELEMENT_NODE) {
171                 continue;
172             }
173 
174             if (node.getNodeName().equals(LDMLBCP47Constants.LDMLBCP47)) {
175                 // Stop iterating over top-level elements, restart iterating over elements
176                 // under ldmlBCP47.
177                 node = node.getFirstChild();
178                 continue;
179             }
180 
181             if (node.getNodeName().equals(LDMLBCP47Constants.KEYWORD)) {
182                 // iterating into elements under keyword
183                 node = node.getFirstChild();
184                 continue;
185             }
186 
187             if (node.getNodeName().equals(LDMLBCP47Constants.KEY)) {
188                 String bcpKey = LDMLUtilities.getAttributeValue(node, LDMLBCP47Constants.NAME);
189                 String key = LDMLUtilities.getAttributeValue(node, LDMLBCP47Constants.ALIAS);
190                 if (key != null && !bcpKey.equals(key)) {
191                     /* keys are case-insensitive */
192                     keyMap.put(escapeKey(key.toLowerCase(Locale.ROOT)), bcpKey);
193                 }
194             }
195         }
196     }
197 
addTypeMaps(Map<String, Map<String, String>> typeMaps, Map<String, Map<String, String>> typeAliases, Document root)198     private static void addTypeMaps(Map<String, Map<String, String>> typeMaps,
199         Map<String, Map<String, String>> typeAliases, Document root) {
200         for (Node node = root.getFirstChild(); node != null; node = node.getNextSibling()) {
201             if (node.getNodeType() != Node.ELEMENT_NODE) {
202                 continue;
203             }
204 
205             if (node.getNodeName().equals(LDMLBCP47Constants.LDMLBCP47)) {
206                 // Stop iterating over top-level elements, restart iterating over elements
207                 // under ldmlBCP47.
208                 node = node.getFirstChild();
209                 continue;
210             }
211 
212             if (node.getNodeName().equals(LDMLBCP47Constants.KEYWORD)) {
213                 // iterating into elements under keyword
214                 node = node.getFirstChild();
215                 continue;
216             }
217 
218             if (node.getNodeName().equals(LDMLBCP47Constants.KEY)) {
219                 String bcpKey = LDMLUtilities.getAttributeValue(node, LDMLBCP47Constants.NAME);
220                 String key = LDMLUtilities.getAttributeValue(node, LDMLBCP47Constants.ALIAS);
221                 if (key == null) {
222                     key = bcpKey;
223                 }
224                 key = key.toLowerCase(Locale.ROOT); // keys are case-insensitive
225                 for (Node node2 = node.getFirstChild(); node2 != null; node2 = node2.getNextSibling()) {
226                     if (node2.getNodeType() != Node.ELEMENT_NODE) {
227                         continue;
228                     }
229                     if (node2.getNodeName().equals(LDMLBCP47Constants.TYPE)) {
230                         String bcpType = LDMLUtilities.getAttributeValue(node2, LDMLBCP47Constants.NAME);
231                         String type = LDMLUtilities.getAttributeValue(node2, LDMLBCP47Constants.ALIAS);
232                         if (type != null) {
233                             // type may contain multiple values delimited by space character
234                             String[] types = type.split(" ");
235                             if (types.length > 1) {
236                                 type = types[0];
237 
238                                 // add 2nd and following type values into the alias map
239                                 Map<String, String> singleTypeAliases = typeAliases.get(key);
240                                 if (singleTypeAliases == null) {
241                                     singleTypeAliases = new TreeMap<String, String>();
242                                     typeAliases.put(key, singleTypeAliases);
243                                 }
244                                 for (int i = 1; i < types.length; i++) {
245                                     singleTypeAliases.put(escapeKey(types[i]), type);
246                                 }
247                             }
248                         }
249 
250                         if (type != null && !bcpType.equals(type)) {
251                             // only populating mapping data when bcp47 representation is different
252                             Map<String, String> singleTypeMap = typeMaps.get(key);
253                             if (singleTypeMap == null) {
254                                 singleTypeMap = new TreeMap<String, String>();
255                                 typeMaps.put(key, singleTypeMap);
256                             }
257                             singleTypeMap.put(escapeKey(type), bcpType);
258                         }
259                     }
260                 }
261             }
262         }
263     }
264 
createTypeMapResource(Map<String, Map<String, String>> typeMaps, String key)265     private Resource createTypeMapResource(Map<String, Map<String, String>> typeMaps, String key) {
266         ResourceTable typeMapRes = new ResourceTable();
267         typeMapRes.name = LDMLBCP47Constants.TYPEMAP;
268 
269         Resource cur = null;
270         for (Entry<String, Map<String, String>> typesForKeyItem : typeMaps.entrySet()) {
271             String itemKey = typesForKeyItem.getKey();
272             if (key != null && !itemKey.equals(key)) {
273                 // skip this key
274                 continue;
275             }
276 
277             String aliasName = null;
278             if (key == null) {
279                 for (String extKey : externalTypeKeys) {
280                     if (extKey.equals(itemKey)) {
281                         aliasName = "/ICUDATA/" + itemKey + EXTERNAL_TYPES_SUFFIX
282                             + "/" + LDMLBCP47Constants.TYPEMAP
283                             + "/" + itemKey;
284                         break;
285                     }
286                 }
287             }
288 
289             Resource res = null;
290             if (aliasName != null) {
291                 // generating alias resource
292                 ResourceAlias typeMapForKeyResAlias = new ResourceAlias();
293                 typeMapForKeyResAlias.name = itemKey;
294                 typeMapForKeyResAlias.val = aliasName;
295 
296                 res = typeMapForKeyResAlias;
297             } else {
298                 // generating type mapping container table per key
299                 ResourceTable typeMapForKeyRes = new ResourceTable();
300                 typeMapForKeyRes.name = itemKey;
301 
302                 Resource curTypeRes = null;
303                 for (Entry<String, String> typeItem : typesForKeyItem.getValue().entrySet()) {
304                     // generating each type map data
305                     ResourceString typeRes = new ResourceString();
306                     typeRes.name = typeItem.getKey();
307                     typeRes.val = typeItem.getValue();
308 
309                     if (curTypeRes == null) {
310                         typeMapForKeyRes.first = typeRes;
311                     } else {
312                         curTypeRes.next = typeRes;
313                     }
314                     curTypeRes = typeRes;
315                 }
316 
317                 res = typeMapForKeyRes;
318             }
319 
320             if (cur == null) {
321                 typeMapRes.first = res;
322             } else {
323                 cur.next = res;
324             }
325             cur = res;
326         }
327 
328         return typeMapRes;
329     }
330 
createTypeAliasResource(Map<String, Map<String, String>> typeAliases, String key)331     private Resource createTypeAliasResource(Map<String, Map<String, String>> typeAliases, String key) {
332         ResourceTable typeAliasRes = new ResourceTable();
333         typeAliasRes.name = LDMLBCP47Constants.TYPEALIAS;
334 
335         Resource cur = null;
336         for (Entry<String, Map<String, String>> aliasesForKeyItem : typeAliases.entrySet()) {
337             String itemKey = aliasesForKeyItem.getKey();
338             if (key != null && !itemKey.equals(key)) {
339                 // skip this key
340                 continue;
341             }
342 
343             String aliasName = null;
344             if (key == null) {
345                 for (String extKey : externalTypeKeys) {
346                     if (extKey.equals(itemKey)) {
347                         aliasName = "/ICUDATA/" + itemKey + EXTERNAL_TYPES_SUFFIX
348                             + "/" + LDMLBCP47Constants.TYPEALIAS
349                             + "/" + itemKey;
350                         break;
351                     }
352                 }
353             }
354 
355             Resource res = null;
356 
357             if (aliasName != null) {
358                 // generating alias resource
359                 ResourceAlias aliasesForKeyResAlias = new ResourceAlias();
360                 aliasesForKeyResAlias.name = itemKey;
361                 aliasesForKeyResAlias.val = aliasName;
362 
363                 res = aliasesForKeyResAlias;
364             } else {
365                 // generating alias mapping container table per key
366                 ResourceTable aliasesForKeyRes = new ResourceTable();
367                 aliasesForKeyRes.name = itemKey;
368 
369                 Resource curAliasRes = null;
370                 for (Entry<String, String> aliasItem : aliasesForKeyItem.getValue().entrySet()) {
371                     // generating each alias map data
372                     ResourceString aliasRes = new ResourceString();
373                     aliasRes.name = aliasItem.getKey();
374                     aliasRes.val = aliasItem.getValue();
375 
376                     if (curAliasRes == null) {
377                         aliasesForKeyRes.first = aliasRes;
378                     } else {
379                         curAliasRes.next = aliasRes;
380                     }
381                     curAliasRes = aliasRes;
382                 }
383 
384                 res = aliasesForKeyRes;
385             }
386 
387             if (cur == null) {
388                 typeAliasRes.first = res;
389             } else {
390                 cur.next = res;
391             }
392             cur = res;
393         }
394 
395         return typeAliasRes;
396     }
397 
escapeKey(String key)398     private static String escapeKey(String key) {
399         if (key.contains("/")) {
400             key = "\"" + key.replace('/', ':') + "\"";
401         }
402         return key;
403     }
404 
dumpMap(Map<String, String> map)405     private void dumpMap(Map<String, String> map) {
406         for (Map.Entry<String, String> e : map.entrySet()) {
407             log.log(e.getKey() + " -> " + e.getValue());
408         }
409     }
410 
411     private static class LDMLBCP47Constants {
412         static final String LDMLBCP47 = "ldmlBCP47";
413         static final String KEYWORD = "keyword";
414         static final String KEY = "key";
415         static final String NAME = "name";
416         static final String ALIAS = "alias";
417         static final String TYPE = "type";
418         static final String KEYTYPEDATA = "keyTypeData";
419         static final String KEYMAP = "keyMap";
420         static final String TYPEMAP = "typeMap";
421         static final String TYPEALIAS = "typeAlias";
422     }
423 }
424