Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions icu4c/source/i18n/measunit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2767,16 +2767,21 @@ StringEnumeration* MeasureUnit::getAvailableTypes(UErrorCode &errorCode) {
}

bool MeasureUnit::validateAndGet(StringPiece type, StringPiece subtype, MeasureUnit &result) {
// Find the type index using binary search
// Find the type and subtype indices using binary search
int32_t typeIdx = binarySearch(gTypes, 0, UPRV_LENGTHOF(gTypes), type);
if (typeIdx == -1) {
return false; // Type not found
}
int32_t subtypeIdx = typeIdx >= 0 ? binarySearch(typeIdx, subtype) : -1;

// Find the subtype within the type's range using binary search
int32_t subtypeIdx = binarySearch(typeIdx, subtype);
if (subtypeIdx == -1) {
return false; // Subtype not found
if (typeIdx >= 0 && subtypeIdx < 0) {
// if we did find the type, but didn't find the subtype, that might be because the sybtype name
// is an alias-- try using MeasureUnit::forIdentifier(), which will resolve aliases
UErrorCode localStatus = U_ZERO_ERROR;
MeasureUnit tempUnit = MeasureUnit::forIdentifier(subtype, localStatus);
if (U_SUCCESS(localStatus) && uprv_strcmp(type.data(), tempUnit.getType()) == 0) {
subtypeIdx = tempUnit.fSubTypeId + gOffsets[tempUnit.fTypeId];
}
}
if (typeIdx < 0 || subtypeIdx < 0) {
return false;
}

// Create the MeasureUnit and return it
Expand Down
2 changes: 1 addition & 1 deletion icu4c/source/i18n/number_skeletons.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,7 @@ void blueprint_helpers::parseMeasureUnitOption(const StringSegment& segment, Mac
if (MeasureUnit::validateAndGet(type.toStringPiece(), subType.toStringPiece(), unit)) {
macros.unit = unit;
return;
}
}

status = U_NUMBER_SKELETON_SYNTAX_ERROR;
}
Expand Down
17 changes: 6 additions & 11 deletions icu4c/source/test/depstest/dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,7 @@ library: i18n
formatting formattable_cnv regex regex_cnv translit
double_conversion number_representation number_output numberformatter
number_skeletons number_usageprefs numberparser
units_extra unitsformatter
units unitsformatter
universal_time_scale
uclean_i18n
display_options
Expand Down Expand Up @@ -1055,7 +1055,7 @@ group: number_skeletons
number_skeletons.o number_capi.o number_asformat.o numrange_capi.o
deps
numberformatter
units_extra
units

group: number_symbolswrapper
number_symbolswrapper.o
Expand Down Expand Up @@ -1126,21 +1126,16 @@ group: sharedbreakiterator
deps
breakiterator

group: units_extra
measunit_extra.o
deps
units bytestriebuilder bytestrie resourcebundle uclean_i18n
double_conversion

group: units
measunit.o currunit.o
measunit.o currunit.o measunit_extra.o
deps
stringenumeration errorcode
stringenumeration errorcode bytestriebuilder bytestrie resourcebundle uclean_i18n
double_conversion

group: unitsformatter
units_data.o units_converter.o units_complexconverter.o units_router.o
deps
resourcebundle units_extra double_conversion number_representation formattable sort
resourcebundle units double_conversion number_representation formattable sort
number_rounding

group: decnumber
Expand Down
1 change: 1 addition & 0 deletions icu4c/source/test/intltest/numbertest.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ class NumberSkeletonTest : public IntlTest {
void perUnitToSkeleton();
void measurementSystemOverride();
void longSkeletonCrash();
void unitAliases();

void runIndexedTest(int32_t index, UBool exec, const char*& name, char* par = nullptr) override;

Expand Down
28 changes: 28 additions & 0 deletions icu4c/source/test/intltest/numbertest_skeletons.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void NumberSkeletonTest::runIndexedTest(int32_t index, UBool exec, const char*&
TESTCASE_AUTO(perUnitToSkeleton);
TESTCASE_AUTO(measurementSystemOverride);
TESTCASE_AUTO(longSkeletonCrash);
TESTCASE_AUTO(unitAliases);
TESTCASE_AUTO_END;
}

Expand Down Expand Up @@ -581,4 +582,31 @@ void NumberSkeletonTest::longSkeletonCrash() {
UnlocalizedNumberFormatter nf = NumberFormatter::forSkeleton(skeleton, err);
}

void NumberSkeletonTest::unitAliases() {
IcuTestErrorCode status(*this, "unitAliases");

struct TestCase {
const char16_t* skeleton;
const char16_t* expectedResult;
} testCases[] = {
{ u"measure-unit/concentr-part-per-1e6", u"3.14 ppm" },
{ u"measure-unit/concentr-part-per-million", u"3.14 ppm" },
{ u"measure-unit/concentr-permillion", u"3.14 ppm" },
{ u"measure-unit/concentr-milligram-ofglucose-per-deciliter", u"3.14 mg/dL" },
{ u"measure-unit/concentr-milligram-per-deciliter", u"3.14 mg/dL" },
{ u"measure-unit/mass-tonne", u"3.14 t" },
{ u"measure-unit/mass-metric-ton", u"3.14 t" },
};

for (const auto& testCase : testCases) {
LocalizedNumberFormatter nf = NumberFormatter::forSkeleton(testCase.skeleton, status).locale(Locale::getUS());
UnicodeString actualResult = nf.formatDouble(3.14, status).toString(status);

status.setScope(testCase.skeleton);
if (!status.errIfFailureAndReset()) {
assertEquals(testCase.skeleton, testCase.expectedResult, actualResult);
}
}
}

#endif /* #if !UCONFIG_NO_FORMATTING */
Original file line number Diff line number Diff line change
Expand Up @@ -1094,11 +1094,12 @@ private static void parseMeasureUnitOption(StringSegment segment, MacroProps mac
}
String type = segment.subSequence(0, firstHyphen).toString();
String subType = segment.subSequence(firstHyphen + 1, segment.length()).toString();
MeasureUnit unit = MeasureUnit.getUnit(type, subType);
MeasureUnit unit = MeasureUnit.validateAndGet(type, subType);
if (unit != null) {
macros.unit = unit;
return;
}

throw new SkeletonSyntaxException("Unknown measure unit", segment);
}

Expand Down
17 changes: 17 additions & 0 deletions icu4j/main/core/src/main/java/com/ibm/icu/util/MeasureUnit.java
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,23 @@ public static MeasureUnit getUnit(String type, String subtype) {
return units != null ? units.get(subtype) : null;
}

@Deprecated
public static MeasureUnit validateAndGet(String type, String subtype) {
MeasureUnit result = MeasureUnit.getUnit(type, subtype);

if (result == null) {
try {
result = MeasureUnit.forIdentifier(subtype);
if (!result.getType().equals(type)) {
Copy link
Member

Choose a reason for hiding this comment

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

Observation: you don't have if (!result.getType().equals(type)) in the C++ code

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I do. The check for typeIdx >= 0 on line 2774 is doing that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, you're right. I don't have that exact check. I'll add it...

result = null;
}
} catch (IllegalArgumentException e) {
// leave result as null
}
}
return result;
}

static final UnicodeSet ASCII = new UnicodeSet('a', 'z').freeze();
static final UnicodeSet ASCII_HYPHEN_DIGITS =
new UnicodeSet('-', '-', '0', '9', 'a', 'z').freeze();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -549,4 +549,45 @@ public void measurementSystemOverride() {
"Wrong result: " + languageTag + ":" + skeleton, expectedResult, actualResult);
}
}

