Skip to content

Commit

Permalink
8176706: Additional Date-Time Formats
Browse files Browse the repository at this point in the history
Reviewed-by: joehw, rriggs
  • Loading branch information
naotoj committed Feb 16, 2022
1 parent 0f3d3ac commit 9b74c3f
Show file tree
Hide file tree
Showing 12 changed files with 809 additions and 96 deletions.
82 changes: 61 additions & 21 deletions make/jdk/src/classes/build/tools/cldrconverter/Bundle.java
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2022, 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
Expand All @@ -25,6 +25,9 @@

package build.tools.cldrconverter;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
Expand All @@ -34,6 +37,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

class Bundle {
Expand All @@ -47,21 +51,21 @@ static enum Type {
FORMATDATA);
}

private final static Map<String, Bundle> bundles = new HashMap<>();
private static final Map<String, Bundle> bundles = new HashMap<>();

private final static String[] NUMBER_PATTERN_KEYS = {
private static final String[] NUMBER_PATTERN_KEYS = {
"NumberPatterns/decimal",
"NumberPatterns/currency",
"NumberPatterns/percent",
"NumberPatterns/accounting"
};

private final static String[] COMPACT_NUMBER_PATTERN_KEYS = {
"short.CompactNumberPatterns",
"long.CompactNumberPatterns"
private static final String[] COMPACT_NUMBER_PATTERN_KEYS = {
"short.CompactNumberPatterns",
"long.CompactNumberPatterns"
};

private final static String[] NUMBER_ELEMENT_KEYS = {
private static final String[] NUMBER_ELEMENT_KEYS = {
"NumberElements/decimal",
"NumberElements/group",
"NumberElements/list",
Expand All @@ -77,41 +81,45 @@ static enum Type {
"NumberElements/currencyGroup",
};

private final static String[] TIME_PATTERN_KEYS = {
private static final String[] TIME_PATTERN_KEYS = {
"DateTimePatterns/full-time",
"DateTimePatterns/long-time",
"DateTimePatterns/medium-time",
"DateTimePatterns/short-time",
};

private final static String[] DATE_PATTERN_KEYS = {
private static final String[] DATE_PATTERN_KEYS = {
"DateTimePatterns/full-date",
"DateTimePatterns/long-date",
"DateTimePatterns/medium-date",
"DateTimePatterns/short-date",
};

private final static String[] DATETIME_PATTERN_KEYS = {
private static final String[] DATETIME_PATTERN_KEYS = {
"DateTimePatterns/full-dateTime",
"DateTimePatterns/long-dateTime",
"DateTimePatterns/medium-dateTime",
"DateTimePatterns/short-dateTime",
};

private final static String[] ERA_KEYS = {
private static final String[] ERA_KEYS = {
"long.Eras",
"Eras",
"narrow.Eras"
};

// DateFormatItem prefix
static final String DATEFORMATITEM_KEY_PREFIX = "DateFormatItem.";
static final String DATEFORMATITEM_INPUT_REGIONS_PREFIX = "DateFormatItemInputRegions.";

// Keys for individual time zone names
private final static String TZ_GEN_LONG_KEY = "timezone.displayname.generic.long";
private final static String TZ_GEN_SHORT_KEY = "timezone.displayname.generic.short";
private final static String TZ_STD_LONG_KEY = "timezone.displayname.standard.long";
private final static String TZ_STD_SHORT_KEY = "timezone.displayname.standard.short";
private final static String TZ_DST_LONG_KEY = "timezone.displayname.daylight.long";
private final static String TZ_DST_SHORT_KEY = "timezone.displayname.daylight.short";
private final static String[] ZONE_NAME_KEYS = {
private static final String TZ_GEN_LONG_KEY = "timezone.displayname.generic.long";
private static final String TZ_GEN_SHORT_KEY = "timezone.displayname.generic.short";
private static final String TZ_STD_LONG_KEY = "timezone.displayname.standard.long";
private static final String TZ_STD_SHORT_KEY = "timezone.displayname.standard.short";
private static final String TZ_DST_LONG_KEY = "timezone.displayname.daylight.long";
private static final String TZ_DST_SHORT_KEY = "timezone.displayname.daylight.short";
private static final String[] ZONE_NAME_KEYS = {
TZ_STD_LONG_KEY,
TZ_STD_SHORT_KEY,
TZ_DST_LONG_KEY,
Expand Down Expand Up @@ -262,7 +270,7 @@ Map<String, Object> getTargetMap() throws Exception {
CLDRConverter.handleAliases(myMap);

// another hack: parentsMap is not used for date-time resources.
if ("root".equals(id)) {
if (isRoot()) {
parentsMap = null;
}

Expand All @@ -287,6 +295,14 @@ Map<String, Object> getTargetMap() throws Exception {
handleDateTimeFormatPatterns(TIME_PATTERN_KEYS, myMap, parentsMap, calendarType, "TimePatterns");
handleDateTimeFormatPatterns(DATE_PATTERN_KEYS, myMap, parentsMap, calendarType, "DatePatterns");
handleDateTimeFormatPatterns(DATETIME_PATTERN_KEYS, myMap, parentsMap, calendarType, "DateTimePatterns");

// Skeleton
handleSkeletonPatterns(myMap, calendarType);
}

// Skeleton input regions
if (isRoot()) {
skeletonInputRegions(myMap);
}

// First, weed out any empty timezone or metazone names from myMap.
Expand Down Expand Up @@ -647,8 +663,9 @@ private String toMetaZoneKey(String tzKey) {
private void convertDateTimePatternLetter(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb) {
switch (cldrLetter) {
case 'u':
// Change cldr letter 'u' to 'y', as 'u' is interpreted as
// "Extended year (numeric)" in CLDR/LDML,
case 'U':
// Change cldr letter 'u'/'U' to 'y', as 'u' is interpreted as
// "Extended year (numeric)", and 'U' as "Cyclic year" in CLDR/LDML,
// which is not supported in SimpleDateFormat and
// j.t.f.DateTimeFormatter, so it is replaced with 'y'
// as the best approximation
Expand Down Expand Up @@ -742,6 +759,19 @@ private static boolean hasNulls(Object[] array) {
return false;
}

private void handleSkeletonPatterns(Map<String, Object> myMap, CalendarType calendarType) {
String calendarPrefix = calendarType.keyElementName();
myMap.putAll(myMap.entrySet().stream()
.filter(e -> e.getKey().startsWith(Bundle.DATEFORMATITEM_KEY_PREFIX))
.collect(Collectors.toMap(
e -> calendarPrefix + e.getKey(),
e -> translateDateFormatLetters(calendarType,
(String)e.getValue(),
this::convertDateTimePatternLetter)
))
);
}

@FunctionalInterface
private interface ConvertDateTimeLetters {
void convert(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb);
Expand Down Expand Up @@ -790,4 +820,14 @@ private String[] createNumberArray(Map<String, Object> myMap, Map<String, Object
}
return numArray;
}

private static void skeletonInputRegions(Map<String, Object> myMap) {
myMap.putAll(myMap.entrySet().stream()
.filter(e -> e.getKey().startsWith(Bundle.DATEFORMATITEM_INPUT_REGIONS_PREFIX))
.collect(Collectors.toMap(
e -> e.getKey(),
e -> ((String)e.getValue()).trim()
))
);
}
}
20 changes: 8 additions & 12 deletions make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2022, 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
Expand Down Expand Up @@ -847,30 +847,26 @@ private static Map<String, Object> extractCalendarData(Map<String, Object> map,
"DateTimePatternChars",
"PluralRules",
"DayPeriodRules",
"DateFormatItem",
};

private static Map<String, Object> extractFormatData(Map<String, Object> map, String id) {
Map<String, Object> formatData = new LinkedHashMap<>();
for (CalendarType calendarType : CalendarType.values()) {
if (calendarType == CalendarType.GENERIC) {
continue;
}
String prefix = calendarType.keyElementName();
for (String element : FORMAT_DATA_ELEMENTS) {
String key = prefix + element;
copyIfPresent(map, "java.time." + key, formatData);
copyIfPresent(map, key, formatData);
}
Arrays.stream(FORMAT_DATA_ELEMENTS)
.flatMap(elem -> map.keySet().stream().filter(k -> k.startsWith(prefix + elem)))
.forEach(key -> {
copyIfPresent(map, "java.time." + key, formatData);
copyIfPresent(map, key, formatData);
});
}

for (String key : map.keySet()) {
// Copy available calendar names
if (key.startsWith(CLDRConverter.LOCALE_TYPE_PREFIX_CA)) {
String type = key.substring(CLDRConverter.LOCALE_TYPE_PREFIX_CA.length());
for (CalendarType calendarType : CalendarType.values()) {
if (calendarType == CalendarType.GENERIC) {
continue;
}
if (type.equals(calendarType.lname())) {
Object value = map.get(key);
String dataKey = key.replace(LOCALE_TYPE_PREFIX_CA,
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2022, 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
Expand Down Expand Up @@ -756,6 +756,14 @@ public void startElement(String uri, String localName, String qName, Attributes
pushStringEntry(qName, attributes, prefix + "DateTimePatterns/" + attributes.getValue("type") + "-dateTime");
}
break;
case "dateFormatItem":
{
// for FormatData
String prefix = (currentCalendarType == null) ? "" : currentCalendarType.keyElementName();
pushStringEntry(qName, attributes,
prefix + Bundle.DATEFORMATITEM_KEY_PREFIX + attributes.getValue("id"));
}
break;
case "localizedPatternChars":
{
// for FormatData
Expand Down Expand Up @@ -1113,7 +1121,7 @@ private Object putIfEntry() {
if (id.equals("root") && key.startsWith("MonthNames")) {
value = new DateFormatSymbols(Locale.US).getShortMonths();
}
return put(entry.getKey(), value);
return put(key, value);
}
}
return null;
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2022, 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
Expand Down Expand Up @@ -27,8 +27,12 @@

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
Expand Down Expand Up @@ -62,10 +66,15 @@ class SupplementDataParseHandler extends AbstractLDMLHandler<Object> {
// parentLocale.<parent_locale_id>=<child_locale_id>(" "<child_locale_id>)+
private final Map<String, String> parentLocalesMap;

// Input Skeleton map for "preferred" and "allowed"
// Map<"preferred"/"allowed", Map<"skeleton", SortedSet<"regions">>>
private final Map<String, Map<String, SortedSet<String>>> inputSkeletonMap;

SupplementDataParseHandler() {
firstDayMap = new HashMap<>();
minDaysMap = new HashMap<>();
parentLocalesMap = new HashMap<>();
inputSkeletonMap = new HashMap<>();
}

/**
Expand All @@ -76,22 +85,25 @@ class SupplementDataParseHandler extends AbstractLDMLHandler<Object> {
* It returns null when there is no firstDay and minDays for the country
* although this should not happen because supplementalData.xml includes
* default value for the world ("001") for firstDay and minDays.
*
* This method also returns Maps for "preferred" and "allowed" skeletons,
* which are grouped by regions. E.g, "h:XX YY ZZ;" which means 'h' pattern
* is "preferred"/"allowed" in "XX", "YY", and "ZZ" regions.
*/
Map<String, Object> getData(String id) {
Map<String, Object> values = new HashMap<>();
if ("root".equals(id)) {
parentLocalesMap.keySet().forEach(key -> {
values.put(CLDRConverter.PARENT_LOCALE_PREFIX+key,
parentLocalesMap.get(key));
});
firstDayMap.keySet().forEach(key -> {
values.put(CLDRConverter.CALENDAR_FIRSTDAY_PREFIX+firstDayMap.get(key),
key);
});
minDaysMap.keySet().forEach(key -> {
values.put(CLDRConverter.CALENDAR_MINDAYS_PREFIX+minDaysMap.get(key),
key);
});
parentLocalesMap.forEach((k, v) -> values.put(CLDRConverter.PARENT_LOCALE_PREFIX + k, v));
firstDayMap.forEach((k, v) -> values.put(CLDRConverter.CALENDAR_FIRSTDAY_PREFIX + v, k));
minDaysMap.forEach((k, v) -> values.put(CLDRConverter.CALENDAR_MINDAYS_PREFIX + v, k));
inputSkeletonMap.get("preferred").forEach((k, v) ->
values.merge(Bundle.DATEFORMATITEM_INPUT_REGIONS_PREFIX + "preferred",
k + ":" + v.stream().collect(Collectors.joining(" ")) + ";",
(old, newVal) -> old + (String)newVal));
inputSkeletonMap.get("allowed").forEach((k, v) ->
values.merge(Bundle.DATEFORMATITEM_INPUT_REGIONS_PREFIX + "allowed",
k + ":" + v.stream().collect(Collectors.joining(" ")) + ";",
(old, newVal) -> old + (String)newVal));
}
return values.isEmpty() ? null : values;
}
Expand Down Expand Up @@ -158,11 +170,23 @@ public void startElement(String uri, String localName, String qName, Attributes
attributes.getValue("locales").replaceAll("_", "-"));
}
break;
case "hours":
if (!isIgnored(attributes)) {
var preferred = attributes.getValue("preferred");
var allowed = attributes.getValue("allowed").replaceFirst(" .*", "").replaceFirst("b", "B"); // take only the first one, "b" -> "B"
var regions = Arrays.stream(attributes.getValue("regions").split(" "))
.map(r -> r.replaceAll("_", "-"))
.collect(Collectors.toSet());
var pmap = inputSkeletonMap.computeIfAbsent("preferred", k -> new HashMap<>());
var amap = inputSkeletonMap.computeIfAbsent("allowed", k -> new HashMap<>());
pmap.computeIfAbsent(preferred, k -> new TreeSet<>()).addAll(regions);
amap.computeIfAbsent(allowed, k -> new TreeSet<>()).addAll(regions);
}
break;
default:
// treat anything else as a container
pushContainer(qName, attributes);
break;
}
}

}

1 comment on commit 9b74c3f

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.