MicroPropsGenerator::~MicroPropsGenerator() = default;
-NumberFormatterImpl* NumberFormatterImpl::fromMacros(const MacroProps& macros, UErrorCode& status) {
- return new NumberFormatterImpl(macros, true, status);
+NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& status)
+ : NumberFormatterImpl(macros, true, status) {
}
-void NumberFormatterImpl::applyStatic(const MacroProps& macros, DecimalQuantity& inValue,
- NumberStringBuilder& outString, UErrorCode& status) {
+int32_t NumberFormatterImpl::formatStatic(const MacroProps& macros, DecimalQuantity& inValue,
+ NumberStringBuilder& outString, UErrorCode& status) {
NumberFormatterImpl impl(macros, false, status);
- impl.applyUnsafe(inValue, outString, status);
+ MicroProps& micros = impl.preProcessUnsafe(inValue, status);
+ if (U_FAILURE(status)) { return 0; }
+ int32_t length = writeNumber(micros, inValue, outString, 0, status);
+ length += writeAffixes(micros, outString, 0, length, status);
+ return length;
}
int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, int8_t signum,
// The "unsafe" method simply re-uses fMicros, eliminating the extra copy operation.
// See MicroProps::processQuantity() for details.
-void NumberFormatterImpl::apply(DecimalQuantity& inValue, NumberStringBuilder& outString,
+int32_t NumberFormatterImpl::format(DecimalQuantity& inValue, NumberStringBuilder& outString,
UErrorCode& status) const {
- if (U_FAILURE(status)) { return; }
MicroProps micros;
- if (!fMicroPropsGenerator) { return; }
- fMicroPropsGenerator->processQuantity(inValue, micros, status);
- if (U_FAILURE(status)) { return; }
- microsToString(micros, inValue, outString, status);
+ preProcess(inValue, micros, status);
+ if (U_FAILURE(status)) { return 0; }
+ int32_t length = writeNumber(micros, inValue, outString, 0, status);
+ length += writeAffixes(micros, outString, 0, length, status);
+ return length;
}
-void NumberFormatterImpl::applyUnsafe(DecimalQuantity& inValue, NumberStringBuilder& outString,
- UErrorCode& status) {
+void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& microsOut,
+ UErrorCode& status) const {
if (U_FAILURE(status)) { return; }
+ if (fMicroPropsGenerator == nullptr) {
+ status = U_INTERNAL_PROGRAM_ERROR;
+ return;
+ }
+ fMicroPropsGenerator->processQuantity(inValue, microsOut, status);
+ microsOut.rounder.apply(inValue, status);
+ microsOut.integerWidth.apply(inValue, status);
+}
+
+MicroProps& NumberFormatterImpl::preProcessUnsafe(DecimalQuantity& inValue, UErrorCode& status) {
+ if (U_FAILURE(status)) {
+ return fMicros; // must always return a value
+ }
+ if (fMicroPropsGenerator == nullptr) {
+ status = U_INTERNAL_PROGRAM_ERROR;
+ return fMicros; // must always return a value
+ }
fMicroPropsGenerator->processQuantity(inValue, fMicros, status);
- if (U_FAILURE(status)) { return; }
- microsToString(fMicros, inValue, outString, status);
+ fMicros.rounder.apply(inValue, status);
+ fMicros.integerWidth.apply(inValue, status);
+ return fMicros;
}
int32_t NumberFormatterImpl::getPrefixSuffix(int8_t signum, StandardPlural::Form plural,
const Modifier* modifier = fImmutablePatternModifier->getModifier(signum, plural);
modifier->apply(outString, 0, 0, status);
if (U_FAILURE(status)) { return 0; }
- return modifier->getPrefixLength(status);
+ return modifier->getPrefixLength();
}
int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(int8_t signum, StandardPlural::Form plural,
fPatternModifier->setNumberProperties(signum, plural);
fPatternModifier->apply(outString, 0, 0, status);
if (U_FAILURE(status)) { return 0; }
- return fPatternModifier->getPrefixLength(status);
+ return fPatternModifier->getPrefixLength();
}
NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) {
// Pre-compute a few values for efficiency.
bool isCurrency = utils::unitIsCurrency(macros.unit);
bool isNoUnit = utils::unitIsNoUnit(macros.unit);
- bool isPercent = isNoUnit && utils::unitIsPercent(macros.unit);
- bool isPermille = isNoUnit && utils::unitIsPermille(macros.unit);
- bool isCldrUnit = !isCurrency && !isNoUnit;
+ bool isPercent = utils::unitIsPercent(macros.unit);
+ bool isPermille = utils::unitIsPermille(macros.unit);
bool isAccounting =
macros.sign == UNUM_SIGN_ACCOUNTING || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS ||
macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO;
- CurrencyUnit currency(nullptr, status);
+ CurrencyUnit currency(u"", status);
if (isCurrency) {
currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit
}
if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) {
unitWidth = macros.unitWidth;
}
+ bool isCldrUnit = !isCurrency && !isNoUnit &&
+ (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME || !(isPercent || isPermille));
// Select the numbering system.
- LocalPointer<const NumberingSystem> nsLocal;
- const NumberingSystem* ns;
+ const NumberingSystem* ns = nullptr;
if (macros.symbols.isNumberingSystem()) {
ns = macros.symbols.getNumberingSystem();
- } else {
- // TODO: Is there a way to avoid creating the NumberingSystem object?
- ns = NumberingSystem::createInstance(macros.locale, status);
- // Give ownership to the function scope.
- nsLocal.adoptInstead(ns);
}
- const char* nsName = U_SUCCESS(status) ? ns->getName() : "latn";
+ // else there is no need to create a NumberingSystem object, it is only used
+ // for two things (Apple rdar://51672521):
+ // 1. Passing to new DecimalFormatSymbols; but if we do not have one, new
+ // DecimalFormat symbols can create one anyway so there is no need to do it
+ // ahead of time, and
+ // 2. Getting the nsName. But with a small DecimalFormatSymbols change (per
+ // rdar://51672521) we can get the name from DecimalFormatSymbols which we need
+ // here anyway.
+ const char* nsName = (ns != nullptr)? ns->getName() : nullptr;
// Resolve the symbols. Do this here because currency may need to customize them.
if (macros.symbols.isDecimalFormatSymbols()) {
fMicros.symbols = macros.symbols.getDecimalFormatSymbols();
} else {
- fMicros.symbols = new DecimalFormatSymbols(macros.locale, *ns, status);
+ fMicros.symbols = (ns != nullptr)?
+ new DecimalFormatSymbols(macros.locale, *ns, status):
+ new DecimalFormatSymbols(macros.locale, status);
// Give ownership to the NumberFormatterImpl.
fSymbols.adoptInstead(fMicros.symbols);
}
+ // Resolve nsName and save (Apple rdar://51672521)
+ if (nsName == nullptr && U_SUCCESS(status)) {
+ nsName = fMicros.symbols->getNSName();
+ }
+ if (nsName == nullptr || nsName[0] == 0) {
+ nsName = "latn";
+ }
+ uprv_strncpy(fMicros.nsName, nsName, 8);
+ fMicros.nsName[8] = 0; // guarantee NUL-terminated
// Load and parse the pattern string. It is used for grouping sizes and affixes only.
// If we are formatting currency, check for a currency-specific pattern.
}
if (pattern == nullptr) {
CldrPatternStyle patternStyle;
- if (isPercent || isPermille) {
+ if (isCldrUnit) {
+ patternStyle = CLDR_PATTERN_STYLE_DECIMAL;
+ } else if (isPercent || isPermille) {
patternStyle = CLDR_PATTERN_STYLE_PERCENT;
} else if (!isCurrency || unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) {
patternStyle = CLDR_PATTERN_STYLE_DECIMAL;
fPatternModifier.adoptInstead(patternModifier);
patternModifier->setPatternInfo(
macros.affixProvider != nullptr ? macros.affixProvider
- : static_cast<const AffixPatternProvider*>(fPatternInfo.getAlias()));
+ : static_cast<const AffixPatternProvider*>(fPatternInfo.getAlias()),
+ UNUM_FIELD_COUNT);
patternModifier->setPatternAttributes(fMicros.sign, isPermille);
if (patternModifier->needsPlurals()) {
patternModifier->setSymbols(
// Outer modifier (CLDR units and currency long names)
if (isCldrUnit) {
fLongNameHandler.adoptInstead(
- new LongNameHandler(
- LongNameHandler::forMeasureUnit(
- macros.locale,
- macros.unit,
- macros.perUnit,
- unitWidth,
- resolvePluralRules(macros.rules, macros.locale, status),
- chain,
- status)));
+ LongNameHandler::forMeasureUnit(
+ macros.locale,
+ macros.unit,
+ macros.perUnit,
+ unitWidth,
+ resolvePluralRules(macros.rules, macros.locale, status),
+ chain,
+ status));
chain = fLongNameHandler.getAlias();
} else if (isCurrency && unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) {
fLongNameHandler.adoptInstead(
- new LongNameHandler(
- LongNameHandler::forCurrencyLongNames(
- macros.locale,
- currency,
- resolvePluralRules(macros.rules, macros.locale, status),
- chain,
- status)));
+ LongNameHandler::forCurrencyLongNames(
+ macros.locale,
+ currency,
+ resolvePluralRules(macros.rules, macros.locale, status),
+ chain,
+ status));
chain = fLongNameHandler.getAlias();
} else {
// No outer modifier required
return fRules.getAlias();
}
-int32_t NumberFormatterImpl::microsToString(const MicroProps& micros, DecimalQuantity& quantity,
- NumberStringBuilder& string, UErrorCode& status) {
- micros.rounder.apply(quantity, status);
- micros.integerWidth.apply(quantity, status);
- int32_t length = writeNumber(micros, quantity, string, status);
- // NOTE: When range formatting is added, these modifiers can bubble up.
- // For now, apply them all here at once.
+int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, NumberStringBuilder& string,
+ int32_t start, int32_t end, UErrorCode& status) {
// Always apply the inner modifier (which is "strong").
- length += micros.modInner->apply(string, 0, length, status);
+ int32_t length = micros.modInner->apply(string, start, end, status);
if (micros.padding.isValid()) {
length += micros.padding
- .padAndApply(*micros.modMiddle, *micros.modOuter, string, 0, length, status);
+ .padAndApply(*micros.modMiddle, *micros.modOuter, string, start, length + end, status);
} else {
- length += micros.modMiddle->apply(string, 0, length, status);
- length += micros.modOuter->apply(string, 0, length, status);
+ length += micros.modMiddle->apply(string, start, length + end, status);
+ length += micros.modOuter->apply(string, start, length + end, status);
}
return length;
}
int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity,
- NumberStringBuilder& string, UErrorCode& status) {
+ NumberStringBuilder& string, int32_t index,
+ UErrorCode& status) {
int32_t length = 0;
if (quantity.isInfinite()) {
length += string.insert(
- length,
+ length + index,
micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kInfinitySymbol),
UNUM_INTEGER_FIELD,
status);
} else if (quantity.isNaN()) {
length += string.insert(
- length,
+ length + index,
micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kNaNSymbol),
UNUM_INTEGER_FIELD,
status);
} else {
// Add the integer digits
- length += writeIntegerDigits(micros, quantity, string, status);
+ length += writeIntegerDigits(micros, quantity, string, length + index, status);
// Add the decimal point
if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) {
length += string.insert(
- length,
+ length + index,
micros.useCurrency ? micros.symbols->getSymbol(
DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol) : micros
.symbols
}
// Add the fraction digits
- length += writeFractionDigits(micros, quantity, string, status);
+ length += writeFractionDigits(micros, quantity, string, length + index, status);
}
return length;
}
int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity,
- NumberStringBuilder& string, UErrorCode& status) {
+ NumberStringBuilder& string, int32_t index,
+ UErrorCode& status) {
int length = 0;
int integerCount = quantity.getUpperDisplayMagnitude() + 1;
for (int i = 0; i < integerCount; i++) {
// Add grouping separator
if (micros.grouping.groupAtPosition(i, quantity)) {
length += string.insert(
- 0,
+ index,
micros.useCurrency ? micros.symbols->getSymbol(
DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol)
: micros.symbols->getSymbol(
// Get and append the next digit value
int8_t nextDigit = quantity.getDigit(i);
length += utils::insertDigitFromSymbols(
- string, 0, nextDigit, *micros.symbols, UNUM_INTEGER_FIELD, status);
+ string, index, nextDigit, *micros.symbols, UNUM_INTEGER_FIELD, status);
}
return length;
}
int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity,
- NumberStringBuilder& string, UErrorCode& status) {
+ NumberStringBuilder& string, int32_t index,
+ UErrorCode& status) {
int length = 0;
int fractionCount = -quantity.getLowerDisplayMagnitude();
for (int i = 0; i < fractionCount; i++) {
// Get and append the next digit value
int8_t nextDigit = quantity.getDigit(-i - 1);
length += utils::insertDigitFromSymbols(
- string, string.length(), nextDigit, *micros.symbols, UNUM_FRACTION_FIELD, status);
+ string, length + index, nextDigit, *micros.symbols, UNUM_FRACTION_FIELD, status);
}
return length;
}