]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/number_formatimpl.cpp
ICU-64243.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / number_formatimpl.cpp
index 3f887128bcc668cec2a3f8a79a1cc067eea7631b..b036b6ef932e0bebaca5d7a97b0a27ece71a5ae7 100644 (file)
@@ -67,14 +67,18 @@ getCurrencyFormatInfo(const Locale& locale, const char* isoCode, UErrorCode& sta
 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,
@@ -89,22 +93,40 @@ int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, int
 // 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,
@@ -115,7 +137,7 @@ int32_t NumberFormatterImpl::getPrefixSuffix(int8_t signum, StandardPlural::Form
     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,
@@ -126,7 +148,7 @@ int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(int8_t signum, StandardPlural
     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) {
@@ -150,13 +172,12 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
     // 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
     }
@@ -172,28 +193,43 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
     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.
@@ -219,7 +255,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
     }
     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;
@@ -322,7 +360,8 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
     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(
@@ -344,25 +383,23 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
     // 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
@@ -404,50 +441,46 @@ NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Local
     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
@@ -458,21 +491,22 @@ int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuanti
         }
 
         // 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(
@@ -484,20 +518,21 @@ int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, Decima
         // 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;
 }