"Fossies" - the Fresh Open Source Software Archive

Member "icu/source/i18n/number_formatimpl.cpp" (22 Apr 2020, 21061 Bytes) of package /linux/misc/icu4c-67_1-src.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "number_formatimpl.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes reports: 67rc_vs_67_1 or 66_1_vs_67_1.

    1 // © 2017 and later: Unicode, Inc. and others.
    2 // License & terms of use: http://www.unicode.org/copyright.html
    3 
    4 #include "unicode/utypes.h"
    5 
    6 #if !UCONFIG_NO_FORMATTING
    7 
    8 #include "cstring.h"
    9 #include "unicode/ures.h"
   10 #include "uresimp.h"
   11 #include "charstr.h"
   12 #include "number_formatimpl.h"
   13 #include "unicode/numfmt.h"
   14 #include "number_patternstring.h"
   15 #include "number_utils.h"
   16 #include "unicode/numberformatter.h"
   17 #include "unicode/dcfmtsym.h"
   18 #include "number_scientific.h"
   19 #include "number_compact.h"
   20 #include "uresimp.h"
   21 #include "ureslocs.h"
   22 
   23 using namespace icu;
   24 using namespace icu::number;
   25 using namespace icu::number::impl;
   26 
   27 
   28 MicroPropsGenerator::~MicroPropsGenerator() = default;
   29 
   30 
   31 NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& status)
   32     : NumberFormatterImpl(macros, true, status) {
   33 }
   34 
   35 int32_t NumberFormatterImpl::formatStatic(const MacroProps& macros, DecimalQuantity& inValue,
   36                                        FormattedStringBuilder& outString, UErrorCode& status) {
   37     NumberFormatterImpl impl(macros, false, status);
   38     MicroProps& micros = impl.preProcessUnsafe(inValue, status);
   39     if (U_FAILURE(status)) { return 0; }
   40     int32_t length = writeNumber(micros, inValue, outString, 0, status);
   41     length += writeAffixes(micros, outString, 0, length, status);
   42     return length;
   43 }
   44 
   45 int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, Signum signum,
   46                                                    StandardPlural::Form plural,
   47                                                    FormattedStringBuilder& outString, UErrorCode& status) {
   48     NumberFormatterImpl impl(macros, false, status);
   49     return impl.getPrefixSuffixUnsafe(signum, plural, outString, status);
   50 }
   51 
   52 // NOTE: C++ SPECIFIC DIFFERENCE FROM JAVA:
   53 // The "safe" apply method uses a new MicroProps. In the MicroPropsGenerator, fMicros is copied into the new instance.
   54 // The "unsafe" method simply re-uses fMicros, eliminating the extra copy operation.
   55 // See MicroProps::processQuantity() for details.
   56 
   57 int32_t NumberFormatterImpl::format(DecimalQuantity& inValue, FormattedStringBuilder& outString,
   58                                 UErrorCode& status) const {
   59     MicroProps micros;
   60     preProcess(inValue, micros, status);
   61     if (U_FAILURE(status)) { return 0; }
   62     int32_t length = writeNumber(micros, inValue, outString, 0, status);
   63     length += writeAffixes(micros, outString, 0, length, status);
   64     return length;
   65 }
   66 
   67 void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& microsOut,
   68                                      UErrorCode& status) const {
   69     if (U_FAILURE(status)) { return; }
   70     if (fMicroPropsGenerator == nullptr) {
   71         status = U_INTERNAL_PROGRAM_ERROR;
   72         return;
   73     }
   74     fMicroPropsGenerator->processQuantity(inValue, microsOut, status);
   75     microsOut.integerWidth.apply(inValue, status);
   76 }
   77 
   78 MicroProps& NumberFormatterImpl::preProcessUnsafe(DecimalQuantity& inValue, UErrorCode& status) {
   79     if (U_FAILURE(status)) {
   80         return fMicros; // must always return a value
   81     }
   82     if (fMicroPropsGenerator == nullptr) {
   83         status = U_INTERNAL_PROGRAM_ERROR;
   84         return fMicros; // must always return a value
   85     }
   86     fMicroPropsGenerator->processQuantity(inValue, fMicros, status);
   87     fMicros.integerWidth.apply(inValue, status);
   88     return fMicros;
   89 }
   90 
   91 int32_t NumberFormatterImpl::getPrefixSuffix(Signum signum, StandardPlural::Form plural,
   92                                              FormattedStringBuilder& outString, UErrorCode& status) const {
   93     if (U_FAILURE(status)) { return 0; }
   94     // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier).
   95     // Safe path: use fImmutablePatternModifier.
   96     const Modifier* modifier = fImmutablePatternModifier->getModifier(signum, plural);
   97     modifier->apply(outString, 0, 0, status);
   98     if (U_FAILURE(status)) { return 0; }
   99     return modifier->getPrefixLength();
  100 }
  101 
  102 int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(Signum signum, StandardPlural::Form plural,
  103                                                    FormattedStringBuilder& outString, UErrorCode& status) {
  104     if (U_FAILURE(status)) { return 0; }
  105     // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier).
  106     // Unsafe path: use fPatternModifier.
  107     fPatternModifier->setNumberProperties(signum, plural);
  108     fPatternModifier->apply(outString, 0, 0, status);
  109     if (U_FAILURE(status)) { return 0; }
  110     return fPatternModifier->getPrefixLength();
  111 }
  112 
  113 NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) {
  114     fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status);
  115 }
  116 
  117 //////////
  118 
  119 const MicroPropsGenerator*
  120 NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, UErrorCode& status) {
  121     if (U_FAILURE(status)) { return nullptr; }
  122     const MicroPropsGenerator* chain = &fMicros;
  123 
  124     // Check that macros is error-free before continuing.
  125     if (macros.copyErrorTo(status)) {
  126         return nullptr;
  127     }
  128 
  129     // TODO: Accept currency symbols from DecimalFormatSymbols?
  130 
  131     // Pre-compute a few values for efficiency.
  132     bool isCurrency = utils::unitIsCurrency(macros.unit);
  133     bool isNoUnit = utils::unitIsNoUnit(macros.unit);
  134     bool isPercent = utils::unitIsPercent(macros.unit);
  135     bool isPermille = utils::unitIsPermille(macros.unit);
  136     bool isAccounting =
  137             macros.sign == UNUM_SIGN_ACCOUNTING || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS ||
  138             macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO;
  139     CurrencyUnit currency(u"", status);
  140     if (isCurrency) {
  141         currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit
  142     }
  143     UNumberUnitWidth unitWidth = UNUM_UNIT_WIDTH_SHORT;
  144     if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) {
  145         unitWidth = macros.unitWidth;
  146     }
  147     bool isCldrUnit = !isCurrency && !isNoUnit &&
  148         (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME || !(isPercent || isPermille));
  149 
  150     // Select the numbering system.
  151     LocalPointer<const NumberingSystem> nsLocal;
  152     const NumberingSystem* ns;
  153     if (macros.symbols.isNumberingSystem()) {
  154         ns = macros.symbols.getNumberingSystem();
  155     } else {
  156         // TODO: Is there a way to avoid creating the NumberingSystem object?
  157         ns = NumberingSystem::createInstance(macros.locale, status);
  158         // Give ownership to the function scope.
  159         nsLocal.adoptInstead(ns);
  160     }
  161     const char* nsName = U_SUCCESS(status) ? ns->getName() : "latn";
  162     uprv_strncpy(fMicros.nsName, nsName, 8);
  163     fMicros.nsName[8] = 0; // guarantee NUL-terminated
  164 
  165     // Resolve the symbols. Do this here because currency may need to customize them.
  166     if (macros.symbols.isDecimalFormatSymbols()) {
  167         fMicros.symbols = macros.symbols.getDecimalFormatSymbols();
  168     } else {
  169         LocalPointer<DecimalFormatSymbols> newSymbols(
  170             new DecimalFormatSymbols(macros.locale, *ns, status), status);
  171         if (U_FAILURE(status)) {
  172             return nullptr;
  173         }
  174         if (isCurrency) {
  175             newSymbols->setCurrency(currency.getISOCurrency(), status);
  176             if (U_FAILURE(status)) {
  177                 return nullptr;
  178             }
  179         }
  180         fMicros.symbols = newSymbols.getAlias();
  181         fSymbols.adoptInstead(newSymbols.orphan());
  182     }
  183 
  184     // Load and parse the pattern string. It is used for grouping sizes and affixes only.
  185     // If we are formatting currency, check for a currency-specific pattern.
  186     const char16_t* pattern = nullptr;
  187     if (isCurrency && fMicros.symbols->getCurrencyPattern() != nullptr) {
  188         pattern = fMicros.symbols->getCurrencyPattern();
  189     }
  190     if (pattern == nullptr) {
  191         CldrPatternStyle patternStyle;
  192         if (isCldrUnit) {
  193             patternStyle = CLDR_PATTERN_STYLE_DECIMAL;
  194         } else if (isPercent || isPermille) {
  195             patternStyle = CLDR_PATTERN_STYLE_PERCENT;
  196         } else if (!isCurrency || unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) {
  197             patternStyle = CLDR_PATTERN_STYLE_DECIMAL;
  198         } else if (isAccounting) {
  199             // NOTE: Although ACCOUNTING and ACCOUNTING_ALWAYS are only supported in currencies right now,
  200             // the API contract allows us to add support to other units in the future.
  201             patternStyle = CLDR_PATTERN_STYLE_ACCOUNTING;
  202         } else {
  203             patternStyle = CLDR_PATTERN_STYLE_CURRENCY;
  204         }
  205         pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status);
  206         if (U_FAILURE(status)) {
  207             return nullptr;
  208         }
  209     }
  210     auto patternInfo = new ParsedPatternInfo();
  211     if (patternInfo == nullptr) {
  212         status = U_MEMORY_ALLOCATION_ERROR;
  213         return nullptr;
  214     }
  215     fPatternInfo.adoptInstead(patternInfo);
  216     PatternParser::parseToPatternInfo(UnicodeString(pattern), *patternInfo, status);
  217     if (U_FAILURE(status)) {
  218         return nullptr;
  219     }
  220 
  221     /////////////////////////////////////////////////////////////////////////////////////
  222     /// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR ///
  223     /////////////////////////////////////////////////////////////////////////////////////
  224 
  225     // Multiplier
  226     if (macros.scale.isValid()) {
  227         fMicros.helpers.multiplier.setAndChain(macros.scale, chain);
  228         chain = &fMicros.helpers.multiplier;
  229     }
  230 
  231     // Rounding strategy
  232     Precision precision;
  233     if (!macros.precision.isBogus()) {
  234         precision = macros.precision;
  235     } else if (macros.notation.fType == Notation::NTN_COMPACT) {
  236         precision = Precision::integer().withMinDigits(2);
  237     } else if (isCurrency) {
  238         precision = Precision::currency(UCURR_USAGE_STANDARD);
  239     } else {
  240         precision = Precision::maxFraction(6);
  241     }
  242     UNumberFormatRoundingMode roundingMode;
  243     if (macros.roundingMode != kDefaultMode) {
  244         roundingMode = macros.roundingMode;
  245     } else {
  246         // Temporary until ICU 64
  247         roundingMode = precision.fRoundingMode;
  248     }
  249     fMicros.rounder = {precision, roundingMode, currency, status};
  250     if (U_FAILURE(status)) {
  251         return nullptr;
  252     }
  253 
  254     // Grouping strategy
  255     if (!macros.grouper.isBogus()) {
  256         fMicros.grouping = macros.grouper;
  257     } else if (macros.notation.fType == Notation::NTN_COMPACT) {
  258         // Compact notation uses minGrouping by default since ICU 59
  259         fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2);
  260     } else {
  261         fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO);
  262     }
  263     fMicros.grouping.setLocaleData(*fPatternInfo, macros.locale);
  264 
  265     // Padding strategy
  266     if (!macros.padder.isBogus()) {
  267         fMicros.padding = macros.padder;
  268     } else {
  269         fMicros.padding = Padder::none();
  270     }
  271 
  272     // Integer width
  273     if (!macros.integerWidth.isBogus()) {
  274         fMicros.integerWidth = macros.integerWidth;
  275     } else {
  276         fMicros.integerWidth = IntegerWidth::standard();
  277     }
  278 
  279     // Sign display
  280     if (macros.sign != UNUM_SIGN_COUNT) {
  281         fMicros.sign = macros.sign;
  282     } else {
  283         fMicros.sign = UNUM_SIGN_AUTO;
  284     }
  285 
  286     // Decimal mark display
  287     if (macros.decimal != UNUM_DECIMAL_SEPARATOR_COUNT) {
  288         fMicros.decimal = macros.decimal;
  289     } else {
  290         fMicros.decimal = UNUM_DECIMAL_SEPARATOR_AUTO;
  291     }
  292 
  293     // Use monetary separator symbols
  294     fMicros.useCurrency = isCurrency;
  295 
  296     // Inner modifier (scientific notation)
  297     if (macros.notation.fType == Notation::NTN_SCIENTIFIC) {
  298         auto newScientificHandler = new ScientificHandler(&macros.notation, fMicros.symbols, chain);
  299         if (newScientificHandler == nullptr) {
  300             status = U_MEMORY_ALLOCATION_ERROR;
  301             return nullptr;
  302         }
  303         fScientificHandler.adoptInstead(newScientificHandler);
  304         chain = fScientificHandler.getAlias();
  305     } else {
  306         // No inner modifier required
  307         fMicros.modInner = &fMicros.helpers.emptyStrongModifier;
  308     }
  309 
  310     // Middle modifier (patterns, positive/negative, currency symbols, percent)
  311     auto patternModifier = new MutablePatternModifier(false);
  312     if (patternModifier == nullptr) {
  313         status = U_MEMORY_ALLOCATION_ERROR;
  314         return nullptr;
  315     }
  316     fPatternModifier.adoptInstead(patternModifier);
  317     patternModifier->setPatternInfo(
  318             macros.affixProvider != nullptr ? macros.affixProvider
  319                                             : static_cast<const AffixPatternProvider*>(fPatternInfo.getAlias()),
  320             kUndefinedField);
  321     patternModifier->setPatternAttributes(fMicros.sign, isPermille);
  322     if (patternModifier->needsPlurals()) {
  323         patternModifier->setSymbols(
  324                 fMicros.symbols,
  325                 currency,
  326                 unitWidth,
  327                 resolvePluralRules(macros.rules, macros.locale, status),
  328                 status);
  329     } else {
  330         patternModifier->setSymbols(fMicros.symbols, currency, unitWidth, nullptr, status);
  331     }
  332     if (safe) {
  333         fImmutablePatternModifier.adoptInstead(patternModifier->createImmutable(status));
  334     }
  335     if (U_FAILURE(status)) {
  336         return nullptr;
  337     }
  338 
  339     // Outer modifier (CLDR units and currency long names)
  340     if (isCldrUnit) {
  341         fLongNameHandler.adoptInstead(
  342                 LongNameHandler::forMeasureUnit(
  343                         macros.locale,
  344                         macros.unit,
  345                         macros.perUnit,
  346                         unitWidth,
  347                         resolvePluralRules(macros.rules, macros.locale, status),
  348                         chain,
  349                         status));
  350         chain = fLongNameHandler.getAlias();
  351     } else if (isCurrency && unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) {
  352         fLongNameHandler.adoptInstead(
  353                 LongNameHandler::forCurrencyLongNames(
  354                         macros.locale,
  355                         currency,
  356                         resolvePluralRules(macros.rules, macros.locale, status),
  357                         chain,
  358                         status));
  359         chain = fLongNameHandler.getAlias();
  360     } else {
  361         // No outer modifier required
  362         fMicros.modOuter = &fMicros.helpers.emptyWeakModifier;
  363     }
  364     if (U_FAILURE(status)) {
  365         return nullptr;
  366     }
  367 
  368     // Compact notation
  369     if (macros.notation.fType == Notation::NTN_COMPACT) {
  370         CompactType compactType = (isCurrency && unitWidth != UNUM_UNIT_WIDTH_FULL_NAME)
  371                                   ? CompactType::TYPE_CURRENCY : CompactType::TYPE_DECIMAL;
  372         auto newCompactHandler = new CompactHandler(
  373             macros.notation.fUnion.compactStyle,
  374             macros.locale,
  375             nsName,
  376             compactType,
  377             resolvePluralRules(macros.rules, macros.locale, status),
  378             patternModifier,
  379             safe,
  380             chain,
  381             status);
  382         if (newCompactHandler == nullptr) {
  383             status = U_MEMORY_ALLOCATION_ERROR;
  384             return nullptr;
  385         }
  386         fCompactHandler.adoptInstead(newCompactHandler);
  387         chain = fCompactHandler.getAlias();
  388     }
  389     if (U_FAILURE(status)) {
  390         return nullptr;
  391     }
  392 
  393     // Always add the pattern modifier as the last element of the chain.
  394     if (safe) {
  395         fImmutablePatternModifier->addToChain(chain);
  396         chain = fImmutablePatternModifier.getAlias();
  397     } else {
  398         patternModifier->addToChain(chain);
  399         chain = patternModifier;
  400     }
  401 
  402     return chain;
  403 }
  404 
  405 const PluralRules*
  406 NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Locale& locale,
  407                                         UErrorCode& status) {
  408     if (rulesPtr != nullptr) {
  409         return rulesPtr;
  410     }
  411     // Lazily create PluralRules
  412     if (fRules.isNull()) {
  413         fRules.adoptInstead(PluralRules::forLocale(locale, status));
  414     }
  415     return fRules.getAlias();
  416 }
  417 
  418 int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, FormattedStringBuilder& string,
  419                                           int32_t start, int32_t end, UErrorCode& status) {
  420     // Always apply the inner modifier (which is "strong").
  421     int32_t length = micros.modInner->apply(string, start, end, status);
  422     if (micros.padding.isValid()) {
  423         length += micros.padding
  424                 .padAndApply(*micros.modMiddle, *micros.modOuter, string, start, length + end, status);
  425     } else {
  426         length += micros.modMiddle->apply(string, start, length + end, status);
  427         length += micros.modOuter->apply(string, start, length + end, status);
  428     }
  429     return length;
  430 }
  431 
  432 int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity,
  433                                          FormattedStringBuilder& string, int32_t index,
  434                                          UErrorCode& status) {
  435     int32_t length = 0;
  436     if (quantity.isInfinite()) {
  437         length += string.insert(
  438                 length + index,
  439                 micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kInfinitySymbol),
  440                 {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD},
  441                 status);
  442 
  443     } else if (quantity.isNaN()) {
  444         length += string.insert(
  445                 length + index,
  446                 micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kNaNSymbol),
  447                 {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD},
  448                 status);
  449 
  450     } else {
  451         // Add the integer digits
  452         length += writeIntegerDigits(micros, quantity, string, length + index, status);
  453 
  454         // Add the decimal point
  455         if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) {
  456             length += string.insert(
  457                     length + index,
  458                     micros.useCurrency ? micros.symbols->getSymbol(
  459                             DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol) : micros
  460                             .symbols
  461                             ->getSymbol(
  462                                     DecimalFormatSymbols::ENumberFormatSymbol::kDecimalSeparatorSymbol),
  463                     {UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD},
  464                     status);
  465         }
  466 
  467         // Add the fraction digits
  468         length += writeFractionDigits(micros, quantity, string, length + index, status);
  469 
  470         if (length == 0) {
  471             // Force output of the digit for value 0
  472             length += utils::insertDigitFromSymbols(
  473                     string,
  474                     index,
  475                     0,
  476                     *micros.symbols,
  477                     {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD},
  478                     status);
  479         }
  480     }
  481 
  482     return length;
  483 }
  484 
  485 int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity,
  486                                                 FormattedStringBuilder& string, int32_t index,
  487                                                 UErrorCode& status) {
  488     int length = 0;
  489     int integerCount = quantity.getUpperDisplayMagnitude() + 1;
  490     for (int i = 0; i < integerCount; i++) {
  491         // Add grouping separator
  492         if (micros.grouping.groupAtPosition(i, quantity)) {
  493             length += string.insert(
  494                     index,
  495                     micros.useCurrency ? micros.symbols->getSymbol(
  496                             DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol)
  497                                        : micros.symbols->getSymbol(
  498                             DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol),
  499                     {UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD},
  500                     status);
  501         }
  502 
  503         // Get and append the next digit value
  504         int8_t nextDigit = quantity.getDigit(i);
  505         length += utils::insertDigitFromSymbols(
  506                 string,
  507                 index,
  508                 nextDigit,
  509                 *micros.symbols,
  510                 {UFIELD_CATEGORY_NUMBER,
  511                 UNUM_INTEGER_FIELD},
  512                 status);
  513     }
  514     return length;
  515 }
  516 
  517 int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity,
  518                                                  FormattedStringBuilder& string, int32_t index,
  519                                                  UErrorCode& status) {
  520     int length = 0;
  521     int fractionCount = -quantity.getLowerDisplayMagnitude();
  522     for (int i = 0; i < fractionCount; i++) {
  523         // Get and append the next digit value
  524         int8_t nextDigit = quantity.getDigit(-i - 1);
  525         length += utils::insertDigitFromSymbols(
  526                 string,
  527                 length + index,
  528                 nextDigit,
  529                 *micros.symbols,
  530                 {UFIELD_CATEGORY_NUMBER, UNUM_FRACTION_FIELD},
  531                 status);
  532     }
  533     return length;
  534 }
  535 
  536 #endif /* #if !UCONFIG_NO_FORMATTING */