ICU-20332 Adding duration-person unit data and APIs to C and J.

- Rebuilds ICU4J data jars.
- Includes a workaround in data loading to be fixed with ICU-20400.
This commit is contained in:
Shane F. Carr 2019-02-08 15:10:59 -08:00
parent 30396f4dd0
commit c70a9db818
14 changed files with 293 additions and 58 deletions

View file

@ -27,7 +27,7 @@
<condition property="is.cldr.dir.set" >
<isset property="env.CLDR_DIR" />
</condition >
<fail unless="is.cldr.dir.set" message="Please set the CLDR_DIR environment variable to the top level ICU source dir (containing 'common')."/>
<fail unless="is.cldr.dir.set" message="Please set the CLDR_DIR environment variable to the top level CLDR source dir (containing 'common')."/>
<available property="cldrtools.dir" value="${env.CLDR_DIR}/cldr-tools" file="${env.CLDR_DIR}/cldr-tools" type="dir"/>
<available property="cldrtools.dir" value="${env.CLDR_DIR}/tools/java" file="${env.CLDR_DIR}/tools/java" type="dir"/>
@ -47,8 +47,11 @@
<echo message="java version: ${java.version}"/>
<echo message="ant java version: ${ant.java.version}"/>
<echo message="${ant.version}"/>
<echo message="cldr tools dir: ${cldrtools.dir}"/>
<echo message="cldr tools jar: ${cldrtools.jar}"/>
<echo message="cldr tools classes: ${env.CLDR_CLASSES}"/>
</target>
<target name="setup">
<target name="setup" depends="init">
<taskdef name="cldr-build" classname="org.unicode.cldr.ant.CLDRBuild">
<classpath>
<pathelement path="${java.class.path}/"/>

View file

@ -69,9 +69,8 @@
#
# CLDR_DIR: Path to root of CLDR sources, below which are the common and
# tools directories.
# CLDR_CLASSES: Defined relative to CLDR_DIR. It only needs to be set if you
# are not running ant jar for CLDR and have a non-default output
# folder for cldr-tools classes.
# CLDR_CLASSES: Path to the CLDR Tools classes directory. If not set, defaults
# to $CLDR_DIR/tools/java/classes
#
# c) ICU-related variables
# These variables only need to be set if you're directly reusing the
@ -140,7 +139,6 @@ export ANT_OPTS="-Xmx3072m -DCLDR_DTD_CACHE=/tmp"
# CLDR_DIR=`cygpath -wp /build/cldr`
export CLDR_DIR=$HOME/cldr/trunk
#export CLDR_CLASSES=$CLDR_DIR/tools/java/classes
# 1c. ICU variables
@ -148,9 +146,11 @@ export ICU4C_DIR=$HOME/icu/trunk/icu4c
export ICU4J_ROOT=$HOME/icu/trunk/icu4j
# 2. Build the CLDR Java tools
# Optionally build the jar, but ant will look inside the classes directory anyway
cd $CLDR_DIR/tools/java
ant jar
ant all
#ant jar
# 3. Configure ICU4C, build and test without new data first, to verify that
# there are no pre-existing errors (configure shown here for MacOSX, adjust
@ -169,12 +169,16 @@ make check 2>&1 | tee /tmp/icu4c-oldData-makeCheck.txt
# This process will take several minutes.
# Keep a log so you can investigate anything that looks suspicious.
#
# Running "ant setup" is not required, but it will print useful errors to
# debug issues with your path when it fails.
#
# If you see timeout errors when building the rbnf data, for example, then the
# system you are building on likely does not have its IP address whitelisted with
# Unicode for access to the CLDR repository, see note on "IP address whitelisting"
# near the top of this file.
cd $ICU4C_DIR/source/data
ant setup
ant clean
ant all 2>&1 | tee /tmp/cldr-newData-buildLog.txt

View file

