Skip to content

Commit bf84dac

Browse files
committed
8247781: Day periods support
Reviewed-by: scolebourne, rriggs, joehw
1 parent 0357db3 commit bf84dac

20 files changed

+1156
-204
lines changed

make/jdk/src/classes/build/tools/cldrconverter/Bundle.java

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,16 @@ Map<String, Object> getTargetMap() throws Exception {
375375
}
376376
}
377377

378+
// rules
379+
String rule = CLDRConverter.pluralRules.get(id);
380+
if (rule != null) {
381+
myMap.put("PluralRules", rule);
382+
}
383+
rule = CLDRConverter.dayPeriodRules.get(id);
384+
if (rule != null) {
385+
myMap.put("DayPeriodRules", rule);
386+
}
387+
378388
// Remove all duplicates
379389
if (Objects.nonNull(parentsMap)) {
380390
for (Iterator<String> it = myMap.keySet().iterator(); it.hasNext();) {
@@ -522,8 +532,6 @@ private void handleDateTimeFormatPatterns(String[] patternKeys, Map<String, Obje
522532
if (pattern != null) {
523533
// Perform date-time format pattern conversion which is
524534
// applicable to both SimpleDateFormat and j.t.f.DateTimeFormatter.
525-
// For example, character 'B' is mapped with 'a', as 'B' is not
526-
// supported in either SimpleDateFormat or j.t.f.DateTimeFormatter
527535
String transPattern = translateDateFormatLetters(calendarType, pattern, this::convertDateTimePatternLetter);
528536
dateTimePatterns.add(i, transPattern);
529537
// Additionally, perform SDF specific date-time format pattern conversion
@@ -653,17 +661,6 @@ private void convertDateTimePatternLetter(CalendarType calendarType, char cldrLe
653661
// as the best approximation
654662
appendN('y', count, sb);
655663
break;
656-
case 'B':
657-
// 'B' character (day period) is not supported by
658-
// SimpleDateFormat and j.t.f.DateTimeFormatter,
659-
// this is a workaround in which 'B' character
660-
// appearing in CLDR date-time pattern is replaced
661-
// with 'a' character and hence resolved with am/pm strings.
662-
// This workaround is based on the the fallback mechanism
663-
// specified in LDML spec for 'B' character, when a locale
664-
// does not have data for day period ('B')
665-
appendN('a', count, sb);
666-
break;
667664
default:
668665
appendN(cldrLetter, count, sb);
669666
break;
@@ -720,6 +717,17 @@ private void convertSDFLetter(CalendarType calendarType, char cldrLetter, int co
720717
}
721718
break;
722719

720+
case 'B':
721+
// 'B' character (day period) is not supported by SimpleDateFormat,
722+
// this is a workaround in which 'B' character
723+
// appearing in CLDR date-time pattern is replaced
724+
// with 'a' character and hence resolved with am/pm strings.
725+
// This workaround is based on the the fallback mechanism
726+
// specified in LDML spec for 'B' character, when a locale
727+
// does not have data for day period ('B')
728+
appendN('a', count, sb);
729+
break;
730+
723731
default:
724732
appendN(cldrLetter, count, sb);
725733
break;

make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java

Lines changed: 31 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public class CLDRConverter {
7070
private static String TIMEZONE_SOURCE_FILE;
7171
private static String WINZONES_SOURCE_FILE;
7272
private static String PLURALS_SOURCE_FILE;
73+
private static String DAYPERIODRULE_SOURCE_FILE;
7374
static String DESTINATION_DIR = "build/gensrc";
7475

7576
static final String LOCALE_NAME_PREFIX = "locale.displayname.";
@@ -100,6 +101,7 @@ public class CLDRConverter {
100101
static NumberingSystemsParseHandler handlerNumbering;
101102
static MetaZonesParseHandler handlerMetaZones;
102103
static TimeZoneParseHandler handlerTimeZone;
104+
static DayPeriodRuleParseHandler handlerDayPeriodRule;
103105
private static BundleGenerator bundleGenerator;
104106

105107
// java.base module related
@@ -116,6 +118,10 @@ public class CLDRConverter {
116118
private static String tzDataDir;
117119
private static final Map<String, String> canonicalTZMap = new HashMap<>();
118120

121+
// rules maps
122+
static Map<String, String> pluralRules;
123+
static Map<String, String> dayPeriodRules;
124+
119125
static enum DraftType {
120126
UNCONFIRMED,
121127
PROVISIONAL,
@@ -248,6 +254,7 @@ public static void main(String[] args) throws Exception {
248254
SPPL_META_SOURCE_FILE = CLDR_BASE + "/supplemental/supplementalMetadata.xml";
249255
WINZONES_SOURCE_FILE = CLDR_BASE + "/supplemental/windowsZones.xml";
250256
PLURALS_SOURCE_FILE = CLDR_BASE + "/supplemental/plurals.xml";
257+
DAYPERIODRULE_SOURCE_FILE = CLDR_BASE + "/supplemental/dayPeriods.xml";
251258

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

269+
// rules maps
270+
pluralRules = generateRules(handlerPlurals);
271+
dayPeriodRules = generateRules(handlerDayPeriodRule);
272+
262273
List<Bundle> bundles = readBundleList();
263274
convertBundles(bundles);
264275

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

269280
// Generate Windows tzmappings
270281
generateWindowsTZMappings();
271-
272-
// Generate Plural rules
273-
generatePluralRules();
274282
}
275283
}
276284

@@ -462,6 +470,10 @@ private static void parseSupplemental() throws Exception {
462470
// Parse plurals
463471
handlerPlurals = new PluralsParseHandler();
464472
parseLDMLFile(new File(PLURALS_SOURCE_FILE), handlerPlurals);
473+
474+
// Parse day period rules
475+
handlerDayPeriodRule = new DayPeriodRuleParseHandler();
476+
parseLDMLFile(new File(DAYPERIODRULE_SOURCE_FILE), handlerDayPeriodRule);
465477
}
466478

467479
// Parsers for data in "bcp47" directory
@@ -809,7 +821,9 @@ private static Map<String, Object> extractCalendarData(Map<String, Object> map,
809821
"TimePatterns",
810822
"DatePatterns",
811823
"DateTimePatterns",
812-
"DateTimePatternChars"
824+
"DateTimePatternChars",
825+
"PluralRules",
826+
"DayPeriodRules",
813827
};
814828

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

11271141
/**
1128-
* Generate ResourceBundle source file for plural rules. The generated
1129-
* class is {@code sun.text.resources.PluralRules} which has one public
1130-
* two dimensional array {@code rulesArray}. Each array element consists
1131-
* of two elements that designate the locale and the locale's plural rules
1132-
* string. The latter has the syntax from Unicode Consortium's
1133-
* <a href="http://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax">
1134-
* Plural rules syntax</a>. {@code samples} and {@code "other"} are being ommited.
1135-
*
1136-
* @throws Exception
1142+
* Generates rules map for Plural rules and DayPeriod rules. The key is the locale id,
1143+
* and the value is rules, defined by the LDML spec. Each rule consists of {@code type:rule}
1144+
* notation, concatenated with a ";" as a delimiter.
1145+
* @param handler handler containing rules
1146+
* @return the map
11371147
*/
1138-
private static void generatePluralRules() throws Exception {
1139-
Files.createDirectories(Paths.get(DESTINATION_DIR, "sun", "text", "resources"));
1140-
Files.write(Paths.get(DESTINATION_DIR, "sun", "text", "resources", "PluralRules.java"),
1141-
Stream.concat(
1142-
Stream.concat(
1143-
Stream.of(
1144-
"package sun.text.resources;",
1145-
"public final class PluralRules {",
1146-
" public static final String[][] rulesArray = {"
1147-
),
1148-
pluralRulesStream().sorted()
1149-
),
1150-
Stream.of(
1151-
" };",
1152-
"}"
1153-
)
1154-
)
1155-
.collect(Collectors.toList()),
1156-
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
1157-
}
1158-
1159-
private static Stream<String> pluralRulesStream() {
1160-
return handlerPlurals.getData().entrySet().stream()
1161-
.filter(e -> !(e.getValue()).isEmpty())
1162-
.map(e -> {
1163-
String loc = e.getKey();
1148+
private static Map<String, String> generateRules(AbstractLDMLHandler<Map<String, String>> handler) {
1149+
return handler.getData().entrySet().stream()
1150+
.collect(Collectors.toMap(Map.Entry::getKey, e -> {
11641151
Map<String, String> rules = e.getValue();
1165-
return " {\"" + loc + "\", \"" +
1166-
rules.entrySet().stream()
1167-
.map(rule -> rule.getKey() + ":" + rule.getValue().replaceFirst("@.*", ""))
1168-
.map(String::trim)
1169-
.collect(Collectors.joining(";")) + "\"},";
1170-
});
1152+
return rules.entrySet().stream()
1153+
.map(rule -> rule.getKey() + ":" + rule.getValue().replaceFirst("@.*", ""))
1154+
.map(String::trim)
1155+
.collect(Collectors.joining(";"));
1156+
}));
11711157
}
11721158

11731159
// for debug
@@ -1188,4 +1174,3 @@ static void dumpMap(Map<String, Object> map) {
11881174
.forEach(System.out::println);
11891175
}
11901176
}
1191-
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright (c) 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 java.io.File;
29+
import java.io.IOException;
30+
import java.util.Arrays;
31+
import java.util.HashMap;
32+
import java.util.Map;
33+
34+
import org.xml.sax.Attributes;
35+
import org.xml.sax.InputSource;
36+
import org.xml.sax.SAXException;
37+
38+
/**
39+
* Handles parsing of dayPeriods.xml for day period rules.
40+
*/
41+
42+
class DayPeriodRuleParseHandler extends AbstractLDMLHandler<Map<String, String>> {
43+
44+
@Override
45+
public InputSource resolveEntity(String publicID, String systemID) throws IOException, SAXException {
46+
// avoid HTTP traffic to unicode.org
47+
if (systemID.startsWith(CLDRConverter.SPPL_LDML_DTD_SYSTEM_ID)) {
48+
return new InputSource((new File(CLDRConverter.LOCAL_SPPL_LDML_DTD)).toURI().toString());
49+
}
50+
return null;
51+
}
52+
53+
@Override
54+
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
55+
switch (qName) {
56+
case "dayPeriodRuleSet":
57+
if (attributes.getValue("type") != null) {
58+
// ignore "selection"
59+
pushIgnoredContainer(qName);
60+
} else {
61+
pushContainer(qName, attributes);
62+
}
63+
break;
64+
case "dayPeriodRules":
65+
if (!isIgnored(attributes)) {
66+
pushKeyContainer(qName, attributes, attributes.getValue("locales"));
67+
} else {
68+
pushIgnoredContainer(qName);
69+
}
70+
break;
71+
case "dayPeriodRule":
72+
if (!isIgnored(attributes) && currentContainer instanceof KeyContainer) {
73+
String at = attributes.getValue("at");
74+
String from = attributes.getValue("from");
75+
String before = attributes.getValue("before");
76+
String output = "";
77+
if (at == null || at.isEmpty()) {
78+
output = from + "-" + before;
79+
} else {
80+
output = at;
81+
}
82+
pushStringEntry(qName, attributes, attributes.getValue("type"), output);
83+
} else {
84+
pushIgnoredContainer(qName);
85+
}
86+
break;
87+
default:
88+
// treat anything else as a container
89+
pushContainer(qName, attributes);
90+
break;
91+
}
92+
}
93+
94+
@Override
95+
public void endElement(String uri, String localName, String qName) throws SAXException {
96+
assert qName.equals(currentContainer.getqName()) : "current=" + currentContainer.getqName() + ", param=" + qName;
97+
switch (qName) {
98+
case "dayPeriodRule":
99+
if (currentContainer instanceof Entry) {
100+
Entry<?> entry = (Entry<?>) currentContainer;
101+
final String type = entry.getKey();
102+
final String rule = (String) entry.getValue();
103+
String locales = ((KeyContainer) (currentContainer.getParent())).getKey();
104+
Arrays.stream(locales.split("\\s"))
105+
.forEach(loc -> {
106+
Map<String, String> rules = get(loc);
107+
if (rules == null) {
108+
rules = new HashMap<>();
109+
put(loc, rules);
110+
}
111+
rules.put(type, rule);
112+
});
113+
}
114+
break;
115+
}
116+
117+
currentContainer = currentContainer.getParent();
118+
}
119+
}

0 commit comments

Comments
 (0)