1 /*
2 * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package build.tools.cldrconverter;
27
28 import static build.tools.cldrconverter.Bundle.jreTimeZoneNames;
29 import build.tools.cldrconverter.BundleGenerator.BundleType;
30 import java.io.File;
31 import java.nio.file.DirectoryStream;
32 import java.nio.file.FileSystems;
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.util.*;
36 import java.util.ResourceBundle.Control;
37 import java.util.logging.Level;
38 import java.util.logging.Logger;
39 import java.util.stream.Collectors;
40 import java.util.stream.IntStream;
41 import javax.xml.parsers.SAXParser;
42 import javax.xml.parsers.SAXParserFactory;
43 import org.xml.sax.SAXNotRecognizedException;
44 import org.xml.sax.SAXNotSupportedException;
45
46
47 /**
48 * Converts locale data from "Locale Data Markup Language" format to
49 * JRE resource bundle format. LDML is the format used by the Common
50 * Locale Data Repository maintained by the Unicode Consortium.
51 */
52 public class CLDRConverter {
53
54 static final String LDML_DTD_SYSTEM_ID = "http://www.unicode.org/cldr/dtd/2.0/ldml.dtd";
55 static final String SPPL_LDML_DTD_SYSTEM_ID = "http://www.unicode.org/cldr/dtd/2.0/ldmlSupplemental.dtd";
56 static final String BCP47_LDML_DTD_SYSTEM_ID = "http://www.unicode.org/cldr/dtd/2.0/ldmlBCP47.dtd";
57
58
59 private static String CLDR_BASE = "../CLDR/21.0.1/";
60 static String LOCAL_LDML_DTD;
61 static String LOCAL_SPPL_LDML_DTD;
62 static String LOCAL_BCP47_LDML_DTD;
63 private static String SOURCE_FILE_DIR;
64 private static String SPPL_SOURCE_FILE;
65 private static String NUMBERING_SOURCE_FILE;
66 private static String METAZONES_SOURCE_FILE;
67 private static String LIKELYSUBTAGS_SOURCE_FILE;
68 private static String TIMEZONE_SOURCE_FILE;
69 static String DESTINATION_DIR = "build/gensrc";
70
71 static final String LOCALE_NAME_PREFIX = "locale.displayname.";
72 static final String LOCALE_SEPARATOR = LOCALE_NAME_PREFIX + "separator";
73 static final String LOCALE_KEYTYPE = LOCALE_NAME_PREFIX + "keytype";
74 static final String LOCALE_KEY_PREFIX = LOCALE_NAME_PREFIX + "key.";
75 static final String LOCALE_TYPE_PREFIX = LOCALE_NAME_PREFIX + "type.";
76 static final String LOCALE_TYPE_PREFIX_CA = LOCALE_TYPE_PREFIX + "ca.";
77 static final String CURRENCY_SYMBOL_PREFIX = "currency.symbol.";
78 static final String CURRENCY_NAME_PREFIX = "currency.displayname.";
79 static final String CALENDAR_NAME_PREFIX = "calendarname.";
80 static final String CALENDAR_FIRSTDAY_PREFIX = "firstDay.";
81 static final String CALENDAR_MINDAYS_PREFIX = "minDays.";
82 static final String TIMEZONE_ID_PREFIX = "timezone.id.";
83 static final String ZONE_NAME_PREFIX = "timezone.displayname.";
84 static final String METAZONE_ID_PREFIX = "metazone.id.";
85 static final String PARENT_LOCALE_PREFIX = "parentLocale.";
86
87 private static SupplementDataParseHandler handlerSuppl;
88 private static LikelySubtagsParseHandler handlerLikelySubtags;
89 static NumberingSystemsParseHandler handlerNumbering;
90 static MetaZonesParseHandler handlerMetaZones;
91 static TimeZoneParseHandler handlerTimeZone;
92 private static BundleGenerator bundleGenerator;
93
94 // java.base module related
95 static boolean isBaseModule = false;
96 static final Set<Locale> BASE_LOCALES = new HashSet<>();
97
98 // "parentLocales" map
99 private static final Map<String, SortedSet<String>> parentLocalesMap = new HashMap<>();
100 private static final ResourceBundle.Control defCon =
101 ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT);
102
103 static enum DraftType {
104 UNCONFIRMED,
105 PROVISIONAL,
106 CONTRIBUTED,
107 APPROVED;
108
109 private static final Map<String, DraftType> map = new HashMap<>();
110 static {
111 for (DraftType dt : values()) {
112 map.put(dt.getKeyword(), dt);
113 }
114 }
115 static private DraftType defaultType = CONTRIBUTED;
116
117 private final String keyword;
118
119 private DraftType() {
120 keyword = this.name().toLowerCase(Locale.ROOT);
121
122 }
178 break;
179
180 case "-basemodule":
181 // indicates java.base module resource generation
182 isBaseModule = true;
183 break;
184
185 case "-o":
186 // output directory
187 DESTINATION_DIR = args[++i];
188 break;
189
190 case "-utf8":
191 USE_UTF8 = true;
192 break;
193
194 case "-verbose":
195 verbose = true;
196 break;
197
198 case "-help":
199 usage();
200 System.exit(0);
201 break;
202
203 default:
204 throw new RuntimeException();
205 }
206 }
207 } catch (RuntimeException e) {
208 severe("unknown or imcomplete arg(s): " + currentArg);
209 usage();
210 System.exit(1);
211 }
212 }
213
214 // Set up path names
215 LOCAL_LDML_DTD = CLDR_BASE + "/dtd/ldml.dtd";
216 LOCAL_SPPL_LDML_DTD = CLDR_BASE + "/dtd/ldmlSupplemental.dtd";
217 LOCAL_BCP47_LDML_DTD = CLDR_BASE + "/dtd/ldmlBCP47.dtd";
218 SOURCE_FILE_DIR = CLDR_BASE + "/main";
219 SPPL_SOURCE_FILE = CLDR_BASE + "/supplemental/supplementalData.xml";
220 LIKELYSUBTAGS_SOURCE_FILE = CLDR_BASE + "/supplemental/likelySubtags.xml";
221 NUMBERING_SOURCE_FILE = CLDR_BASE + "/supplemental/numberingSystems.xml";
222 METAZONES_SOURCE_FILE = CLDR_BASE + "/supplemental/metaZones.xml";
223 TIMEZONE_SOURCE_FILE = CLDR_BASE + "/bcp47/timezone.xml";
224
225 if (BASE_LOCALES.isEmpty()) {
226 setupBaseLocales("en-US");
227 }
228
229 bundleGenerator = new ResourceBundleGenerator();
230
231 // Parse data independent of locales
232 parseSupplemental();
233 parseBCP47();
234
235 List<Bundle> bundles = readBundleList();
236 convertBundles(bundles);
237 }
238
239 private static void usage() {
240 errout("Usage: java CLDRConverter [options]%n"
241 + "\t-help output this usage message and exit%n"
242 + "\t-verbose output information%n"
243 + "\t-draft [contributed | approved | provisional | unconfirmed]%n"
244 + "\t\t draft level for using data (default: contributed)%n"
245 + "\t-base dir base directory for CLDR input files%n"
246 + "\t-basemodule generates bundles that go into java.base module%n"
247 + "\t-baselocales loc(,loc)* locales that go into the base module%n"
248 + "\t-o dir output directory (default: ./build/gensrc)%n"
249 + "\t-o dir output directory (defaut: ./build/gensrc)%n"
250 + "\t-utf8 use UTF-8 rather than \\uxxxx (for debug)%n");
251 }
252
253 static void info(String fmt, Object... args) {
254 if (verbose) {
255 System.out.printf(fmt, args);
256 }
257 }
258
259 static void info(String msg) {
260 if (verbose) {
261 System.out.println(msg);
262 }
263 }
264
265 static void warning(String fmt, Object... args) {
266 System.err.print("Warning: ");
267 System.err.printf(fmt, args);
268 }
269
384 parseLDMLFile(new File(SPPL_SOURCE_FILE), handlerSuppl);
385 Map<String, Object> parentData = handlerSuppl.getData("root");
386 parentData.keySet().stream()
387 .filter(key -> key.startsWith(PARENT_LOCALE_PREFIX))
388 .forEach(key -> {
389 parentLocalesMap.put(key, new TreeSet(
390 Arrays.asList(((String)parentData.get(key)).split(" "))));
391 });
392
393 // Parse numberingSystems to get digit zero character information.
394 handlerNumbering = new NumberingSystemsParseHandler();
395 parseLDMLFile(new File(NUMBERING_SOURCE_FILE), handlerNumbering);
396
397 // Parse metaZones to create mappings between Olson tzids and CLDR meta zone names
398 handlerMetaZones = new MetaZonesParseHandler();
399 parseLDMLFile(new File(METAZONES_SOURCE_FILE), handlerMetaZones);
400
401 // Parse likelySubtags
402 handlerLikelySubtags = new LikelySubtagsParseHandler();
403 parseLDMLFile(new File(LIKELYSUBTAGS_SOURCE_FILE), handlerLikelySubtags);
404 }
405
406 // Parsers for data in "bcp47" directory
407 //
408 private static void parseBCP47() throws Exception {
409 // Parse timezone
410 handlerTimeZone = new TimeZoneParseHandler();
411 parseLDMLFile(new File(TIMEZONE_SOURCE_FILE), handlerTimeZone);
412 }
413
414 private static void parseLDMLFile(File srcfile, AbstractLDMLHandler handler) throws Exception {
415 info("..... Parsing " + srcfile.getName() + " .....");
416 SAXParserFactory pf = SAXParserFactory.newInstance();
417 pf.setValidating(true);
418 SAXParser parser = pf.newSAXParser();
419 enableFileAccess(parser);
420 parser.parse(srcfile, handler);
421 }
422
423 private static StringBuilder getCandLocales(Locale cldrLoc) {
639 names.put(tzid, cldrMeta.get().getValue());
640 } else {
641 // check the JRE meta key, add if there is not.
642 Optional<Map.Entry<String[], String>> jreMeta =
643 jreMetaMap.entrySet().stream()
644 .filter(jm -> Arrays.deepEquals(data, jm.getKey()))
645 .findAny();
646 if (jreMeta.isPresent()) {
647 names.put(tzid, jreMeta.get().getValue());
648 } else {
649 String metaName = "JRE_" + tzid.replaceAll("[/-]", "_");
650 names.put(METAZONE_ID_PREFIX + metaName, data);
651 names.put(tzid, metaName);
652 jreMetaMap.put(data, metaName);
653 }
654 }
655 }
656 });
657 }
658
659 for (String tzid : handlerMetaZones.keySet()) {
660 String tzKey = TIMEZONE_ID_PREFIX + tzid;
661 Object data = map.get(tzKey);
662 if (data instanceof String[]) {
663 names.put(tzid, data);
664 } else {
665 String meta = handlerMetaZones.get(tzid);
666 if (meta != null) {
667 String metaKey = METAZONE_ID_PREFIX + meta;
668 data = map.get(metaKey);
669 if (data instanceof String[]) {
670 // Keep the metazone prefix here.
671 names.put(metaKey, data);
672 names.put(tzid, meta);
673 }
674 }
675 }
676 }
677 return names;
678 }
679
680 /**
681 * Extracts the language independent calendar data. Each of the two keys,
682 * "firstDayOfWeek" and "minimalDaysInFirstWeek" has a string value consists of
683 * one or multiple occurrences of:
684 * i: rg1 rg2 ... rgn;
685 * where "i" is the data for the following regions (delimited by a space) after
686 * ":", and ends with a ";".
687 */
688 private static Map<String, Object> extractCalendarData(Map<String, Object> map, String id) {
689 Map<String, Object> calendarData = new LinkedHashMap<>();
690 if (id.equals("root")) {
691 calendarData.put("firstDayOfWeek",
692 IntStream.range(1, 8)
693 .mapToObj(String::valueOf)
694 .filter(d -> map.keySet().contains(CALENDAR_FIRSTDAY_PREFIX + d))
695 .map(d -> d + ": " + map.get(CALENDAR_FIRSTDAY_PREFIX + d))
696 .collect(Collectors.joining(";")));
931 "root".equals(parent) ? Locale.ROOT : Locale.forLanguageTag(parent));
932 });
933 });
934 }
935
936 // check irregular parents
937 for (int i = 0; i < candidates.size(); i++) {
938 Locale l = candidates.get(i);
939 Locale p = childToParentLocaleMap.get(l);
940 if (!l.equals(Locale.ROOT) &&
941 Objects.nonNull(p) &&
942 !candidates.get(i+1).equals(p)) {
943 List<Locale> applied = candidates.subList(0, i+1);
944 applied.addAll(applyParentLocales(baseName, defCon.getCandidateLocales(baseName, p)));
945 return applied;
946 }
947 }
948
949 return candidates;
950 }
951 }
|
1 /*
2 * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package build.tools.cldrconverter;
27
28 import static build.tools.cldrconverter.Bundle.jreTimeZoneNames;
29 import build.tools.cldrconverter.BundleGenerator.BundleType;
30 import java.io.File;
31 import java.nio.file.*;
32 import java.time.*;
33 import java.util.*;
34 import java.util.ResourceBundle.Control;
35 import java.util.logging.Level;
36 import java.util.logging.Logger;
37 import java.util.stream.Collectors;
38 import java.util.stream.IntStream;
39 import java.util.stream.Stream;
40 import javax.xml.parsers.SAXParser;
41 import javax.xml.parsers.SAXParserFactory;
42 import org.xml.sax.SAXNotRecognizedException;
43 import org.xml.sax.SAXNotSupportedException;
44
45
46 /**
47 * Converts locale data from "Locale Data Markup Language" format to
48 * JRE resource bundle format. LDML is the format used by the Common
49 * Locale Data Repository maintained by the Unicode Consortium.
50 */
51 public class CLDRConverter {
52
53 static final String LDML_DTD_SYSTEM_ID = "http://www.unicode.org/cldr/dtd/2.0/ldml.dtd";
54 static final String SPPL_LDML_DTD_SYSTEM_ID = "http://www.unicode.org/cldr/dtd/2.0/ldmlSupplemental.dtd";
55 static final String BCP47_LDML_DTD_SYSTEM_ID = "http://www.unicode.org/cldr/dtd/2.0/ldmlBCP47.dtd";
56
57
58 private static String CLDR_BASE;
59 static String LOCAL_LDML_DTD;
60 static String LOCAL_SPPL_LDML_DTD;
61 static String LOCAL_BCP47_LDML_DTD;
62 private static String SOURCE_FILE_DIR;
63 private static String SPPL_SOURCE_FILE;
64 private static String SPPL_META_SOURCE_FILE;
65 private static String NUMBERING_SOURCE_FILE;
66 private static String METAZONES_SOURCE_FILE;
67 private static String LIKELYSUBTAGS_SOURCE_FILE;
68 private static String TIMEZONE_SOURCE_FILE;
69 static String DESTINATION_DIR = "build/gensrc";
70
71 static final String LOCALE_NAME_PREFIX = "locale.displayname.";
72 static final String LOCALE_SEPARATOR = LOCALE_NAME_PREFIX + "separator";
73 static final String LOCALE_KEYTYPE = LOCALE_NAME_PREFIX + "keytype";
74 static final String LOCALE_KEY_PREFIX = LOCALE_NAME_PREFIX + "key.";
75 static final String LOCALE_TYPE_PREFIX = LOCALE_NAME_PREFIX + "type.";
76 static final String LOCALE_TYPE_PREFIX_CA = LOCALE_TYPE_PREFIX + "ca.";
77 static final String CURRENCY_SYMBOL_PREFIX = "currency.symbol.";
78 static final String CURRENCY_NAME_PREFIX = "currency.displayname.";
79 static final String CALENDAR_NAME_PREFIX = "calendarname.";
80 static final String CALENDAR_FIRSTDAY_PREFIX = "firstDay.";
81 static final String CALENDAR_MINDAYS_PREFIX = "minDays.";
82 static final String TIMEZONE_ID_PREFIX = "timezone.id.";
83 static final String ZONE_NAME_PREFIX = "timezone.displayname.";
84 static final String METAZONE_ID_PREFIX = "metazone.id.";
85 static final String PARENT_LOCALE_PREFIX = "parentLocale.";
86
87 private static SupplementDataParseHandler handlerSuppl;
88 private static SupplementalMetadataParseHandler handlerSupplMeta;
89 private static LikelySubtagsParseHandler handlerLikelySubtags;
90 static NumberingSystemsParseHandler handlerNumbering;
91 static MetaZonesParseHandler handlerMetaZones;
92 static TimeZoneParseHandler handlerTimeZone;
93 private static BundleGenerator bundleGenerator;
94
95 // java.base module related
96 static boolean isBaseModule = false;
97 static final Set<Locale> BASE_LOCALES = new HashSet<>();
98
99 // "parentLocales" map
100 private static final Map<String, SortedSet<String>> parentLocalesMap = new HashMap<>();
101 private static final ResourceBundle.Control defCon =
102 ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT);
103
104 private static final String[] AVAILABLE_TZIDS = TimeZone.getAvailableIDs();
105 private static String zoneNameTempFile;
106
107 static enum DraftType {
108 UNCONFIRMED,
109 PROVISIONAL,
110 CONTRIBUTED,
111 APPROVED;
112
113 private static final Map<String, DraftType> map = new HashMap<>();
114 static {
115 for (DraftType dt : values()) {
116 map.put(dt.getKeyword(), dt);
117 }
118 }
119 static private DraftType defaultType = CONTRIBUTED;
120
121 private final String keyword;
122
123 private DraftType() {
124 keyword = this.name().toLowerCase(Locale.ROOT);
125
126 }
182 break;
183
184 case "-basemodule":
185 // indicates java.base module resource generation
186 isBaseModule = true;
187 break;
188
189 case "-o":
190 // output directory
191 DESTINATION_DIR = args[++i];
192 break;
193
194 case "-utf8":
195 USE_UTF8 = true;
196 break;
197
198 case "-verbose":
199 verbose = true;
200 break;
201
202 case "-zntempfile":
203 zoneNameTempFile = args[++i];
204 break;
205
206 case "-help":
207 usage();
208 System.exit(0);
209 break;
210
211 default:
212 throw new RuntimeException();
213 }
214 }
215 } catch (RuntimeException e) {
216 severe("unknown or imcomplete arg(s): " + currentArg);
217 usage();
218 System.exit(1);
219 }
220 }
221
222 // Set up path names
223 LOCAL_LDML_DTD = CLDR_BASE + "/dtd/ldml.dtd";
224 LOCAL_SPPL_LDML_DTD = CLDR_BASE + "/dtd/ldmlSupplemental.dtd";
225 LOCAL_BCP47_LDML_DTD = CLDR_BASE + "/dtd/ldmlBCP47.dtd";
226 SOURCE_FILE_DIR = CLDR_BASE + "/main";
227 SPPL_SOURCE_FILE = CLDR_BASE + "/supplemental/supplementalData.xml";
228 LIKELYSUBTAGS_SOURCE_FILE = CLDR_BASE + "/supplemental/likelySubtags.xml";
229 NUMBERING_SOURCE_FILE = CLDR_BASE + "/supplemental/numberingSystems.xml";
230 METAZONES_SOURCE_FILE = CLDR_BASE + "/supplemental/metaZones.xml";
231 TIMEZONE_SOURCE_FILE = CLDR_BASE + "/bcp47/timezone.xml";
232 SPPL_META_SOURCE_FILE = CLDR_BASE + "/supplemental/supplementalMetadata.xml";
233
234 if (BASE_LOCALES.isEmpty()) {
235 setupBaseLocales("en-US");
236 }
237
238 bundleGenerator = new ResourceBundleGenerator();
239
240 // Parse data independent of locales
241 parseSupplemental();
242 parseBCP47();
243
244 List<Bundle> bundles = readBundleList();
245 convertBundles(bundles);
246
247 // Generate java.time.format.ZoneName.java
248 if (isBaseModule) {
249 generateZoneName();
250 }
251 }
252
253 private static void usage() {
254 errout("Usage: java CLDRConverter [options]%n"
255 + "\t-help output this usage message and exit%n"
256 + "\t-verbose output information%n"
257 + "\t-draft [contributed | approved | provisional | unconfirmed]%n"
258 + "\t\t draft level for using data (default: contributed)%n"
259 + "\t-base dir base directory for CLDR input files%n"
260 + "\t-basemodule generates bundles that go into java.base module%n"
261 + "\t-baselocales loc(,loc)* locales that go into the base module%n"
262 + "\t-o dir output directory (default: ./build/gensrc)%n"
263 + "\t-zntempfile template file for java.time.format.ZoneName.java%n"
264 + "\t-utf8 use UTF-8 rather than \\uxxxx (for debug)%n");
265 }
266
267 static void info(String fmt, Object... args) {
268 if (verbose) {
269 System.out.printf(fmt, args);
270 }
271 }
272
273 static void info(String msg) {
274 if (verbose) {
275 System.out.println(msg);
276 }
277 }
278
279 static void warning(String fmt, Object... args) {
280 System.err.print("Warning: ");
281 System.err.printf(fmt, args);
282 }
283
398 parseLDMLFile(new File(SPPL_SOURCE_FILE), handlerSuppl);
399 Map<String, Object> parentData = handlerSuppl.getData("root");
400 parentData.keySet().stream()
401 .filter(key -> key.startsWith(PARENT_LOCALE_PREFIX))
402 .forEach(key -> {
403 parentLocalesMap.put(key, new TreeSet(
404 Arrays.asList(((String)parentData.get(key)).split(" "))));
405 });
406
407 // Parse numberingSystems to get digit zero character information.
408 handlerNumbering = new NumberingSystemsParseHandler();
409 parseLDMLFile(new File(NUMBERING_SOURCE_FILE), handlerNumbering);
410
411 // Parse metaZones to create mappings between Olson tzids and CLDR meta zone names
412 handlerMetaZones = new MetaZonesParseHandler();
413 parseLDMLFile(new File(METAZONES_SOURCE_FILE), handlerMetaZones);
414
415 // Parse likelySubtags
416 handlerLikelySubtags = new LikelySubtagsParseHandler();
417 parseLDMLFile(new File(LIKELYSUBTAGS_SOURCE_FILE), handlerLikelySubtags);
418
419 // Parse supplementalMetadata
420 // Currently only interested in deprecated time zone ids.
421 handlerSupplMeta = new SupplementalMetadataParseHandler();
422 parseLDMLFile(new File(SPPL_META_SOURCE_FILE), handlerSupplMeta);
423 }
424
425 // Parsers for data in "bcp47" directory
426 //
427 private static void parseBCP47() throws Exception {
428 // Parse timezone
429 handlerTimeZone = new TimeZoneParseHandler();
430 parseLDMLFile(new File(TIMEZONE_SOURCE_FILE), handlerTimeZone);
431 }
432
433 private static void parseLDMLFile(File srcfile, AbstractLDMLHandler handler) throws Exception {
434 info("..... Parsing " + srcfile.getName() + " .....");
435 SAXParserFactory pf = SAXParserFactory.newInstance();
436 pf.setValidating(true);
437 SAXParser parser = pf.newSAXParser();
438 enableFileAccess(parser);
439 parser.parse(srcfile, handler);
440 }
441
442 private static StringBuilder getCandLocales(Locale cldrLoc) {
658 names.put(tzid, cldrMeta.get().getValue());
659 } else {
660 // check the JRE meta key, add if there is not.
661 Optional<Map.Entry<String[], String>> jreMeta =
662 jreMetaMap.entrySet().stream()
663 .filter(jm -> Arrays.deepEquals(data, jm.getKey()))
664 .findAny();
665 if (jreMeta.isPresent()) {
666 names.put(tzid, jreMeta.get().getValue());
667 } else {
668 String metaName = "JRE_" + tzid.replaceAll("[/-]", "_");
669 names.put(METAZONE_ID_PREFIX + metaName, data);
670 names.put(tzid, metaName);
671 jreMetaMap.put(data, metaName);
672 }
673 }
674 }
675 });
676 }
677
678 Arrays.stream(AVAILABLE_TZIDS).forEach(tzid -> {
679 // If the tzid is deprecated, get the data for the replacement id
680 String tzKey = Optional.ofNullable((String)handlerSupplMeta.get(tzid))
681 .orElse(tzid);
682 Object data = map.get(TIMEZONE_ID_PREFIX + tzKey);
683
684 if (data instanceof String[]) {
685 names.put(tzid, data);
686 } else {
687 String meta = handlerMetaZones.get(tzKey);
688 if (meta != null) {
689 String metaKey = METAZONE_ID_PREFIX + meta;
690 data = map.get(metaKey);
691 if (data instanceof String[]) {
692 // Keep the metazone prefix here.
693 names.put(metaKey, data);
694 names.put(tzid, meta);
695 }
696 }
697 }
698 });
699
700 return names;
701 }
702
703 /**
704 * Extracts the language independent calendar data. Each of the two keys,
705 * "firstDayOfWeek" and "minimalDaysInFirstWeek" has a string value consists of
706 * one or multiple occurrences of:
707 * i: rg1 rg2 ... rgn;
708 * where "i" is the data for the following regions (delimited by a space) after
709 * ":", and ends with a ";".
710 */
711 private static Map<String, Object> extractCalendarData(Map<String, Object> map, String id) {
712 Map<String, Object> calendarData = new LinkedHashMap<>();
713 if (id.equals("root")) {
714 calendarData.put("firstDayOfWeek",
715 IntStream.range(1, 8)
716 .mapToObj(String::valueOf)
717 .filter(d -> map.keySet().contains(CALENDAR_FIRSTDAY_PREFIX + d))
718 .map(d -> d + ": " + map.get(CALENDAR_FIRSTDAY_PREFIX + d))
719 .collect(Collectors.joining(";")));
954 "root".equals(parent) ? Locale.ROOT : Locale.forLanguageTag(parent));
955 });
956 });
957 }
958
959 // check irregular parents
960 for (int i = 0; i < candidates.size(); i++) {
961 Locale l = candidates.get(i);
962 Locale p = childToParentLocaleMap.get(l);
963 if (!l.equals(Locale.ROOT) &&
964 Objects.nonNull(p) &&
965 !candidates.get(i+1).equals(p)) {
966 List<Locale> applied = candidates.subList(0, i+1);
967 applied.addAll(applyParentLocales(baseName, defCon.getCandidateLocales(baseName, p)));
968 return applied;
969 }
970 }
971
972 return candidates;
973 }
974
975 private static void generateZoneName() throws Exception {
976 Files.createDirectories(Paths.get(DESTINATION_DIR, "java", "time", "format"));
977 Files.write(Paths.get(DESTINATION_DIR, "java", "time", "format", "ZoneName.java"),
978 Files.lines(Paths.get(zoneNameTempFile))
979 .flatMap(l -> {
980 if (l.equals("%%%%ZIDMAP%%%%")) {
981 return zidMapEntry();
982 } else if (l.equals("%%%%MZONEMAP%%%%")) {
983 return handlerMetaZones.mzoneMapEntry();
984 } else if (l.equals("%%%%DEPRECATED%%%%")) {
985 return handlerSupplMeta.deprecatedMap();
986 } else {
987 return Stream.of(l);
988 }
989 })
990 .collect(Collectors.toList()),
991 StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
992 }
993
994 private static Stream<String> zidMapEntry() {
995 Map<String, String> canonMap = new HashMap<>();
996 handlerTimeZone.getData().entrySet().stream()
997 .forEach(e -> {
998 String[] ids = ((String)e.getValue()).split("\\s");
999 for (int i = 1; i < ids.length; i++) {
1000 canonMap.put(ids[i], ids[0]);
1001 }});
1002 return ZoneId.getAvailableZoneIds().stream()
1003 .map(id -> {
1004 String canonId = canonMap.getOrDefault(id, id);
1005 String meta = handlerMetaZones.get(canonId);
1006 String zone001 = handlerMetaZones.zidMap().get(meta);
1007 return zone001 == null ? "" :
1008 String.format(" \"%s\", \"%s\", \"%s\",",
1009 id, meta, zone001);
1010 })
1011 .filter(s -> !s.isEmpty())
1012 .sorted();
1013 }
1014 }
|