Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8247781: Day periods support #938

Closed
wants to merge 18 commits into from
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -375,6 +375,16 @@ String getCurrencies() {
}
}

// rules
String rule = CLDRConverter.pluralRules.get(id);
if (rule != null) {
myMap.put("PluralRules", rule);
}
rule = CLDRConverter.dayPeriodRules.get(id);
if (rule != null) {
myMap.put("DayPeriodRules", rule);
}

// Remove all duplicates
if (Objects.nonNull(parentsMap)) {
for (Iterator<String> it = myMap.keySet().iterator(); it.hasNext();) {
@@ -522,8 +532,6 @@ private void handleDateTimeFormatPatterns(String[] patternKeys, Map<String, Obje
if (pattern != null) {
// Perform date-time format pattern conversion which is
// applicable to both SimpleDateFormat and j.t.f.DateTimeFormatter.
// For example, character 'B' is mapped with 'a', as 'B' is not
// supported in either SimpleDateFormat or j.t.f.DateTimeFormatter
String transPattern = translateDateFormatLetters(calendarType, pattern, this::convertDateTimePatternLetter);
dateTimePatterns.add(i, transPattern);
// Additionally, perform SDF specific date-time format pattern conversion
@@ -653,17 +661,6 @@ private void convertDateTimePatternLetter(CalendarType calendarType, char cldrLe
// as the best approximation
appendN('y', count, sb);
break;
case 'B':
// 'B' character (day period) is not supported by
// SimpleDateFormat and j.t.f.DateTimeFormatter,
// this is a workaround in which 'B' character
// appearing in CLDR date-time pattern is replaced
// with 'a' character and hence resolved with am/pm strings.
// This workaround is based on the the fallback mechanism
// specified in LDML spec for 'B' character, when a locale
// does not have data for day period ('B')
appendN('a', count, sb);
break;
default:
appendN(cldrLetter, count, sb);
break;
@@ -720,6 +717,17 @@ private void convertSDFLetter(CalendarType calendarType, char cldrLetter, int co
}
break;

case 'B':
// 'B' character (day period) is not supported by SimpleDateFormat,
// this is a workaround in which 'B' character
// appearing in CLDR date-time pattern is replaced
// with 'a' character and hence resolved with am/pm strings.
// This workaround is based on the the fallback mechanism
// specified in LDML spec for 'B' character, when a locale
// does not have data for day period ('B')
appendN('a', count, sb);
break;

default:
appendN(cldrLetter, count, sb);
break;
@@ -70,6 +70,7 @@
private static String TIMEZONE_SOURCE_FILE;
private static String WINZONES_SOURCE_FILE;
private static String PLURALS_SOURCE_FILE;
private static String DAYPERIODRULE_SOURCE_FILE;
static String DESTINATION_DIR = "build/gensrc";

static final String LOCALE_NAME_PREFIX = "locale.displayname.";
@@ -100,6 +101,7 @@
static NumberingSystemsParseHandler handlerNumbering;
static MetaZonesParseHandler handlerMetaZones;
static TimeZoneParseHandler handlerTimeZone;
static DayPeriodRuleParseHandler handlerDayPeriodRule;
private static BundleGenerator bundleGenerator;

// java.base module related
@@ -116,6 +118,10 @@
private static String tzDataDir;
private static final Map<String, String> canonicalTZMap = new HashMap<>();

// rules maps
static Map<String, String> pluralRules;
static Map<String, String> dayPeriodRules;

static enum DraftType {
UNCONFIRMED,
PROVISIONAL,
@@ -248,6 +254,7 @@ public static void main(String[] args) throws Exception {
SPPL_META_SOURCE_FILE = CLDR_BASE + "/supplemental/supplementalMetadata.xml";
WINZONES_SOURCE_FILE = CLDR_BASE + "/supplemental/windowsZones.xml";
PLURALS_SOURCE_FILE = CLDR_BASE + "/supplemental/plurals.xml";
DAYPERIODRULE_SOURCE_FILE = CLDR_BASE + "/supplemental/dayPeriods.xml";

if (BASE_LOCALES.isEmpty()) {
setupBaseLocales("en-US");
@@ -259,6 +266,10 @@ public static void main(String[] args) throws Exception {
parseSupplemental();
parseBCP47();

// rules maps
pluralRules = generateRules(handlerPlurals);
dayPeriodRules = generateRules(handlerDayPeriodRule);

List<Bundle> bundles = readBundleList();
convertBundles(bundles);

@@ -268,9 +279,6 @@ public static void main(String[] args) throws Exception {

// Generate Windows tzmappings
generateWindowsTZMappings();

// Generate Plural rules
generatePluralRules();
}
}

@@ -462,6 +470,10 @@ private static void parseSupplemental() throws Exception {
// Parse plurals
handlerPlurals = new PluralsParseHandler();
parseLDMLFile(new File(PLURALS_SOURCE_FILE), handlerPlurals);

// Parse day period rules
handlerDayPeriodRule = new DayPeriodRuleParseHandler();
parseLDMLFile(new File(DAYPERIODRULE_SOURCE_FILE), handlerDayPeriodRule);
}

// Parsers for data in "bcp47" directory
@@ -809,7 +821,9 @@ private boolean isDigit(char c) {
"TimePatterns",
"DatePatterns",
"DateTimePatterns",
"DateTimePatternChars"
"DateTimePatternChars",
"PluralRules",
"DayPeriodRules",
};

private static Map<String, Object> extractFormatData(Map<String, Object> map, String id) {
@@ -1125,49 +1139,21 @@ public int compare(String t1, String t2) {
}

/**
* Generate ResourceBundle source file for plural rules. The generated
* class is {@code sun.text.resources.PluralRules} which has one public
* two dimensional array {@code rulesArray}. Each array element consists
* of two elements that designate the locale and the locale's plural rules
* string. The latter has the syntax from Unicode Consortium's
* <a href="http://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax">
* Plural rules syntax</a>. {@code samples} and {@code "other"} are being ommited.
*
* @throws Exception
* Generates rules map for Plural rules and DayPeriod rules. The key is the locale id,
* and the value is rules, defined by the LDML spec. Each rule consists of {@code type:rule}
* notation, concatenated with a ";" as a delimiter.
* @param handler handler containing rules
* @return the map
*/
private static void generatePluralRules() throws Exception {
Files.createDirectories(Paths.get(DESTINATION_DIR, "sun", "text", "resources"));
Files.write(Paths.get(DESTINATION_DIR, "sun", "text", "resources", "PluralRules.java"),
Stream.concat(
Stream.concat(
Stream.of(
"package sun.text.resources;",
"public final class PluralRules {",
" public static final String[][] rulesArray = {"
),
pluralRulesStream().sorted()
),
Stream.of(
" };",
"}"
)
)
.collect(Collectors.toList()),
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
}

private static Stream<String> pluralRulesStream() {
return handlerPlurals.getData().entrySet().stream()
.filter(e -> !(e.getValue()).isEmpty())
.map(e -> {
String loc = e.getKey();
private static Map<String, String> generateRules(AbstractLDMLHandler<Map<String, String>> handler) {
return handler.getData().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> {
Map<String, String> rules = e.getValue();
return " {\"" + loc + "\", \"" +
rules.entrySet().stream()
.map(rule -> rule.getKey() + ":" + rule.getValue().replaceFirst("@.*", ""))
.map(String::trim)
.collect(Collectors.joining(";")) + "\"},";
});
return rules.entrySet().stream()
.map(rule -> rule.getKey() + ":" + rule.getValue().replaceFirst("@.*", ""))
.map(String::trim)
.collect(Collectors.joining(";"));
}));
}

// for debug
@@ -1188,4 +1174,3 @@ static void dumpMap(Map<String, Object> map) {
.forEach(System.out::println);
}
}

@@ -0,0 +1,119 @@
/*
* Copyright (c) 2020, 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 java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
* Handles parsing of dayPeriods.xml for day period rules.
*/

class DayPeriodRuleParseHandler extends AbstractLDMLHandler<Map<String, String>> {

@Override
public InputSource resolveEntity(String publicID, String systemID) throws IOException, SAXException {
// avoid HTTP traffic to unicode.org
if (systemID.startsWith(CLDRConverter.SPPL_LDML_DTD_SYSTEM_ID)) {
return new InputSource((new File(CLDRConverter.LOCAL_SPPL_LDML_DTD)).toURI().toString());
}
return null;
}

@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
switch (qName) {
case "dayPeriodRuleSet":
if (attributes.getValue("type") != null) {
// ignore "selection"
pushIgnoredContainer(qName);
} else {
pushContainer(qName, attributes);
}
break;
case "dayPeriodRules":
if (!isIgnored(attributes)) {
pushKeyContainer(qName, attributes, attributes.getValue("locales"));
} else {
pushIgnoredContainer(qName);
}
break;
case "dayPeriodRule":
if (!isIgnored(attributes) && currentContainer instanceof KeyContainer) {
String at = attributes.getValue("at");
String from = attributes.getValue("from");
String before = attributes.getValue("before");
String output = "";
if (at == null || at.isEmpty()) {
output = from + "-" + before;
} else {
output = at;
}
pushStringEntry(qName, attributes, attributes.getValue("type"), output);
} else {
pushIgnoredContainer(qName);
}
break;
default:
// treat anything else as a container
pushContainer(qName, attributes);
break;
}
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
assert qName.equals(currentContainer.getqName()) : "current=" + currentContainer.getqName() + ", param=" + qName;
switch (qName) {
case "dayPeriodRule":
if (currentContainer instanceof Entry) {
Entry<?> entry = (Entry<?>) currentContainer;
final String type = entry.getKey();
final String rule = (String) entry.getValue();
String locales = ((KeyContainer) (currentContainer.getParent())).getKey();
Arrays.stream(locales.split("\\s"))
.forEach(loc -> {
Map<String, String> rules = get(loc);
if (rules == null) {
rules = new HashMap<>();
put(loc, rules);
}
rules.put(type, rule);
});
}
break;
}

currentContainer = currentContainer.getParent();
}
}