@ -198,6 +198,7 @@ root{
other{"{0} d"}
per{"{0}/d"}
}
day-person:alias{"/LOCALE/unitsShort/duration/day"}
hour{
dnam{"hr"}
other{"{0} h"}
@ -221,6 +222,7 @@ root{
other{"{0} m"}
per{"{0}/m"}
}
month-person:alias{"/LOCALE/unitsShort/duration/month"}
nanosecond{
dnam{"ns"}
other{"{0} ns"}
@ -235,11 +237,13 @@ root{
other{"{0} w"}
per{"{0}/w"}
}
week-person:alias{"/LOCALE/unitsShort/duration/week"}
year{
dnam{"yr"}
other{"{0} y"}
per{"{0}/y"}
}
year-person:alias{"/LOCALE/unitsShort/duration/year"}
}
electric{
ampere{

View file

@ -47,7 +47,7 @@ U_NAMESPACE_BEGIN
static constexpr int32_t PER_UNIT_INDEX = StandardPlural::COUNT;
static constexpr int32_t PATTERN_COUNT = PER_UNIT_INDEX + 1;
static constexpr int32_t MEAS_UNIT_COUNT = 142; // see assertion in MeasureFormatCacheData constructor
static constexpr int32_t MEAS_UNIT_COUNT = 146; // see assertion in MeasureFormatCacheData constructor
static constexpr int32_t WIDTH_INDEX_COUNT = UMEASFMT_WIDTH_NARROW + 1;
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat)

View file

@ -43,19 +43,19 @@ static const int32_t gOffsets[] = {
26,
325,
336,
347,
351,
357,
355,
361,
381,
382,
393,
396,
402,
408,
365,
385,
386,
397,
400,
406,
412,
416,
441
420,
445
};
static const int32_t gIndexes[] = {
@ -67,19 +67,19 @@ static const int32_t gIndexes[] = {
26,
26,
37,
48,
52,
58,
56,
62,
82,
83,
94,
97,
103,
109,
66,
86,
87,
98,
101,
107,
113,
117,
142
121,
146
};
// Must be sorted alphabetically.
@ -446,15 +446,19 @@ static const char * const gSubTypes[] = {
"terabyte",
"century",
"day",
"day-person",
"hour",
"microsecond",
"millisecond",
"minute",
"month",
"month-person",
"nanosecond",
"second",
"week",
"week-person",
"year",
"year-person",
"ampere",
"milliampere",
"ohm",
@ -553,14 +557,14 @@ static const char * const gSubTypes[] = {
// Must be sorted by first value and then second value.
static int32_t unitPerUnitToSingleUnit[][4] = {
{368, 338, 17, 0},
{370, 344, 17, 2},
{372, 338, 17, 3},
{372, 430, 4, 2},
{372, 431, 4, 3},
{387, 428, 3, 1},
{390, 11, 16, 5},
{433, 368, 4, 1}
{372, 339, 17, 0},
{374, 346, 17, 2},
{376, 339, 17, 3},
{376, 434, 4, 2},
{376, 435, 4, 3},
{391, 432, 3, 1},
{394, 11, 16, 5},
{437, 372, 4, 1}
};
// Shortcuts to the base unit in order to make the default constructor fast
@ -879,78 +883,110 @@ MeasureUnit MeasureUnit::getDay() {
return MeasureUnit(7, 1);
}
MeasureUnit *MeasureUnit::createHour(UErrorCode &status) {
MeasureUnit *MeasureUnit::createDayPerson(UErrorCode &status) {
return MeasureUnit::create(7, 2, status);
}
MeasureUnit MeasureUnit::getHour() {
MeasureUnit MeasureUnit::getDayPerson() {
return MeasureUnit(7, 2);
}
MeasureUnit *MeasureUnit::createMicrosecond(UErrorCode &status) {
MeasureUnit *MeasureUnit::createHour(UErrorCode &status) {
return MeasureUnit::create(7, 3, status);
}
MeasureUnit MeasureUnit::getMicrosecond() {
MeasureUnit MeasureUnit::getHour() {
return MeasureUnit(7, 3);
}
MeasureUnit *MeasureUnit::createMillisecond(UErrorCode &status) {
MeasureUnit *MeasureUnit::createMicrosecond(UErrorCode &status) {
return MeasureUnit::create(7, 4, status);
}
MeasureUnit MeasureUnit::getMillisecond() {
MeasureUnit MeasureUnit::getMicrosecond() {
return MeasureUnit(7, 4);
}
MeasureUnit *MeasureUnit::createMinute(UErrorCode &status) {
MeasureUnit *MeasureUnit::createMillisecond(UErrorCode &status) {
return MeasureUnit::create(7, 5, status);
}
MeasureUnit MeasureUnit::getMinute() {
MeasureUnit MeasureUnit::getMillisecond() {
return MeasureUnit(7, 5);
}
MeasureUnit *MeasureUnit::createMonth(UErrorCode &status) {
MeasureUnit *MeasureUnit::createMinute(UErrorCode &status) {
return MeasureUnit::create(7, 6, status);
}
MeasureUnit MeasureUnit::getMonth() {
MeasureUnit MeasureUnit::getMinute() {
return MeasureUnit(7, 6);
}
MeasureUnit *MeasureUnit::createNanosecond(UErrorCode &status) {
MeasureUnit *MeasureUnit::createMonth(UErrorCode &status) {
return MeasureUnit::create(7, 7, status);
}
MeasureUnit MeasureUnit::getNanosecond() {
MeasureUnit MeasureUnit::getMonth() {
return MeasureUnit(7, 7);
}
MeasureUnit *MeasureUnit::createSecond(UErrorCode &status) {
MeasureUnit *MeasureUnit::createMonthPerson(UErrorCode &status) {
return MeasureUnit::create(7, 8, status);
}
MeasureUnit MeasureUnit::getSecond() {
MeasureUnit MeasureUnit::getMonthPerson() {
return MeasureUnit(7, 8);
}
MeasureUnit *MeasureUnit::createWeek(UErrorCode &status) {
MeasureUnit *MeasureUnit::createNanosecond(UErrorCode &status) {
return MeasureUnit::create(7, 9, status);
}
MeasureUnit MeasureUnit::getWeek() {
MeasureUnit MeasureUnit::getNanosecond() {
return MeasureUnit(7, 9);
}
MeasureUnit *MeasureUnit::createYear(UErrorCode &status) {
MeasureUnit *MeasureUnit::createSecond(UErrorCode &status) {
return MeasureUnit::create(7, 10, status);
}
MeasureUnit MeasureUnit::getYear() {
MeasureUnit MeasureUnit::getSecond() {
return MeasureUnit(7, 10);
}
MeasureUnit *MeasureUnit::createWeek(UErrorCode &status) {
return MeasureUnit::create(7, 11, status);
}
MeasureUnit MeasureUnit::getWeek() {
return MeasureUnit(7, 11);
}
MeasureUnit *MeasureUnit::createWeekPerson(UErrorCode &status) {
return MeasureUnit::create(7, 12, status);
}
MeasureUnit MeasureUnit::getWeekPerson() {
return MeasureUnit(7, 12);
}
MeasureUnit *MeasureUnit::createYear(UErrorCode &status) {
return MeasureUnit::create(7, 13, status);
}
MeasureUnit MeasureUnit::getYear() {
return MeasureUnit(7, 13);
}
MeasureUnit *MeasureUnit::createYearPerson(UErrorCode &status) {
return MeasureUnit::create(7, 14, status);
}
MeasureUnit MeasureUnit::getYearPerson() {
return MeasureUnit(7, 14);
}
MeasureUnit *MeasureUnit::createAmpere(UErrorCode &status) {
return MeasureUnit::create(8, 0, status);
}

View file

@ -101,7 +101,16 @@ void getMeasureData(const Locale &locale, const MeasureUnit &unit, const UNumber
key.append("/", status);
key.append(unit.getType(), status);
key.append("/", status);
key.append(unit.getSubtype(), status);
// Map duration-year-person, duration-week-person, etc. to duration-year, duration-week, ...
// TODO(ICU-20400): Get duration-*-person data properly with aliases.
int32_t subtypeLen = static_cast<int32_t>(uprv_strlen(unit.getSubtype()));
if (subtypeLen > 7 && uprv_strcmp(unit.getSubtype() + subtypeLen - 7, "-person") == 0) {
key.append({unit.getSubtype(), subtypeLen - 7}, status);
} else {
key.append(unit.getSubtype(), status);
}
ures_getAllItemsWithFallback(unitsBundle.getAlias(), key.data(), sink, status);
}

View file

@ -838,6 +838,24 @@ class U_I18N_API MeasureUnit: public UObject {
*/
static MeasureUnit getDay();
#ifndef U_HIDE_DRAFT_API
/**
* Returns by pointer, unit of duration: day-person.
* Caller owns returned value and must free it.
* Also see {@link #getDayPerson()}.
* @param status ICU error code.
* @draft ICU 63
*/
static MeasureUnit *createDayPerson(UErrorCode &status);
/**
* Returns by value, unit of duration: day-person.
* Also see {@link #createDayPerson()}.
* @draft ICU 64
*/
static MeasureUnit getDayPerson();
#endif /* U_HIDE_DRAFT_API */
/**
* Returns by pointer, unit of duration: hour.
* Caller owns returned value and must free it.
@ -918,6 +936,24 @@ class U_I18N_API MeasureUnit: public UObject {
*/
static MeasureUnit getMonth();
#ifndef U_HIDE_DRAFT_API
/**
* Returns by pointer, unit of duration: month-person.
* Caller owns returned value and must free it.
* Also see {@link #getMonthPerson()}.
* @param status ICU error code.
* @draft ICU 63
*/
static MeasureUnit *createMonthPerson(UErrorCode &status);
/**
* Returns by value, unit of duration: month-person.
* Also see {@link #createMonthPerson()}.
* @draft ICU 64
*/
static MeasureUnit getMonthPerson();
#endif /* U_HIDE_DRAFT_API */
/**
* Returns by pointer, unit of duration: nanosecond.
* Caller owns returned value and must free it.
@ -966,6 +1002,24 @@ class U_I18N_API MeasureUnit: public UObject {
*/
static MeasureUnit getWeek();
#ifndef U_HIDE_DRAFT_API
/**
* Returns by pointer, unit of duration: week-person.
* Caller owns returned value and must free it.
* Also see {@link #getWeekPerson()}.
* @param status ICU error code.
* @draft ICU 63
*/
static MeasureUnit *createWeekPerson(UErrorCode &status);
/**
* Returns by value, unit of duration: week-person.
* Also see {@link #createWeekPerson()}.
* @draft ICU 64
*/
static MeasureUnit getWeekPerson();
#endif /* U_HIDE_DRAFT_API */
/**
* Returns by pointer, unit of duration: year.
* Caller owns returned value and must free it.
@ -982,6 +1036,24 @@ class U_I18N_API MeasureUnit: public UObject {
*/
static MeasureUnit getYear();
#ifndef U_HIDE_DRAFT_API
/**
* Returns by pointer, unit of duration: year-person.
* Caller owns returned value and must free it.
* Also see {@link #getYearPerson()}.
* @param status ICU error code.
* @draft ICU 63
*/
static MeasureUnit *createYearPerson(UErrorCode &status);
/**
* Returns by value, unit of duration: year-person.
* Also see {@link #createYearPerson()}.
* @draft ICU 64
*/
static MeasureUnit getYearPerson();
#endif /* U_HIDE_DRAFT_API */
/**
* Returns by pointer, unit of electric: ampere.
* Caller owns returned value and must free it.

View file

@ -74,6 +74,7 @@ private:
void TestDoubleZero();
void TestUnitPerUnitResolution();
void TestIndividualPluralFallback();
void Test20332_PersonUnits();
void verifyFormat(
const char *description,
const MeasureFormat &fmt,
@ -173,6 +174,7 @@ void MeasureFormatTest::runIndexedTest(
TESTCASE_AUTO(TestDoubleZero);
TESTCASE_AUTO(TestUnitPerUnitResolution);
TESTCASE_AUTO(TestIndividualPluralFallback);
TESTCASE_AUTO(Test20332_PersonUnits);
TESTCASE_AUTO_END;
}
@ -2615,6 +2617,38 @@ void MeasureFormatTest::TestIndividualPluralFallback() {
assertEquals("2 deg temp in fr_CA", expected, mf.format(twoDeg.orphan(), actual, errorCode), TRUE);
}
void MeasureFormatTest::Test20332_PersonUnits() {
if (logKnownIssue("ICU-20400")) {
return;
}
IcuTestErrorCode status(*this, "Test20332_PersonUnits");
const struct TestCase {
const char* locale;
MeasureUnit* unitToAdopt;
UMeasureFormatWidth width;
const char* expected;
} cases[] = {
{"en-us", MeasureUnit::createYearPerson(status), UMEASFMT_WIDTH_NARROW, "25y"},
{"en-us", MeasureUnit::createYearPerson(status), UMEASFMT_WIDTH_SHORT, "25 yrs"},
{"en-us", MeasureUnit::createYearPerson(status), UMEASFMT_WIDTH_WIDE, "25 years"},
{"en-us", MeasureUnit::createMonthPerson(status), UMEASFMT_WIDTH_NARROW, "25m"},
{"en-us", MeasureUnit::createMonthPerson(status), UMEASFMT_WIDTH_SHORT, "25 mths"},
{"en-us", MeasureUnit::createMonthPerson(status), UMEASFMT_WIDTH_WIDE, "25 months"},
{"en-us", MeasureUnit::createWeekPerson(status), UMEASFMT_WIDTH_NARROW, "25w"},
{"en-us", MeasureUnit::createWeekPerson(status), UMEASFMT_WIDTH_SHORT, "25 wks"},
{"en-us", MeasureUnit::createWeekPerson(status), UMEASFMT_WIDTH_WIDE, "25 weeks"},
{"en-us", MeasureUnit::createDayPerson(status), UMEASFMT_WIDTH_NARROW, "25d"},
{"en-us", MeasureUnit::createDayPerson(status), UMEASFMT_WIDTH_SHORT, "25 Days"},
{"en-us", MeasureUnit::createDayPerson(status), UMEASFMT_WIDTH_WIDE, "25 days"}
};
for (const auto& cas : cases) {
MeasureFormat mf(cas.locale, cas.width, status);
Measure measure(25, cas.unitToAdopt, status);
verifyFormat(cas.locale, mf, &measure, 1, cas.expected);
}
}
void MeasureFormatTest::verifyFieldPosition(
const char *description,
const MeasureFormat &fmt,

View file

@ -562,6 +562,15 @@ void NumberFormatterApiTest::unitMeasure() {
Locale("es-MX"),
1,
u"kelvin");
assertFormatSingle(
u"Person unit not in short form",
u"measure-unit/duration-year-person unit-width-full-name",
NumberFormatter::with().unit(MeasureUnit::getYearPerson())
.unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
Locale("es-MX"),
5,
u"5 a\u00F1os");
}
void NumberFormatterApiTest::unitCompoundMeasure() {

View file

@ -97,7 +97,15 @@ public class LongNameHandler implements MicroPropsGenerator, ModifierStore {
key.append("/");
key.append(unit.getType());
key.append("/");
key.append(unit.getSubtype());
// Map duration-year-person, duration-week-person, etc. to duration-year, duration-week, ...
// TODO(ICU-20400): Get duration-*-person data properly with aliases.
if (unit.getSubtype().endsWith("-person")) {
key.append(unit.getSubtype(), 0, unit.getSubtype().length() - 7);
} else {
key.append(unit.getSubtype());
}
try {
resource.getAllItemsWithFallback(key.toString(), sink);
} catch (MissingResourceException e) {

View file

@ -610,6 +610,13 @@ public class MeasureUnit implements Serializable {
*/
public static final TimeUnit DAY = (TimeUnit) MeasureUnit.internalGetInstance("duration", "day");
/**
* Constant for unit of duration: day-person
* @draft ICU 64
* @provisional This API might change or be removed in a future release.
*/
public static final MeasureUnit DAY_PERSON = MeasureUnit.internalGetInstance("duration", "day-person");
/**
* Constant for unit of duration: hour
* @stable ICU 4.0
@ -640,6 +647,13 @@ public class MeasureUnit implements Serializable {
*/
public static final TimeUnit MONTH = (TimeUnit) MeasureUnit.internalGetInstance("duration", "month");
/**
* Constant for unit of duration: month-person
* @draft ICU 64
* @provisional This API might change or be removed in a future release.
*/
public static final MeasureUnit MONTH_PERSON = MeasureUnit.internalGetInstance("duration", "month-person");
/**
* Constant for unit of duration: nanosecond
* @stable ICU 54
@ -658,12 +672,26 @@ public class MeasureUnit implements Serializable {
*/
public static final TimeUnit WEEK = (TimeUnit) MeasureUnit.internalGetInstance("duration", "week");
/**
* Constant for unit of duration: week-person
* @draft ICU 64
* @provisional This API might change or be removed in a future release.
*/
public static final MeasureUnit WEEK_PERSON = MeasureUnit.internalGetInstance("duration", "week-person");
/**
* Constant for unit of duration: year
* @stable ICU 4.0
*/
public static final TimeUnit YEAR = (TimeUnit) MeasureUnit.internalGetInstance("duration", "year");
/**
* Constant for unit of duration: year-person
* @draft ICU 64
* @provisional This API might change or be removed in a future release.
*/
public static final MeasureUnit YEAR_PERSON = MeasureUnit.internalGetInstance("duration", "year-person");
/**
* Constant for unit of electric: ampere
* @stable ICU 54

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:22491b0308503cc3a472815b978444811c3ab5fcf7cae071832c39a7ba178465
size 12694176
oid sha256:bff554a5fd35fddf6a105bb560ec56d120cee51c4655e941f2306a90297ebe53
size 12696138

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d72644d74c804bede2359aa1ae18164b45dbab98d997826c98e82dca5dd685e9
size 94046
oid sha256:9e8b58bfaaf6e5d236337ee6bc0a7aee7dcb32e5d7054a8bd584650416490d2f
size 94059

View file

@ -2193,6 +2193,34 @@ public class MeasureUnitTest extends TestFmwk {
// Should not throw an exception.
}
@Test
public void test20332_PersonUnits() {
Object[][] cases = new Object[][] {
{ULocale.US, MeasureUnit.YEAR_PERSON, MeasureFormat.FormatWidth.NARROW, "25y"},
{ULocale.US, MeasureUnit.YEAR_PERSON, MeasureFormat.FormatWidth.SHORT, "25 yrs"},
{ULocale.US, MeasureUnit.YEAR_PERSON, MeasureFormat.FormatWidth.WIDE, "25 years"},
{ULocale.US, MeasureUnit.MONTH_PERSON, MeasureFormat.FormatWidth.NARROW, "25m"},
{ULocale.US, MeasureUnit.MONTH_PERSON, MeasureFormat.FormatWidth.SHORT, "25 mths"},
{ULocale.US, MeasureUnit.MONTH_PERSON, MeasureFormat.FormatWidth.WIDE, "25 months"},
{ULocale.US, MeasureUnit.WEEK_PERSON, MeasureFormat.FormatWidth.NARROW, "25w"},
{ULocale.US, MeasureUnit.WEEK_PERSON, MeasureFormat.FormatWidth.SHORT, "25 wks"},
{ULocale.US, MeasureUnit.WEEK_PERSON, MeasureFormat.FormatWidth.WIDE, "25 weeks"},
{ULocale.US, MeasureUnit.DAY_PERSON, MeasureFormat.FormatWidth.NARROW, "25d"},
{ULocale.US, MeasureUnit.DAY_PERSON, MeasureFormat.FormatWidth.SHORT, "25 days"},
{ULocale.US, MeasureUnit.DAY_PERSON, MeasureFormat.FormatWidth.WIDE, "25 days"}
};
for (Object[] cas : cases) {
ULocale locale = (ULocale) cas[0];
MeasureUnit unit = (MeasureUnit) cas[1];
MeasureFormat.FormatWidth width = (MeasureFormat.FormatWidth) cas[2];
String expected = (String) cas[3];
MeasureFormat fmt = MeasureFormat.getInstance(locale, width);
String result = fmt.formatMeasures(new Measure(25, unit));
assertEquals("" + locale + " " + unit + " " + width, expected, result);
}
}
// DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
// for MeasureFormat during the release process.
static Map<MeasureUnit, Pair<MeasureUnit, MeasureUnit>> getUnitsToPerParts() {