From 3681a6803b16a908c3e94f075244eb010cb4ce3e Mon Sep 17 00:00:00 2001 From: Shane Carr Date: Sat, 3 Mar 2018 08:26:58 +0000 Subject: [PATCH] ICU-13574 Replacing decimfmt.cpp and compactdecimalformat.cpp with new, empty files. Removing most private and internal functions from decimfmt.h, and updating call sites to use newer API. X-SVN-Rev: 41063 --- icu4c/source/i18n/compactdecimalformat.cpp | 1010 +----- icu4c/source/i18n/decimfmt.cpp | 3289 +------------------ icu4c/source/i18n/msgfmt.cpp | 9 +- icu4c/source/i18n/plurfmt.cpp | 26 +- icu4c/source/i18n/plurrule.cpp | 7 +- icu4c/source/i18n/quantityformatter.cpp | 9 +- icu4c/source/i18n/unicode/decimfmt.h | 284 +- icu4c/source/i18n/unicode/numberformatter.h | 12 + 8 files changed, 55 insertions(+), 4591 deletions(-) diff --git a/icu4c/source/i18n/compactdecimalformat.cpp b/icu4c/source/i18n/compactdecimalformat.cpp index bb2e541cd89..99fbfb54bd4 100644 --- a/icu4c/source/i18n/compactdecimalformat.cpp +++ b/icu4c/source/i18n/compactdecimalformat.cpp @@ -1,1013 +1,19 @@ -// © 2016 and later: Unicode, Inc. and others. +// © 2018 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2015, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -* -* File COMPACTDECIMALFORMAT.CPP -* -******************************************************************************** -*/ -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "charstr.h" -#include "cstring.h" -#include "digitlst.h" -#include "mutex.h" -#include "unicode/compactdecimalformat.h" -#include "unicode/numsys.h" -#include "unicode/plurrule.h" -#include "unicode/ures.h" -#include "ucln_in.h" -#include "uhash.h" -#include "umutex.h" -#include "unicode/ures.h" -#include "uresimp.h" - -// Maps locale name to CDFLocaleData struct. -static UHashtable* gCompactDecimalData = NULL; -static UMutex gCompactDecimalMetaLock = U_MUTEX_INITIALIZER; - -U_NAMESPACE_BEGIN - -static const int32_t MAX_DIGITS = 15; -static const char gOther[] = "other"; -static const char gLatnTag[] = "latn"; -static const char gNumberElementsTag[] = "NumberElements"; -static const char gDecimalFormatTag[] = "decimalFormat"; -static const char gPatternsShort[] = "patternsShort"; -static const char gPatternsLong[] = "patternsLong"; -static const char gLatnPath[] = "NumberElements/latn"; - -static const UChar u_0 = 0x30; -static const UChar u_apos = 0x27; - -static const UChar kZero[] = {u_0}; - -// Used to unescape single quotes. -enum QuoteState { - OUTSIDE, - INSIDE_EMPTY, - INSIDE_FULL -}; - -enum FallbackFlags { - ANY = 0, - MUST = 1, - NOT_ROOT = 2 - // Next one will be 4 then 6 etc. -}; - - -// CDFUnit represents a prefix-suffix pair for a particular variant -// and log10 value. -struct CDFUnit : public UMemory { - UnicodeString prefix; - UnicodeString suffix; - inline CDFUnit() : prefix(), suffix() { - prefix.setToBogus(); - } - inline ~CDFUnit() {} - inline UBool isSet() const { - return !prefix.isBogus(); - } - inline void markAsSet() { - prefix.remove(); - } -}; - -// CDFLocaleStyleData contains formatting data for a particular locale -// and style. -class CDFLocaleStyleData : public UMemory { - public: - // What to divide by for each log10 value when formatting. These values - // will be powers of 10. For English, would be: - // 1, 1, 1, 1000, 1000, 1000, 1000000, 1000000, 1000000, 1000000000 ... - double divisors[MAX_DIGITS]; - // Maps plural variants to CDFUnit[MAX_DIGITS] arrays. - // To format a number x, - // first compute log10(x). Compute displayNum = (x / divisors[log10(x)]). - // Compute the plural variant for displayNum - // (e.g zero, one, two, few, many, other). - // Compute cdfUnits = unitsByVariant[pluralVariant]. - // Prefix and suffix to use at cdfUnits[log10(x)] - UHashtable* unitsByVariant; - // A flag for whether or not this CDFLocaleStyleData was loaded from the - // Latin numbering system as a fallback from the locale numbering system. - // This value is meaningless if the object is bogus or empty. - UBool fromFallback; - inline CDFLocaleStyleData() : unitsByVariant(NULL), fromFallback(FALSE) { - uprv_memset(divisors, 0, sizeof(divisors)); - } - ~CDFLocaleStyleData(); - // Init initializes this object. - void Init(UErrorCode& status); - inline UBool isBogus() const { - return unitsByVariant == NULL; - } - void setToBogus(); - UBool isEmpty() { - return unitsByVariant == NULL || unitsByVariant->count == 0; - } - private: - CDFLocaleStyleData(const CDFLocaleStyleData&); - CDFLocaleStyleData& operator=(const CDFLocaleStyleData&); -}; - -// CDFLocaleData contains formatting data for a particular locale. -struct CDFLocaleData : public UMemory { - CDFLocaleStyleData shortData; - CDFLocaleStyleData longData; - inline CDFLocaleData() : shortData(), longData() { } - inline ~CDFLocaleData() { } - // Init initializes this object. - void Init(UErrorCode& status); -}; - -U_NAMESPACE_END - -U_CDECL_BEGIN - -static UBool U_CALLCONV cdf_cleanup(void) { - if (gCompactDecimalData != NULL) { - uhash_close(gCompactDecimalData); - gCompactDecimalData = NULL; - } - return TRUE; -} - -static void U_CALLCONV deleteCDFUnits(void* ptr) { - delete [] (icu::CDFUnit*) ptr; -} - -static void U_CALLCONV deleteCDFLocaleData(void* ptr) { - delete (icu::CDFLocaleData*) ptr; -} - -U_CDECL_END - -U_NAMESPACE_BEGIN - -static UBool divisors_equal(const double* lhs, const double* rhs); -static const CDFLocaleStyleData* getCDFLocaleStyleData(const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status); - -static const CDFLocaleStyleData* extractDataByStyleEnum(const CDFLocaleData& data, UNumberCompactStyle style, UErrorCode& status); -static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& status); -static void load(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status); -static int32_t populatePrefixSuffix(const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UBool overwrite, UErrorCode& status); -static double calculateDivisor(double power10, int32_t numZeros); -static UBool onlySpaces(UnicodeString u); -static void fixQuotes(UnicodeString& s); -static void checkForOtherVariants(CDFLocaleStyleData* result, UErrorCode& status); -static void fillInMissing(CDFLocaleStyleData* result); -static int32_t computeLog10(double x, UBool inRange); -static CDFUnit* createCDFUnit(const char* variant, int32_t log10Value, UHashtable* table, UErrorCode& status); -static const CDFUnit* getCDFUnitFallback(const UHashtable* table, const UnicodeString& variant, int32_t log10Value); - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CompactDecimalFormat) - -CompactDecimalFormat::CompactDecimalFormat( - const DecimalFormat& decimalFormat, - const UHashtable* unitsByVariant, - const double* divisors, - PluralRules* pluralRules) - : DecimalFormat(decimalFormat), _unitsByVariant(unitsByVariant), _divisors(divisors), _pluralRules(pluralRules) { -} - -CompactDecimalFormat::CompactDecimalFormat(const CompactDecimalFormat& source) - : DecimalFormat(source), _unitsByVariant(source._unitsByVariant), _divisors(source._divisors), _pluralRules(source._pluralRules->clone()) { -} - -CompactDecimalFormat* U_EXPORT2 -CompactDecimalFormat::createInstance( - const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status) { - LocalPointer decfmt((DecimalFormat*) NumberFormat::makeInstance(inLocale, UNUM_DECIMAL, TRUE, status)); - if (U_FAILURE(status)) { - return NULL; - } - LocalPointer pluralRules(PluralRules::forLocale(inLocale, status)); - if (U_FAILURE(status)) { - return NULL; - } - const CDFLocaleStyleData* data = getCDFLocaleStyleData(inLocale, style, status); - if (U_FAILURE(status)) { - return NULL; - } - CompactDecimalFormat* result = - new CompactDecimalFormat(*decfmt, data->unitsByVariant, data->divisors, pluralRules.getAlias()); - if (result == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - pluralRules.orphan(); - result->setMaximumSignificantDigits(3); - result->setSignificantDigitsUsed(TRUE); - result->setGroupingUsed(FALSE); - return result; -} - -CompactDecimalFormat& -CompactDecimalFormat::operator=(const CompactDecimalFormat& rhs) { - if (this != &rhs) { - DecimalFormat::operator=(rhs); - _unitsByVariant = rhs._unitsByVariant; - _divisors = rhs._divisors; - delete _pluralRules; - _pluralRules = rhs._pluralRules->clone(); - } - return *this; -} - -CompactDecimalFormat::~CompactDecimalFormat() { - delete _pluralRules; -} - - -Format* -CompactDecimalFormat::clone(void) const { - return new CompactDecimalFormat(*this); -} - -UBool -CompactDecimalFormat::operator==(const Format& that) const { - if (this == &that) { - return TRUE; - } - return (DecimalFormat::operator==(that) && eqHelper((const CompactDecimalFormat&) that)); -} - -UBool -CompactDecimalFormat::eqHelper(const CompactDecimalFormat& that) const { - return uhash_equals(_unitsByVariant, that._unitsByVariant) && divisors_equal(_divisors, that._divisors) && (*_pluralRules == *that._pluralRules); -} - -UnicodeString& -CompactDecimalFormat::format( - double number, - UnicodeString& appendTo, - FieldPosition& pos) const { - UErrorCode status = U_ZERO_ERROR; - return format(number, appendTo, pos, status); -} - -UnicodeString& -CompactDecimalFormat::format( - double number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return appendTo; - } - DigitList orig, rounded; - orig.set(number); - UBool isNegative; - _round(orig, rounded, isNegative, status); - if (U_FAILURE(status)) { - return appendTo; - } - double roundedDouble = rounded.getDouble(); - if (isNegative) { - roundedDouble = -roundedDouble; - } - int32_t baseIdx = computeLog10(roundedDouble, TRUE); - double numberToFormat = roundedDouble / _divisors[baseIdx]; - UnicodeString variant = _pluralRules->select(numberToFormat); - if (isNegative) { - numberToFormat = -numberToFormat; - } - const CDFUnit* unit = getCDFUnitFallback(_unitsByVariant, variant, baseIdx); - appendTo += unit->prefix; - DecimalFormat::format(numberToFormat, appendTo, pos); - appendTo += unit->suffix; - return appendTo; -} - -UnicodeString& -CompactDecimalFormat::format( - double /* number */, - UnicodeString& appendTo, - FieldPositionIterator* /* posIter */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; - return appendTo; -} - -UnicodeString& -CompactDecimalFormat::format( - int32_t number, - UnicodeString& appendTo, - FieldPosition& pos) const { - return format((double) number, appendTo, pos); -} - -UnicodeString& -CompactDecimalFormat::format( - int32_t number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - return format((double) number, appendTo, pos, status); -} - -UnicodeString& -CompactDecimalFormat::format( - int32_t /* number */, - UnicodeString& appendTo, - FieldPositionIterator* /* posIter */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; - return appendTo; -} - -UnicodeString& -CompactDecimalFormat::format( - int64_t number, - UnicodeString& appendTo, - FieldPosition& pos) const { - return format((double) number, appendTo, pos); -} - -UnicodeString& -CompactDecimalFormat::format( - int64_t number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - return format((double) number, appendTo, pos, status); -} - -UnicodeString& -CompactDecimalFormat::format( - int64_t /* number */, - UnicodeString& appendTo, - FieldPositionIterator* /* posIter */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; - return appendTo; -} - -UnicodeString& -CompactDecimalFormat::format( - StringPiece /* number */, - UnicodeString& appendTo, - FieldPositionIterator* /* posIter */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; - return appendTo; -} - -UnicodeString& -CompactDecimalFormat::format( - const DigitList& /* number */, - UnicodeString& appendTo, - FieldPositionIterator* /* posIter */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; - return appendTo; -} - -UnicodeString& -CompactDecimalFormat::format(const DigitList& /* number */, - UnicodeString& appendTo, - FieldPosition& /* pos */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; - return appendTo; -} - -void -CompactDecimalFormat::parse( - const UnicodeString& /* text */, - Formattable& /* result */, - ParsePosition& /* parsePosition */) const { -} - -void -CompactDecimalFormat::parse( - const UnicodeString& /* text */, - Formattable& /* result */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; -} - -CurrencyAmount* -CompactDecimalFormat::parseCurrency( - const UnicodeString& /* text */, - ParsePosition& /* pos */) const { - return NULL; -} - -void CDFLocaleStyleData::Init(UErrorCode& status) { - if (unitsByVariant != NULL) { - return; - } - unitsByVariant = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); - if (U_FAILURE(status)) { - return; - } - uhash_setKeyDeleter(unitsByVariant, uprv_free); - uhash_setValueDeleter(unitsByVariant, deleteCDFUnits); -} - -CDFLocaleStyleData::~CDFLocaleStyleData() { - setToBogus(); -} - -void CDFLocaleStyleData::setToBogus() { - if (unitsByVariant != NULL) { - uhash_close(unitsByVariant); - unitsByVariant = NULL; - } -} -void CDFLocaleData::Init(UErrorCode& status) { - shortData.Init(status); - if (U_FAILURE(status)) { - return; - } - longData.Init(status); -} - -// Helper method for operator= -static UBool divisors_equal(const double* lhs, const double* rhs) { - for (int32_t i = 0; i < MAX_DIGITS; ++i) { - if (lhs[i] != rhs[i]) { - return FALSE; - } - } - return TRUE; -} - -// getCDFLocaleStyleData returns pointer to formatting data for given locale and -// style within the global cache. On cache miss, getCDFLocaleStyleData loads -// the data from CLDR into the global cache before returning the pointer. If a -// UNUM_LONG data is requested for a locale, and that locale does not have -// UNUM_LONG data, getCDFLocaleStyleData will fall back to UNUM_SHORT data for -// that locale. -static const CDFLocaleStyleData* getCDFLocaleStyleData(const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status) { - if (U_FAILURE(status)) { - return NULL; - } - CDFLocaleData* result = NULL; - const char* key = inLocale.getName(); - { - Mutex lock(&gCompactDecimalMetaLock); - if (gCompactDecimalData == NULL) { - gCompactDecimalData = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); - if (U_FAILURE(status)) { - return NULL; - } - uhash_setKeyDeleter(gCompactDecimalData, uprv_free); - uhash_setValueDeleter(gCompactDecimalData, deleteCDFLocaleData); - ucln_i18n_registerCleanup(UCLN_I18N_CDFINFO, cdf_cleanup); - } else { - result = (CDFLocaleData*) uhash_get(gCompactDecimalData, key); - } - } - if (result != NULL) { - return extractDataByStyleEnum(*result, style, status); - } - - result = loadCDFLocaleData(inLocale, status); - if (U_FAILURE(status)) { - return NULL; - } - - { - Mutex lock(&gCompactDecimalMetaLock); - CDFLocaleData* temp = (CDFLocaleData*) uhash_get(gCompactDecimalData, key); - if (temp != NULL) { - delete result; - result = temp; - } else { - uhash_put(gCompactDecimalData, uprv_strdup(key), (void*) result, &status); - if (U_FAILURE(status)) { - return NULL; - } - } - } - return extractDataByStyleEnum(*result, style, status); -} - -static const CDFLocaleStyleData* extractDataByStyleEnum(const CDFLocaleData& data, UNumberCompactStyle style, UErrorCode& status) { - switch (style) { - case UNUM_SHORT: - return &data.shortData; - case UNUM_LONG: - if (!data.longData.isBogus()) { - return &data.longData; - } - return &data.shortData; - default: - status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } -} - -// loadCDFLocaleData loads formatting data from CLDR for a given locale. The -// caller owns the returned pointer. -static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& status) { - if (U_FAILURE(status)) { - return NULL; - } - CDFLocaleData* result = new CDFLocaleData; - if (result == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - result->Init(status); - if (U_FAILURE(status)) { - delete result; - return NULL; - } - - load(inLocale, result, status); - - if (U_FAILURE(status)) { - delete result; - return NULL; - } - return result; -} - -namespace { - -struct CmptDecDataSink : public ResourceSink { - - CDFLocaleData& dataBundle; // Where to save values when they are read - UBool isLatin; // Whether or not we are traversing the Latin tree - UBool isFallback; // Whether or not we are traversing the Latin tree as fallback - - enum EPatternsTableKey { PATTERNS_SHORT, PATTERNS_LONG }; - enum EFormatsTableKey { DECIMAL_FORMAT, CURRENCY_FORMAT }; - - /* - * NumberElements{ <-- top (numbering system table) - * latn{ <-- patternsTable (one per numbering system) - * patternsLong{ <-- formatsTable (one per pattern) - * decimalFormat{ <-- powersOfTenTable (one per format) - * 1000{ <-- pluralVariantsTable (one per power of ten) - * one{"0 thousand"} <-- plural variant and template - */ - - CmptDecDataSink(CDFLocaleData& _dataBundle) - : dataBundle(_dataBundle), isLatin(FALSE), isFallback(FALSE) {} - virtual ~CmptDecDataSink(); - - virtual void put(const char *key, ResourceValue &value, UBool isRoot, UErrorCode &errorCode) { - // SPECIAL CASE: Don't consume root in the non-Latin numbering system - if (isRoot && !isLatin) { return; } - - ResourceTable patternsTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int i1 = 0; patternsTable.getKeyAndValue(i1, key, value); ++i1) { - - // Check for patternsShort or patternsLong - EPatternsTableKey patternsTableKey; - if (uprv_strcmp(key, gPatternsShort) == 0) { - patternsTableKey = PATTERNS_SHORT; - } else if (uprv_strcmp(key, gPatternsLong) == 0) { - patternsTableKey = PATTERNS_LONG; - } else { - continue; - } - - // Traverse into the formats table - ResourceTable formatsTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int i2 = 0; formatsTable.getKeyAndValue(i2, key, value); ++i2) { - - // Check for decimalFormat or currencyFormat - EFormatsTableKey formatsTableKey; - if (uprv_strcmp(key, gDecimalFormatTag) == 0) { - formatsTableKey = DECIMAL_FORMAT; - // TODO: Enable this statement when currency support is added - // } else if (uprv_strcmp(key, gCurrencyFormat) == 0) { - // formatsTableKey = CURRENCY_FORMAT; - } else { - continue; - } - - // Set the current style and destination based on the two keys - UNumberCompactStyle style; - CDFLocaleStyleData* destination = NULL; - if (patternsTableKey == PATTERNS_LONG - && formatsTableKey == DECIMAL_FORMAT) { - style = UNUM_LONG; - destination = &dataBundle.longData; - } else if (patternsTableKey == PATTERNS_SHORT - && formatsTableKey == DECIMAL_FORMAT) { - style = UNUM_SHORT; - destination = &dataBundle.shortData; - // TODO: Enable the following statements when currency support is added - // } else if (patternsTableKey == PATTERNS_SHORT - // && formatsTableKey == CURRENCY_FORMAT) { - // style = UNUM_SHORT_CURRENCY; // or whatever the enum gets named - // destination = &dataBundle.shortCurrencyData; - // } else { - // // Silently ignore this case - // continue; - } - - // SPECIAL CASE: RULES FOR WHETHER OR NOT TO CONSUME THIS TABLE: - // 1) Don't consume longData if shortData was consumed from the non-Latin - // locale numbering system - // 2) Don't consume longData for the first time if this is the root bundle and - // shortData is already populated from a more specific locale. Note that if - // both longData and shortData are both only in root, longData will be - // consumed since it is alphabetically before shortData in the bundle. - if (isFallback - && style == UNUM_LONG - && !dataBundle.shortData.isEmpty() - && !dataBundle.shortData.fromFallback) { - continue; - } - if (isRoot - && style == UNUM_LONG - && dataBundle.longData.isEmpty() - && !dataBundle.shortData.isEmpty()) { - continue; - } - - // Set the "fromFallback" flag on the data object - destination->fromFallback = isFallback; - - // Traverse into the powers of ten table - ResourceTable powersOfTenTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int i3 = 0; powersOfTenTable.getKeyAndValue(i3, key, value); ++i3) { - - // The key will always be some even power of 10. e.g 10000. - char* endPtr = NULL; - double power10 = uprv_strtod(key, &endPtr); - if (*endPtr != 0) { - errorCode = U_INTERNAL_PROGRAM_ERROR; - return; - } - int32_t log10Value = computeLog10(power10, FALSE); - - // Silently ignore divisors that are too big. - if (log10Value >= MAX_DIGITS) continue; - - // Iterate over the plural variants ("one", "other", etc) - ResourceTable pluralVariantsTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int i4 = 0; pluralVariantsTable.getKeyAndValue(i4, key, value); ++i4) { - const char* pluralVariant = key; - const UnicodeString formatStr = value.getUnicodeString(errorCode); - - // Copy the data into the in-memory data bundle (do not overwrite - // existing values) - int32_t numZeros = populatePrefixSuffix( - pluralVariant, log10Value, formatStr, - destination->unitsByVariant, FALSE, errorCode); - - // If populatePrefixSuffix returns -1, it means that this key has been - // encountered already. - if (numZeros < 0) { - continue; - } - - // Set the divisor, which is based on the number of zeros in the template - // string. If the divisor from here is different from the one previously - // stored, it means that the number of zeros in different plural variants - // differs; throw an exception. - // TODO: How should I check for floating-point errors here? - // Is there a good reason why "divisor" is double and not long like Java? - double divisor = calculateDivisor(power10, numZeros); - if (destination->divisors[log10Value] != 0.0 - && destination->divisors[log10Value] != divisor) { - errorCode = U_INTERNAL_PROGRAM_ERROR; - return; - } - destination->divisors[log10Value] = divisor; - } - } - } - } - } -}; - -// Virtual destructors must be defined out of line. -CmptDecDataSink::~CmptDecDataSink() {} - -} // namespace - -static void load(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status) { - LocalPointer ns(NumberingSystem::createInstance(inLocale, status)); - if (U_FAILURE(status)) { - return; - } - const char* nsName = ns->getName(); - - LocalUResourceBundlePointer resource(ures_open(NULL, inLocale.getName(), &status)); - if (U_FAILURE(status)) { - return; - } - CmptDecDataSink sink(*result); - sink.isFallback = FALSE; - - // First load the number elements data if nsName is not Latin. - if (uprv_strcmp(nsName, gLatnTag) != 0) { - sink.isLatin = FALSE; - CharString path; - path.append(gNumberElementsTag, status) - .append('/', status) - .append(nsName, status); - ures_getAllItemsWithFallback(resource.getAlias(), path.data(), sink, status); - if (status == U_MISSING_RESOURCE_ERROR) { - // Silently ignore and use Latin - status = U_ZERO_ERROR; - } else if (U_FAILURE(status)) { - return; - } - sink.isFallback = TRUE; - } - - // Now load Latin. - sink.isLatin = TRUE; - ures_getAllItemsWithFallback(resource.getAlias(), gLatnPath, sink, status); - if (U_FAILURE(status)) return; - - // If longData is empty, default it to be equal to shortData - if (result->longData.isEmpty()) { - result->longData.setToBogus(); - } - - // Check for "other" variants in each of the three data classes, and resolve missing elements. - - if (!result->longData.isBogus()) { - checkForOtherVariants(&result->longData, status); - if (U_FAILURE(status)) return; - fillInMissing(&result->longData); - } - - checkForOtherVariants(&result->shortData, status); - if (U_FAILURE(status)) return; - fillInMissing(&result->shortData); - - // TODO: Enable this statement when currency support is added - // checkForOtherVariants(&result->shortCurrencyData, status); - // if (U_FAILURE(status)) return; - // fillInMissing(&result->shortCurrencyData); -} - -// populatePrefixSuffix Adds a specific prefix-suffix pair to result for a -// given variant and log10 value. -// variant is 'zero', 'one', 'two', 'few', 'many', or 'other'. -// formatStr is the format string from which the prefix and suffix are -// extracted. It is usually of form 'Pefix 000 suffix'. -// populatePrefixSuffix returns the number of 0's found in formatStr -// before the decimal point. -// In the special case that formatStr contains only spaces for prefix -// and suffix, populatePrefixSuffix returns log10Value + 1. -static int32_t populatePrefixSuffix( - const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UBool overwrite, UErrorCode& status) { - if (U_FAILURE(status)) { - return 0; - } - - // ICU 59 HACK: Ignore negative part of format string, mimicking ICU 58 behavior. - // TODO(sffc): Make sure this is fixed during the overhaul port in ICU 60. - int32_t semiPos = formatStr.indexOf(';', 0); - if (semiPos == -1) { - semiPos = formatStr.length(); - } - UnicodeString positivePart = formatStr.tempSubString(0, semiPos); - - int32_t firstIdx = positivePart.indexOf(kZero, UPRV_LENGTHOF(kZero), 0); - // We must have 0's in format string. - if (firstIdx == -1) { - status = U_INTERNAL_PROGRAM_ERROR; - return 0; - } - int32_t lastIdx = positivePart.lastIndexOf(kZero, UPRV_LENGTHOF(kZero), firstIdx); - CDFUnit* unit = createCDFUnit(variant, log10Value, result, status); - if (U_FAILURE(status)) { - return 0; - } - - // Return -1 if we are not overwriting an existing value - if (unit->isSet() && !overwrite) { - return -1; - } - unit->markAsSet(); - - // Everything up to first 0 is the prefix - unit->prefix = positivePart.tempSubString(0, firstIdx); - fixQuotes(unit->prefix); - // Everything beyond the last 0 is the suffix - unit->suffix = positivePart.tempSubString(lastIdx + 1); - fixQuotes(unit->suffix); - - // If there is effectively no prefix or suffix, ignore the actual number of - // 0's and act as if the number of 0's matches the size of the number. - if (onlySpaces(unit->prefix) && onlySpaces(unit->suffix)) { - return log10Value + 1; - } - - // Calculate number of zeros before decimal point - int32_t idx = firstIdx + 1; - while (idx <= lastIdx && positivePart.charAt(idx) == u_0) { - ++idx; - } - return (idx - firstIdx); -} - -// Calculate a divisor based on the magnitude and number of zeros in the -// template string. -static double calculateDivisor(double power10, int32_t numZeros) { - double divisor = power10; - for (int32_t i = 1; i < numZeros; ++i) { - divisor /= 10.0; - } - return divisor; -} - -static UBool onlySpaces(UnicodeString u) { - return u.trim().length() == 0; -} - -// fixQuotes unescapes single quotes. Don''t -> Don't. Letter 'j' -> Letter j. -// Modifies s in place. -static void fixQuotes(UnicodeString& s) { - QuoteState state = OUTSIDE; - int32_t len = s.length(); - int32_t dest = 0; - for (int32_t i = 0; i < len; ++i) { - UChar ch = s.charAt(i); - if (ch == u_apos) { - if (state == INSIDE_EMPTY) { - s.setCharAt(dest, ch); - ++dest; - } - } else { - s.setCharAt(dest, ch); - ++dest; - } +#include "unicode/utypes.h" - // Update state - switch (state) { - case OUTSIDE: - state = ch == u_apos ? INSIDE_EMPTY : OUTSIDE; - break; - case INSIDE_EMPTY: - case INSIDE_FULL: - state = ch == u_apos ? OUTSIDE : INSIDE_FULL; - break; - default: - break; - } - } - s.truncate(dest); -} +#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT -// Checks to make sure that an "other" variant is present in all -// powers of 10. -static void checkForOtherVariants(CDFLocaleStyleData* result, - UErrorCode& status) { - if (result == NULL || result->unitsByVariant == NULL) { - return; - } +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT - const CDFUnit* otherByBase = - (const CDFUnit*) uhash_get(result->unitsByVariant, gOther); - if (otherByBase == NULL) { - status = U_INTERNAL_PROGRAM_ERROR; - return; - } +using namespace icu; - // Check all other plural variants, and make sure that if - // any of them are populated, then other is also populated - int32_t pos = UHASH_FIRST; - const UHashElement* element; - while ((element = uhash_nextElement(result->unitsByVariant, &pos)) != NULL) { - CDFUnit* variantsByBase = (CDFUnit*) element->value.pointer; - if (variantsByBase == otherByBase) continue; - for (int32_t log10Value = 0; log10Value < MAX_DIGITS; ++log10Value) { - if (variantsByBase[log10Value].isSet() - && !otherByBase[log10Value].isSet()) { - status = U_INTERNAL_PROGRAM_ERROR; - return; - } - } - } -} -// fillInMissing ensures that the data in result is complete. -// result data is complete if for each variant in result, there exists -// a prefix-suffix pair for each log10 value and there also exists -// a divisor for each log10 value. -// -// First this function figures out for which log10 values, the other -// variant already had data. These are the same log10 values defined -// in CLDR. -// -// For each log10 value not defined in CLDR, it uses the divisor for -// the last defined log10 value or 1. -// -// Then for each variant, it does the following. For each log10 -// value not defined in CLDR, copy the prefix-suffix pair from the -// previous log10 value. If log10 value is defined in CLDR but is -// missing from given variant, copy the prefix-suffix pair for that -// log10 value from the 'other' variant. -static void fillInMissing(CDFLocaleStyleData* result) { - const CDFUnit* otherUnits = - (const CDFUnit*) uhash_get(result->unitsByVariant, gOther); - UBool definedInCLDR[MAX_DIGITS]; - double lastDivisor = 1.0; - for (int32_t i = 0; i < MAX_DIGITS; ++i) { - if (!otherUnits[i].isSet()) { - result->divisors[i] = lastDivisor; - definedInCLDR[i] = FALSE; - } else { - lastDivisor = result->divisors[i]; - definedInCLDR[i] = TRUE; - } - } - // Iterate over each variant. - int32_t pos = UHASH_FIRST; - const UHashElement* element = uhash_nextElement(result->unitsByVariant, &pos); - for (;element != NULL; element = uhash_nextElement(result->unitsByVariant, &pos)) { - CDFUnit* units = (CDFUnit*) element->value.pointer; - for (int32_t i = 0; i < MAX_DIGITS; ++i) { - if (definedInCLDR[i]) { - if (!units[i].isSet()) { - units[i] = otherUnits[i]; - } - } else { - if (i == 0) { - units[0].markAsSet(); - } else { - units[i] = units[i - 1]; - } - } - } - } -} -// computeLog10 computes floor(log10(x)). If inRange is TRUE, the biggest -// value computeLog10 will return MAX_DIGITS -1 even for -// numbers > 10^MAX_DIGITS. If inRange is FALSE, computeLog10 will return -// up to MAX_DIGITS. -static int32_t computeLog10(double x, UBool inRange) { - int32_t result = 0; - int32_t max = inRange ? MAX_DIGITS - 1 : MAX_DIGITS; - while (x >= 10.0) { - x /= 10.0; - ++result; - if (result == max) { - break; - } - } - return result; -} -// createCDFUnit returns a pointer to the prefix-suffix pair for a given -// variant and log10 value within table. If no such prefix-suffix pair is -// stored in table, one is created within table before returning pointer. -static CDFUnit* createCDFUnit(const char* variant, int32_t log10Value, UHashtable* table, UErrorCode& status) { - if (U_FAILURE(status)) { - return NULL; - } - CDFUnit *cdfUnit = (CDFUnit*) uhash_get(table, variant); - if (cdfUnit == NULL) { - cdfUnit = new CDFUnit[MAX_DIGITS]; - if (cdfUnit == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uhash_put(table, uprv_strdup(variant), cdfUnit, &status); - if (U_FAILURE(status)) { - return NULL; - } - } - CDFUnit* result = &cdfUnit[log10Value]; - return result; -} -// getCDFUnitFallback returns a pointer to the prefix-suffix pair for a given -// variant and log10 value within table. If the given variant doesn't exist, it -// falls back to the OTHER variant. Therefore, this method will always return -// some non-NULL value. -static const CDFUnit* getCDFUnitFallback(const UHashtable* table, const UnicodeString& variant, int32_t log10Value) { - CharString cvariant; - UErrorCode status = U_ZERO_ERROR; - const CDFUnit *cdfUnit = NULL; - cvariant.appendInvariantChars(variant, status); - if (!U_FAILURE(status)) { - cdfUnit = (const CDFUnit*) uhash_get(table, cvariant.data()); - } - if (cdfUnit == NULL) { - cdfUnit = (const CDFUnit*) uhash_get(table, gOther); - } - return &cdfUnit[log10Value]; -} -U_NAMESPACE_END -#endif +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/decimfmt.cpp b/icu4c/source/i18n/decimfmt.cpp index 713abd7f9fe..99fbfb54bd4 100644 --- a/icu4c/source/i18n/decimfmt.cpp +++ b/icu4c/source/i18n/decimfmt.cpp @@ -1,3296 +1,19 @@ -// © 2016 and later: Unicode, Inc. and others. +// © 2018 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2015, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -* -* File DECIMFMT.CPP -* -* Modification History: -* -* Date Name Description -* 02/19/97 aliu Converted from java. -* 03/20/97 clhuang Implemented with new APIs. -* 03/31/97 aliu Moved isLONG_MIN to DigitList, and fixed it. -* 04/3/97 aliu Rewrote parsing and formatting completely, and -* cleaned up and debugged. Actually works now. -* Implemented NAN and INF handling, for both parsing -* and formatting. Extensive testing & debugging. -* 04/10/97 aliu Modified to compile on AIX. -* 04/16/97 aliu Rewrote to use DigitList, which has been resurrected. -* Changed DigitCount to int per code review. -* 07/09/97 helena Made ParsePosition into a class. -* 08/26/97 aliu Extensive changes to applyPattern; completely -* rewritten from the Java. -* 09/09/97 aliu Ported over support for exponential formats. -* 07/20/98 stephen JDK 1.2 sync up. -* Various instances of '0' replaced with 'NULL' -* Check for grouping size in subFormat() -* Brought subParse() in line with Java 1.2 -* Added method appendAffix() -* 08/24/1998 srl Removed Mutex calls. This is not a thread safe class! -* 02/22/99 stephen Removed character literals for EBCDIC safety -* 06/24/99 helena Integrated Alan's NF enhancements and Java2 bug fixes -* 06/28/99 stephen Fixed bugs in toPattern(). -* 06/29/99 stephen Fixed operator= to copy fFormatWidth, fPad, -* fPadPosition -******************************************************************************** -*/ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING +#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT -#include "unicode/uniset.h" -#include "unicode/currpinf.h" -#include "unicode/plurrule.h" -#include "unicode/utf16.h" -#include "unicode/numsys.h" -#include "unicode/localpointer.h" -#include "unicode/ustring.h" -#include "uresimp.h" -#include "ucurrimp.h" -#include "charstr.h" -#include "patternprops.h" -#include "cstring.h" -#include "uassert.h" -#include "hash.h" -#include "decfmtst.h" -#include "plurrule_impl.h" -#include "decimalformatpattern.h" -#include "fmtableimp.h" -#include "decimfmtimpl.h" -#include "visibledigits.h" +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT -/* - * On certain platforms, round is a macro defined in math.h - * This undefine is to avoid conflict between the macro and - * the function defined below. - */ -#ifdef round -#undef round -#endif +using namespace icu; -U_NAMESPACE_BEGIN -#ifdef FMT_DEBUG -#include -static void _debugout(const char *f, int l, const UnicodeString& s) { - char buf[2000]; - s.extract((int32_t) 0, s.length(), buf, "utf-8"); - printf("%s:%d: %s\n", f,l, buf); -} -#define debugout(x) _debugout(__FILE__,__LINE__,x) -#define debug(x) printf("%s:%d: %s\n", __FILE__,__LINE__, x); -static const UnicodeString dbg_null("",""); -#define DEREFSTR(x) ((x!=NULL)?(*x):(dbg_null)) -#else -#define debugout(x) -#define debug(x) -#endif -/* For currency parsing purose, - * Need to remember all prefix patterns and suffix patterns of - * every currency format pattern, - * including the pattern of default currecny style - * and plural currency style. And the patterns are set through applyPattern. - */ -struct AffixPatternsForCurrency : public UMemory { - // negative prefix pattern - UnicodeString negPrefixPatternForCurrency; - // negative suffix pattern - UnicodeString negSuffixPatternForCurrency; - // positive prefix pattern - UnicodeString posPrefixPatternForCurrency; - // positive suffix pattern - UnicodeString posSuffixPatternForCurrency; - int8_t patternType; - - AffixPatternsForCurrency(const UnicodeString& negPrefix, - const UnicodeString& negSuffix, - const UnicodeString& posPrefix, - const UnicodeString& posSuffix, - int8_t type) { - negPrefixPatternForCurrency = negPrefix; - negSuffixPatternForCurrency = negSuffix; - posPrefixPatternForCurrency = posPrefix; - posSuffixPatternForCurrency = posSuffix; - patternType = type; - } -#ifdef FMT_DEBUG - void dump() const { - debugout( UnicodeString("AffixPatternsForCurrency( -=\"") + - negPrefixPatternForCurrency + (UnicodeString)"\"/\"" + - negSuffixPatternForCurrency + (UnicodeString)"\" +=\"" + - posPrefixPatternForCurrency + (UnicodeString)"\"/\"" + - posSuffixPatternForCurrency + (UnicodeString)"\" )"); - } -#endif -}; - -/* affix for currency formatting when the currency sign in the pattern - * equals to 3, such as the pattern contains 3 currency sign or - * the formatter style is currency plural format style. - */ -struct AffixesForCurrency : public UMemory { - // negative prefix - UnicodeString negPrefixForCurrency; - // negative suffix - UnicodeString negSuffixForCurrency; - // positive prefix - UnicodeString posPrefixForCurrency; - // positive suffix - UnicodeString posSuffixForCurrency; - - int32_t formatWidth; - - AffixesForCurrency(const UnicodeString& negPrefix, - const UnicodeString& negSuffix, - const UnicodeString& posPrefix, - const UnicodeString& posSuffix) { - negPrefixForCurrency = negPrefix; - negSuffixForCurrency = negSuffix; - posPrefixForCurrency = posPrefix; - posSuffixForCurrency = posSuffix; - } -#ifdef FMT_DEBUG - void dump() const { - debugout( UnicodeString("AffixesForCurrency( -=\"") + - negPrefixForCurrency + (UnicodeString)"\"/\"" + - negSuffixForCurrency + (UnicodeString)"\" +=\"" + - posPrefixForCurrency + (UnicodeString)"\"/\"" + - posSuffixForCurrency + (UnicodeString)"\" )"); - } -#endif -}; - -U_CDECL_BEGIN - -/** - * @internal ICU 4.2 - */ -static UBool U_CALLCONV decimfmtAffixPatternValueComparator(UHashTok val1, UHashTok val2); - - -static UBool -U_CALLCONV decimfmtAffixPatternValueComparator(UHashTok val1, UHashTok val2) { - const AffixPatternsForCurrency* affix_1 = - (AffixPatternsForCurrency*)val1.pointer; - const AffixPatternsForCurrency* affix_2 = - (AffixPatternsForCurrency*)val2.pointer; - return affix_1->negPrefixPatternForCurrency == - affix_2->negPrefixPatternForCurrency && - affix_1->negSuffixPatternForCurrency == - affix_2->negSuffixPatternForCurrency && - affix_1->posPrefixPatternForCurrency == - affix_2->posPrefixPatternForCurrency && - affix_1->posSuffixPatternForCurrency == - affix_2->posSuffixPatternForCurrency && - affix_1->patternType == affix_2->patternType; -} - -U_CDECL_END - - - - -// ***************************************************************************** -// class DecimalFormat -// ***************************************************************************** - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormat) - -// Constants for characters used in programmatic (unlocalized) patterns. -#define kPatternZeroDigit ((UChar)0x0030) /*'0'*/ -#define kPatternSignificantDigit ((UChar)0x0040) /*'@'*/ -#define kPatternGroupingSeparator ((UChar)0x002C) /*','*/ -#define kPatternDecimalSeparator ((UChar)0x002E) /*'.'*/ -#define kPatternPerMill ((UChar)0x2030) -#define kPatternPercent ((UChar)0x0025) /*'%'*/ -#define kPatternDigit ((UChar)0x0023) /*'#'*/ -#define kPatternSeparator ((UChar)0x003B) /*';'*/ -#define kPatternExponent ((UChar)0x0045) /*'E'*/ -#define kPatternPlus ((UChar)0x002B) /*'+'*/ -#define kPatternMinus ((UChar)0x002D) /*'-'*/ -#define kPatternPadEscape ((UChar)0x002A) /*'*'*/ -#define kQuote ((UChar)0x0027) /*'\''*/ -/** - * The CURRENCY_SIGN is the standard Unicode symbol for currency. It - * is used in patterns and substitued with either the currency symbol, - * or if it is doubled, with the international currency symbol. If the - * CURRENCY_SIGN is seen in a pattern, then the decimal separator is - * replaced with the monetary decimal separator. - */ -#define kCurrencySign ((UChar)0x00A4) -#define kDefaultPad ((UChar)0x0020) /* */ - -const int32_t DecimalFormat::kDoubleIntegerDigits = 309; -const int32_t DecimalFormat::kDoubleFractionDigits = 340; - -const int32_t DecimalFormat::kMaxScientificIntegerDigits = 8; - -/** - * These are the tags we expect to see in normal resource bundle files associated - * with a locale. - */ -const char DecimalFormat::fgNumberPatterns[]="NumberPatterns"; // Deprecated - not used -static const char fgNumberElements[]="NumberElements"; -static const char fgLatn[]="latn"; -static const char fgPatterns[]="patterns"; -static const char fgDecimalFormat[]="decimalFormat"; -static const char fgCurrencyFormat[]="currencyFormat"; - -inline int32_t _min(int32_t a, int32_t b) { return (a adoptedSymbols(symbolsToAdopt); - if (U_FAILURE(status)) - return; - - if (adoptedSymbols.isNull()) - { - adoptedSymbols.adoptInstead( - new DecimalFormatSymbols(Locale::getDefault(), status)); - if (adoptedSymbols.isNull() && U_SUCCESS(status)) { - status = U_MEMORY_ALLOCATION_ERROR; - } - if (U_FAILURE(status)) { - return; - } - } - fStaticSets = DecimalFormatStaticSets::getStaticSets(status); - if (U_FAILURE(status)) { - return; - } - - UnicodeString str; - // Uses the default locale's number format pattern if there isn't - // one specified. - if (pattern == NULL) - { - UErrorCode nsStatus = U_ZERO_ERROR; - LocalPointer ns( - NumberingSystem::createInstance(nsStatus)); - if (U_FAILURE(nsStatus)) { - status = nsStatus; - return; - } - - int32_t len = 0; - UResourceBundle *top = ures_open(NULL, Locale::getDefault().getName(), &status); - - UResourceBundle *resource = ures_getByKeyWithFallback(top, fgNumberElements, NULL, &status); - resource = ures_getByKeyWithFallback(resource, ns->getName(), resource, &status); - resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &status); - const UChar *resStr = ures_getStringByKeyWithFallback(resource, fgDecimalFormat, &len, &status); - if ( status == U_MISSING_RESOURCE_ERROR && uprv_strcmp(fgLatn,ns->getName())) { - status = U_ZERO_ERROR; - resource = ures_getByKeyWithFallback(top, fgNumberElements, resource, &status); - resource = ures_getByKeyWithFallback(resource, fgLatn, resource, &status); - resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &status); - resStr = ures_getStringByKeyWithFallback(resource, fgDecimalFormat, &len, &status); - } - str.setTo(TRUE, resStr, len); - pattern = &str; - ures_close(resource); - ures_close(top); - } - - fImpl = new DecimalFormatImpl(this, *pattern, adoptedSymbols.getAlias(), parseErr, status); - if (fImpl) { - adoptedSymbols.orphan(); - } else if (U_SUCCESS(status)) { - status = U_MEMORY_ALLOCATION_ERROR; - } - if (U_FAILURE(status)) { - return; - } - - if (U_FAILURE(status)) - { - return; - } - - const UnicodeString* patternUsed; - UnicodeString currencyPluralPatternForOther; - // apply pattern - if (fStyle == UNUM_CURRENCY_PLURAL) { - fCurrencyPluralInfo = new CurrencyPluralInfo(fImpl->fSymbols->getLocale(), status); - if (U_FAILURE(status)) { - return; - } - - // the pattern used in format is not fixed until formatting, - // in which, the number is known and - // will be used to pick the right pattern based on plural count. - // Here, set the pattern as the pattern of plural count == "other". - // For most locale, the patterns are probably the same for all - // plural count. If not, the right pattern need to be re-applied - // during format. - fCurrencyPluralInfo->getCurrencyPluralPattern(UNICODE_STRING("other", 5), currencyPluralPatternForOther); - // TODO(refactor): Revisit, we are setting the pattern twice. - fImpl->applyPatternFavorCurrencyPrecision( - currencyPluralPatternForOther, status); - patternUsed = ¤cyPluralPatternForOther; - - } else { - patternUsed = pattern; - } - - if (patternUsed->indexOf(kCurrencySign) != -1) { - // initialize for currency, not only for plural format, - // but also for mix parsing - handleCurrencySignInPattern(status); - } -} - -void -DecimalFormat::handleCurrencySignInPattern(UErrorCode& status) { - // initialize for currency, not only for plural format, - // but also for mix parsing - if (U_FAILURE(status)) { - return; - } - if (fCurrencyPluralInfo == NULL) { - fCurrencyPluralInfo = new CurrencyPluralInfo(fImpl->fSymbols->getLocale(), status); - if (U_FAILURE(status)) { - return; - } - } - // need it for mix parsing - if (fAffixPatternsForCurrency == NULL) { - setupCurrencyAffixPatterns(status); - } -} - -static void -applyPatternWithNoSideEffects( - const UnicodeString& pattern, - UParseError& parseError, - UnicodeString &negPrefix, - UnicodeString &negSuffix, - UnicodeString &posPrefix, - UnicodeString &posSuffix, - UErrorCode& status) { - if (U_FAILURE(status)) - { - return; - } - DecimalFormatPatternParser patternParser; - DecimalFormatPattern out; - patternParser.applyPatternWithoutExpandAffix( - pattern, - out, - parseError, - status); - if (U_FAILURE(status)) { - return; - } - negPrefix = out.fNegPrefixPattern; - negSuffix = out.fNegSuffixPattern; - posPrefix = out.fPosPrefixPattern; - posSuffix = out.fPosSuffixPattern; -} - -void -DecimalFormat::setupCurrencyAffixPatterns(UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - UParseError parseErr; - fAffixPatternsForCurrency = initHashForAffixPattern(status); - if (U_FAILURE(status)) { - return; - } - - NumberingSystem *ns = NumberingSystem::createInstance(fImpl->fSymbols->getLocale(),status); - if (U_FAILURE(status)) { - return; - } - - // Save the default currency patterns of this locale. - // Here, chose onlyApplyPatternWithoutExpandAffix without - // expanding the affix patterns into affixes. - UnicodeString currencyPattern; - UErrorCode error = U_ZERO_ERROR; - - UResourceBundle *resource = ures_open(NULL, fImpl->fSymbols->getLocale().getName(), &error); - UResourceBundle *numElements = ures_getByKeyWithFallback(resource, fgNumberElements, NULL, &error); - resource = ures_getByKeyWithFallback(numElements, ns->getName(), resource, &error); - resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &error); - int32_t patLen = 0; - const UChar *patResStr = ures_getStringByKeyWithFallback(resource, fgCurrencyFormat, &patLen, &error); - if ( error == U_MISSING_RESOURCE_ERROR && uprv_strcmp(ns->getName(),fgLatn)) { - error = U_ZERO_ERROR; - resource = ures_getByKeyWithFallback(numElements, fgLatn, resource, &error); - resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &error); - patResStr = ures_getStringByKeyWithFallback(resource, fgCurrencyFormat, &patLen, &error); - } - ures_close(numElements); - ures_close(resource); - delete ns; - - if (U_SUCCESS(error)) { - UnicodeString negPrefix; - UnicodeString negSuffix; - UnicodeString posPrefix; - UnicodeString posSuffix; - applyPatternWithNoSideEffects(UnicodeString(patResStr, patLen), - parseErr, - negPrefix, negSuffix, posPrefix, posSuffix, status); - AffixPatternsForCurrency* affixPtn = new AffixPatternsForCurrency( - negPrefix, - negSuffix, - posPrefix, - posSuffix, - UCURR_SYMBOL_NAME); - fAffixPatternsForCurrency->put(UNICODE_STRING("default", 7), affixPtn, status); - } - - // save the unique currency plural patterns of this locale. - Hashtable* pluralPtn = fCurrencyPluralInfo->fPluralCountToCurrencyUnitPattern; - const UHashElement* element = NULL; - int32_t pos = UHASH_FIRST; - Hashtable pluralPatternSet; - while ((element = pluralPtn->nextElement(pos)) != NULL) { - const UHashTok valueTok = element->value; - const UnicodeString* value = (UnicodeString*)valueTok.pointer; - const UHashTok keyTok = element->key; - const UnicodeString* key = (UnicodeString*)keyTok.pointer; - if (pluralPatternSet.geti(*value) != 1) { - UnicodeString negPrefix; - UnicodeString negSuffix; - UnicodeString posPrefix; - UnicodeString posSuffix; - pluralPatternSet.puti(*value, 1, status); - applyPatternWithNoSideEffects( - *value, parseErr, - negPrefix, negSuffix, posPrefix, posSuffix, status); - AffixPatternsForCurrency* affixPtn = new AffixPatternsForCurrency( - negPrefix, - negSuffix, - posPrefix, - posSuffix, - UCURR_LONG_NAME); - fAffixPatternsForCurrency->put(*key, affixPtn, status); - } - } -} - - -//------------------------------------------------------------------------------ - -DecimalFormat::~DecimalFormat() -{ - deleteHashForAffixPattern(); - delete fCurrencyPluralInfo; - delete fImpl; -} - -//------------------------------------------------------------------------------ -// copy constructor - -DecimalFormat::DecimalFormat(const DecimalFormat &source) : - NumberFormat(source) { - init(); - *this = source; -} - -//------------------------------------------------------------------------------ -// assignment operator - -template -static void _clone_ptr(T** pdest, const T* source) { - delete *pdest; - if (source == NULL) { - *pdest = NULL; - } else { - *pdest = static_cast(source->clone()); - } -} - -DecimalFormat& -DecimalFormat::operator=(const DecimalFormat& rhs) -{ - if(this != &rhs) { - UErrorCode status = U_ZERO_ERROR; - NumberFormat::operator=(rhs); - if (fImpl == NULL) { - fImpl = new DecimalFormatImpl(this, *rhs.fImpl, status); - } else { - fImpl->assign(*rhs.fImpl, status); - } - fStaticSets = DecimalFormatStaticSets::getStaticSets(status); - fStyle = rhs.fStyle; - _clone_ptr(&fCurrencyPluralInfo, rhs.fCurrencyPluralInfo); - deleteHashForAffixPattern(); - if (rhs.fAffixPatternsForCurrency) { - UErrorCode status = U_ZERO_ERROR; - fAffixPatternsForCurrency = initHashForAffixPattern(status); - copyHashForAffixPattern(rhs.fAffixPatternsForCurrency, - fAffixPatternsForCurrency, status); - } - } - - return *this; -} - -//------------------------------------------------------------------------------ - -UBool -DecimalFormat::operator==(const Format& that) const -{ - if (this == &that) - return TRUE; - - // NumberFormat::operator== guarantees this cast is safe - const DecimalFormat* other = (DecimalFormat*)&that; - - return ( - NumberFormat::operator==(that) && - fBoolFlags.getAll() == other->fBoolFlags.getAll() && - *fImpl == *other->fImpl); - -} - -//------------------------------------------------------------------------------ - -Format* -DecimalFormat::clone() const -{ - return new DecimalFormat(*this); -} - - -FixedDecimal -DecimalFormat::getFixedDecimal(double number, UErrorCode &status) const { - VisibleDigitsWithExponent digits; - initVisibleDigitsWithExponent(number, digits, status); - if (U_FAILURE(status)) { - return FixedDecimal(); - } - return FixedDecimal(digits.getMantissa()); -} - -VisibleDigitsWithExponent & -DecimalFormat::initVisibleDigitsWithExponent( - double number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - return fImpl->initVisibleDigitsWithExponent(number, digits, status); -} - -FixedDecimal -DecimalFormat::getFixedDecimal(const Formattable &number, UErrorCode &status) const { - VisibleDigitsWithExponent digits; - initVisibleDigitsWithExponent(number, digits, status); - if (U_FAILURE(status)) { - return FixedDecimal(); - } - return FixedDecimal(digits.getMantissa()); -} - -VisibleDigitsWithExponent & -DecimalFormat::initVisibleDigitsWithExponent( - const Formattable &number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return digits; - } - if (!number.isNumeric()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return digits; - } - - DigitList *dl = number.getDigitList(); - if (dl != NULL) { - DigitList dlCopy(*dl); - return fImpl->initVisibleDigitsWithExponent( - dlCopy, digits, status); - } - - Formattable::Type type = number.getType(); - if (type == Formattable::kDouble || type == Formattable::kLong) { - return fImpl->initVisibleDigitsWithExponent( - number.getDouble(status), digits, status); - } - return fImpl->initVisibleDigitsWithExponent( - number.getInt64(), digits, status); -} - - -// Create a fixed decimal from a DigitList. -// The digit list may be modified. -// Internal function only. -FixedDecimal -DecimalFormat::getFixedDecimal(DigitList &number, UErrorCode &status) const { - VisibleDigitsWithExponent digits; - initVisibleDigitsWithExponent(number, digits, status); - if (U_FAILURE(status)) { - return FixedDecimal(); - } - return FixedDecimal(digits.getMantissa()); -} - -VisibleDigitsWithExponent & -DecimalFormat::initVisibleDigitsWithExponent( - DigitList &number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - return fImpl->initVisibleDigitsWithExponent( - number, digits, status); -} - - -//------------------------------------------------------------------------------ - -UnicodeString& -DecimalFormat::format(int32_t number, - UnicodeString& appendTo, - FieldPosition& fieldPosition) const -{ - UErrorCode status = U_ZERO_ERROR; - return fImpl->format(number, appendTo, fieldPosition, status); -} - -UnicodeString& -DecimalFormat::format(int32_t number, - UnicodeString& appendTo, - FieldPosition& fieldPosition, - UErrorCode& status) const -{ - return fImpl->format(number, appendTo, fieldPosition, status); -} - -UnicodeString& -DecimalFormat::format(int32_t number, - UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const -{ - return fImpl->format(number, appendTo, posIter, status); -} - - -//------------------------------------------------------------------------------ - -UnicodeString& -DecimalFormat::format(int64_t number, - UnicodeString& appendTo, - FieldPosition& fieldPosition) const -{ - UErrorCode status = U_ZERO_ERROR; /* ignored */ - return fImpl->format(number, appendTo, fieldPosition, status); -} - -UnicodeString& -DecimalFormat::format(int64_t number, - UnicodeString& appendTo, - FieldPosition& fieldPosition, - UErrorCode& status) const -{ - return fImpl->format(number, appendTo, fieldPosition, status); -} - -UnicodeString& -DecimalFormat::format(int64_t number, - UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const -{ - return fImpl->format(number, appendTo, posIter, status); -} - -//------------------------------------------------------------------------------ - -UnicodeString& -DecimalFormat::format( double number, - UnicodeString& appendTo, - FieldPosition& fieldPosition) const -{ - UErrorCode status = U_ZERO_ERROR; /* ignored */ - return fImpl->format(number, appendTo, fieldPosition, status); -} - -UnicodeString& -DecimalFormat::format( double number, - UnicodeString& appendTo, - FieldPosition& fieldPosition, - UErrorCode& status) const -{ - return fImpl->format(number, appendTo, fieldPosition, status); -} - -UnicodeString& -DecimalFormat::format( double number, - UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const -{ - return fImpl->format(number, appendTo, posIter, status); -} - -//------------------------------------------------------------------------------ - - -UnicodeString& -DecimalFormat::format(StringPiece number, - UnicodeString &toAppendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const -{ - return fImpl->format(number, toAppendTo, posIter, status); -} - - -UnicodeString& -DecimalFormat::format(const DigitList &number, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const { - return fImpl->format(number, appendTo, posIter, status); -} - - -UnicodeString& -DecimalFormat::format(const DigitList &number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - return fImpl->format(number, appendTo, pos, status); -} - -UnicodeString& -DecimalFormat::format(const VisibleDigitsWithExponent &number, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const { - return fImpl->format(number, appendTo, posIter, status); -} - - -UnicodeString& -DecimalFormat::format(const VisibleDigitsWithExponent &number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - return fImpl->format(number, appendTo, pos, status); -} - -DigitList& -DecimalFormat::_round(const DigitList& number, DigitList& adjustedNum, UBool& isNegative, UErrorCode& status) const { - adjustedNum = number; - fImpl->round(adjustedNum, status); - isNegative = !adjustedNum.isPositive(); - return adjustedNum; -} - -void -DecimalFormat::parse(const UnicodeString& text, - Formattable& result, - ParsePosition& parsePosition) const { - parse(text, result, parsePosition, NULL); -} - -CurrencyAmount* DecimalFormat::parseCurrency(const UnicodeString& text, - ParsePosition& pos) const { - Formattable parseResult; - int32_t start = pos.getIndex(); - UChar curbuf[4] = {}; - parse(text, parseResult, pos, curbuf); - if (pos.getIndex() != start) { - UErrorCode ec = U_ZERO_ERROR; - LocalPointer currAmt(new CurrencyAmount(parseResult, curbuf, ec), ec); - if (U_FAILURE(ec)) { - pos.setIndex(start); // indicate failure - } else { - return currAmt.orphan(); - } - } - return NULL; -} - -/** - * Parses the given text as a number, optionally providing a currency amount. - * @param text the string to parse - * @param result output parameter for the numeric result. - * @param parsePosition input-output position; on input, the - * position within text to match; must have 0 <= pos.getIndex() < - * text.length(); on output, the position after the last matched - * character. If the parse fails, the position in unchanged upon - * output. - * @param currency if non-NULL, it should point to a 4-UChar buffer. - * In this case the text is parsed as a currency format, and the - * ISO 4217 code for the parsed currency is put into the buffer. - * Otherwise the text is parsed as a non-currency format. - */ -void DecimalFormat::parse(const UnicodeString& text, - Formattable& result, - ParsePosition& parsePosition, - UChar* currency) const { - int32_t startIdx, backup; - int32_t i = startIdx = backup = parsePosition.getIndex(); - - // clear any old contents in the result. In particular, clears any DigitList - // that it may be holding. - result.setLong(0); - if (currency != NULL) { - for (int32_t ci=0; ci<4; ci++) { - currency[ci] = 0; - } - } - - // Handle NaN as a special case: - int32_t formatWidth = fImpl->getOldFormatWidth(); - - // Skip padding characters, if around prefix - if (formatWidth > 0 && ( - fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadBeforePrefix || - fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadAfterPrefix)) { - i = skipPadding(text, i); - } - - if (isLenient()) { - // skip any leading whitespace - i = backup = skipUWhiteSpace(text, i); - } - - // If the text is composed of the representation of NaN, returns NaN.length - const UnicodeString *nan = &fImpl->getConstSymbol(DecimalFormatSymbols::kNaNSymbol); - int32_t nanLen = (text.compare(i, nan->length(), *nan) - ? 0 : nan->length()); - if (nanLen) { - i += nanLen; - if (formatWidth > 0 && (fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadBeforeSuffix || fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadAfterSuffix)) { - i = skipPadding(text, i); - } - parsePosition.setIndex(i); - result.setDouble(uprv_getNaN()); - return; - } - - // NaN parse failed; start over - i = backup; - parsePosition.setIndex(i); - - // status is used to record whether a number is infinite. - UBool status[fgStatusLength]; - - DigitList *digits = result.getInternalDigitList(); // get one from the stack buffer - if (digits == NULL) { - return; // no way to report error from here. - } - - if (fImpl->fMonetary) { - if (!parseForCurrency(text, parsePosition, *digits, - status, currency)) { - return; - } - } else { - if (!subparse(text, - &fImpl->fAffixes.fNegativePrefix.getOtherVariant().toString(), - &fImpl->fAffixes.fNegativeSuffix.getOtherVariant().toString(), - &fImpl->fAffixes.fPositivePrefix.getOtherVariant().toString(), - &fImpl->fAffixes.fPositiveSuffix.getOtherVariant().toString(), - FALSE, UCURR_SYMBOL_NAME, - parsePosition, *digits, status, currency)) { - debug("!subparse(...) - rewind"); - parsePosition.setIndex(startIdx); - return; - } - } - - // Handle infinity - if (status[fgStatusInfinite]) { - double inf = uprv_getInfinity(); - result.setDouble(digits->isPositive() ? inf : -inf); - // TODO: set the dl to infinity, and let it fall into the code below. - } - - else { - - if (!fImpl->fMultiplier.isZero()) { - UErrorCode ec = U_ZERO_ERROR; - digits->div(fImpl->fMultiplier, ec); - } - - if (fImpl->fScale != 0) { - DigitList ten; - ten.set((int32_t)10); - if (fImpl->fScale > 0) { - for (int32_t i = fImpl->fScale; i > 0; i--) { - UErrorCode ec = U_ZERO_ERROR; - digits->div(ten,ec); - } - } else { - for (int32_t i = fImpl->fScale; i < 0; i++) { - UErrorCode ec = U_ZERO_ERROR; - digits->mult(ten,ec); - } - } - } - - // Negative zero special case: - // if parsing integerOnly, change to +0, which goes into an int32 in a Formattable. - // if not parsing integerOnly, leave as -0, which a double can represent. - if (digits->isZero() && !digits->isPositive() && isParseIntegerOnly()) { - digits->setPositive(TRUE); - } - result.adoptDigitList(digits); - } -} - - - -UBool -DecimalFormat::parseForCurrency(const UnicodeString& text, - ParsePosition& parsePosition, - DigitList& digits, - UBool* status, - UChar* currency) const { - UnicodeString positivePrefix; - UnicodeString positiveSuffix; - UnicodeString negativePrefix; - UnicodeString negativeSuffix; - fImpl->fPositivePrefixPattern.toString(positivePrefix); - fImpl->fPositiveSuffixPattern.toString(positiveSuffix); - fImpl->fNegativePrefixPattern.toString(negativePrefix); - fImpl->fNegativeSuffixPattern.toString(negativeSuffix); - - int origPos = parsePosition.getIndex(); - int maxPosIndex = origPos; - int maxErrorPos = -1; - // First, parse against current pattern. - // Since current pattern could be set by applyPattern(), - // it could be an arbitrary pattern, and it may not be the one - // defined in current locale. - UBool tmpStatus[fgStatusLength]; - ParsePosition tmpPos(origPos); - DigitList tmpDigitList; - UBool found; - if (fStyle == UNUM_CURRENCY_PLURAL) { - found = subparse(text, - &negativePrefix, &negativeSuffix, - &positivePrefix, &positiveSuffix, - TRUE, UCURR_LONG_NAME, - tmpPos, tmpDigitList, tmpStatus, currency); - } else { - found = subparse(text, - &negativePrefix, &negativeSuffix, - &positivePrefix, &positiveSuffix, - TRUE, UCURR_SYMBOL_NAME, - tmpPos, tmpDigitList, tmpStatus, currency); - } - if (found) { - if (tmpPos.getIndex() > maxPosIndex) { - maxPosIndex = tmpPos.getIndex(); - for (int32_t i = 0; i < fgStatusLength; ++i) { - status[i] = tmpStatus[i]; - } - digits = tmpDigitList; - } - } else { - maxErrorPos = tmpPos.getErrorIndex(); - } - // Then, parse against affix patterns. - // Those are currency patterns and currency plural patterns. - int32_t pos = UHASH_FIRST; - const UHashElement* element = NULL; - while ( (element = fAffixPatternsForCurrency->nextElement(pos)) != NULL ) { - const UHashTok valueTok = element->value; - const AffixPatternsForCurrency* affixPtn = (AffixPatternsForCurrency*)valueTok.pointer; - UBool tmpStatus[fgStatusLength]; - ParsePosition tmpPos(origPos); - DigitList tmpDigitList; - -#ifdef FMT_DEBUG - debug("trying affix for currency.."); - affixPtn->dump(); -#endif - - UBool result = subparse(text, - &affixPtn->negPrefixPatternForCurrency, - &affixPtn->negSuffixPatternForCurrency, - &affixPtn->posPrefixPatternForCurrency, - &affixPtn->posSuffixPatternForCurrency, - TRUE, affixPtn->patternType, - tmpPos, tmpDigitList, tmpStatus, currency); - if (result) { - found = true; - if (tmpPos.getIndex() > maxPosIndex) { - maxPosIndex = tmpPos.getIndex(); - for (int32_t i = 0; i < fgStatusLength; ++i) { - status[i] = tmpStatus[i]; - } - digits = tmpDigitList; - } - } else { - maxErrorPos = (tmpPos.getErrorIndex() > maxErrorPos) ? - tmpPos.getErrorIndex() : maxErrorPos; - } - } - // Finally, parse against simple affix to find the match. - // For example, in TestMonster suite, - // if the to-be-parsed text is "-\u00A40,00". - // complexAffixCompare will not find match, - // since there is no ISO code matches "\u00A4", - // and the parse stops at "\u00A4". - // We will just use simple affix comparison (look for exact match) - // to pass it. - // - // TODO: We should parse against simple affix first when - // output currency is not requested. After the complex currency - // parsing implementation was introduced, the default currency - // instance parsing slowed down because of the new code flow. - // I filed #10312 - Yoshito - UBool tmpStatus_2[fgStatusLength]; - ParsePosition tmpPos_2(origPos); - DigitList tmpDigitList_2; - - // Disable complex currency parsing and try it again. - UBool result = subparse(text, - &fImpl->fAffixes.fNegativePrefix.getOtherVariant().toString(), - &fImpl->fAffixes.fNegativeSuffix.getOtherVariant().toString(), - &fImpl->fAffixes.fPositivePrefix.getOtherVariant().toString(), - &fImpl->fAffixes.fPositiveSuffix.getOtherVariant().toString(), - FALSE /* disable complex currency parsing */, UCURR_SYMBOL_NAME, - tmpPos_2, tmpDigitList_2, tmpStatus_2, - currency); - if (result) { - if (tmpPos_2.getIndex() > maxPosIndex) { - maxPosIndex = tmpPos_2.getIndex(); - for (int32_t i = 0; i < fgStatusLength; ++i) { - status[i] = tmpStatus_2[i]; - } - digits = tmpDigitList_2; - } - found = true; - } else { - maxErrorPos = (tmpPos_2.getErrorIndex() > maxErrorPos) ? - tmpPos_2.getErrorIndex() : maxErrorPos; - } - - if (!found) { - //parsePosition.setIndex(origPos); - parsePosition.setErrorIndex(maxErrorPos); - } else { - parsePosition.setIndex(maxPosIndex); - parsePosition.setErrorIndex(-1); - } - return found; -} - - -/** - * Parse the given text into a number. The text is parsed beginning at - * parsePosition, until an unparseable character is seen. - * @param text the string to parse. - * @param negPrefix negative prefix. - * @param negSuffix negative suffix. - * @param posPrefix positive prefix. - * @param posSuffix positive suffix. - * @param complexCurrencyParsing whether it is complex currency parsing or not. - * @param type the currency type to parse against, LONG_NAME only or not. - * @param parsePosition The position at which to being parsing. Upon - * return, the first unparsed character. - * @param digits the DigitList to set to the parsed value. - * @param status output param containing boolean status flags indicating - * whether the value was infinite and whether it was positive. - * @param currency return value for parsed currency, for generic - * currency parsing mode, or NULL for normal parsing. In generic - * currency parsing mode, any currency is parsed, not just the - * currency that this formatter is set to. - */ -UBool DecimalFormat::subparse(const UnicodeString& text, - const UnicodeString* negPrefix, - const UnicodeString* negSuffix, - const UnicodeString* posPrefix, - const UnicodeString* posSuffix, - UBool complexCurrencyParsing, - int8_t type, - ParsePosition& parsePosition, - DigitList& digits, UBool* status, - UChar* currency) const -{ - // The parsing process builds up the number as char string, in the neutral format that - // will be acceptable to the decNumber library, then at the end passes that string - // off for conversion to a decNumber. - UErrorCode err = U_ZERO_ERROR; - CharString parsedNum; - digits.setToZero(); - - int32_t position = parsePosition.getIndex(); - int32_t oldStart = position; - int32_t textLength = text.length(); // One less pointer to follow - UBool strictParse = !isLenient(); - UChar32 zero = fImpl->getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); - const UnicodeString *groupingString = &fImpl->getConstSymbol( - !fImpl->fMonetary ? - DecimalFormatSymbols::kGroupingSeparatorSymbol : DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol); - UChar32 groupingChar = groupingString->char32At(0); - int32_t groupingStringLength = groupingString->length(); - int32_t groupingCharLength = U16_LENGTH(groupingChar); - UBool groupingUsed = isGroupingUsed(); -#ifdef FMT_DEBUG - UChar dbgbuf[300]; - UnicodeString s(dbgbuf,0,300);; - s.append((UnicodeString)"PARSE \"").append(text.tempSubString(position)).append((UnicodeString)"\" " ); -#define DBGAPPD(x) if(x) { s.append(UnicodeString(#x "=")); if(x->isEmpty()) { s.append(UnicodeString("")); } else { s.append(*x); } s.append(UnicodeString(" ")); } else { s.append(UnicodeString(#x "=NULL ")); } - DBGAPPD(negPrefix); - DBGAPPD(negSuffix); - DBGAPPD(posPrefix); - DBGAPPD(posSuffix); - debugout(s); -#endif - - UBool fastParseOk = false; /* TRUE iff fast parse is OK */ - // UBool fastParseHadDecimal = FALSE; /* true if fast parse saw a decimal point. */ - if((fImpl->isParseFastpath()) && !fImpl->fMonetary && - text.length()>0 && - text.length()<32 && - (posPrefix==NULL||posPrefix->isEmpty()) && - (posSuffix==NULL||posSuffix->isEmpty()) && - // (negPrefix==NULL||negPrefix->isEmpty()) && - // (negSuffix==NULL||(negSuffix->isEmpty()) ) && - TRUE) { // optimized path - int j=position; - int l=text.length(); - int digitCount=0; - UChar32 ch = text.char32At(j); - const UnicodeString *decimalString = &fImpl->getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); - UChar32 decimalChar = 0; - UBool intOnly = FALSE; - UChar32 lookForGroup = (groupingUsed&&intOnly&&strictParse)?groupingChar:0; - - int32_t decimalCount = decimalString->countChar32(0,3); - if(isParseIntegerOnly()) { - decimalChar = 0; // not allowed - intOnly = TRUE; // Don't look for decimals. - } else if(decimalCount==1) { - decimalChar = decimalString->char32At(0); // Look for this decimal - } else if(decimalCount==0) { - decimalChar=0; // NO decimal set - } else { - j=l+1;//Set counter to end of line, so that we break. Unknown decimal situation. - } - -#ifdef FMT_DEBUG - printf("Preparing to do fastpath parse: decimalChar=U+%04X, groupingChar=U+%04X, first ch=U+%04X intOnly=%c strictParse=%c\n", - decimalChar, groupingChar, ch, - (intOnly)?'y':'n', - (strictParse)?'y':'n'); -#endif - if(ch==0x002D) { // '-' - j=l+1;//=break - negative number. - - /* - parsedNum.append('-',err); - j+=U16_LENGTH(ch); - if(j=0 && digit <= 9) { - parsedNum.append((char)(digit + '0'), err); - if((digitCount>0) || digit!=0 || j==(l-1)) { - digitCount++; - } - } else if(ch == 0) { // break out - digitCount=-1; - break; - } else if(ch == decimalChar) { - parsedNum.append((char)('.'), err); - decimalChar=0; // no more decimals. - // fastParseHadDecimal=TRUE; - } else if(ch == lookForGroup) { - // ignore grouping char. No decimals, so it has to be an ignorable grouping sep - } else if(intOnly && (lookForGroup!=0) && !u_isdigit(ch)) { - // parsing integer only and can fall through - } else { - digitCount=-1; // fail - fall through to slow parse - break; - } - j+=U16_LENGTH(ch); - ch = text.char32At(j); // for next - } - if( - ((j==l)||intOnly) // end OR only parsing integer - && (digitCount>0)) { // and have at least one digit - fastParseOk=true; // Fast parse OK! - -#ifdef SKIP_OPT - debug("SKIP_OPT"); - /* for testing, try it the slow way. also */ - fastParseOk=false; - parsedNum.clear(); -#else - parsePosition.setIndex(position=j); - status[fgStatusInfinite]=false; -#endif - } else { - // was not OK. reset, retry -#ifdef FMT_DEBUG - printf("Fall through: j=%d, l=%d, digitCount=%d\n", j, l, digitCount); -#endif - parsedNum.clear(); - } - } else { -#ifdef FMT_DEBUG - printf("Could not fastpath parse. "); - printf("text.length()=%d ", text.length()); - printf("posPrefix=%p posSuffix=%p ", posPrefix, posSuffix); - - printf("\n"); -#endif - } - - UnicodeString formatPattern; - toPattern(formatPattern); - - if(!fastParseOk -#if UCONFIG_HAVE_PARSEALLINPUT - && fParseAllInput!=UNUM_YES -#endif - ) - { - int32_t formatWidth = fImpl->getOldFormatWidth(); - // Match padding before prefix - if (formatWidth > 0 && fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadBeforePrefix) { - position = skipPadding(text, position); - } - - // Match positive and negative prefixes; prefer longest match. - int32_t posMatch = compareAffix(text, position, FALSE, TRUE, posPrefix, complexCurrencyParsing, type, currency); - int32_t negMatch = compareAffix(text, position, TRUE, TRUE, negPrefix, complexCurrencyParsing, type, currency); - if (posMatch >= 0 && negMatch >= 0) { - if (posMatch > negMatch) { - negMatch = -1; - } else if (negMatch > posMatch) { - posMatch = -1; - } - } - if (posMatch >= 0) { - position += posMatch; - parsedNum.append('+', err); - } else if (negMatch >= 0) { - position += negMatch; - parsedNum.append('-', err); - } else if (strictParse){ - parsePosition.setErrorIndex(position); - return FALSE; - } else { - // Temporary set positive. This might be changed after checking suffix - parsedNum.append('+', err); - } - - // Match padding before prefix - if (formatWidth > 0 && fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadAfterPrefix) { - position = skipPadding(text, position); - } - - if (! strictParse) { - position = skipUWhiteSpace(text, position); - } - - // process digits or Inf, find decimal position - const UnicodeString *inf = &fImpl->getConstSymbol(DecimalFormatSymbols::kInfinitySymbol); - int32_t infLen = (text.compare(position, inf->length(), *inf) - ? 0 : inf->length()); - position += infLen; // infLen is non-zero when it does equal to infinity - status[fgStatusInfinite] = infLen != 0; - - if (infLen != 0) { - parsedNum.append("Infinity", err); - } else { - // We now have a string of digits, possibly with grouping symbols, - // and decimal points. We want to process these into a DigitList. - // We don't want to put a bunch of leading zeros into the DigitList - // though, so we keep track of the location of the decimal point, - // put only significant digits into the DigitList, and adjust the - // exponent as needed. - - - UBool strictFail = FALSE; // did we exit with a strict parse failure? - int32_t lastGroup = -1; // after which digit index did we last see a grouping separator? - int32_t currGroup = -1; // for temporary storage the digit index of the current grouping separator - int32_t gs2 = fImpl->fEffGrouping.fGrouping2 == 0 ? fImpl->fEffGrouping.fGrouping : fImpl->fEffGrouping.fGrouping2; - - const UnicodeString *decimalString; - if (fImpl->fMonetary) { - decimalString = &fImpl->getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); - } else { - decimalString = &fImpl->getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); - } - UChar32 decimalChar = decimalString->char32At(0); - int32_t decimalStringLength = decimalString->length(); - int32_t decimalCharLength = U16_LENGTH(decimalChar); - - UBool sawDecimal = FALSE; - UChar32 sawDecimalChar = 0xFFFF; - UBool sawGrouping = FALSE; - UChar32 sawGroupingChar = 0xFFFF; - UBool sawDigit = FALSE; - int32_t backup = -1; - int32_t digit; - - // equivalent grouping and decimal support - const UnicodeSet *decimalSet = NULL; - const UnicodeSet *groupingSet = NULL; - - if (decimalCharLength == decimalStringLength) { - decimalSet = DecimalFormatStaticSets::getSimilarDecimals(decimalChar, strictParse); - } - - if (groupingCharLength == groupingStringLength) { - if (strictParse) { - groupingSet = fStaticSets->fStrictDefaultGroupingSeparators; - } else { - groupingSet = fStaticSets->fDefaultGroupingSeparators; - } - } - - // We need to test groupingChar and decimalChar separately from groupingSet and decimalSet, if the sets are even initialized. - // If sawDecimal is TRUE, only consider sawDecimalChar and NOT decimalSet - // If a character matches decimalSet, don't consider it to be a member of the groupingSet. - - // We have to track digitCount ourselves, because digits.fCount will - // pin when the maximum allowable digits is reached. - int32_t digitCount = 0; - int32_t integerDigitCount = 0; - - for (; position < textLength; ) - { - UChar32 ch = text.char32At(position); - - /* We recognize all digit ranges, not only the Latin digit range - * '0'..'9'. We do so by using the Character.digit() method, - * which converts a valid Unicode digit to the range 0..9. - * - * The character 'ch' may be a digit. If so, place its value - * from 0 to 9 in 'digit'. First try using the locale digit, - * which may or MAY NOT be a standard Unicode digit range. If - * this fails, try using the standard Unicode digit ranges by - * calling Character.digit(). If this also fails, digit will - * have a value outside the range 0..9. - */ - digit = ch - zero; - if (digit < 0 || digit > 9) - { - digit = u_charDigitValue(ch); - } - - // As a last resort, look through the localized digits if the zero digit - // is not a "standard" Unicode digit. - if ( (digit < 0 || digit > 9) && u_charDigitValue(zero) != 0) { - digit = 0; - if ( fImpl->getConstSymbol((DecimalFormatSymbols::ENumberFormatSymbol)(DecimalFormatSymbols::kZeroDigitSymbol)).char32At(0) == ch ) { - break; - } - for (digit = 1 ; digit < 10 ; digit++ ) { - if ( fImpl->getConstSymbol((DecimalFormatSymbols::ENumberFormatSymbol)(DecimalFormatSymbols::kOneDigitSymbol+digit-1)).char32At(0) == ch ) { - break; - } - } - } - - if (digit >= 0 && digit <= 9) - { - if (strictParse && backup != -1) { - // comma followed by digit, so group before comma is a - // secondary group. If there was a group separator - // before that, the group must == the secondary group - // length, else it can be <= the the secondary group - // length. - if ((lastGroup != -1 && currGroup - lastGroup != gs2) || - (lastGroup == -1 && digitCount - 1 > gs2)) { - strictFail = TRUE; - break; - } - - lastGroup = currGroup; - } - - // Cancel out backup setting (see grouping handler below) - currGroup = -1; - backup = -1; - sawDigit = TRUE; - - // Note: this will append leading zeros - parsedNum.append((char)(digit + '0'), err); - - // count any digit that's not a leading zero - if (digit > 0 || digitCount > 0 || sawDecimal) { - digitCount += 1; - - // count any integer digit that's not a leading zero - if (! sawDecimal) { - integerDigitCount += 1; - } - } - - position += U16_LENGTH(ch); - } - else if (groupingStringLength > 0 && - matchGrouping(groupingChar, sawGrouping, sawGroupingChar, groupingSet, - decimalChar, decimalSet, - ch) && groupingUsed) - { - if (sawDecimal) { - break; - } - - if (strictParse) { - if ((!sawDigit || backup != -1)) { - // leading group, or two group separators in a row - strictFail = TRUE; - break; - } - } - - // Ignore grouping characters, if we are using them, but require - // that they be followed by a digit. Otherwise we backup and - // reprocess them. - currGroup = digitCount; - backup = position; - position += groupingStringLength; - sawGrouping=TRUE; - // Once we see a grouping character, we only accept that grouping character from then on. - sawGroupingChar=ch; - } - else if (matchDecimal(decimalChar,sawDecimal,sawDecimalChar, decimalSet, ch)) - { - if (strictParse) { - if (backup != -1 || - (lastGroup != -1 && digitCount - lastGroup != fImpl->fEffGrouping.fGrouping)) { - strictFail = TRUE; - break; - } - } - - // If we're only parsing integers, or if we ALREADY saw the - // decimal, then don't parse this one. - if (isParseIntegerOnly() || sawDecimal) { - break; - } - - parsedNum.append('.', err); - position += decimalStringLength; - sawDecimal = TRUE; - // Once we see a decimal character, we only accept that decimal character from then on. - sawDecimalChar=ch; - // decimalSet is considered to consist of (ch,ch) - } - else { - - if(!fBoolFlags.contains(UNUM_PARSE_NO_EXPONENT) || // don't parse if this is set unless.. - isScientificNotation()) { // .. it's an exponent format - ignore setting and parse anyways - const UnicodeString *tmp; - tmp = &fImpl->getConstSymbol(DecimalFormatSymbols::kExponentialSymbol); - // TODO: CASE - if (!text.caseCompare(position, tmp->length(), *tmp, U_FOLD_CASE_DEFAULT)) // error code is set below if !sawDigit - { - // Parse sign, if present - int32_t pos = position + tmp->length(); - char exponentSign = '+'; - - if (pos < textLength) - { - tmp = &fImpl->getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); - if (!text.compare(pos, tmp->length(), *tmp)) - { - pos += tmp->length(); - } - else { - tmp = &fImpl->getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); - if (!text.compare(pos, tmp->length(), *tmp)) - { - exponentSign = '-'; - pos += tmp->length(); - } - } - } - - UBool sawExponentDigit = FALSE; - while (pos < textLength) { - ch = text.char32At(pos); - digit = ch - zero; - - if (digit < 0 || digit > 9) { - digit = u_charDigitValue(ch); - } - if (0 <= digit && digit <= 9) { - if (!sawExponentDigit) { - parsedNum.append('E', err); - parsedNum.append(exponentSign, err); - sawExponentDigit = TRUE; - } - pos += U16_LENGTH(ch); - parsedNum.append((char)(digit + '0'), err); - } else { - break; - } - } - - if (sawExponentDigit) { - position = pos; // Advance past the exponent - } - - break; // Whether we fail or succeed, we exit this loop - } else { - break; - } - } else { // not parsing exponent - break; - } - } - } - - // if we didn't see a decimal and it is required, check to see if the pattern had one - if(!sawDecimal && isDecimalPatternMatchRequired()) - { - if(formatPattern.indexOf(kPatternDecimalSeparator) != -1) - { - parsePosition.setIndex(oldStart); - parsePosition.setErrorIndex(position); - debug("decimal point match required fail!"); - return FALSE; - } - } - - if (backup != -1) - { - position = backup; - } - - if (strictParse && !sawDecimal) { - if (lastGroup != -1 && digitCount - lastGroup != fImpl->fEffGrouping.fGrouping) { - strictFail = TRUE; - } - } - - if (strictFail) { - // only set with strictParse and a grouping separator error - - parsePosition.setIndex(oldStart); - parsePosition.setErrorIndex(position); - debug("strictFail!"); - return FALSE; - } - - // If there was no decimal point we have an integer - - // If none of the text string was recognized. For example, parse - // "x" with pattern "#0.00" (return index and error index both 0) - // parse "$" with pattern "$#0.00". (return index 0 and error index - // 1). - if (!sawDigit && digitCount == 0) { -#ifdef FMT_DEBUG - debug("none of text rec"); - printf("position=%d\n",position); -#endif - parsePosition.setIndex(oldStart); - parsePosition.setErrorIndex(oldStart); - return FALSE; - } - } - - // Match padding before suffix - if (formatWidth > 0 && fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadBeforeSuffix) { - position = skipPadding(text, position); - } - - int32_t posSuffixMatch = -1, negSuffixMatch = -1; - - // Match positive and negative suffixes; prefer longest match. - if (posMatch >= 0 || (!strictParse && negMatch < 0)) { - posSuffixMatch = compareAffix(text, position, FALSE, FALSE, posSuffix, complexCurrencyParsing, type, currency); - } - if (negMatch >= 0) { - negSuffixMatch = compareAffix(text, position, TRUE, FALSE, negSuffix, complexCurrencyParsing, type, currency); - } - if (posSuffixMatch >= 0 && negSuffixMatch >= 0) { - if (posSuffixMatch > negSuffixMatch) { - negSuffixMatch = -1; - } else if (negSuffixMatch > posSuffixMatch) { - posSuffixMatch = -1; - } - } - - // Fail if neither or both - if (strictParse && ((posSuffixMatch >= 0) == (negSuffixMatch >= 0))) { - parsePosition.setErrorIndex(position); - debug("neither or both"); - return FALSE; - } - - position += (posSuffixMatch >= 0 ? posSuffixMatch : (negSuffixMatch >= 0 ? negSuffixMatch : 0)); - - // Match padding before suffix - if (formatWidth > 0 && fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadAfterSuffix) { - position = skipPadding(text, position); - } - - parsePosition.setIndex(position); - - parsedNum.data()[0] = (posSuffixMatch >= 0 || (!strictParse && negMatch < 0 && negSuffixMatch < 0)) ? '+' : '-'; -#ifdef FMT_DEBUG -printf("PP -> %d, SLOW = [%s]! pp=%d, os=%d, err=%s\n", position, parsedNum.data(), parsePosition.getIndex(),oldStart,u_errorName(err)); -#endif - } /* end SLOW parse */ - if(parsePosition.getIndex() == oldStart) - { -#ifdef FMT_DEBUG - printf(" PP didnt move, err\n"); -#endif - parsePosition.setErrorIndex(position); - return FALSE; - } -#if UCONFIG_HAVE_PARSEALLINPUT - else if (fParseAllInput==UNUM_YES&&parsePosition.getIndex()!=textLength) - { -#ifdef FMT_DEBUG - printf(" PP didnt consume all (UNUM_YES), err\n"); -#endif - parsePosition.setErrorIndex(position); - return FALSE; - } -#endif - // uint32_t bits = (fastParseOk?kFastpathOk:0) | - // (fastParseHadDecimal?0:kNoDecimal); - //printf("FPOK=%d, FPHD=%d, bits=%08X\n", fastParseOk, fastParseHadDecimal, bits); - digits.set(parsedNum.toStringPiece(), - err, - 0//bits - ); - - if (U_FAILURE(err)) { -#ifdef FMT_DEBUG - printf(" err setting %s\n", u_errorName(err)); -#endif - parsePosition.setErrorIndex(position); - return FALSE; - } - - // check if we missed a required decimal point - if(fastParseOk && isDecimalPatternMatchRequired()) - { - if(formatPattern.indexOf(kPatternDecimalSeparator) != -1) - { - parsePosition.setIndex(oldStart); - parsePosition.setErrorIndex(position); - debug("decimal point match required fail!"); - return FALSE; - } - } - - - return TRUE; -} - -/** - * Starting at position, advance past a run of pad characters, if any. - * Return the index of the first character after position that is not a pad - * character. Result is >= position. - */ -int32_t DecimalFormat::skipPadding(const UnicodeString& text, int32_t position) const { - int32_t padLen = U16_LENGTH(fImpl->fAffixes.fPadChar); - while (position < text.length() && - text.char32At(position) == fImpl->fAffixes.fPadChar) { - position += padLen; - } - return position; -} - -/** - * Return the length matched by the given affix, or -1 if none. - * Runs of white space in the affix, match runs of white space in - * the input. Pattern white space and input white space are - * determined differently; see code. - * @param text input text - * @param pos offset into input at which to begin matching - * @param isNegative - * @param isPrefix - * @param affixPat affix pattern used for currency affix comparison. - * @param complexCurrencyParsing whether it is currency parsing or not - * @param type the currency type to parse against, LONG_NAME only or not. - * @param currency return value for parsed currency, for generic - * currency parsing mode, or null for normal parsing. In generic - * currency parsing mode, any currency is parsed, not just the - * currency that this formatter is set to. - * @return length of input that matches, or -1 if match failure - */ -int32_t DecimalFormat::compareAffix(const UnicodeString& text, - int32_t pos, - UBool isNegative, - UBool isPrefix, - const UnicodeString* affixPat, - UBool complexCurrencyParsing, - int8_t type, - UChar* currency) const -{ - const UnicodeString *patternToCompare; - if (currency != NULL || - (fImpl->fMonetary && complexCurrencyParsing)) { - - if (affixPat != NULL) { - return compareComplexAffix(*affixPat, text, pos, type, currency); - } - } - - if (isNegative) { - if (isPrefix) { - patternToCompare = &fImpl->fAffixes.fNegativePrefix.getOtherVariant().toString(); - } - else { - patternToCompare = &fImpl->fAffixes.fNegativeSuffix.getOtherVariant().toString(); - } - } - else { - if (isPrefix) { - patternToCompare = &fImpl->fAffixes.fPositivePrefix.getOtherVariant().toString(); - } - else { - patternToCompare = &fImpl->fAffixes.fPositiveSuffix.getOtherVariant().toString(); - } - } - return compareSimpleAffix(*patternToCompare, text, pos, isLenient()); -} - -UBool DecimalFormat::equalWithSignCompatibility(UChar32 lhs, UChar32 rhs) const { - if (lhs == rhs) { - return TRUE; - } - U_ASSERT(fStaticSets != NULL); // should already be loaded - const UnicodeSet *minusSigns = fStaticSets->fMinusSigns; - const UnicodeSet *plusSigns = fStaticSets->fPlusSigns; - return (minusSigns->contains(lhs) && minusSigns->contains(rhs)) || - (plusSigns->contains(lhs) && plusSigns->contains(rhs)); -} - -// check for LRM 0x200E, RLM 0x200F, ALM 0x061C -#define IS_BIDI_MARK(c) (c==0x200E || c==0x200F || c==0x061C) - -#define TRIM_BUFLEN 32 -UnicodeString& DecimalFormat::trimMarksFromAffix(const UnicodeString& affix, UnicodeString& trimmedAffix) { - UChar trimBuf[TRIM_BUFLEN]; - int32_t affixLen = affix.length(); - int32_t affixPos, trimLen = 0; - - for (affixPos = 0; affixPos < affixLen; affixPos++) { - UChar c = affix.charAt(affixPos); - if (!IS_BIDI_MARK(c)) { - if (trimLen < TRIM_BUFLEN) { - trimBuf[trimLen++] = c; - } else { - trimLen = 0; - break; - } - } - } - return (trimLen > 0)? trimmedAffix.setTo(trimBuf, trimLen): trimmedAffix.setTo(affix); -} - -/** - * Return the length matched by the given affix, or -1 if none. - * Runs of white space in the affix, match runs of white space in - * the input. Pattern white space and input white space are - * determined differently; see code. - * @param affix pattern string, taken as a literal - * @param input input text - * @param pos offset into input at which to begin matching - * @return length of input that matches, or -1 if match failure - */ -int32_t DecimalFormat::compareSimpleAffix(const UnicodeString& affix, - const UnicodeString& input, - int32_t pos, - UBool lenient) const { - int32_t start = pos; - UnicodeString trimmedAffix; - // For more efficiency we should keep lazily-created trimmed affixes around in - // instance variables instead of trimming each time they are used (the next step) - trimMarksFromAffix(affix, trimmedAffix); - UChar32 affixChar = trimmedAffix.char32At(0); - int32_t affixLength = trimmedAffix.length(); - int32_t inputLength = input.length(); - int32_t affixCharLength = U16_LENGTH(affixChar); - UnicodeSet *affixSet; - UErrorCode status = U_ZERO_ERROR; - - U_ASSERT(fStaticSets != NULL); // should already be loaded - - if (U_FAILURE(status)) { - return -1; - } - if (!lenient) { - affixSet = fStaticSets->fStrictDashEquivalents; - - // If the trimmedAffix is exactly one character long and that character - // is in the dash set and the very next input character is also - // in the dash set, return a match. - if (affixCharLength == affixLength && affixSet->contains(affixChar)) { - UChar32 ic = input.char32At(pos); - if (affixSet->contains(ic)) { - pos += U16_LENGTH(ic); - pos = skipBidiMarks(input, pos); // skip any trailing bidi marks - return pos - start; - } - } - - for (int32_t i = 0; i < affixLength; ) { - UChar32 c = trimmedAffix.char32At(i); - int32_t len = U16_LENGTH(c); - if (PatternProps::isWhiteSpace(c)) { - // We may have a pattern like: \u200F \u0020 - // and input text like: \u200F \u0020 - // Note that U+200F and U+0020 are Pattern_White_Space but only - // U+0020 is UWhiteSpace. So we have to first do a direct - // match of the run of Pattern_White_Space in the pattern, - // then match any extra characters. - UBool literalMatch = FALSE; - while (pos < inputLength) { - UChar32 ic = input.char32At(pos); - if (ic == c) { - literalMatch = TRUE; - i += len; - pos += len; - if (i == affixLength) { - break; - } - c = trimmedAffix.char32At(i); - len = U16_LENGTH(c); - if (!PatternProps::isWhiteSpace(c)) { - break; - } - } else if (IS_BIDI_MARK(ic)) { - pos ++; // just skip over this input text - } else { - break; - } - } - - // Advance over run in pattern - i = skipPatternWhiteSpace(trimmedAffix, i); - - // Advance over run in input text - // Must see at least one white space char in input, - // unless we've already matched some characters literally. - int32_t s = pos; - pos = skipUWhiteSpace(input, pos); - if (pos == s && !literalMatch) { - return -1; - } - - // If we skip UWhiteSpace in the input text, we need to skip it in the pattern. - // Otherwise, the previous lines may have skipped over text (such as U+00A0) that - // is also in the trimmedAffix. - i = skipUWhiteSpace(trimmedAffix, i); - } else { - UBool match = FALSE; - while (pos < inputLength) { - UChar32 ic = input.char32At(pos); - if (!match && ic == c) { - i += len; - pos += len; - match = TRUE; - } else if (IS_BIDI_MARK(ic)) { - pos++; // just skip over this input text - } else { - break; - } - } - if (!match) { - return -1; - } - } - } - } else { - UBool match = FALSE; - - affixSet = fStaticSets->fDashEquivalents; - - if (affixCharLength == affixLength && affixSet->contains(affixChar)) { - pos = skipUWhiteSpaceAndMarks(input, pos); - UChar32 ic = input.char32At(pos); - - if (affixSet->contains(ic)) { - pos += U16_LENGTH(ic); - pos = skipBidiMarks(input, pos); - return pos - start; - } - } - - for (int32_t i = 0; i < affixLength; ) - { - //i = skipRuleWhiteSpace(trimmedAffix, i); - i = skipUWhiteSpace(trimmedAffix, i); - pos = skipUWhiteSpaceAndMarks(input, pos); - - if (i >= affixLength || pos >= inputLength) { - break; - } - - UChar32 c = trimmedAffix.char32At(i); - UChar32 ic = input.char32At(pos); - - if (!equalWithSignCompatibility(ic, c)) { - return -1; - } - - match = TRUE; - i += U16_LENGTH(c); - pos += U16_LENGTH(ic); - pos = skipBidiMarks(input, pos); - } - - if (affixLength > 0 && ! match) { - return -1; - } - } - return pos - start; -} - -/** - * Skip over a run of zero or more Pattern_White_Space characters at - * pos in text. - */ -int32_t DecimalFormat::skipPatternWhiteSpace(const UnicodeString& text, int32_t pos) { - const UChar* s = text.getBuffer(); - return (int32_t)(PatternProps::skipWhiteSpace(s + pos, text.length() - pos) - s); -} - -/** - * Skip over a run of zero or more isUWhiteSpace() characters at pos - * in text. - */ -int32_t DecimalFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) { - while (pos < text.length()) { - UChar32 c = text.char32At(pos); - if (!u_isUWhiteSpace(c)) { - break; - } - pos += U16_LENGTH(c); - } - return pos; -} - -/** - * Skip over a run of zero or more isUWhiteSpace() characters or bidi marks at pos - * in text. - */ -int32_t DecimalFormat::skipUWhiteSpaceAndMarks(const UnicodeString& text, int32_t pos) { - while (pos < text.length()) { - UChar32 c = text.char32At(pos); - if (!u_isUWhiteSpace(c) && !IS_BIDI_MARK(c)) { // u_isUWhiteSpace doesn't include LRM,RLM,ALM - break; - } - pos += U16_LENGTH(c); - } - return pos; -} - -/** - * Skip over a run of zero or more bidi marks at pos in text. - */ -int32_t DecimalFormat::skipBidiMarks(const UnicodeString& text, int32_t pos) { - while (pos < text.length()) { - UChar c = text.charAt(pos); - if (!IS_BIDI_MARK(c)) { - break; - } - pos++; - } - return pos; -} - -/** - * Return the length matched by the given affix, or -1 if none. - * @param affixPat pattern string - * @param input input text - * @param pos offset into input at which to begin matching - * @param type the currency type to parse against, LONG_NAME only or not. - * @param currency return value for parsed currency, for generic - * currency parsing mode, or null for normal parsing. In generic - * currency parsing mode, any currency is parsed, not just the - * currency that this formatter is set to. - * @return length of input that matches, or -1 if match failure - */ -int32_t DecimalFormat::compareComplexAffix(const UnicodeString& affixPat, - const UnicodeString& text, - int32_t pos, - int8_t type, - UChar* currency) const -{ - int32_t start = pos; - U_ASSERT(currency != NULL || fImpl->fMonetary); - - for (int32_t i=0; - i= 0; ) { - UChar32 c = affixPat.char32At(i); - i += U16_LENGTH(c); - - if (c == kQuote) { - U_ASSERT(i <= affixPat.length()); - c = affixPat.char32At(i); - i += U16_LENGTH(c); - - const UnicodeString* affix = NULL; - - switch (c) { - case kCurrencySign: { - // since the currency names in choice format is saved - // the same way as other currency names, - // do not need to do currency choice parsing here. - // the general currency parsing parse against all names, - // including names in choice format. - UBool intl = igetLocale().getName(); - ParsePosition ppos(pos); - int32_t currMatchLen = 0; - UChar curr[4]; - UErrorCode ec = U_ZERO_ERROR; - // Delegate parse of display name => ISO code to Currency - uprv_parseCurrency(loc, text, ppos, type, &currMatchLen, curr, ec); - - // If parse succeeds, populate currency[0] - if (U_SUCCESS(ec) && ppos.getIndex() != pos) { - if (currency) { - u_strcpy(currency, curr); - } else { - // The formatter is currency-style but the client has not requested - // the value of the parsed currency. In this case, if that value does - // not match the formatter's current value, then the parse fails. - UChar effectiveCurr[4]; - getEffectiveCurrency(effectiveCurr, ec); - if ( U_FAILURE(ec) || u_strncmp(curr,effectiveCurr,4) != 0 ) { - pos = -1; - continue; - } - } - pos = ppos.getIndex(); - } else if (!isLenient()){ - pos = -1; - } - continue; - } - case kPatternPercent: - affix = &fImpl->getConstSymbol(DecimalFormatSymbols::kPercentSymbol); - break; - case kPatternPerMill: - affix = &fImpl->getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); - break; - case kPatternPlus: - affix = &fImpl->getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); - break; - case kPatternMinus: - affix = &fImpl->getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); - break; - default: - // fall through to affix!=0 test, which will fail - break; - } - - if (affix != NULL) { - pos = match(text, pos, *affix); - continue; - } - } - - pos = match(text, pos, c); - if (PatternProps::isWhiteSpace(c)) { - i = skipPatternWhiteSpace(affixPat, i); - } - } - return pos - start; -} - -/** - * Match a single character at text[pos] and return the index of the - * next character upon success. Return -1 on failure. If - * ch is a Pattern_White_Space then match a run of white space in text. - */ -int32_t DecimalFormat::match(const UnicodeString& text, int32_t pos, UChar32 ch) { - if (PatternProps::isWhiteSpace(ch)) { - // Advance over run of white space in input text - // Must see at least one white space char in input - int32_t s = pos; - pos = skipPatternWhiteSpace(text, pos); - if (pos == s) { - return -1; - } - return pos; - } - return (pos >= 0 && text.char32At(pos) == ch) ? - (pos + U16_LENGTH(ch)) : -1; -} - -/** - * Match a string at text[pos] and return the index of the next - * character upon success. Return -1 on failure. Match a run of - * white space in str with a run of white space in text. - */ -int32_t DecimalFormat::match(const UnicodeString& text, int32_t pos, const UnicodeString& str) { - for (int32_t i=0; i= 0; ) { - UChar32 ch = str.char32At(i); - i += U16_LENGTH(ch); - if (PatternProps::isWhiteSpace(ch)) { - i = skipPatternWhiteSpace(str, i); - } - pos = match(text, pos, ch); - } - return pos; -} - -UBool DecimalFormat::matchSymbol(const UnicodeString &text, int32_t position, int32_t length, const UnicodeString &symbol, - UnicodeSet *sset, UChar32 schar) -{ - if (sset != NULL) { - return sset->contains(schar); - } - - return text.compare(position, length, symbol) == 0; -} - -UBool DecimalFormat::matchDecimal(UChar32 symbolChar, - UBool sawDecimal, UChar32 sawDecimalChar, - const UnicodeSet *sset, UChar32 schar) { - if(sawDecimal) { - return schar==sawDecimalChar; - } else if(schar==symbolChar) { - return TRUE; - } else if(sset!=NULL) { - return sset->contains(schar); - } else { - return FALSE; - } -} - -UBool DecimalFormat::matchGrouping(UChar32 groupingChar, - UBool sawGrouping, UChar32 sawGroupingChar, - const UnicodeSet *sset, - UChar32 /*decimalChar*/, const UnicodeSet *decimalSet, - UChar32 schar) { - if(sawGrouping) { - return schar==sawGroupingChar; // previously found - } else if(schar==groupingChar) { - return TRUE; // char from symbols - } else if(sset!=NULL) { - return sset->contains(schar) && // in groupingSet but... - ((decimalSet==NULL) || !decimalSet->contains(schar)); // Exclude decimalSet from groupingSet - } else { - return FALSE; - } -} - - - -//------------------------------------------------------------------------------ -// Gets the pointer to the localized decimal format symbols - -const DecimalFormatSymbols* -DecimalFormat::getDecimalFormatSymbols() const -{ - return &fImpl->getDecimalFormatSymbols(); -} - -//------------------------------------------------------------------------------ -// De-owning the current localized symbols and adopt the new symbols. - -void -DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) -{ - if (symbolsToAdopt == NULL) { - return; // do not allow caller to set fSymbols to NULL - } - fImpl->adoptDecimalFormatSymbols(symbolsToAdopt); -} -//------------------------------------------------------------------------------ -// Setting the symbols is equlivalent to adopting a newly created localized -// symbols. - -void -DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) -{ - adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols)); -} - - -const CurrencyPluralInfo* -DecimalFormat::getCurrencyPluralInfo(void) const -{ - return fCurrencyPluralInfo; -} - - -void -DecimalFormat::adoptCurrencyPluralInfo(CurrencyPluralInfo* toAdopt) -{ - if (toAdopt != NULL) { - delete fCurrencyPluralInfo; - fCurrencyPluralInfo = toAdopt; - // re-set currency affix patterns and currency affixes. - if (fImpl->fMonetary) { - UErrorCode status = U_ZERO_ERROR; - if (fAffixPatternsForCurrency) { - deleteHashForAffixPattern(); - } - setupCurrencyAffixPatterns(status); - } - } -} - -void -DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) -{ - adoptCurrencyPluralInfo(info.clone()); -} - - -//------------------------------------------------------------------------------ -// Gets the positive prefix of the number pattern. - -UnicodeString& -DecimalFormat::getPositivePrefix(UnicodeString& result) const -{ - return fImpl->getPositivePrefix(result); -} - -//------------------------------------------------------------------------------ -// Sets the positive prefix of the number pattern. - -void -DecimalFormat::setPositivePrefix(const UnicodeString& newValue) -{ - fImpl->setPositivePrefix(newValue); -} - -//------------------------------------------------------------------------------ -// Gets the negative prefix of the number pattern. - -UnicodeString& -DecimalFormat::getNegativePrefix(UnicodeString& result) const -{ - return fImpl->getNegativePrefix(result); -} - -//------------------------------------------------------------------------------ -// Gets the negative prefix of the number pattern. - -void -DecimalFormat::setNegativePrefix(const UnicodeString& newValue) -{ - fImpl->setNegativePrefix(newValue); -} - -//------------------------------------------------------------------------------ -// Gets the positive suffix of the number pattern. - -UnicodeString& -DecimalFormat::getPositiveSuffix(UnicodeString& result) const -{ - return fImpl->getPositiveSuffix(result); -} - -//------------------------------------------------------------------------------ -// Sets the positive suffix of the number pattern. - -void -DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) -{ - fImpl->setPositiveSuffix(newValue); -} - -//------------------------------------------------------------------------------ -// Gets the negative suffix of the number pattern. - -UnicodeString& -DecimalFormat::getNegativeSuffix(UnicodeString& result) const -{ - return fImpl->getNegativeSuffix(result); -} - -//------------------------------------------------------------------------------ -// Sets the negative suffix of the number pattern. - -void -DecimalFormat::setNegativeSuffix(const UnicodeString& newValue) -{ - fImpl->setNegativeSuffix(newValue); -} - -//------------------------------------------------------------------------------ -// Gets the multiplier of the number pattern. -// Multipliers are stored as decimal numbers (DigitLists) because that -// is the most convenient for muliplying or dividing the numbers to be formatted. -// A NULL multiplier implies one, and the scaling operations are skipped. - -int32_t -DecimalFormat::getMultiplier() const -{ - return fImpl->getMultiplier(); -} - -//------------------------------------------------------------------------------ -// Sets the multiplier of the number pattern. -void -DecimalFormat::setMultiplier(int32_t newValue) -{ - fImpl->setMultiplier(newValue); -} - -/** - * Get the rounding increment. - * @return A positive rounding increment, or 0.0 if rounding - * is not in effect. - * @see #setRoundingIncrement - * @see #getRoundingMode - * @see #setRoundingMode - */ -double DecimalFormat::getRoundingIncrement() const { - return fImpl->getRoundingIncrement(); -} - -/** - * Set the rounding increment. This method also controls whether - * rounding is enabled. - * @param newValue A positive rounding increment, or 0.0 to disable rounding. - * Negative increments are equivalent to 0.0. - * @see #getRoundingIncrement - * @see #getRoundingMode - * @see #setRoundingMode - */ -void DecimalFormat::setRoundingIncrement(double newValue) { - fImpl->setRoundingIncrement(newValue); -} - -/** - * Get the rounding mode. - * @return A rounding mode - * @see #setRoundingIncrement - * @see #getRoundingIncrement - * @see #setRoundingMode - */ -DecimalFormat::ERoundingMode DecimalFormat::getRoundingMode() const { - return fImpl->getRoundingMode(); -} - -/** - * Set the rounding mode. This has no effect unless the rounding - * increment is greater than zero. - * @param roundingMode A rounding mode - * @see #setRoundingIncrement - * @see #getRoundingIncrement - * @see #getRoundingMode - */ -void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) { - fImpl->setRoundingMode(roundingMode); -} - -/** - * Get the width to which the output of format() is padded. - * @return the format width, or zero if no padding is in effect - * @see #setFormatWidth - * @see #getPadCharacter - * @see #setPadCharacter - * @see #getPadPosition - * @see #setPadPosition - */ -int32_t DecimalFormat::getFormatWidth() const { - return fImpl->getFormatWidth(); -} - -/** - * Set the width to which the output of format() is padded. - * This method also controls whether padding is enabled. - * @param width the width to which to pad the result of - * format(), or zero to disable padding. A negative - * width is equivalent to 0. - * @see #getFormatWidth - * @see #getPadCharacter - * @see #setPadCharacter - * @see #getPadPosition - * @see #setPadPosition - */ -void DecimalFormat::setFormatWidth(int32_t width) { - int32_t formatWidth = (width > 0) ? width : 0; - fImpl->setFormatWidth(formatWidth); -} - -UnicodeString DecimalFormat::getPadCharacterString() const { - return UnicodeString(fImpl->getPadCharacter()); -} - -void DecimalFormat::setPadCharacter(const UnicodeString &padChar) { - UChar32 pad; - if (padChar.length() > 0) { - pad = padChar.char32At(0); - } - else { - pad = kDefaultPad; - } - fImpl->setPadCharacter(pad); -} - -static DecimalFormat::EPadPosition fromPadPosition(DigitAffixesAndPadding::EPadPosition padPos) { - switch (padPos) { - case DigitAffixesAndPadding::kPadBeforePrefix: - return DecimalFormat::kPadBeforePrefix; - case DigitAffixesAndPadding::kPadAfterPrefix: - return DecimalFormat::kPadAfterPrefix; - case DigitAffixesAndPadding::kPadBeforeSuffix: - return DecimalFormat::kPadBeforeSuffix; - case DigitAffixesAndPadding::kPadAfterSuffix: - return DecimalFormat::kPadAfterSuffix; - default: - U_ASSERT(FALSE); - break; - } - return DecimalFormat::kPadBeforePrefix; -} - -/** - * Get the position at which padding will take place. This is the location - * at which padding will be inserted if the result of format() - * is shorter than the format width. - * @return the pad position, one of kPadBeforePrefix, - * kPadAfterPrefix, kPadBeforeSuffix, or - * kPadAfterSuffix. - * @see #setFormatWidth - * @see #getFormatWidth - * @see #setPadCharacter - * @see #getPadCharacter - * @see #setPadPosition - * @see #kPadBeforePrefix - * @see #kPadAfterPrefix - * @see #kPadBeforeSuffix - * @see #kPadAfterSuffix - */ -DecimalFormat::EPadPosition DecimalFormat::getPadPosition() const { - return fromPadPosition(fImpl->getPadPosition()); -} - -static DigitAffixesAndPadding::EPadPosition toPadPosition(DecimalFormat::EPadPosition padPos) { - switch (padPos) { - case DecimalFormat::kPadBeforePrefix: - return DigitAffixesAndPadding::kPadBeforePrefix; - case DecimalFormat::kPadAfterPrefix: - return DigitAffixesAndPadding::kPadAfterPrefix; - case DecimalFormat::kPadBeforeSuffix: - return DigitAffixesAndPadding::kPadBeforeSuffix; - case DecimalFormat::kPadAfterSuffix: - return DigitAffixesAndPadding::kPadAfterSuffix; - default: - U_ASSERT(FALSE); - break; - } - return DigitAffixesAndPadding::kPadBeforePrefix; -} - -/** - * NEW - * Set the position at which padding will take place. This is the location - * at which padding will be inserted if the result of format() - * is shorter than the format width. This has no effect unless padding is - * enabled. - * @param padPos the pad position, one of kPadBeforePrefix, - * kPadAfterPrefix, kPadBeforeSuffix, or - * kPadAfterSuffix. - * @see #setFormatWidth - * @see #getFormatWidth - * @see #setPadCharacter - * @see #getPadCharacter - * @see #getPadPosition - * @see #kPadBeforePrefix - * @see #kPadAfterPrefix - * @see #kPadBeforeSuffix - * @see #kPadAfterSuffix - */ -void DecimalFormat::setPadPosition(EPadPosition padPos) { - fImpl->setPadPosition(toPadPosition(padPos)); -} - -/** - * Return whether or not scientific notation is used. - * @return TRUE if this object formats and parses scientific notation - * @see #setScientificNotation - * @see #getMinimumExponentDigits - * @see #setMinimumExponentDigits - * @see #isExponentSignAlwaysShown - * @see #setExponentSignAlwaysShown - */ -UBool DecimalFormat::isScientificNotation() const { - return fImpl->isScientificNotation(); -} - -/** - * Set whether or not scientific notation is used. - * @param useScientific TRUE if this object formats and parses scientific - * notation - * @see #isScientificNotation - * @see #getMinimumExponentDigits - * @see #setMinimumExponentDigits - * @see #isExponentSignAlwaysShown - * @see #setExponentSignAlwaysShown - */ -void DecimalFormat::setScientificNotation(UBool useScientific) { - fImpl->setScientificNotation(useScientific); -} - -/** - * Return the minimum exponent digits that will be shown. - * @return the minimum exponent digits that will be shown - * @see #setScientificNotation - * @see #isScientificNotation - * @see #setMinimumExponentDigits - * @see #isExponentSignAlwaysShown - * @see #setExponentSignAlwaysShown - */ -int8_t DecimalFormat::getMinimumExponentDigits() const { - return fImpl->getMinimumExponentDigits(); -} - -/** - * Set the minimum exponent digits that will be shown. This has no - * effect unless scientific notation is in use. - * @param minExpDig a value >= 1 indicating the fewest exponent digits - * that will be shown. Values less than 1 will be treated as 1. - * @see #setScientificNotation - * @see #isScientificNotation - * @see #getMinimumExponentDigits - * @see #isExponentSignAlwaysShown - * @see #setExponentSignAlwaysShown - */ -void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) { - int32_t minExponentDigits = (int8_t)((minExpDig > 0) ? minExpDig : 1); - fImpl->setMinimumExponentDigits(minExponentDigits); -} - -/** - * Return whether the exponent sign is always shown. - * @return TRUE if the exponent is always prefixed with either the - * localized minus sign or the localized plus sign, false if only negative - * exponents are prefixed with the localized minus sign. - * @see #setScientificNotation - * @see #isScientificNotation - * @see #setMinimumExponentDigits - * @see #getMinimumExponentDigits - * @see #setExponentSignAlwaysShown - */ -UBool DecimalFormat::isExponentSignAlwaysShown() const { - return fImpl->isExponentSignAlwaysShown(); -} - -/** - * Set whether the exponent sign is always shown. This has no effect - * unless scientific notation is in use. - * @param expSignAlways TRUE if the exponent is always prefixed with either - * the localized minus sign or the localized plus sign, false if only - * negative exponents are prefixed with the localized minus sign. - * @see #setScientificNotation - * @see #isScientificNotation - * @see #setMinimumExponentDigits - * @see #getMinimumExponentDigits - * @see #isExponentSignAlwaysShown - */ -void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) { - fImpl->setExponentSignAlwaysShown(expSignAlways); -} - -//------------------------------------------------------------------------------ -// Gets the grouping size of the number pattern. For example, thousand or 10 -// thousand groupings. - -int32_t -DecimalFormat::getGroupingSize() const -{ - return fImpl->getGroupingSize(); -} - -//------------------------------------------------------------------------------ -// Gets the grouping size of the number pattern. - -void -DecimalFormat::setGroupingSize(int32_t newValue) -{ - fImpl->setGroupingSize(newValue); -} - -//------------------------------------------------------------------------------ - -int32_t -DecimalFormat::getSecondaryGroupingSize() const -{ - return fImpl->getSecondaryGroupingSize(); -} - -//------------------------------------------------------------------------------ - -void -DecimalFormat::setSecondaryGroupingSize(int32_t newValue) -{ - fImpl->setSecondaryGroupingSize(newValue); -} - -//------------------------------------------------------------------------------ - -int32_t -DecimalFormat::getMinimumGroupingDigits() const -{ - return fImpl->getMinimumGroupingDigits(); -} - -//------------------------------------------------------------------------------ - -void -DecimalFormat::setMinimumGroupingDigits(int32_t newValue) -{ - fImpl->setMinimumGroupingDigits(newValue); -} - -//------------------------------------------------------------------------------ -// Checks if to show the decimal separator. - -UBool -DecimalFormat::isDecimalSeparatorAlwaysShown() const -{ - return fImpl->isDecimalSeparatorAlwaysShown(); -} - -//------------------------------------------------------------------------------ -// Sets to always show the decimal separator. - -void -DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) -{ - fImpl->setDecimalSeparatorAlwaysShown(newValue); -} - -//------------------------------------------------------------------------------ -// Checks if decimal point pattern match is required -UBool -DecimalFormat::isDecimalPatternMatchRequired(void) const -{ - return static_cast(fBoolFlags.contains(UNUM_PARSE_DECIMAL_MARK_REQUIRED)); -} - -//------------------------------------------------------------------------------ -// Checks if decimal point pattern match is required - -void -DecimalFormat::setDecimalPatternMatchRequired(UBool newValue) -{ - fBoolFlags.set(UNUM_PARSE_DECIMAL_MARK_REQUIRED, newValue); -} - - -//------------------------------------------------------------------------------ -// Emits the pattern of this DecimalFormat instance. - -UnicodeString& -DecimalFormat::toPattern(UnicodeString& result) const -{ - return fImpl->toPattern(result); -} - -//------------------------------------------------------------------------------ -// Emits the localized pattern this DecimalFormat instance. - -UnicodeString& -DecimalFormat::toLocalizedPattern(UnicodeString& result) const -{ - // toLocalizedPattern is deprecated, so we just make it the same as - // toPattern. - return fImpl->toPattern(result); -} - -//------------------------------------------------------------------------------ - -void -DecimalFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) -{ - if (pattern.indexOf(kCurrencySign) != -1) { - handleCurrencySignInPattern(status); - } - fImpl->applyPattern(pattern, status); -} - -//------------------------------------------------------------------------------ - -void -DecimalFormat::applyPattern(const UnicodeString& pattern, - UParseError& parseError, - UErrorCode& status) -{ - if (pattern.indexOf(kCurrencySign) != -1) { - handleCurrencySignInPattern(status); - } - fImpl->applyPattern(pattern, parseError, status); -} -//------------------------------------------------------------------------------ - -void -DecimalFormat::applyLocalizedPattern(const UnicodeString& pattern, UErrorCode& status) -{ - if (pattern.indexOf(kCurrencySign) != -1) { - handleCurrencySignInPattern(status); - } - fImpl->applyLocalizedPattern(pattern, status); -} - -//------------------------------------------------------------------------------ - -void -DecimalFormat::applyLocalizedPattern(const UnicodeString& pattern, - UParseError& parseError, - UErrorCode& status) -{ - if (pattern.indexOf(kCurrencySign) != -1) { - handleCurrencySignInPattern(status); - } - fImpl->applyLocalizedPattern(pattern, parseError, status); -} - -//------------------------------------------------------------------------------ - -/** - * Sets the maximum number of digits allowed in the integer portion of a - * number. - * @see NumberFormat#setMaximumIntegerDigits - */ -void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) { - newValue = _min(newValue, gDefaultMaxIntegerDigits); - NumberFormat::setMaximumIntegerDigits(newValue); - fImpl->updatePrecision(); -} - -/** - * Sets the minimum number of digits allowed in the integer portion of a - * number. This override limits the integer digit count to 309. - * @see NumberFormat#setMinimumIntegerDigits - */ -void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) { - newValue = _min(newValue, kDoubleIntegerDigits); - NumberFormat::setMinimumIntegerDigits(newValue); - fImpl->updatePrecision(); -} - -/** - * Sets the maximum number of digits allowed in the fraction portion of a - * number. This override limits the fraction digit count to 340. - * @see NumberFormat#setMaximumFractionDigits - */ -void DecimalFormat::setMaximumFractionDigits(int32_t newValue) { - newValue = _min(newValue, kDoubleFractionDigits); - NumberFormat::setMaximumFractionDigits(newValue); - fImpl->updatePrecision(); -} - -/** - * Sets the minimum number of digits allowed in the fraction portion of a - * number. This override limits the fraction digit count to 340. - * @see NumberFormat#setMinimumFractionDigits - */ -void DecimalFormat::setMinimumFractionDigits(int32_t newValue) { - newValue = _min(newValue, kDoubleFractionDigits); - NumberFormat::setMinimumFractionDigits(newValue); - fImpl->updatePrecision(); -} - -int32_t DecimalFormat::getMinimumSignificantDigits() const { - return fImpl->getMinimumSignificantDigits(); -} - -int32_t DecimalFormat::getMaximumSignificantDigits() const { - return fImpl->getMaximumSignificantDigits(); -} - -void DecimalFormat::setMinimumSignificantDigits(int32_t min) { - if (min < 1) { - min = 1; - } - // pin max sig dig to >= min - int32_t max = _max(fImpl->fMaxSigDigits, min); - fImpl->setMinMaxSignificantDigits(min, max); -} - -void DecimalFormat::setMaximumSignificantDigits(int32_t max) { - if (max < 1) { - max = 1; - } - // pin min sig dig to 1..max - U_ASSERT(fImpl->fMinSigDigits >= 1); - int32_t min = _min(fImpl->fMinSigDigits, max); - fImpl->setMinMaxSignificantDigits(min, max); -} - -UBool DecimalFormat::areSignificantDigitsUsed() const { - return fImpl->areSignificantDigitsUsed(); -} - -void DecimalFormat::setSignificantDigitsUsed(UBool useSignificantDigits) { - fImpl->setSignificantDigitsUsed(useSignificantDigits); -} - -void DecimalFormat::setCurrency(const UChar* theCurrency, UErrorCode& ec) { - // set the currency before compute affixes to get the right currency names - NumberFormat::setCurrency(theCurrency, ec); - fImpl->updateCurrency(ec); -} - -void DecimalFormat::setCurrencyUsage(UCurrencyUsage newContext, UErrorCode* ec){ - fImpl->setCurrencyUsage(newContext, *ec); -} - -UCurrencyUsage DecimalFormat::getCurrencyUsage() const { - return fImpl->getCurrencyUsage(); -} - -// Deprecated variant with no UErrorCode parameter -void DecimalFormat::setCurrency(const UChar* theCurrency) { - UErrorCode ec = U_ZERO_ERROR; - setCurrency(theCurrency, ec); -} - -void DecimalFormat::getEffectiveCurrency(UChar* result, UErrorCode& ec) const { - if (fImpl->fSymbols == NULL) { - ec = U_MEMORY_ALLOCATION_ERROR; - return; - } - ec = U_ZERO_ERROR; - const UChar* c = getCurrency(); - if (*c == 0) { - const UnicodeString &intl = - fImpl->getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol); - c = intl.getBuffer(); // ok for intl to go out of scope - } - u_strncpy(result, c, 3); - result[3] = 0; -} - -Hashtable* -DecimalFormat::initHashForAffixPattern(UErrorCode& status) { - if ( U_FAILURE(status) ) { - return NULL; - } - Hashtable* hTable; - if ( (hTable = new Hashtable(TRUE, status)) == NULL ) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - if ( U_FAILURE(status) ) { - delete hTable; - return NULL; - } - hTable->setValueComparator(decimfmtAffixPatternValueComparator); - return hTable; -} - -void -DecimalFormat::deleteHashForAffixPattern() -{ - if ( fAffixPatternsForCurrency == NULL ) { - return; - } - int32_t pos = UHASH_FIRST; - const UHashElement* element = NULL; - while ( (element = fAffixPatternsForCurrency->nextElement(pos)) != NULL ) { - const UHashTok valueTok = element->value; - const AffixPatternsForCurrency* value = (AffixPatternsForCurrency*)valueTok.pointer; - delete value; - } - delete fAffixPatternsForCurrency; - fAffixPatternsForCurrency = NULL; -} - - -void -DecimalFormat::copyHashForAffixPattern(const Hashtable* source, - Hashtable* target, - UErrorCode& status) { - if ( U_FAILURE(status) ) { - return; - } - int32_t pos = UHASH_FIRST; - const UHashElement* element = NULL; - if ( source ) { - while ( (element = source->nextElement(pos)) != NULL ) { - const UHashTok keyTok = element->key; - const UnicodeString* key = (UnicodeString*)keyTok.pointer; - const UHashTok valueTok = element->value; - const AffixPatternsForCurrency* value = (AffixPatternsForCurrency*)valueTok.pointer; - AffixPatternsForCurrency* copy = new AffixPatternsForCurrency( - value->negPrefixPatternForCurrency, - value->negSuffixPatternForCurrency, - value->posPrefixPatternForCurrency, - value->posSuffixPatternForCurrency, - value->patternType); - target->put(UnicodeString(*key), copy, status); - if ( U_FAILURE(status) ) { - return; - } - } - } -} - -void -DecimalFormat::setGroupingUsed(UBool newValue) { - NumberFormat::setGroupingUsed(newValue); - fImpl->updateGrouping(); -} - -void -DecimalFormat::setParseIntegerOnly(UBool newValue) { - NumberFormat::setParseIntegerOnly(newValue); -} - -void -DecimalFormat::setContext(UDisplayContext value, UErrorCode& status) { - NumberFormat::setContext(value, status); -} - -DecimalFormat& DecimalFormat::setAttribute( UNumberFormatAttribute attr, - int32_t newValue, - UErrorCode &status) { - if(U_FAILURE(status)) return *this; - - switch(attr) { - case UNUM_LENIENT_PARSE: - setLenient(newValue!=0); - break; - - case UNUM_PARSE_INT_ONLY: - setParseIntegerOnly(newValue!=0); - break; - - case UNUM_GROUPING_USED: - setGroupingUsed(newValue!=0); - break; - - case UNUM_DECIMAL_ALWAYS_SHOWN: - setDecimalSeparatorAlwaysShown(newValue!=0); - break; - - case UNUM_MAX_INTEGER_DIGITS: - setMaximumIntegerDigits(newValue); - break; - - case UNUM_MIN_INTEGER_DIGITS: - setMinimumIntegerDigits(newValue); - break; - - case UNUM_INTEGER_DIGITS: - setMinimumIntegerDigits(newValue); - setMaximumIntegerDigits(newValue); - break; - - case UNUM_MAX_FRACTION_DIGITS: - setMaximumFractionDigits(newValue); - break; - - case UNUM_MIN_FRACTION_DIGITS: - setMinimumFractionDigits(newValue); - break; - - case UNUM_FRACTION_DIGITS: - setMinimumFractionDigits(newValue); - setMaximumFractionDigits(newValue); - break; - - case UNUM_SIGNIFICANT_DIGITS_USED: - setSignificantDigitsUsed(newValue!=0); - break; - - case UNUM_MAX_SIGNIFICANT_DIGITS: - setMaximumSignificantDigits(newValue); - break; - - case UNUM_MIN_SIGNIFICANT_DIGITS: - setMinimumSignificantDigits(newValue); - break; - - case UNUM_MULTIPLIER: - setMultiplier(newValue); - break; - - case UNUM_GROUPING_SIZE: - setGroupingSize(newValue); - break; - - case UNUM_ROUNDING_MODE: - setRoundingMode((DecimalFormat::ERoundingMode)newValue); - break; - - case UNUM_FORMAT_WIDTH: - setFormatWidth(newValue); - break; - - case UNUM_PADDING_POSITION: - /** The position at which padding will take place. */ - setPadPosition((DecimalFormat::EPadPosition)newValue); - break; - - case UNUM_SECONDARY_GROUPING_SIZE: - setSecondaryGroupingSize(newValue); - break; - -#if UCONFIG_HAVE_PARSEALLINPUT - case UNUM_PARSE_ALL_INPUT: - setParseAllInput((UNumberFormatAttributeValue)newValue); - break; -#endif - - /* These are stored in fBoolFlags */ - case UNUM_PARSE_NO_EXPONENT: - case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: - case UNUM_PARSE_DECIMAL_MARK_REQUIRED: - if(!fBoolFlags.isValidValue(newValue)) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } else { - if (attr == UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS) { - fImpl->setFailIfMoreThanMaxDigits((UBool) newValue); - } - fBoolFlags.set(attr, newValue); - } - break; - - case UNUM_SCALE: - fImpl->setScale(newValue); - break; - - case UNUM_CURRENCY_USAGE: - setCurrencyUsage((UCurrencyUsage)newValue, &status); - break; - - case UNUM_MINIMUM_GROUPING_DIGITS: - setMinimumGroupingDigits(newValue); - break; - - default: - status = U_UNSUPPORTED_ERROR; - break; - } - return *this; -} - -int32_t DecimalFormat::getAttribute( UNumberFormatAttribute attr, - UErrorCode &status ) const { - if(U_FAILURE(status)) return -1; - switch(attr) { - case UNUM_LENIENT_PARSE: - return isLenient(); - - case UNUM_PARSE_INT_ONLY: - return isParseIntegerOnly(); - - case UNUM_GROUPING_USED: - return isGroupingUsed(); - - case UNUM_DECIMAL_ALWAYS_SHOWN: - return isDecimalSeparatorAlwaysShown(); - - case UNUM_MAX_INTEGER_DIGITS: - return getMaximumIntegerDigits(); - - case UNUM_MIN_INTEGER_DIGITS: - return getMinimumIntegerDigits(); - - case UNUM_INTEGER_DIGITS: - // TBD: what should this return? - return getMinimumIntegerDigits(); - - case UNUM_MAX_FRACTION_DIGITS: - return getMaximumFractionDigits(); - - case UNUM_MIN_FRACTION_DIGITS: - return getMinimumFractionDigits(); - - case UNUM_FRACTION_DIGITS: - // TBD: what should this return? - return getMinimumFractionDigits(); - - case UNUM_SIGNIFICANT_DIGITS_USED: - return areSignificantDigitsUsed(); - - case UNUM_MAX_SIGNIFICANT_DIGITS: - return getMaximumSignificantDigits(); - - case UNUM_MIN_SIGNIFICANT_DIGITS: - return getMinimumSignificantDigits(); - - case UNUM_MULTIPLIER: - return getMultiplier(); - - case UNUM_GROUPING_SIZE: - return getGroupingSize(); - - case UNUM_ROUNDING_MODE: - return getRoundingMode(); - - case UNUM_FORMAT_WIDTH: - return getFormatWidth(); - - case UNUM_PADDING_POSITION: - return getPadPosition(); - - case UNUM_SECONDARY_GROUPING_SIZE: - return getSecondaryGroupingSize(); - - /* These are stored in fBoolFlags */ - case UNUM_PARSE_NO_EXPONENT: - case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: - case UNUM_PARSE_DECIMAL_MARK_REQUIRED: - return fBoolFlags.get(attr); - - case UNUM_SCALE: - return fImpl->fScale; - - case UNUM_CURRENCY_USAGE: - return fImpl->getCurrencyUsage(); - - case UNUM_MINIMUM_GROUPING_DIGITS: - return getMinimumGroupingDigits(); - - default: - status = U_UNSUPPORTED_ERROR; - break; - } - - return -1; /* undefined */ -} - -#if UCONFIG_HAVE_PARSEALLINPUT -void DecimalFormat::setParseAllInput(UNumberFormatAttributeValue value) { - fParseAllInput = value; -} -#endif - -U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof diff --git a/icu4c/source/i18n/msgfmt.cpp b/icu4c/source/i18n/msgfmt.cpp index 064585665ae..c2be3f7bd13 100644 --- a/icu4c/source/i18n/msgfmt.cpp +++ b/icu4c/source/i18n/msgfmt.cpp @@ -1959,14 +1959,15 @@ UnicodeString MessageFormat::PluralSelectorProvider::select(void *ctx, double nu return UnicodeString(FALSE, OTHER_STRING, 5); } context.formatter->format(context.number, context.numberString, ec); - const DecimalFormat *decFmt = dynamic_cast(context.formatter); + auto* decFmt = dynamic_cast(context.formatter); if(decFmt != NULL) { - VisibleDigitsWithExponent digits; - decFmt->initVisibleDigitsWithExponent(context.number, digits, ec); + const IFixedDecimal& dec = decFmt->toNumberFormatter() + .formatDouble(context.number.getDouble(ec), ec) + .getFixedDecimal(ec); if (U_FAILURE(ec)) { return UnicodeString(FALSE, OTHER_STRING, 5); } - return rules->select(digits); + return rules->select(dec); } else { return rules->select(number); } diff --git a/icu4c/source/i18n/plurfmt.cpp b/icu4c/source/i18n/plurfmt.cpp index e14ef6d831e..f0e18f9cc0d 100644 --- a/icu4c/source/i18n/plurfmt.cpp +++ b/icu4c/source/i18n/plurfmt.cpp @@ -272,12 +272,13 @@ PluralFormat::format(const Formattable& numberObject, double number, if (offset == 0) { DecimalFormat *decFmt = dynamic_cast(numberFormat); if(decFmt != NULL) { - decFmt->initVisibleDigitsWithExponent( - numberObject, dec, status); - if (U_FAILURE(status)) { - return appendTo; - } - decFmt->format(dec, numberString, ignorePos, status); +// decFmt->initVisibleDigitsWithExponent( +// numberObject, dec, status); +// if (U_FAILURE(status)) { +// return appendTo; +// } +// decFmt->format(dec, numberString, ignorePos, status); + decFmt->format(numberObject, numberString, ignorePos, status); } else { numberFormat->format( numberObject, numberString, ignorePos, status); // could be BigDecimal etc. @@ -285,12 +286,13 @@ PluralFormat::format(const Formattable& numberObject, double number, } else { DecimalFormat *decFmt = dynamic_cast(numberFormat); if(decFmt != NULL) { - decFmt->initVisibleDigitsWithExponent( - numberMinusOffset, dec, status); - if (U_FAILURE(status)) { - return appendTo; - } - decFmt->format(dec, numberString, ignorePos, status); +// decFmt->initVisibleDigitsWithExponent( +// numberMinusOffset, dec, status); +// if (U_FAILURE(status)) { +// return appendTo; +// } +// decFmt->format(dec, numberString, ignorePos, status); + decFmt->format(numberObject, numberString, ignorePos, status); } else { numberFormat->format( numberMinusOffset, numberString, ignorePos, status); diff --git a/icu4c/source/i18n/plurrule.cpp b/icu4c/source/i18n/plurrule.cpp index 14b5fe6d9d1..3da36d64b54 100644 --- a/icu4c/source/i18n/plurrule.cpp +++ b/icu4c/source/i18n/plurrule.cpp @@ -254,10 +254,11 @@ PluralRules::select(const Formattable& obj, const NumberFormat& fmt, UErrorCode& if (U_SUCCESS(status)) { const DecimalFormat *decFmt = dynamic_cast(&fmt); if (decFmt != NULL) { - VisibleDigitsWithExponent digits; - decFmt->initVisibleDigitsWithExponent(obj, digits, status); + const IFixedDecimal& dec = decFmt->toNumberFormatter() + .formatDouble(obj.getDouble(status), status) + .getFixedDecimal(status); if (U_SUCCESS(status)) { - return select(digits); + return select(dec); } } else { double number = obj.getDouble(status); diff --git a/icu4c/source/i18n/quantityformatter.cpp b/icu4c/source/i18n/quantityformatter.cpp index 208e064700a..eaf92212755 100644 --- a/icu4c/source/i18n/quantityformatter.cpp +++ b/icu4c/source/i18n/quantityformatter.cpp @@ -149,15 +149,16 @@ StandardPlural::Form QuantityFormatter::selectPlural( return StandardPlural::OTHER; } UnicodeString pluralKeyword; - VisibleDigitsWithExponent digits; const DecimalFormat *decFmt = dynamic_cast(&fmt); if (decFmt != NULL) { - decFmt->initVisibleDigitsWithExponent(number, digits, status); + const IFixedDecimal& dec = decFmt->toNumberFormatter() + .formatDouble(number.getDouble(status), status) + .getFixedDecimal(status); if (U_FAILURE(status)) { return StandardPlural::OTHER; } - pluralKeyword = rules.select(digits); - decFmt->format(digits, formattedNumber, pos, status); + pluralKeyword = rules.select(dec); + decFmt->format(number, formattedNumber, pos, status); } else { if (number.getType() == Formattable::kDouble) { pluralKeyword = rules.select(number.getDouble()); diff --git a/icu4c/source/i18n/unicode/decimfmt.h b/icu4c/source/i18n/unicode/decimfmt.h index 7e39239964c..dbd01a347d9 100644 --- a/icu4c/source/i18n/unicode/decimfmt.h +++ b/icu4c/source/i18n/unicode/decimfmt.h @@ -1931,7 +1931,6 @@ public: */ void setSignificantDigitsUsed(UBool useSignificantDigits); - public: /** * Sets the currency used to display currency * amounts. This takes effect immediately, if this format is a @@ -1968,77 +1967,6 @@ public: */ UCurrencyUsage getCurrencyUsage() const; - - /** - * The resource tags we use to retrieve decimal format data from - * locale resource bundles. - * @deprecated ICU 3.4. This string has no public purpose. Please don't use it. - */ - static const char fgNumberPatterns[]; - -#ifndef U_HIDE_INTERNAL_API - /** - * Get a FixedDecimal corresponding to a double as it would be - * formatted by this DecimalFormat. - * Internal, not intended for public use. - * @internal - */ - FixedDecimal getFixedDecimal(double number, UErrorCode &status) const; - - /** - * Get a FixedDecimal corresponding to a formattable as it would be - * formatted by this DecimalFormat. - * Internal, not intended for public use. - * @internal - */ - FixedDecimal getFixedDecimal(const Formattable &number, UErrorCode &status) const; - - /** - * Get a FixedDecimal corresponding to a DigitList as it would be - * formatted by this DecimalFormat. Note: the DigitList may be modified. - * Internal, not intended for public use. - * @internal - */ - FixedDecimal getFixedDecimal(DigitList &number, UErrorCode &status) const; - - /** - * Get a VisibleDigitsWithExponent corresponding to a double - * as it would be formatted by this DecimalFormat. - * Internal, not intended for public use. - * @internal - */ - VisibleDigitsWithExponent &initVisibleDigitsWithExponent( - double number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - - /** - * Get a VisibleDigitsWithExponent corresponding to a formattable - * as it would be formatted by this DecimalFormat. - * Internal, not intended for public use. - * @internal - */ - VisibleDigitsWithExponent &initVisibleDigitsWithExponent( - const Formattable &number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - - /** - * Get a VisibleDigitsWithExponent corresponding to a DigitList - * as it would be formatted by this DecimalFormat. - * Note: the DigitList may be modified. - * Internal, not intended for public use. - * @internal - */ - VisibleDigitsWithExponent &initVisibleDigitsWithExponent( - DigitList &number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - -#endif /* U_HIDE_INTERNAL_API */ - -public: - /** * Converts this DecimalFormat to a NumberFormatter. Starting in ICU 60, * NumberFormatter is the recommended way to format numbers. @@ -2046,7 +1974,7 @@ public: * @return An instance of LocalizedNumberFormatter with the same behavior as this DecimalFormat. * @draft ICU 62 */ - number::LocalizedNumberFormatter getNumberFormatter() const; + number::LocalizedNumberFormatter toNumberFormatter() const; /** * Return the class ID for this class. This is useful only for @@ -2074,216 +2002,6 @@ public: */ virtual UClassID getDynamicClassID(void) const U_OVERRIDE; -private: - - DecimalFormat(); // default constructor not implemented - - /** - * Initialize all fields of a new DecimalFormatter to a safe default value. - * Common code for use by constructors. - */ - void init(); - - /** - * Do real work of constructing a new DecimalFormat. - */ - void construct(UErrorCode& status, - UParseError& parseErr, - const UnicodeString* pattern = 0, - DecimalFormatSymbols* symbolsToAdopt = 0 - ); - - void handleCurrencySignInPattern(UErrorCode& status); - - void parse(const UnicodeString& text, - Formattable& result, - ParsePosition& pos, - char16_t* currency) const; - - enum { - fgStatusInfinite, - fgStatusLength // Leave last in list. - } StatusFlags; - - UBool subparse(const UnicodeString& text, - const UnicodeString* negPrefix, - const UnicodeString* negSuffix, - const UnicodeString* posPrefix, - const UnicodeString* posSuffix, - UBool complexCurrencyParsing, - int8_t type, - ParsePosition& parsePosition, - DigitList& digits, UBool* status, - char16_t* currency) const; - - // Mixed style parsing for currency. - // It parses against the current currency pattern - // using complex affix comparison - // parses against the currency plural patterns using complex affix comparison, - // and parses against the current pattern using simple affix comparison. - UBool parseForCurrency(const UnicodeString& text, - ParsePosition& parsePosition, - DigitList& digits, - UBool* status, - char16_t* currency) const; - - int32_t skipPadding(const UnicodeString& text, int32_t position) const; - - int32_t compareAffix(const UnicodeString& input, - int32_t pos, - UBool isNegative, - UBool isPrefix, - const UnicodeString* affixPat, - UBool complexCurrencyParsing, - int8_t type, - char16_t* currency) const; - - static UnicodeString& trimMarksFromAffix(const UnicodeString& affix, UnicodeString& trimmedAffix); - - UBool equalWithSignCompatibility(UChar32 lhs, UChar32 rhs) const; - - int32_t compareSimpleAffix(const UnicodeString& affix, - const UnicodeString& input, - int32_t pos, - UBool lenient) const; - - static int32_t skipPatternWhiteSpace(const UnicodeString& text, int32_t pos); - - static int32_t skipUWhiteSpace(const UnicodeString& text, int32_t pos); - - static int32_t skipUWhiteSpaceAndMarks(const UnicodeString& text, int32_t pos); - - static int32_t skipBidiMarks(const UnicodeString& text, int32_t pos); - - int32_t compareComplexAffix(const UnicodeString& affixPat, - const UnicodeString& input, - int32_t pos, - int8_t type, - char16_t* currency) const; - - static int32_t match(const UnicodeString& text, int32_t pos, UChar32 ch); - - static int32_t match(const UnicodeString& text, int32_t pos, const UnicodeString& str); - - static UBool matchSymbol(const UnicodeString &text, int32_t position, int32_t length, const UnicodeString &symbol, - UnicodeSet *sset, UChar32 schar); - - static UBool matchDecimal(UChar32 symbolChar, - UBool sawDecimal, UChar32 sawDecimalChar, - const UnicodeSet *sset, UChar32 schar); - - static UBool matchGrouping(UChar32 groupingChar, - UBool sawGrouping, UChar32 sawGroupingChar, - const UnicodeSet *sset, - UChar32 decimalChar, const UnicodeSet *decimalSet, - UChar32 schar); - - // set up currency affix patterns for mix parsing. - // The patterns saved here are the affix patterns of default currency - // pattern and the unique affix patterns of the plural currency patterns. - // Those patterns are used by parseForCurrency(). - void setupCurrencyAffixPatterns(UErrorCode& status); - - // get the currency rounding with respect to currency usage - double getCurrencyRounding(const char16_t* currency, - UErrorCode* ec) const; - - // get the currency fraction with respect to currency usage - int getCurrencyFractionDigits(const char16_t* currency, - UErrorCode* ec) const; - - // hashtable operations - Hashtable* initHashForAffixPattern(UErrorCode& status); - - void deleteHashForAffixPattern(); - - void copyHashForAffixPattern(const Hashtable* source, - Hashtable* target, UErrorCode& status); - - DecimalFormatImpl *fImpl; - - /** - * Constants. - */ - - - EnumSet - fBoolFlags; - - - // style is only valid when decimal formatter is constructed by - // DecimalFormat(pattern, decimalFormatSymbol, style) - int fStyle; - - - // Affix pattern set for currency. - // It is a set of AffixPatternsForCurrency, - // each element of the set saves the negative prefix pattern, - // negative suffix pattern, positive prefix pattern, - // and positive suffix pattern of a pattern. - // It is used for currency mixed style parsing. - // It is actually is a set. - // The set contains the default currency pattern from the locale, - // and the currency plural patterns. - // Since it is a set, it does not contain duplicated items. - // For example, if 2 currency plural patterns are the same, only one pattern - // is included in the set. When parsing, we do not check whether the plural - // count match or not. - Hashtable* fAffixPatternsForCurrency; - - // Information needed for DecimalFormat to format/parse currency plural. - CurrencyPluralInfo* fCurrencyPluralInfo; - -#if UCONFIG_HAVE_PARSEALLINPUT - UNumberFormatAttributeValue fParseAllInput; -#endif - - // Decimal Format Static Sets singleton. - const DecimalFormatStaticSets *fStaticSets; - -protected: - -#ifndef U_HIDE_INTERNAL_API - /** - * Rounds a value according to the rules of this object. - * @internal - */ - DigitList& _round(const DigitList& number, DigitList& adjustedNum, UBool& isNegative, UErrorCode& status) const; -#endif /* U_HIDE_INTERNAL_API */ - - /** - * Returns the currency in effect for this formatter. Subclasses - * should override this method as needed. Unlike getCurrency(), - * this method should never return "". - * @result output parameter for null-terminated result, which must - * have a capacity of at least 4 - * @internal - */ - void getEffectiveCurrency(char16_t* result, UErrorCode& ec) const U_OVERRIDE; - - /** number of integer digits - * @stable ICU 2.4 - */ - static const int32_t kDoubleIntegerDigits; - /** number of fraction digits - * @stable ICU 2.4 - */ - static const int32_t kDoubleFractionDigits; - - /** - * When someone turns on scientific mode, we assume that more than this - * number of digits is due to flipping from some other mode that didn't - * restrict the maximum, and so we force 1 integer digit. We don't bother - * to track and see if someone is using exponential notation with more than - * this number, it wouldn't make sense anyway, and this is just to make sure - * that someone turning on scientific mode with default settings doesn't - * end up with lots of zeroes. - * @stable ICU 2.8 - */ - static const int32_t kMaxScientificIntegerDigits; - }; U_NAMESPACE_END diff --git a/icu4c/source/i18n/unicode/numberformatter.h b/icu4c/source/i18n/unicode/numberformatter.h index f83f9738804..878b6089a07 100644 --- a/icu4c/source/i18n/unicode/numberformatter.h +++ b/icu4c/source/i18n/unicode/numberformatter.h @@ -381,6 +381,9 @@ typedef enum UNumberDecimalSeparatorDisplay { U_NAMESPACE_BEGIN +// Forward declarations: +class IFixedDecimal; + namespace numparse { namespace impl { @@ -2126,6 +2129,15 @@ class U_I18N_API FormattedNumber : public UMemory { */ void populateFieldPositionIterator(FieldPositionIterator &iterator, UErrorCode &status); +#ifndef U_HIDE_INTERNAL_API + /** + * Get an IFixedDecimal for plural rule selection. + * Internal, not intended for public use. + * @internal + */ + const IFixedDecimal& getFixedDecimal(UErrorCode &status) const; +#endif + /** * Destruct an instance of FormattedNumber, cleaning up any memory it might own. * @draft ICU 60 -- 2.40.0