Derived NumberRangeFormatterSettings<Derived>::numberFormatterBoth(const UnlocalizedNumberFormatter& formatter) const& {
Derived copy(*this);
copy.fMacros.formatter1 = formatter;
- copy.fMacros.formatter2 = formatter;
+ copy.fMacros.singleFormatter = true;
return copy;
}
Derived NumberRangeFormatterSettings<Derived>::numberFormatterBoth(const UnlocalizedNumberFormatter& formatter) && {
Derived move(std::move(*this));
move.fMacros.formatter1 = formatter;
- move.fMacros.formatter2 = formatter;
+ move.fMacros.singleFormatter = true;
return move;
}
template<typename Derived>
Derived NumberRangeFormatterSettings<Derived>::numberFormatterBoth(UnlocalizedNumberFormatter&& formatter) const& {
Derived copy(*this);
- copy.fMacros.formatter1 = formatter;
- copy.fMacros.formatter2 = std::move(formatter);
+ copy.fMacros.formatter1 = std::move(formatter);
+ copy.fMacros.singleFormatter = true;
return copy;
}
template<typename Derived>
Derived NumberRangeFormatterSettings<Derived>::numberFormatterBoth(UnlocalizedNumberFormatter&& formatter) && {
Derived move(std::move(*this));
- move.fMacros.formatter1 = formatter;
- move.fMacros.formatter2 = std::move(formatter);
+ move.fMacros.formatter1 = std::move(formatter);
+ move.fMacros.singleFormatter = true;
return move;
}
Derived NumberRangeFormatterSettings<Derived>::numberFormatterFirst(const UnlocalizedNumberFormatter& formatter) const& {
Derived copy(*this);
copy.fMacros.formatter1 = formatter;
+ copy.fMacros.singleFormatter = false;
return copy;
}
Derived NumberRangeFormatterSettings<Derived>::numberFormatterFirst(const UnlocalizedNumberFormatter& formatter) && {
Derived move(std::move(*this));
move.fMacros.formatter1 = formatter;
+ move.fMacros.singleFormatter = false;
return move;
}
Derived NumberRangeFormatterSettings<Derived>::numberFormatterFirst(UnlocalizedNumberFormatter&& formatter) const& {
Derived copy(*this);
copy.fMacros.formatter1 = std::move(formatter);
+ copy.fMacros.singleFormatter = false;
return copy;
}
Derived NumberRangeFormatterSettings<Derived>::numberFormatterFirst(UnlocalizedNumberFormatter&& formatter) && {
Derived move(std::move(*this));
move.fMacros.formatter1 = std::move(formatter);
+ move.fMacros.singleFormatter = false;
return move;
}
Derived NumberRangeFormatterSettings<Derived>::numberFormatterSecond(const UnlocalizedNumberFormatter& formatter) const& {
Derived copy(*this);
copy.fMacros.formatter2 = formatter;
+ copy.fMacros.singleFormatter = false;
return copy;
}
Derived NumberRangeFormatterSettings<Derived>::numberFormatterSecond(const UnlocalizedNumberFormatter& formatter) && {
Derived move(std::move(*this));
move.fMacros.formatter2 = formatter;
+ move.fMacros.singleFormatter = false;
return move;
}
Derived NumberRangeFormatterSettings<Derived>::numberFormatterSecond(UnlocalizedNumberFormatter&& formatter) const& {
Derived copy(*this);
copy.fMacros.formatter2 = std::move(formatter);
+ copy.fMacros.singleFormatter = false;
return copy;
}
Derived NumberRangeFormatterSettings<Derived>::numberFormatterSecond(UnlocalizedNumberFormatter&& formatter) && {
Derived move(std::move(*this));
move.fMacros.formatter2 = std::move(formatter);
+ move.fMacros.singleFormatter = false;
return move;
}
if (U_FAILURE(status)) { return; }
for (int i = 0; miscTable.getKeyAndValue(i, key, value); i++) {
if (uprv_strcmp(key, "range") == 0) {
- if (fData.rangePattern.getArgumentLimit() == 0) {
+ if (fData.rangePattern.getArgumentLimit() != 0) {
continue; // have already seen this pattern
}
fData.rangePattern = {value.getUnicodeString(status), status};
} else if (uprv_strcmp(key, "approximately") == 0) {
- if (fData.approximatelyPattern.getArgumentLimit() == 0) {
+ if (fData.approximatelyPattern.getArgumentLimit() != 0) {
continue; // have already seen this pattern
}
fData.approximatelyPattern = {value.getUnicodeString(status), status};
ures_getAllItemsWithFallback(rb.getAlias(), dataPath.data(), sink, status);
if (U_FAILURE(status)) { return; }
- if (uprv_strcmp(nsName, "latn") != 0 && (data.rangePattern.getArgumentLimit() == 0
- || data.approximatelyPattern.getArgumentLimit() == 0)) {
- // fall back to Latin data
- ures_getAllItemsWithFallback(rb.getAlias(), "NumberElements/latn/miscPatterns", sink, status);
- if (U_FAILURE(status)) { return; }
- }
+ // TODO: Is it necessary to maually fall back to latn, or does the data sink take care of that?
if (data.rangePattern.getArgumentLimit() == 0) {
// No data!
NumberRangeFormatterImpl::NumberRangeFormatterImpl(const RangeMacroProps& macros, UErrorCode& status)
: formatterImpl1(macros.formatter1.fMacros, status),
formatterImpl2(macros.formatter2.fMacros, status),
- fSameFormatters(true), // FIXME
+ fSameFormatters(macros.singleFormatter),
fCollapse(macros.collapse),
fIdentityFallback(macros.identityFallback) {
- // TODO: get local ns
+
+ // TODO: As of this writing (ICU 63), there is no locale that has different number miscPatterns
+ // based on numbering system. Therefore, data is loaded only from latn. If this changes,
+ // this part of the code should be updated to load from the local numbering system.
+ // The numbering system could come from the one specified in the NumberFormatter passed to
+ // numberFormatterBoth() or similar.
+
NumberRangeData data;
getNumberRangeData(macros.locale.getName(), "latn", data, status);
if (U_FAILURE(status)) { return; }
return;
}
- // Identity case 1: equal before rounding
- if (equalBeforeRounding) {
- }
-
MicroProps micros1;
MicroProps micros2;
- formatterImpl1.preProcess(data.quantity1, micros1, status);
- formatterImpl2.preProcess(data.quantity2, micros2, status);
- if (U_FAILURE(status)) {
+ if (fSameFormatters) {
+ formatterImpl1.preProcess(data.quantity1, micros1, status);
+ formatterImpl1.preProcess(data.quantity2, micros2, status);
+ } else {
+ // If the formatters are different, an identity is not possible.
+ // Always use formatRange().
+ formatterImpl1.preProcess(data.quantity1, micros1, status);
+ formatterImpl2.preProcess(data.quantity2, micros2, status);
+ formatRange(data, micros1, micros2, status);
return;
}
void NumberRangeFormatterImpl::formatSingleValue(UFormattedNumberRangeData& data,
MicroProps& micros1, MicroProps& micros2,
UErrorCode& status) const {
+ if (U_FAILURE(status)) { return; }
if (fSameFormatters) {
formatterImpl1.format(data.quantity1, data.string, status);
} else {
void NumberRangeFormatterImpl::formatApproximately (UFormattedNumberRangeData& data,
MicroProps& micros1, MicroProps& micros2,
UErrorCode& status) const {
+ if (U_FAILURE(status)) { return; }
if (fSameFormatters) {
int32_t length = formatterImpl1.format(data.quantity1, data.string, status);
fApproximatelyModifier.apply(data.string, 0, length, status);
void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data,
MicroProps& micros1, MicroProps& micros2,
UErrorCode& status) const {
+ if (U_FAILURE(status)) { return; }
// modInner is always notation (scientific); collapsable in ALL.
// modOuter is always units; collapsable in ALL, AUTO, and UNIT.
if (U_FAILURE(status)) { return; }
lengthInfix = lengthRange - lengthPrefix - lengthSuffix;
+ // SPACING HEURISTIC
+ // Add spacing unless all modifiers are collapsed.
+ {
+ bool repeatInner = !collapseInner && micros1.modInner->getCodePointCount() > 0;
+ bool repeatMiddle = !collapseMiddle && micros1.modMiddle->getCodePointCount() > 0;
+ bool repeatOuter = !collapseOuter && micros1.modOuter->getCodePointCount() > 0;
+ if (repeatInner || repeatMiddle || repeatOuter) {
+ // Add spacing
+ lengthInfix += string.insertCodePoint(UPRV_INDEX_1, u'\u0020', UNUM_FIELD_COUNT, status);
+ lengthInfix += string.insertCodePoint(UPRV_INDEX_2, u'\u0020', UNUM_FIELD_COUNT, status);
+ }
+ }
+
length1 += NumberFormatterImpl::writeNumber(micros1, data.quantity1, string, UPRV_INDEX_0, status);
length2 += NumberFormatterImpl::writeNumber(micros2, data.quantity2, string, UPRV_INDEX_2, status);
lengthInfix += micros1.modInner->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
} else {
length1 += micros1.modInner->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status);
- length2 += micros1.modInner->apply(string, UPRV_INDEX_2, UPRV_INDEX_3, status);
+ length2 += micros2.modInner->apply(string, UPRV_INDEX_2, UPRV_INDEX_3, status);
}
if (collapseMiddle) {
lengthInfix += micros1.modMiddle->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
} else {
length1 += micros1.modMiddle->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status);
- length2 += micros1.modMiddle->apply(string, UPRV_INDEX_2, UPRV_INDEX_3, status);
+ length2 += micros2.modMiddle->apply(string, UPRV_INDEX_2, UPRV_INDEX_3, status);
}
if (collapseOuter) {
- micros1.modOuter->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
+ lengthInfix += micros1.modOuter->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
} else {
- micros1.modOuter->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status);
- micros1.modOuter->apply(string, UPRV_INDEX_2, UPRV_INDEX_3, status);
+ length1 += micros1.modOuter->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status);
+ length2 += micros2.modOuter->apply(string, UPRV_INDEX_2, UPRV_INDEX_3, status);
}
}
#include <cmath>
#include <numparse_affixes.h>
-using icu::unisets::get;
+// Horrible workaround for the lack of a status code in the constructor...
+// (Also affects numbertest_api.cpp)
+UErrorCode globalNumberRangeFormatterTestStatus = U_ZERO_ERROR;
+
+NumberRangeFormatterTest::NumberRangeFormatterTest()
+ : NumberRangeFormatterTest(globalNumberRangeFormatterTestStatus) {
+}
+
+NumberRangeFormatterTest::NumberRangeFormatterTest(UErrorCode& status)
+ : USD(u"USD", status),
+ GBP(u"GBP", status),
+ PTE(u"PTE", status) {
+
+ // Check for error on the first MeasureUnit in case there is no data
+ LocalPointer<MeasureUnit> unit(MeasureUnit::createMeter(status));
+ if (U_FAILURE(status)) {
+ dataerrln("%s %d status = %s", __FILE__, __LINE__, u_errorName(status));
+ return;
+ }
+ METER = *unit;
+
+ KILOMETER = *LocalPointer<MeasureUnit>(MeasureUnit::createKilometer(status));
+ FAHRENHEIT = *LocalPointer<MeasureUnit>(MeasureUnit::createFahrenheit(status));
+ KELVIN = *LocalPointer<MeasureUnit>(MeasureUnit::createKelvin(status));
+}
void NumberRangeFormatterTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) {
if (exec) {
u"Basic",
NumberRangeFormatter::with(),
Locale("en-us"),
- u"1 --- 5",
+ u"1–5",
u"~5",
u"~5",
- u"0 --- 3",
+ u"0–3",
u"~0",
- u"3 --- 3,000",
- u"3,000 --- 5,000",
- u"4,999 --- 5,001",
+ u"3–3,000",
+ u"3,000–5,000",
+ u"4,999–5,001",
u"~5,000",
- u"5,000 --- 5,000,000");
+ u"5,000–5,000,000");
+
+ assertFormatRange(
+ u"Basic with units",
+ NumberRangeFormatter::with()
+ .numberFormatterBoth(NumberFormatter::with().unit(METER)),
+ Locale("en-us"),
+ u"1–5 m",
+ u"~5 m",
+ u"~5 m",
+ u"0–3 m",
+ u"~0 m",
+ u"3–3,000 m",
+ u"3,000–5,000 m",
+ u"4,999–5,001 m",
+ u"~5,000 m",
+ u"5,000–5,000,000 m");
+
+ assertFormatRange(
+ u"Basic with different units",
+ NumberRangeFormatter::with()
+ .numberFormatterFirst(NumberFormatter::with().unit(METER))
+ .numberFormatterSecond(NumberFormatter::with().unit(KILOMETER)),
+ Locale("en-us"),
+ u"1 m – 5 km",
+ u"5 m – 5 km",
+ u"5 m – 5 km",
+ u"0 m – 3 km",
+ u"0 m – 0 km",
+ u"3 m – 3,000 km",
+ u"3,000 m – 5,000 km",
+ u"4,999 m – 5,001 km",
+ u"5,000 m – 5,000 km",
+ u"5,000 m – 5,000,000 km");
}
void NumberRangeFormatterTest::assertFormatRange(