--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/make/src/classes/build/tools/cldrconverter/CLDRConverter.java Thu Nov 14 11:19:32 2013 +0100
@@ -0,0 +1,748 @@
+/*
+ * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package build.tools.cldrconverter;
+
+import build.tools.cldrconverter.BundleGenerator.BundleType;
+import java.io.File;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+
+
+/**
+ * Converts locale data from "Locale Data Markup Language" format to
+ * JRE resource bundle format. LDML is the format used by the Common
+ * Locale Data Repository maintained by the Unicode Consortium.
+ */
+public class CLDRConverter {
+
+ static final String LDML_DTD_SYSTEM_ID = "http://www.unicode.org/cldr/dtd/2.0/ldml.dtd";
+ static final String SPPL_LDML_DTD_SYSTEM_ID = "http://www.unicode.org/cldr/dtd/2.0/ldmlSupplemental.dtd";
+
+ private static String CLDR_BASE = "../CLDR/21.0.1/";
+ static String LOCAL_LDML_DTD;
+ static String LOCAL_SPPL_LDML_DTD;
+ private static String SOURCE_FILE_DIR;
+ private static String SPPL_SOURCE_FILE;
+ private static String NUMBERING_SOURCE_FILE;
+ private static String METAZONES_SOURCE_FILE;
+ static String DESTINATION_DIR = "build/gensrc";
+
+ static final String LOCALE_NAME_PREFIX = "locale.displayname.";
+ static final String CURRENCY_SYMBOL_PREFIX = "currency.symbol.";
+ static final String CURRENCY_NAME_PREFIX = "currency.displayname.";
+ static final String CALENDAR_NAME_PREFIX = "calendarname.";
+ static final String TIMEZONE_ID_PREFIX = "timezone.id.";
+ static final String ZONE_NAME_PREFIX = "timezone.displayname.";
+ static final String METAZONE_ID_PREFIX = "metazone.id.";
+
+ private static SupplementDataParseHandler handlerSuppl;
+ static NumberingSystemsParseHandler handlerNumbering;
+ static MetaZonesParseHandler handlerMetaZones;
+ private static BundleGenerator bundleGenerator;
+
+ static enum DraftType {
+ UNCONFIRMED,
+ PROVISIONAL,
+ CONTRIBUTED,
+ APPROVED;
+
+ private static final Map<String, DraftType> map = new HashMap<>();
+ static {
+ for (DraftType dt : values()) {
+ map.put(dt.getKeyword(), dt);
+ }
+ }
+ static private DraftType defaultType = CONTRIBUTED;
+
+ private final String keyword;
+
+ private DraftType() {
+ keyword = this.name().toLowerCase(Locale.ROOT);
+
+ }
+
+ static DraftType forKeyword(String keyword) {
+ return map.get(keyword);
+ }
+
+ static DraftType getDefault() {
+ return defaultType;
+ }
+
+ static void setDefault(String keyword) {
+ defaultType = Objects.requireNonNull(forKeyword(keyword));
+ }
+
+ String getKeyword() {
+ return keyword;
+ }
+ }
+
+ static boolean USE_UTF8 = false;
+ private static boolean verbose;
+
+ private CLDRConverter() {
+ // no instantiation
+ }
+
+ @SuppressWarnings("AssignmentToForLoopParameter")
+ public static void main(String[] args) throws Exception {
+ if (args.length != 0) {
+ String currentArg = null;
+ try {
+ for (int i = 0; i < args.length; i++) {
+ currentArg = args[i];
+ switch (currentArg) {
+ case "-draft":
+ String draftDataType = args[++i];
+ try {
+ DraftType.setDefault(draftDataType);
+ } catch (NullPointerException e) {
+ severe("Error: incorrect draft value: %s%n", draftDataType);
+ System.exit(1);
+ }
+ info("Using the specified data type: %s%n", draftDataType);
+ break;
+
+ case "-base":
+ // base directory for input files
+ CLDR_BASE = args[++i];
+ if (!CLDR_BASE.endsWith("/")) {
+ CLDR_BASE += "/";
+ }
+ break;
+
+ case "-o":
+ // output directory
+ DESTINATION_DIR = args[++i];
+ break;
+
+ case "-utf8":
+ USE_UTF8 = true;
+ break;
+
+ case "-verbose":
+ verbose = true;
+ break;
+
+ case "-help":
+ usage();
+ System.exit(0);
+ break;
+
+ default:
+ throw new RuntimeException();
+ }
+ }
+ } catch (RuntimeException e) {
+ severe("unknown or imcomplete arg(s): " + currentArg);
+ usage();
+ System.exit(1);
+ }
+ }
+
+ // Set up path names
+ LOCAL_LDML_DTD = CLDR_BASE + "common/dtd/ldml.dtd";
+ LOCAL_SPPL_LDML_DTD = CLDR_BASE + "common/dtd/ldmlSupplemental.dtd";
+ SOURCE_FILE_DIR = CLDR_BASE + "common/main";
+ SPPL_SOURCE_FILE = CLDR_BASE + "common/supplemental/supplementalData.xml";
+ NUMBERING_SOURCE_FILE = CLDR_BASE + "common/supplemental/numberingSystems.xml";
+ METAZONES_SOURCE_FILE = CLDR_BASE + "common/supplemental/metaZones.xml";
+
+ bundleGenerator = new ResourceBundleGenerator();
+
+ List<Bundle> bundles = readBundleList();
+ convertBundles(bundles);
+ }
+
+ private static void usage() {
+ errout("Usage: java CLDRConverter [options]%n"
+ + "\t-help output this usage message and exit%n"
+ + "\t-verbose output information%n"
+ + "\t-draft [approved | provisional | unconfirmed]%n"
+ + "\t\t draft level for using data (default: approved)%n"
+ + "\t-base dir base directory for CLDR input files%n"
+ + "\t-o dir output directory (defaut: ./build/gensrc)%n"
+ + "\t-utf8 use UTF-8 rather than \\uxxxx (for debug)%n");
+ }
+
+ static void info(String fmt, Object... args) {
+ if (verbose) {
+ System.out.printf(fmt, args);
+ }
+ }
+
+ static void info(String msg) {
+ if (verbose) {
+ System.out.println(msg);
+ }
+ }
+
+ static void warning(String fmt, Object... args) {
+ System.err.print("Warning: ");
+ System.err.printf(fmt, args);
+ }
+
+ static void warning(String msg) {
+ System.err.print("Warning: ");
+ errout(msg);
+ }
+
+ static void severe(String fmt, Object... args) {
+ System.err.print("Error: ");
+ System.err.printf(fmt, args);
+ }
+
+ static void severe(String msg) {
+ System.err.print("Error: ");
+ errout(msg);
+ }
+
+ private static void errout(String msg) {
+ if (msg.contains("%n")) {
+ System.err.printf(msg);
+ } else {
+ System.err.println(msg);
+ }
+ }
+
+ /**
+ * Configure the parser to allow access to DTDs on the file system.
+ */
+ private static void enableFileAccess(SAXParser parser) throws SAXNotSupportedException {
+ try {
+ parser.setProperty("http://javax.xml.XMLConstants/property/accessExternalDTD", "file");
+ } catch (SAXNotRecognizedException ignore) {
+ // property requires >= JAXP 1.5
+ }
+ }
+
+ private static List<Bundle> readBundleList() throws Exception {
+ ResourceBundle.Control defCon = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT);
+ List<Bundle> retList = new ArrayList<>();
+ Path path = FileSystems.getDefault().getPath(SOURCE_FILE_DIR);
+ try (DirectoryStream<Path> dirStr = Files.newDirectoryStream(path)) {
+ for (Path entry : dirStr) {
+ String fileName = entry.getFileName().toString();
+ if (fileName.endsWith(".xml")) {
+ String id = fileName.substring(0, fileName.indexOf('.'));
+ Locale cldrLoc = Locale.forLanguageTag(toLanguageTag(id));
+ List<Locale> candList = defCon.getCandidateLocales("", cldrLoc);
+ StringBuilder sb = new StringBuilder();
+ for (Locale loc : candList) {
+ if (!loc.equals(Locale.ROOT)) {
+ sb.append(toLocaleName(loc.toLanguageTag()));
+ sb.append(",");
+ }
+ }
+ if (sb.indexOf("root") == -1) {
+ sb.append("root");
+ }
+ Bundle b = new Bundle(id, sb.toString(), null, null);
+ // Insert the bundle for en at the top so that it will get
+ // processed first.
+ if ("en".equals(id)) {
+ retList.add(0, b);
+ } else {
+ retList.add(b);
+ }
+ }
+ }
+ }
+ return retList;
+ }
+
+ private static Map<String, Map<String, Object>> cldrBundles = new HashMap<>();
+
+ static Map<String, Object> getCLDRBundle(String id) throws Exception {
+ Map<String, Object> bundle = cldrBundles.get(id);
+ if (bundle != null) {
+ return bundle;
+ }
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setValidating(true);
+ SAXParser parser = factory.newSAXParser();
+ enableFileAccess(parser);
+ LDMLParseHandler handler = new LDMLParseHandler(id);
+ File file = new File(SOURCE_FILE_DIR + File.separator + id + ".xml");
+ if (!file.exists()) {
+ // Skip if the file doesn't exist.
+ return Collections.emptyMap();
+ }
+
+ info("..... main directory .....");
+ info("Reading file " + file);
+ parser.parse(file, handler);
+
+ bundle = handler.getData();
+ cldrBundles.put(id, bundle);
+ String country = getCountryCode(id);
+ if (country != null) {
+ bundle = handlerSuppl.getData(country);
+ if (bundle != null) {
+ //merge two maps into one map
+ Map<String, Object> temp = cldrBundles.remove(id);
+ bundle.putAll(temp);
+ cldrBundles.put(id, bundle);
+ }
+ }
+ return bundle;
+ }
+
+ private static void convertBundles(List<Bundle> bundles) throws Exception {
+ // Parse SupplementalData file and store the information in the HashMap
+ // Calendar information such as firstDay and minDay are stored in
+ // supplementalData.xml as of CLDR1.4. Individual territory is listed
+ // with its ISO 3166 country code while default is listed using UNM49
+ // region and composition numerical code (001 for World.)
+ SAXParserFactory factorySuppl = SAXParserFactory.newInstance();
+ factorySuppl.setValidating(true);
+ SAXParser parserSuppl = factorySuppl.newSAXParser();
+ enableFileAccess(parserSuppl);
+ handlerSuppl = new SupplementDataParseHandler();
+ File fileSupply = new File(SPPL_SOURCE_FILE);
+ parserSuppl.parse(fileSupply, handlerSuppl);
+
+ // Parse numberingSystems to get digit zero character information.
+ SAXParserFactory numberingParser = SAXParserFactory.newInstance();
+ numberingParser.setValidating(true);
+ SAXParser parserNumbering = numberingParser.newSAXParser();
+ enableFileAccess(parserNumbering);
+ handlerNumbering = new NumberingSystemsParseHandler();
+ File fileNumbering = new File(NUMBERING_SOURCE_FILE);
+ parserNumbering.parse(fileNumbering, handlerNumbering);
+
+ // Parse metaZones to create mappings between Olson tzids and CLDR meta zone names
+ SAXParserFactory metazonesParser = SAXParserFactory.newInstance();
+ metazonesParser.setValidating(true);
+ SAXParser parserMetaZones = metazonesParser.newSAXParser();
+ enableFileAccess(parserMetaZones);
+ handlerMetaZones = new MetaZonesParseHandler();
+ File fileMetaZones = new File(METAZONES_SOURCE_FILE);
+ parserNumbering.parse(fileMetaZones, handlerMetaZones);
+
+ // For generating information on supported locales.
+ Map<String, SortedSet<String>> metaInfo = new HashMap<>();
+ metaInfo.put("LocaleNames", new TreeSet<String>());
+ metaInfo.put("CurrencyNames", new TreeSet<String>());
+ metaInfo.put("TimeZoneNames", new TreeSet<String>());
+ metaInfo.put("CalendarData", new TreeSet<String>());
+ metaInfo.put("FormatData", new TreeSet<String>());
+
+ for (Bundle bundle : bundles) {
+ // Get the target map, which contains all the data that should be
+ // visible for the bundle's locale
+
+ Map<String, Object> targetMap = bundle.getTargetMap();
+
+ EnumSet<Bundle.Type> bundleTypes = bundle.getBundleTypes();
+
+ // Fill in any missing resources in the base bundle from en and en-US data.
+ // This is because CLDR root.xml is supposed to be language neutral and doesn't
+ // provide some resource data. Currently, the runtime assumes that there are all
+ // resources though the parent resource bundle chain.
+ if (bundle.isRoot()) {
+ Map<String, Object> enData = new HashMap<>();
+ // Create a superset of en-US and en bundles data in order to
+ // fill in any missing resources in the base bundle.
+ enData.putAll(Bundle.getBundle("en").getTargetMap());
+ enData.putAll(Bundle.getBundle("en_US").getTargetMap());
+ for (String key : enData.keySet()) {
+ if (!targetMap.containsKey(key)) {
+ targetMap.put(key, enData.get(key));
+ }
+ }
+ // Add DateTimePatternChars because CLDR no longer supports localized patterns.
+ targetMap.put("DateTimePatternChars", "GyMdkHmsSEDFwWahKzZ");
+ }
+
+ // Now the map contains just the entries that need to be in the resources bundles.
+ // Go ahead and generate them.
+ if (bundleTypes.contains(Bundle.Type.LOCALENAMES)) {
+ Map<String, Object> localeNamesMap = extractLocaleNames(targetMap, bundle.getID());
+ if (!localeNamesMap.isEmpty() || bundle.isRoot()) {
+ metaInfo.get("LocaleNames").add(toLanguageTag(bundle.getID()));
+ bundleGenerator.generateBundle("util", "LocaleNames", bundle.getID(), true, localeNamesMap, BundleType.OPEN);
+ }
+ }
+ if (bundleTypes.contains(Bundle.Type.CURRENCYNAMES)) {
+ Map<String, Object> currencyNamesMap = extractCurrencyNames(targetMap, bundle.getID(), bundle.getCurrencies());
+ if (!currencyNamesMap.isEmpty() || bundle.isRoot()) {
+ metaInfo.get("CurrencyNames").add(toLanguageTag(bundle.getID()));
+ bundleGenerator.generateBundle("util", "CurrencyNames", bundle.getID(), true, currencyNamesMap, BundleType.OPEN);
+ }
+ }
+ if (bundleTypes.contains(Bundle.Type.TIMEZONENAMES)) {
+ Map<String, Object> zoneNamesMap = extractZoneNames(targetMap, bundle.getID());
+ if (!zoneNamesMap.isEmpty() || bundle.isRoot()) {
+ metaInfo.get("TimeZoneNames").add(toLanguageTag(bundle.getID()));
+ bundleGenerator.generateBundle("util", "TimeZoneNames", bundle.getID(), true, zoneNamesMap, BundleType.TIMEZONE);
+ }
+ }
+ if (bundleTypes.contains(Bundle.Type.CALENDARDATA)) {
+ Map<String, Object> calendarDataMap = extractCalendarData(targetMap, bundle.getID());
+ if (!calendarDataMap.isEmpty() || bundle.isRoot()) {
+ metaInfo.get("CalendarData").add(toLanguageTag(bundle.getID()));
+ bundleGenerator.generateBundle("util", "CalendarData", bundle.getID(), true, calendarDataMap, BundleType.PLAIN);
+ }
+ }
+ if (bundleTypes.contains(Bundle.Type.FORMATDATA)) {
+ Map<String, Object> formatDataMap = extractFormatData(targetMap, bundle.getID());
+ // LocaleData.getAvailableLocales depends on having FormatData bundles around
+ if (!formatDataMap.isEmpty() || bundle.isRoot()) {
+ metaInfo.get("FormatData").add(toLanguageTag(bundle.getID()));
+ bundleGenerator.generateBundle("text", "FormatData", bundle.getID(), true, formatDataMap, BundleType.PLAIN);
+ }
+ }
+
+ // For testing
+ SortedSet<String> allLocales = new TreeSet<>();
+ allLocales.addAll(metaInfo.get("CurrencyNames"));
+ allLocales.addAll(metaInfo.get("LocaleNames"));
+ allLocales.addAll(metaInfo.get("CalendarData"));
+ allLocales.addAll(metaInfo.get("FormatData"));
+ metaInfo.put("All", allLocales);
+ }
+
+ bundleGenerator.generateMetaInfo(metaInfo);
+ }
+
+ /*
+ * Returns the language portion of the given id.
+ * If id is "root", "" is returned.
+ */
+ static String getLanguageCode(String id) {
+ int index = id.indexOf('_');
+ String lang = null;
+ if (index != -1) {
+ lang = id.substring(0, index);
+ } else {
+ lang = "root".equals(id) ? "" : id;
+ }
+ return lang;
+ }
+
+ /**
+ * Examine if the id includes the country (territory) code. If it does, it returns
+ * the country code.
+ * Otherwise, it returns null. eg. when the id is "zh_Hans_SG", it return "SG".
+ */
+ private static String getCountryCode(String id) {
+ //Truncate a variant code with '@' if there is any
+ //(eg. de_DE@collation=phonebook,currency=DOM)
+ if (id.indexOf('@') != -1) {
+ id = id.substring(0, id.indexOf('@'));
+ }
+ String[] tokens = id.split("_");
+ for (int index = 1; index < tokens.length; ++index) {
+ if (tokens[index].length() == 2
+ && Character.isLetter(tokens[index].charAt(0))
+ && Character.isLetter(tokens[index].charAt(1))) {
+ return tokens[index];
+ }
+ }
+ return null;
+ }
+
+ private static class KeyComparator implements Comparator<String> {
+ static KeyComparator INSTANCE = new KeyComparator();
+
+ private KeyComparator() {
+ }
+
+ @Override
+ public int compare(String o1, String o2) {
+ int len1 = o1.length();
+ int len2 = o2.length();
+ if (!isDigit(o1.charAt(0)) && !isDigit(o2.charAt(0))) {
+ // Shorter string comes first unless either starts with a digit.
+ if (len1 < len2) {
+ return -1;
+ }
+ if (len1 > len2) {
+ return 1;
+ }
+ }
+ return o1.compareTo(o2);
+ }
+
+ private boolean isDigit(char c) {
+ return c >= '0' && c <= '9';
+ }
+ }
+
+ private static Map<String, Object> extractLocaleNames(Map<String, Object> map, String id) {
+ Map<String, Object> localeNames = new TreeMap<>(KeyComparator.INSTANCE);
+ for (String key : map.keySet()) {
+ if (key.startsWith(LOCALE_NAME_PREFIX)) {
+ localeNames.put(key.substring(LOCALE_NAME_PREFIX.length()), map.get(key));
+ }
+ }
+ return localeNames;
+ }
+
+ @SuppressWarnings("AssignmentToForLoopParameter")
+ private static Map<String, Object> extractCurrencyNames(Map<String, Object> map, String id, String names)
+ throws Exception {
+ Map<String, Object> currencyNames = new TreeMap<>(KeyComparator.INSTANCE);
+ for (String key : map.keySet()) {
+ if (key.startsWith(CURRENCY_NAME_PREFIX)) {
+ currencyNames.put(key.substring(CURRENCY_NAME_PREFIX.length()), map.get(key));
+ } else if (key.startsWith(CURRENCY_SYMBOL_PREFIX)) {
+ currencyNames.put(key.substring(CURRENCY_SYMBOL_PREFIX.length()), map.get(key));
+ }
+ }
+ return currencyNames;
+ }
+
+ private static Map<String, Object> extractZoneNames(Map<String, Object> map, String id) {
+ Map<String, Object> names = new HashMap<>();
+ for (String tzid : handlerMetaZones.keySet()) {
+ String tzKey = TIMEZONE_ID_PREFIX + tzid;
+ Object data = map.get(tzKey);
+ if (data instanceof String[]) {
+ names.put(tzid, data);
+ } else {
+ String meta = handlerMetaZones.get(tzid);
+ if (meta != null) {
+ String metaKey = METAZONE_ID_PREFIX + meta;
+ data = map.get(metaKey);
+ if (data instanceof String[]) {
+ // Keep the metazone prefix here.
+ names.put(metaKey, data);
+ names.put(tzid, meta);
+ }
+ }
+ }
+ }
+ return names;
+ }
+
+ private static Map<String, Object> extractCalendarData(Map<String, Object> map, String id) {
+ Map<String, Object> calendarData = new LinkedHashMap<>();
+ copyIfPresent(map, "firstDayOfWeek", calendarData);
+ copyIfPresent(map, "minimalDaysInFirstWeek", calendarData);
+ return calendarData;
+ }
+
+ static final String[] FORMAT_DATA_ELEMENTS = {
+ "MonthNames",
+ "standalone.MonthNames",
+ "MonthAbbreviations",
+ "standalone.MonthAbbreviations",
+ "MonthNarrows",
+ "standalone.MonthNarrows",
+ "DayNames",
+ "standalone.DayNames",
+ "DayAbbreviations",
+ "standalone.DayAbbreviations",
+ "DayNarrows",
+ "standalone.DayNarrows",
+ "QuarterNames",
+ "standalone.QuarterNames",
+ "QuarterAbbreviations",
+ "standalone.QuarterAbbreviations",
+ "QuarterNarrows",
+ "standalone.QuarterNarrows",
+ "AmPmMarkers",
+ "narrow.AmPmMarkers",
+ "long.Eras",
+ "Eras",
+ "narrow.Eras",
+ "field.era",
+ "field.year",
+ "field.month",
+ "field.week",
+ "field.weekday",
+ "field.dayperiod",
+ "field.hour",
+ "field.minute",
+ "field.second",
+ "field.zone",
+ "TimePatterns",
+ "DatePatterns",
+ "DateTimePatterns",
+ "DateTimePatternChars"
+ };
+
+ private static Map<String, Object> extractFormatData(Map<String, Object> map, String id) {
+ Map<String, Object> formatData = new LinkedHashMap<>();
+ for (CalendarType calendarType : CalendarType.values()) {
+ String prefix = calendarType.keyElementName();
+ for (String element : FORMAT_DATA_ELEMENTS) {
+ String key = prefix + element;
+ copyIfPresent(map, "java.time." + key, formatData);
+ copyIfPresent(map, key, formatData);
+ }
+ }
+ // Workaround for islamic-umalqura name support (JDK-8015986)
+ switch (id) {
+ case "ar":
+ map.put(CLDRConverter.CALENDAR_NAME_PREFIX
+ + CalendarType.ISLAMIC_UMALQURA.lname(),
+ // derived from CLDR 24 draft
+ "\u0627\u0644\u062a\u0642\u0648\u064a\u0645 "
+ +"\u0627\u0644\u0625\u0633\u0644\u0627\u0645\u064a "
+ +"[\u0623\u0645 \u0627\u0644\u0642\u0631\u0649]");
+ break;
+ case "en":
+ map.put(CLDRConverter.CALENDAR_NAME_PREFIX
+ + CalendarType.ISLAMIC_UMALQURA.lname(),
+ // derived from CLDR 24 draft
+ "Islamic Calendar [Umm al-Qura]");
+ break;
+ }
+ // Copy available calendar names
+ for (String key : map.keySet()) {
+ if (key.startsWith(CLDRConverter.CALENDAR_NAME_PREFIX)) {
+ String type = key.substring(CLDRConverter.CALENDAR_NAME_PREFIX.length());
+ for (CalendarType calendarType : CalendarType.values()) {
+ if (type.equals(calendarType.lname())) {
+ Object value = map.get(key);
+ formatData.put(key, value);
+ String ukey = CLDRConverter.CALENDAR_NAME_PREFIX + calendarType.uname();
+ if (!key.equals(ukey)) {
+ formatData.put(ukey, value);
+ }
+ }
+ }
+ }
+ }
+
+ copyIfPresent(map, "DefaultNumberingSystem", formatData);
+
+ @SuppressWarnings("unchecked")
+ List<String> numberingScripts = (List<String>) map.remove("numberingScripts");
+ if (numberingScripts != null) {
+ for (String script : numberingScripts) {
+ copyIfPresent(map, script + "." + "NumberElements", formatData);
+ }
+ } else {
+ copyIfPresent(map, "NumberElements", formatData);
+ }
+ copyIfPresent(map, "NumberPatterns", formatData);
+ return formatData;
+ }
+
+ private static void copyIfPresent(Map<String, Object> src, String key, Map<String, Object> dest) {
+ Object value = src.get(key);
+ if (value != null) {
+ dest.put(key, value);
+ }
+ }
+
+ // --- code below here is adapted from java.util.Properties ---
+ private static final String specialSaveCharsJava = "\"";
+ private static final String specialSaveCharsProperties = "=: \t\r\n\f#!";
+
+ /*
+ * Converts unicodes to encoded \uxxxx
+ * and writes out any of the characters in specialSaveChars
+ * with a preceding slash
+ */
+ static String saveConvert(String theString, boolean useJava) {
+ if (theString == null) {
+ return "";
+ }
+
+ String specialSaveChars;
+ if (useJava) {
+ specialSaveChars = specialSaveCharsJava;
+ } else {
+ specialSaveChars = specialSaveCharsProperties;
+ }
+ boolean escapeSpace = false;
+
+ int len = theString.length();
+ StringBuilder outBuffer = new StringBuilder(len * 2);
+ Formatter formatter = new Formatter(outBuffer, Locale.ROOT);
+
+ for (int x = 0; x < len; x++) {
+ char aChar = theString.charAt(x);
+ switch (aChar) {
+ case ' ':
+ if (x == 0 || escapeSpace) {
+ outBuffer.append('\\');
+ }
+ outBuffer.append(' ');
+ break;
+ case '\\':
+ outBuffer.append('\\');
+ outBuffer.append('\\');
+ break;
+ case '\t':
+ outBuffer.append('\\');
+ outBuffer.append('t');
+ break;
+ case '\n':
+ outBuffer.append('\\');
+ outBuffer.append('n');
+ break;
+ case '\r':
+ outBuffer.append('\\');
+ outBuffer.append('r');
+ break;
+ case '\f':
+ outBuffer.append('\\');
+ outBuffer.append('f');
+ break;
+ default:
+ if (aChar < 0x0020 || (!USE_UTF8 && aChar > 0x007e)) {
+ formatter.format("\\u%04x", (int)aChar);
+ } else {
+ if (specialSaveChars.indexOf(aChar) != -1) {
+ outBuffer.append('\\');
+ }
+ outBuffer.append(aChar);
+ }
+ }
+ }
+ return outBuffer.toString();
+ }
+
+ private static String toLanguageTag(String locName) {
+ if (locName.indexOf('_') == -1) {
+ return locName;
+ }
+ String tag = locName.replaceAll("_", "-");
+ Locale loc = Locale.forLanguageTag(tag);
+ return loc.toLanguageTag();
+ }
+
+ private static String toLocaleName(String tag) {
+ if (tag.indexOf('-') == -1) {
+ return tag;
+ }
+ return tag.replaceAll("-", "_");
+ }
+}