Use new unit identifier parsing code in number skeletons

This commit is contained in:
Shane F. Carr 2020-01-21 23:12:49 +01:00
parent 96b1bacfb0
commit ce3fa7af06
8 changed files with 57 additions and 64 deletions

View file

@ -535,7 +535,7 @@ static const char * const gSubTypes[] = {
"solar-mass",
"stone",
"ton",
"base",
"one",
"percent",
"permille",
"gigawatt",
@ -2030,6 +2030,8 @@ MeasureUnit::MeasureUnit(char* idToAdopt)
if (fId == nullptr) {
// Invalid; reset to the base dimensionless unit
setTo(kBaseTypeIdx, kBaseSubTypeIdx);
} else if (findBySubType(idToAdopt, this)) {
// findBySubType frees fId
}
}
@ -2202,38 +2204,6 @@ bool MeasureUnit::findBySubType(StringPiece subType, MeasureUnit* output) {
return false;
}
bool MeasureUnit::parseCoreUnitIdentifier(
StringPiece coreUnitIdentifier,
MeasureUnit* numerator,
MeasureUnit* denominator,
UErrorCode& status) {
if (U_FAILURE(status)) {
return false;
}
// First search for the whole code unit identifier as a subType
if (findBySubType(coreUnitIdentifier, numerator)) {
return false; // found a numerator but not denominator
}
// If not found, try breaking apart numerator and denominator
int32_t perIdx = coreUnitIdentifier.find("-per-", 0);
if (perIdx == -1) {
// String does not contain "-per-"
status = U_ILLEGAL_ARGUMENT_ERROR;
return false;
}
StringPiece numeratorStr(coreUnitIdentifier, 0, perIdx);
StringPiece denominatorStr(coreUnitIdentifier, perIdx + 5);
if (findBySubType(numeratorStr, numerator) && findBySubType(denominatorStr, denominator)) {
return true; // found both a numerator and denominator
}
// The numerator or denominator were invalid
status = U_ILLEGAL_ARGUMENT_ERROR;
return false;
}
MeasureUnit MeasureUnit::resolveUnitPerUnit(
const MeasureUnit &unit, const MeasureUnit &perUnit, bool* isResolved) {
int32_t unitOffset = unit.getOffset();

View file

@ -377,6 +377,9 @@ public:
typedef MaybeStackVector<SingleUnit, 3> SingleUnitList;
void append(SingleUnit&& singleUnit, UErrorCode& status) {
if (singleUnit.simpleUnitIndex == 0) {
return;
}
if (singleUnit.power >= 0) {
appendImpl(numerator, std::move(singleUnit), status);
} else {
@ -420,7 +423,7 @@ public:
}
bool isSingle() const {
return numerator.length() + denominator.length() == 1;
return numerator.length() + denominator.length() <= 1;
}
bool isEmpty() const {

View file

@ -11,7 +11,7 @@ U_NAMESPACE_BEGIN
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NoUnit)
NoUnit U_EXPORT2 NoUnit::base() {
return NoUnit("base");
return NoUnit("one");
}
NoUnit U_EXPORT2 NoUnit::percent() {

View file

@ -188,6 +188,12 @@ LongNameHandler*
LongNameHandler::forMeasureUnit(const Locale &loc, const MeasureUnit &unitRef, const MeasureUnit &perUnit,
const UNumberUnitWidth &width, const PluralRules *rules,
const MicroPropsGenerator *parent, UErrorCode &status) {
if (uprv_strlen(unitRef.getType()) == 0 || uprv_strlen(perUnit.getType()) == 0) {
// TODO(ICU-20941): Unsanctioned unit. Not yet fully supported. Set an error code.
status = U_UNSUPPORTED_ERROR;
return nullptr;
}
MeasureUnit unit = unitRef;
if (uprv_strcmp(perUnit.getType(), "none") != 0) {
// Compound unit: first try to simplify (e.g., meters per second is its own unit).

View file

@ -1040,12 +1040,22 @@ void blueprint_helpers::parseIdentifierUnitOption(const StringSegment& segment,
SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
ErrorCode internalStatus;
MeasureUnit::parseCoreUnitIdentifier(buffer.toStringPiece(), &macros.unit, &macros.perUnit, internalStatus);
MeasureUnit fullUnit = MeasureUnit::forIdentifier(buffer.toStringPiece(), internalStatus);
auto subUnits = fullUnit.getSingleUnits(internalStatus);
if (internalStatus.isFailure()) {
// throw new SkeletonSyntaxException("Invalid core unit identifier", segment, e);
status = U_NUMBER_SKELETON_SYNTAX_ERROR;
return;
}
for (int32_t i = 0; i < subUnits.length(); i++) {
const MeasureUnit& subUnit = subUnits[i];
if (subUnit.getPower(status) > 0) {
macros.unit = macros.unit.product(subUnit, status);
} else {
macros.perUnit = macros.perUnit.product(subUnit.reciprocal(status), status);
}
}
}
void blueprint_helpers::parseFractionStem(const StringSegment& segment, MacroProps& macros,

View file

@ -553,26 +553,6 @@ class U_I18N_API MeasureUnit: public UObject {
*/
static int32_t internalGetIndexForTypeAndSubtype(const char *type, const char *subtype);
/**
* ICU use only.
* @return Whether subType is known to ICU.
* @internal
*/
static bool findBySubType(StringPiece subType, MeasureUnit* output);
/**
* ICU use only.
* Parse a core unit identifier into a numerator and denominator unit.
* @param coreUnitIdentifier The string to parse.
* @param numerator Output: set to the numerator unit.
* @param denominator Output: set to the denominator unit, if present.
* @param status Set to U_ILLEGAL_ARGUMENT_ERROR if the core unit identifier is not known.
* @return Whether both a numerator and denominator are returned.
* @internal
*/
static bool parseCoreUnitIdentifier(
StringPiece coreUnitIdentifier, MeasureUnit* numerator, MeasureUnit* denominator, UErrorCode& status);
/**
* ICU use only.
* @internal
@ -3725,6 +3705,11 @@ private:
void setTo(int32_t typeId, int32_t subTypeId);
int32_t getOffset() const;
static MeasureUnit *create(int typeId, int subTypeId, UErrorCode &status);
/**
* @return Whether subType is known to ICU.
*/
static bool findBySubType(StringPiece subType, MeasureUnit* output);
};
U_NAMESPACE_END

View file

@ -3372,8 +3372,7 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
assertTrue("order matters inequality", footInch != inchFoot);
// TODO(ICU-20920): Enable the one1 tests when the dimensionless base unit ID is updated
// MeasureUnit one1;
MeasureUnit one1;
MeasureUnit one2 = MeasureUnit::forIdentifier("one", status);
MeasureUnit one3 = MeasureUnit::forIdentifier("", status);
MeasureUnit squareOne = one2.withPower(2, status);
@ -3382,20 +3381,23 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
MeasureUnit onePerSquareKiloOne = squareKiloOne.reciprocal(status);
MeasureUnit oneOne = MeasureUnit::forIdentifier("one-one", status);
MeasureUnit onePlusOne = MeasureUnit::forIdentifier("one+one", status);
MeasureUnit kilometer2 = one2.product(kilometer, status);
// verifySingleUnit(one1, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(one1, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(one2, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(one3, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(squareOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(onePerOne, UMEASURE_SI_PREFIX_ONE, -1, "one-per-one");
verifySingleUnit(onePerOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(squareKiloOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(onePerSquareKiloOne, UMEASURE_SI_PREFIX_ONE, -1, "one-per-one");
verifySingleUnit(onePerSquareKiloOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(oneOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(onePlusOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(kilometer2, UMEASURE_SI_PREFIX_KILO, 1, "kilometer");
// assertTrue("one equality", one1 == one2);
assertTrue("one equality", one1 == one2);
assertTrue("one equality", one2 == one3);
assertTrue("one-per-one equality", onePerOne == onePerSquareKiloOne);
assertTrue("kilometer equality", kilometer == kilometer2);
}

View file

@ -674,7 +674,7 @@ void NumberFormatterApiTest::unitCompoundMeasure() {
assertFormatDescending(
u"Meters Per Second Short (unit that simplifies) and perUnit method",
u"measure-unit/length-meter per-measure-unit/duration-second",
u"~unit/meter-per-second", // does not round-trip to the full skeleton above
u"unit/meter-per-second",
NumberFormatter::with().unit(METER).perUnit(SECOND),
Locale::getEnglish(),
u"87,650 m/s",
@ -718,6 +718,23 @@ void NumberFormatterApiTest::unitCompoundMeasure() {
u"0.08765 J/fur",
u"0.008765 J/fur",
u"0 J/fur");
// TODO(ICU-20941): Support constructions such as this one.
// assertFormatDescending(
// u"Joules Per Furlong Short with unit identifier via API",
// u"measure-unit/energy-joule per-measure-unit/length-furlong",
// u"unit/joule-per-furlong",
// NumberFormatter::with().unit(MeasureUnit::forIdentifier("joule-per-furlong", status)),
// Locale::getEnglish(),
// u"87,650 J/fur",
// u"8,765 J/fur",
// u"876.5 J/fur",
// u"87.65 J/fur",
// u"8.765 J/fur",
// u"0.8765 J/fur",
// u"0.08765 J/fur",
// u"0.008765 J/fur",
// u"0 J/fur");
}
void NumberFormatterApiTest::unitCurrency() {
@ -2777,7 +2794,7 @@ void NumberFormatterApiTest::fieldPositionCoverage() {
FormattedNumber result = assertFormatSingle(
message,
u"measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name",
u"~unit/meter-per-second unit-width-full-name", // does not round-trip to the full skeleton above
u"unit/meter-per-second unit-width-full-name",
NumberFormatter::with().unit(METER).perUnit(SECOND).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
"ky", // locale with the interesting data
68,