@Test
public void unitAliases() {
class TestCase {
String skeleton;
String expectedResult;

TestCase(String skeleton, String expectedResult) {
this.skeleton = skeleton;
this.expectedResult = expectedResult;
}
}

TestCase[] testCases =
new TestCase[] {
new TestCase("measure-unit/concentr-part-per-1e6", "3.14 ppm"),
new TestCase("measure-unit/concentr-part-per-million", "3.14 ppm"),
new TestCase("measure-unit/concentr-permillion", "3.14 ppm"),
new TestCase(
"measure-unit/concentr-milligram-ofglucose-per-deciliter",
"3.14 mg/dL"),
new TestCase("measure-unit/concentr-milligram-per-deciliter", "3.14 mg/dL"),
new TestCase("measure-unit/mass-tonne", "3.14 t"),
new TestCase("measure-unit/mass-metric-ton", "3.14 t"),
};

for (TestCase testCase : testCases) {
try {
LocalizedNumberFormatter nf =
NumberFormatter.forSkeleton(testCase.skeleton).locale(Locale.US);
String actualResult = nf.format(3.14).toString();

assertEquals(
"Wrong result for " + testCase.skeleton + ":",
testCase.expectedResult,
actualResult);
} catch (SkeletonSyntaxException e) {
fail(testCase.skeleton);
}
}
}
}
Loading