1 /*
2 * Copyright (c) 2012, 2019, 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.io.IOException;
32 import java.io.UncheckedIOException;
33 import java.nio.file.*;
34 import java.text.MessageFormat;
35 import java.time.*;
36 import java.util.*;
37 import java.util.ResourceBundle.Control;
38 import java.util.logging.Level;
39 import java.util.logging.Logger;
40 import java.util.stream.Collectors;
41 import java.util.stream.IntStream;
42 import java.util.stream.Stream;
43 import javax.xml.parsers.SAXParser;
44 import javax.xml.parsers.SAXParserFactory;
45 import org.xml.sax.SAXNotRecognizedException;
46 import org.xml.sax.SAXNotSupportedException;
47
48
72 private static String WINZONES_SOURCE_FILE;
73 private static String PLURALS_SOURCE_FILE;
74 static String DESTINATION_DIR = "build/gensrc";
75
76 static final String LOCALE_NAME_PREFIX = "locale.displayname.";
77 static final String LOCALE_SEPARATOR = LOCALE_NAME_PREFIX + "separator";
78 static final String LOCALE_KEYTYPE = LOCALE_NAME_PREFIX + "keytype";
79 static final String LOCALE_KEY_PREFIX = LOCALE_NAME_PREFIX + "key.";
80 static final String LOCALE_TYPE_PREFIX = LOCALE_NAME_PREFIX + "type.";
81 static final String LOCALE_TYPE_PREFIX_CA = LOCALE_TYPE_PREFIX + "ca.";
82 static final String CURRENCY_SYMBOL_PREFIX = "currency.symbol.";
83 static final String CURRENCY_NAME_PREFIX = "currency.displayname.";
84 static final String CALENDAR_NAME_PREFIX = "calendarname.";
85 static final String CALENDAR_FIRSTDAY_PREFIX = "firstDay.";
86 static final String CALENDAR_MINDAYS_PREFIX = "minDays.";
87 static final String TIMEZONE_ID_PREFIX = "timezone.id.";
88 static final String EXEMPLAR_CITY_PREFIX = "timezone.excity.";
89 static final String ZONE_NAME_PREFIX = "timezone.displayname.";
90 static final String METAZONE_ID_PREFIX = "metazone.id.";
91 static final String PARENT_LOCALE_PREFIX = "parentLocale.";
92 static final String[] EMPTY_ZONE = {"", "", "", "", "", ""};
93
94 private static SupplementDataParseHandler handlerSuppl;
95 private static LikelySubtagsParseHandler handlerLikelySubtags;
96 private static WinZonesParseHandler handlerWinZones;
97 static PluralsParseHandler handlerPlurals;
98 static SupplementalMetadataParseHandler handlerSupplMeta;
99 static NumberingSystemsParseHandler handlerNumbering;
100 static MetaZonesParseHandler handlerMetaZones;
101 static TimeZoneParseHandler handlerTimeZone;
102 private static BundleGenerator bundleGenerator;
103
104 // java.base module related
105 static boolean isBaseModule = false;
106 static final Set<Locale> BASE_LOCALES = new HashSet<>();
107
108 // "parentLocales" map
109 private static final Map<String, SortedSet<String>> parentLocalesMap = new HashMap<>();
110 private static final ResourceBundle.Control defCon =
111 ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT);
112
669 return localeNames;
670 }
671
672 @SuppressWarnings("AssignmentToForLoopParameter")
673 private static Map<String, Object> extractCurrencyNames(Map<String, Object> map, String id, String names)
674 throws Exception {
675 Map<String, Object> currencyNames = new TreeMap<>(KeyComparator.INSTANCE);
676 for (String key : map.keySet()) {
677 if (key.startsWith(CURRENCY_NAME_PREFIX)) {
678 currencyNames.put(key.substring(CURRENCY_NAME_PREFIX.length()), map.get(key));
679 } else if (key.startsWith(CURRENCY_SYMBOL_PREFIX)) {
680 currencyNames.put(key.substring(CURRENCY_SYMBOL_PREFIX.length()), map.get(key));
681 }
682 }
683 return currencyNames;
684 }
685
686 private static Map<String, Object> extractZoneNames(Map<String, Object> map, String id) {
687 Map<String, Object> names = new HashMap<>();
688
689 // Copy over missing time zone ids from JRE for English locale
690 if (id.equals("en")) {
691 Map<String[], String> jreMetaMap = new HashMap<>();
692 jreTimeZoneNames.stream().forEach(e -> {
693 String tzid = (String)e[0];
694 String[] data = (String[])e[1];
695
696 if (map.get(TIMEZONE_ID_PREFIX + tzid) == null &&
697 handlerMetaZones.get(tzid) == null ||
698 handlerMetaZones.get(tzid) != null &&
699 map.get(METAZONE_ID_PREFIX + handlerMetaZones.get(tzid)) == null) {
700
701 // First, check the alias
702 String canonID = canonicalTZMap.get(tzid);
703 if (canonID != null && !tzid.equals(canonID)) {
704 Object value = map.get(TIMEZONE_ID_PREFIX + canonID);
705 if (value != null) {
706 names.put(tzid, value);
707 return;
708 } else {
709 String meta = handlerMetaZones.get(canonID);
710 if (meta != null) {
711 value = map.get(METAZONE_ID_PREFIX + meta);
712 if (value != null) {
713 names.put(tzid, meta);
714 return;
715 }
716 }
717 }
718 }
719
720 // Check the CLDR meta key
721 Optional<Map.Entry<String, String>> cldrMeta =
722 handlerMetaZones.getData().entrySet().stream()
723 .filter(me ->
724 Arrays.deepEquals(data,
725 (String[])map.get(METAZONE_ID_PREFIX + me.getValue())))
726 .findAny();
727 cldrMeta.ifPresentOrElse(meta -> names.put(tzid, meta.getValue()), () -> {
728 // Check the JRE meta key, add if there is not.
729 Optional<Map.Entry<String[], String>> jreMeta =
730 jreMetaMap.entrySet().stream()
731 .filter(jm -> Arrays.deepEquals(data, jm.getKey()))
732 .findAny();
733 jreMeta.ifPresentOrElse(meta -> names.put(tzid, meta.getValue()), () -> {
734 String metaName = "JRE_" + tzid.replaceAll("[/-]", "_");
735 names.put(METAZONE_ID_PREFIX + metaName, data);
736 names.put(tzid, metaName);
737 });
738 });
739 }
740 });
741 }
742
743 getAvailableZoneIds().stream().forEach(tzid -> {
744 // If the tzid is deprecated, get the data for the replacement id
745 String tzKey = Optional.ofNullable((String)handlerSupplMeta.get(tzid))
746 .orElse(tzid);
747 Object data = map.get(TIMEZONE_ID_PREFIX + tzKey);
748
749 if (data instanceof String[]) {
750 names.put(tzid, data);
751 } else {
752 String meta = handlerMetaZones.get(tzKey);
753 if (meta != null) {
754 String metaKey = METAZONE_ID_PREFIX + meta;
755 data = map.get(metaKey);
756 if (data instanceof String[]) {
757 // Keep the metazone prefix here.
758 names.put(metaKey, data);
759 names.put(tzid, meta);
760 }
761 }
762 }
763 });
764
765 // exemplar cities.
766 Map<String, Object> exCities = map.entrySet().stream()
767 .filter(e -> e.getKey().startsWith(CLDRConverter.EXEMPLAR_CITY_PREFIX))
768 .collect(Collectors
769 .toMap(Map.Entry::getKey, Map.Entry::getValue));
770 names.putAll(exCities);
771
772 if (!id.equals("en") &&
773 !names.isEmpty()) {
774 // CLDR does not have UTC entry, so add it here.
775 names.put("UTC", EMPTY_ZONE);
776
777 // no metazone zones
778 Arrays.asList(handlerMetaZones.get(MetaZonesParseHandler.NO_METAZONE_KEY)
779 .split("\\s")).stream()
780 .forEach(tz -> {
781 names.put(tz, EMPTY_ZONE);
782 });
783 }
784
785 return names;
786 }
787
788 /**
789 * Extracts the language independent calendar data. Each of the two keys,
790 * "firstDayOfWeek" and "minimalDaysInFirstWeek" has a string value consists of
791 * one or multiple occurrences of:
792 * i: rg1 rg2 ... rgn;
793 * where "i" is the data for the following regions (delimited by a space) after
794 * ":", and ends with a ";".
795 */
796 private static Map<String, Object> extractCalendarData(Map<String, Object> map, String id) {
797 Map<String, Object> calendarData = new LinkedHashMap<>();
798 if (id.equals("root")) {
799 calendarData.put("firstDayOfWeek",
800 IntStream.range(1, 8)
801 .mapToObj(String::valueOf)
802 .filter(d -> map.keySet().contains(CALENDAR_FIRSTDAY_PREFIX + d))
803 .map(d -> d + ": " + map.get(CALENDAR_FIRSTDAY_PREFIX + d))
804 .collect(Collectors.joining(";")));
|
1 /*
2 * Copyright (c) 2012, 2020, 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 build.tools.cldrconverter.BundleGenerator.BundleType;
29 import java.io.File;
30 import java.io.IOException;
31 import java.io.UncheckedIOException;
32 import java.nio.file.*;
33 import java.text.MessageFormat;
34 import java.time.*;
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 java.util.stream.Stream;
42 import javax.xml.parsers.SAXParser;
43 import javax.xml.parsers.SAXParserFactory;
44 import org.xml.sax.SAXNotRecognizedException;
45 import org.xml.sax.SAXNotSupportedException;
46
47
71 private static String WINZONES_SOURCE_FILE;
72 private static String PLURALS_SOURCE_FILE;
73 static String DESTINATION_DIR = "build/gensrc";
74
75 static final String LOCALE_NAME_PREFIX = "locale.displayname.";
76 static final String LOCALE_SEPARATOR = LOCALE_NAME_PREFIX + "separator";
77 static final String LOCALE_KEYTYPE = LOCALE_NAME_PREFIX + "keytype";
78 static final String LOCALE_KEY_PREFIX = LOCALE_NAME_PREFIX + "key.";
79 static final String LOCALE_TYPE_PREFIX = LOCALE_NAME_PREFIX + "type.";
80 static final String LOCALE_TYPE_PREFIX_CA = LOCALE_TYPE_PREFIX + "ca.";
81 static final String CURRENCY_SYMBOL_PREFIX = "currency.symbol.";
82 static final String CURRENCY_NAME_PREFIX = "currency.displayname.";
83 static final String CALENDAR_NAME_PREFIX = "calendarname.";
84 static final String CALENDAR_FIRSTDAY_PREFIX = "firstDay.";
85 static final String CALENDAR_MINDAYS_PREFIX = "minDays.";
86 static final String TIMEZONE_ID_PREFIX = "timezone.id.";
87 static final String EXEMPLAR_CITY_PREFIX = "timezone.excity.";
88 static final String ZONE_NAME_PREFIX = "timezone.displayname.";
89 static final String METAZONE_ID_PREFIX = "metazone.id.";
90 static final String PARENT_LOCALE_PREFIX = "parentLocale.";
91 static final String META_EMPTY_ZONE_NAME = "EMPTY_ZONE";
92 static final String[] EMPTY_ZONE = {"", "", "", "", "", ""};
93 static final String META_ETCUTC_ZONE_NAME = "ETC_UTC";
94
95 // Old 3-letter-id mappings for compatibility. Copied from sun.util.calendar.ZoneInfoFile.
96 private static final String[][] oldMappings = new String[][] {
97 { "ACT", "Australia/Darwin" },
98 { "AET", "Australia/Sydney" },
99 { "AGT", "America/Argentina/Buenos_Aires" },
100 { "ART", "Africa/Cairo" },
101 { "AST", "America/Anchorage" },
102 { "BET", "America/Sao_Paulo" },
103 { "BST", "Asia/Dhaka" },
104 { "CAT", "Africa/Harare" },
105 { "CNT", "America/St_Johns" },
106 { "CST", "America/Chicago" },
107 { "CTT", "Asia/Shanghai" },
108 { "EAT", "Africa/Addis_Ababa" },
109 { "ECT", "Europe/Paris" },
110 { "IET", "America/Indiana/Indianapolis" },
111 { "IST", "Asia/Kolkata" },
112 { "JST", "Asia/Tokyo" },
113 { "MIT", "Pacific/Apia" },
114 { "NET", "Asia/Yerevan" },
115 { "NST", "Pacific/Auckland" },
116 { "PLT", "Asia/Karachi" },
117 { "PNT", "America/Phoenix" },
118 { "PRT", "America/Puerto_Rico" },
119 { "PST", "America/Los_Angeles" },
120 { "SST", "Pacific/Guadalcanal" },
121 { "VST", "Asia/Ho_Chi_Minh" },
122 };
123
124 private static SupplementDataParseHandler handlerSuppl;
125 private static LikelySubtagsParseHandler handlerLikelySubtags;
126 private static WinZonesParseHandler handlerWinZones;
127 static PluralsParseHandler handlerPlurals;
128 static SupplementalMetadataParseHandler handlerSupplMeta;
129 static NumberingSystemsParseHandler handlerNumbering;
130 static MetaZonesParseHandler handlerMetaZones;
131 static TimeZoneParseHandler handlerTimeZone;
132 private static BundleGenerator bundleGenerator;
133
134 // java.base module related
135 static boolean isBaseModule = false;
136 static final Set<Locale> BASE_LOCALES = new HashSet<>();
137
138 // "parentLocales" map
139 private static final Map<String, SortedSet<String>> parentLocalesMap = new HashMap<>();
140 private static final ResourceBundle.Control defCon =
141 ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT);
142
699 return localeNames;
700 }
701
702 @SuppressWarnings("AssignmentToForLoopParameter")
703 private static Map<String, Object> extractCurrencyNames(Map<String, Object> map, String id, String names)
704 throws Exception {
705 Map<String, Object> currencyNames = new TreeMap<>(KeyComparator.INSTANCE);
706 for (String key : map.keySet()) {
707 if (key.startsWith(CURRENCY_NAME_PREFIX)) {
708 currencyNames.put(key.substring(CURRENCY_NAME_PREFIX.length()), map.get(key));
709 } else if (key.startsWith(CURRENCY_SYMBOL_PREFIX)) {
710 currencyNames.put(key.substring(CURRENCY_SYMBOL_PREFIX.length()), map.get(key));
711 }
712 }
713 return currencyNames;
714 }
715
716 private static Map<String, Object> extractZoneNames(Map<String, Object> map, String id) {
717 Map<String, Object> names = new HashMap<>();
718
719 getAvailableZoneIds().stream().forEach(tzid -> {
720 // If the tzid is deprecated, get the data for the replacement id
721 String tzKey = Optional.ofNullable((String)handlerSupplMeta.get(tzid))
722 .orElse(tzid);
723 Object data = map.get(TIMEZONE_ID_PREFIX + tzKey);
724
725 if (data instanceof String[]) {
726 // Hack for UTC. UTC is an alias to Etc/UTC in CLDR
727 if (tzid.equals("Etc/UTC") && !map.containsKey(TIMEZONE_ID_PREFIX + "UTC")) {
728 names.put(METAZONE_ID_PREFIX + META_ETCUTC_ZONE_NAME, data);
729 names.put(tzid, META_ETCUTC_ZONE_NAME);
730 names.put("UTC", META_ETCUTC_ZONE_NAME);
731 } else {
732 names.put(tzid, data);
733 }
734 } else {
735 String meta = handlerMetaZones.get(tzKey);
736 if (meta != null) {
737 String metaKey = METAZONE_ID_PREFIX + meta;
738 data = map.get(metaKey);
739 if (data instanceof String[]) {
740 // Keep the metazone prefix here.
741 names.put(metaKey, data);
742 names.put(tzid, meta);
743 }
744 }
745 }
746 });
747
748 // exemplar cities.
749 Map<String, Object> exCities = map.entrySet().stream()
750 .filter(e -> e.getKey().startsWith(CLDRConverter.EXEMPLAR_CITY_PREFIX))
751 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
752 names.putAll(exCities);
753
754 // If there's no UTC entry at this point, add an empty one
755 if (!names.isEmpty() && !names.containsKey("UTC")) {
756 names.putIfAbsent(METAZONE_ID_PREFIX + META_EMPTY_ZONE_NAME, EMPTY_ZONE);
757 names.put("UTC", META_EMPTY_ZONE_NAME);
758 }
759
760 // Finally some compatibility stuff
761 Arrays.stream(oldMappings)
762 .filter(oldmap -> !names.containsKey(oldmap[0]) && names.containsKey(oldmap[1]))
763 .forEach(oldmap -> {
764 names.put(oldmap[0], names.get(oldmap[1]));
765 });
766
767 return names;
768 }
769
770 /**
771 * Extracts the language independent calendar data. Each of the two keys,
772 * "firstDayOfWeek" and "minimalDaysInFirstWeek" has a string value consists of
773 * one or multiple occurrences of:
774 * i: rg1 rg2 ... rgn;
775 * where "i" is the data for the following regions (delimited by a space) after
776 * ":", and ends with a ";".
777 */
778 private static Map<String, Object> extractCalendarData(Map<String, Object> map, String id) {
779 Map<String, Object> calendarData = new LinkedHashMap<>();
780 if (id.equals("root")) {
781 calendarData.put("firstDayOfWeek",
782 IntStream.range(1, 8)
783 .mapToObj(String::valueOf)
784 .filter(d -> map.keySet().contains(CALENDAR_FIRSTDAY_PREFIX + d))
785 .map(d -> d + ": " + map.get(CALENDAR_FIRSTDAY_PREFIX + d))
786 .collect(Collectors.joining(";")));
|