X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/46f4442e9a5a4f3b98b7c1083586332f6a8a99a4..a0b4f637ba1a6c3c5651b61a69303b029bacf7d3:/icuSources/i18n/decimfmt.cpp?ds=sidebyside diff --git a/icuSources/i18n/decimfmt.cpp b/icuSources/i18n/decimfmt.cpp index 288a685c..5473c897 100644 --- a/icuSources/i18n/decimfmt.cpp +++ b/icuSources/i18n/decimfmt.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 1997-2009, International Business Machines Corporation and * +* Copyright (C) 1997-2015, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* * @@ -32,15 +32,16 @@ * 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, +* 06/29/99 stephen Fixed operator= to copy fFormatWidth, fPad, * fPadPosition ******************************************************************************** */ - + #include "unicode/utypes.h" #if !UCONFIG_NO_FORMATTING +#include "fphdlimp.h" #include "unicode/decimfmt.h" #include "unicode/choicfmt.h" #include "unicode/ucurr.h" @@ -50,40 +51,193 @@ #include "unicode/uchar.h" #include "unicode/uniset.h" #include "unicode/curramt.h" -#include "decfmtst.h" +#include "unicode/currpinf.h" +#include "unicode/plurrule.h" +#include "unicode/utf16.h" +#include "unicode/numsys.h" +#include "unicode/localpointer.h" +#include "uresimp.h" #include "ucurrimp.h" -#include "util.h" -#include "digitlst.h" +#include "charstr.h" #include "cmemory.h" +#include "patternprops.h" +#include "digitlst.h" #include "cstring.h" #include "umutex.h" #include "uassert.h" #include "putilimp.h" +#include +#include "hash.h" +#include "decfmtst.h" +#include "dcfmtimp.h" +#include "plurrule_impl.h" +#include "decimalformatpattern.h" +#include "fmtableimp.h" + +/* + * 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 -U_NAMESPACE_BEGIN -//#define FMT_DEBUG +U_NAMESPACE_BEGIN #ifdef FMT_DEBUG #include -static void debugout(UnicodeString s) { +static void _debugout(const char *f, int l, const UnicodeString& s) { char buf[2000]; - s.extract((int32_t) 0, s.length(), buf); - printf("%s\n", buf); + s.extract((int32_t) 0, s.length(), buf, "utf-8"); + printf("%s:%d: %s\n", f,l, buf); } -#define debug(x) printf("%s\n", x); +#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 -// Set to 1 to make leading zeroes be an error -// when doing a strict parse. -#define CHECK_FOR_LEADING_ZERO 0 -// Set to 1 to make misplaced grouping separators -// be an error when doing a strict parse. -#define CHECK_FOR_MISPLACED_GROUPING 0 + +/* == Fastpath calculation. == + */ +#if UCONFIG_FORMAT_FASTPATHS_49 +inline DecimalFormatInternal& internalData(uint8_t *reserved) { + return *reinterpret_cast(reserved); +} +inline const DecimalFormatInternal& internalData(const uint8_t *reserved) { + return *reinterpret_cast(reserved); +} +#else +#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 decimfmtAffixValueComparator(UHashTok val1, UHashTok val2); + +/** + * @internal ICU 4.2 + */ +static UBool U_CALLCONV decimfmtAffixPatternValueComparator(UHashTok val1, UHashTok val2); + + +static UBool +U_CALLCONV decimfmtAffixValueComparator(UHashTok val1, UHashTok val2) { + const AffixesForCurrency* affix_1 = + (AffixesForCurrency*)val1.pointer; + const AffixesForCurrency* affix_2 = + (AffixesForCurrency*)val2.pointer; + return affix_1->negPrefixForCurrency == affix_2->negPrefixForCurrency && + affix_1->negSuffixForCurrency == affix_2->negSuffixForCurrency && + affix_1->posPrefixForCurrency == affix_2->posPrefixForCurrency && + affix_1->posSuffixForCurrency == affix_2->posSuffixForCurrency; +} + + +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 @@ -124,33 +278,44 @@ 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"; +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"; + +static const UChar fgTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0}; inline int32_t _min(int32_t a, int32_t b) { return (agetName(), 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); } + delete ns; + if (U_FAILURE(status)) { return; @@ -337,62 +518,260 @@ DecimalFormat::construct(UErrorCode& status, // then do the time consuming lookup. setCurrencyForSymbols(); } else { - setCurrency(NULL, status); + setCurrencyInternally(NULL, status); + } + + const UnicodeString* patternUsed; + UnicodeString currencyPluralPatternForOther; + // apply pattern + if (fStyle == UNUM_CURRENCY_PLURAL) { + fCurrencyPluralInfo = new CurrencyPluralInfo(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); + patternUsed = ¤cyPluralPatternForOther; + // TODO: not needed? + setCurrencyForSymbols(); + + } else { + patternUsed = pattern; + } + + if (patternUsed->indexOf(kCurrencySign) != -1) { + // initialize for currency, not only for plural format, + // but also for mix parsing + if (fCurrencyPluralInfo == NULL) { + fCurrencyPluralInfo = new CurrencyPluralInfo(fSymbols->getLocale(), status); + if (U_FAILURE(status)) { + return; + } + } + // need it for mix parsing + setupCurrencyAffixPatterns(status); + // expanded affixes for plural names + if (patternUsed->indexOf(fgTripleCurrencySign, 3, 0) != -1) { + setupCurrencyAffixes(*patternUsed, TRUE, TRUE, status); + } + } + + applyPatternWithoutExpandAffix(*patternUsed,FALSE, parseErr, status); + + // expand affixes + if (fCurrencySignCount != fgCurrencySignCountInPluralFormat) { + expandAffixAdjustWidth(NULL); } - applyPattern(*pattern, FALSE /*not localized*/,parseErr, status); - // If it was a currency format, apply the appropriate rounding by // resetting the currency. NOTE: this copies fCurrency on top of itself. - if (fIsCurrencyFormat) { - setCurrency(getCurrency(), status); + if (fCurrencySignCount != fgCurrencySignCountZero) { + setCurrencyInternally(getCurrency(), status); + } +#if UCONFIG_FORMAT_FASTPATHS_49 + DecimalFormatInternal &data = internalData(fReserved); + data.fFastFormatStatus = kFastpathNO; // allow it to be calculated + data.fFastParseStatus = kFastpathNO; // allow it to be calculated + handleChanged(); +#endif +} + + +void +DecimalFormat::setupCurrencyAffixPatterns(UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + UParseError parseErr; + fAffixPatternsForCurrency = initHashForAffixPattern(status); + if (U_FAILURE(status)) { + return; + } + + NumberingSystem *ns = NumberingSystem::createInstance(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, 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)) { + applyPatternWithoutExpandAffix(UnicodeString(patResStr, patLen), false, + parseErr, status); + AffixPatternsForCurrency* affixPtn = new AffixPatternsForCurrency( + *fNegPrefixPattern, + *fNegSuffixPattern, + *fPosPrefixPattern, + *fPosSuffixPattern, + 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) { + pluralPatternSet.puti(*value, 1, status); + applyPatternWithoutExpandAffix(*value, false, parseErr, status); + AffixPatternsForCurrency* affixPtn = new AffixPatternsForCurrency( + *fNegPrefixPattern, + *fNegSuffixPattern, + *fPosPrefixPattern, + *fPosSuffixPattern, + UCURR_LONG_NAME); + fAffixPatternsForCurrency->put(*key, affixPtn, status); + } + } +} + + +void +DecimalFormat::setupCurrencyAffixes(const UnicodeString& pattern, + UBool setupForCurrentPattern, + UBool setupForPluralPattern, + UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + UParseError parseErr; + if (setupForCurrentPattern) { + if (fAffixesForCurrency) { + deleteHashForAffix(fAffixesForCurrency); + } + fAffixesForCurrency = initHashForAffix(status); + if (U_SUCCESS(status)) { + applyPatternWithoutExpandAffix(pattern, false, parseErr, status); + const PluralRules* pluralRules = fCurrencyPluralInfo->getPluralRules(); + StringEnumeration* keywords = pluralRules->getKeywords(status); + if (U_SUCCESS(status)) { + const UnicodeString* pluralCount; + while ((pluralCount = keywords->snext(status)) != NULL) { + if ( U_SUCCESS(status) ) { + expandAffixAdjustWidth(pluralCount); + AffixesForCurrency* affix = new AffixesForCurrency( + fNegativePrefix, fNegativeSuffix, fPositivePrefix, fPositiveSuffix); + fAffixesForCurrency->put(*pluralCount, affix, status); + } + } + } + delete keywords; + } + } + + if (U_FAILURE(status)) { + return; + } + + if (setupForPluralPattern) { + if (fPluralAffixesForCurrency) { + deleteHashForAffix(fPluralAffixesForCurrency); + } + fPluralAffixesForCurrency = initHashForAffix(status); + if (U_SUCCESS(status)) { + const PluralRules* pluralRules = fCurrencyPluralInfo->getPluralRules(); + StringEnumeration* keywords = pluralRules->getKeywords(status); + if (U_SUCCESS(status)) { + const UnicodeString* pluralCount; + while ((pluralCount = keywords->snext(status)) != NULL) { + if ( U_SUCCESS(status) ) { + UnicodeString ptn; + fCurrencyPluralInfo->getCurrencyPluralPattern(*pluralCount, ptn); + applyPatternInternally(*pluralCount, ptn, false, parseErr, status); + AffixesForCurrency* affix = new AffixesForCurrency( + fNegativePrefix, fNegativeSuffix, fPositivePrefix, fPositiveSuffix); + fPluralAffixesForCurrency->put(*pluralCount, affix, status); + } + } + } + delete keywords; + } } } + //------------------------------------------------------------------------------ DecimalFormat::~DecimalFormat() { -// delete fDigitList; delete fPosPrefixPattern; delete fPosSuffixPattern; delete fNegPrefixPattern; delete fNegSuffixPattern; delete fCurrencyChoice; + delete fMultiplier; delete fSymbols; delete fRoundingIncrement; + deleteHashForAffixPattern(); + deleteHashForAffix(fAffixesForCurrency); + deleteHashForAffix(fPluralAffixesForCurrency); + delete fCurrencyPluralInfo; } //------------------------------------------------------------------------------ // copy constructor -DecimalFormat::DecimalFormat(const DecimalFormat &source) -: NumberFormat(source), -// fDigitList(NULL), - fPosPrefixPattern(NULL), - fPosSuffixPattern(NULL), - fNegPrefixPattern(NULL), - fNegSuffixPattern(NULL), - fCurrencyChoice(NULL), - fSymbols(NULL), - fRoundingIncrement(NULL) -{ +DecimalFormat::DecimalFormat(const DecimalFormat &source) : + NumberFormat(source) { + init(); *this = source; } //------------------------------------------------------------------------------ // assignment operator -// Note that fDigitList is not considered a significant part of the -// DecimalFormat because it's used as a buffer to process the numbers. -static void _copy_us_ptr(UnicodeString** pdest, const UnicodeString* source) { +template +static void _copy_ptr(T** pdest, const T* source) { if (source == NULL) { delete *pdest; *pdest = NULL; } else if (*pdest == NULL) { - *pdest = new UnicodeString(*source); + *pdest = new T(*source); + } else { + **pdest = *source; + } +} + +template +static void _clone_ptr(T** pdest, const T* source) { + delete *pdest; + if (source == NULL) { + *pdest = NULL; } else { - **pdest = *source; + *pdest = static_cast(source->clone()); } } @@ -400,51 +779,33 @@ DecimalFormat& DecimalFormat::operator=(const DecimalFormat& rhs) { if(this != &rhs) { + UErrorCode status = U_ZERO_ERROR; NumberFormat::operator=(rhs); + fStaticSets = DecimalFormatStaticSets::getStaticSets(status); fPositivePrefix = rhs.fPositivePrefix; fPositiveSuffix = rhs.fPositiveSuffix; fNegativePrefix = rhs.fNegativePrefix; fNegativeSuffix = rhs.fNegativeSuffix; - _copy_us_ptr(&fPosPrefixPattern, rhs.fPosPrefixPattern); - _copy_us_ptr(&fPosSuffixPattern, rhs.fPosSuffixPattern); - _copy_us_ptr(&fNegPrefixPattern, rhs.fNegPrefixPattern); - _copy_us_ptr(&fNegSuffixPattern, rhs.fNegSuffixPattern); - if (rhs.fCurrencyChoice == 0) { - delete fCurrencyChoice; - fCurrencyChoice = 0; - } else { - fCurrencyChoice = (ChoiceFormat*) rhs.fCurrencyChoice->clone(); - } - if(rhs.fRoundingIncrement == NULL) { - delete fRoundingIncrement; - fRoundingIncrement = NULL; - } - else if(fRoundingIncrement == NULL) { - fRoundingIncrement = new DigitList(*rhs.fRoundingIncrement); - } - else { - *fRoundingIncrement = *rhs.fRoundingIncrement; - } - fRoundingDouble = rhs.fRoundingDouble; + _copy_ptr(&fPosPrefixPattern, rhs.fPosPrefixPattern); + _copy_ptr(&fPosSuffixPattern, rhs.fPosSuffixPattern); + _copy_ptr(&fNegPrefixPattern, rhs.fNegPrefixPattern); + _copy_ptr(&fNegSuffixPattern, rhs.fNegSuffixPattern); + _clone_ptr(&fCurrencyChoice, rhs.fCurrencyChoice); + setRoundingIncrement(rhs.getRoundingIncrement()); fRoundingMode = rhs.fRoundingMode; - fMultiplier = rhs.fMultiplier; + setMultiplier(rhs.getMultiplier()); fGroupingSize = rhs.fGroupingSize; fGroupingSize2 = rhs.fGroupingSize2; fDecimalSeparatorAlwaysShown = rhs.fDecimalSeparatorAlwaysShown; - if(fSymbols == NULL) { - fSymbols = new DecimalFormatSymbols(*rhs.fSymbols); - } else { - *fSymbols = *rhs.fSymbols; - } + _copy_ptr(&fSymbols, rhs.fSymbols); fUseExponentialNotation = rhs.fUseExponentialNotation; fExponentSignAlwaysShown = rhs.fExponentSignAlwaysShown; + fBoolFlags = rhs.fBoolFlags; /*Bertrand A. D. Update 98.03.17*/ - fIsCurrencyFormat = rhs.fIsCurrencyFormat; + fCurrencySignCount = rhs.fCurrencySignCount; /*end of Update*/ fMinExponentDigits = rhs.fMinExponentDigits; - // if (fDigitList == NULL) - // fDigitList = new DigitList(); - + /* sfb 990629 */ fFormatWidth = rhs.fFormatWidth; fPad = rhs.fPad; @@ -453,6 +814,34 @@ DecimalFormat::operator=(const DecimalFormat& rhs) fMinSignificantDigits = rhs.fMinSignificantDigits; fMaxSignificantDigits = rhs.fMaxSignificantDigits; fUseSignificantDigits = rhs.fUseSignificantDigits; + fFormatPattern = rhs.fFormatPattern; + fCurrencyUsage = rhs.fCurrencyUsage; + 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); + } + deleteHashForAffix(fAffixesForCurrency); + if (rhs.fAffixesForCurrency) { + UErrorCode status = U_ZERO_ERROR; + fAffixesForCurrency = initHashForAffixPattern(status); + copyHashForAffix(rhs.fAffixesForCurrency, fAffixesForCurrency, status); + } + deleteHashForAffix(fPluralAffixesForCurrency); + if (rhs.fPluralAffixesForCurrency) { + UErrorCode status = U_ZERO_ERROR; + fPluralAffixesForCurrency = initHashForAffixPattern(status); + copyHashForAffix(rhs.fPluralAffixesForCurrency, fPluralAffixesForCurrency, status); + } +#if UCONFIG_FORMAT_FASTPATHS_49 + DecimalFormatInternal &data = internalData(fReserved); + const DecimalFormatInternal &rhsData = internalData(rhs.fReserved); + data = rhsData; +#endif } return *this; } @@ -475,7 +864,7 @@ DecimalFormat::operator==(const Format& that) const if (!NumberFormat::operator==(that)) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("NumberFormat::!="); - } + } else { if (!((fPosPrefixPattern == other->fPosPrefixPattern && // both null fPositivePrefix == other->fPositivePrefix) || (fPosPrefixPattern != 0 && other->fPosPrefixPattern != 0 && @@ -541,9 +930,13 @@ DecimalFormat::operator==(const Format& that) const if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Rounding Increment !="); } - if (fMultiplier != other->fMultiplier) { + if (fRoundingMode != other->fRoundingMode) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + printf("Rounding Mode %d != %d", (int)fRoundingMode, (int)other->fRoundingMode); + } + if (getMultiplier() != other->getMultiplier()) { if (first) { printf("[ "); first = FALSE; } - printf("Multiplier %ld != %ld", fMultiplier, other->fMultiplier); + printf("Multiplier %ld != %ld", getMultiplier(), other->getMultiplier()); } if (fGroupingSize != other->fGroupingSize) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } @@ -555,58 +948,156 @@ DecimalFormat::operator==(const Format& that) const } if (fDecimalSeparatorAlwaysShown != other->fDecimalSeparatorAlwaysShown) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } - printf("Dec Sep Always %d != %d", fDecimalSeparatorAlwaysShown, other->fDecimalSeparatorAlwaysShown); + printf("fDecimalSeparatorAlwaysShown %d != %d", fDecimalSeparatorAlwaysShown, other->fDecimalSeparatorAlwaysShown); } if (fUseExponentialNotation != other->fUseExponentialNotation) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } - debug("Use Exp !="); + debug("fUseExponentialNotation !="); + } + if (fUseExponentialNotation && + fMinExponentDigits != other->fMinExponentDigits) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fMinExponentDigits !="); } - if (!(!fUseExponentialNotation || - fMinExponentDigits != other->fMinExponentDigits)) { + if (fUseExponentialNotation && + fExponentSignAlwaysShown != other->fExponentSignAlwaysShown) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } - debug("Exp Digits !="); + debug("fExponentSignAlwaysShown !="); + } + if (fBoolFlags.getAll() != other->fBoolFlags.getAll()) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fBoolFlags !="); } if (*fSymbols != *(other->fSymbols)) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Symbols !="); } // TODO Add debug stuff for significant digits here + if (fUseSignificantDigits != other->fUseSignificantDigits) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fUseSignificantDigits !="); + } + if (fUseSignificantDigits && + fMinSignificantDigits != other->fMinSignificantDigits) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fMinSignificantDigits !="); + } + if (fUseSignificantDigits && + fMaxSignificantDigits != other->fMaxSignificantDigits) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fMaxSignificantDigits !="); + } + if (fFormatWidth != other->fFormatWidth) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fFormatWidth !="); + } + if (fPad != other->fPad) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fPad !="); + } + if (fPadPosition != other->fPadPosition) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fPadPosition !="); + } + if (fStyle == UNUM_CURRENCY_PLURAL && + fStyle != other->fStyle) + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fStyle !="); + } + if (fStyle == UNUM_CURRENCY_PLURAL && + fFormatPattern != other->fFormatPattern) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fFormatPattern !="); + } + if (!first) { printf(" ]"); } + if (fCurrencySignCount != other->fCurrencySignCount) { + debug("fCurrencySignCount !="); + } + if (fCurrencyPluralInfo == other->fCurrencyPluralInfo) { + debug("fCurrencyPluralInfo == "); + if (fCurrencyPluralInfo == NULL) { + debug("fCurrencyPluralInfo == NULL"); + } + } + if (fCurrencyPluralInfo != NULL && other->fCurrencyPluralInfo != NULL && + *fCurrencyPluralInfo != *(other->fCurrencyPluralInfo)) { + debug("fCurrencyPluralInfo !="); + } + if (fCurrencyPluralInfo != NULL && other->fCurrencyPluralInfo == NULL || + fCurrencyPluralInfo == NULL && other->fCurrencyPluralInfo != NULL) { + debug("fCurrencyPluralInfo one NULL, the other not"); + } + if (fCurrencyPluralInfo == NULL && other->fCurrencyPluralInfo == NULL) { + debug("fCurrencyPluralInfo == "); + } + } #endif - return (NumberFormat::operator==(that) && - ((fPosPrefixPattern == other->fPosPrefixPattern && // both null - fPositivePrefix == other->fPositivePrefix) - || (fPosPrefixPattern != 0 && other->fPosPrefixPattern != 0 && - *fPosPrefixPattern == *other->fPosPrefixPattern)) && - ((fPosSuffixPattern == other->fPosSuffixPattern && // both null - fPositiveSuffix == other->fPositiveSuffix) - || (fPosSuffixPattern != 0 && other->fPosSuffixPattern != 0 && - *fPosSuffixPattern == *other->fPosSuffixPattern)) && - ((fNegPrefixPattern == other->fNegPrefixPattern && // both null - fNegativePrefix == other->fNegativePrefix) - || (fNegPrefixPattern != 0 && other->fNegPrefixPattern != 0 && - *fNegPrefixPattern == *other->fNegPrefixPattern)) && - ((fNegSuffixPattern == other->fNegSuffixPattern && // both null - fNegativeSuffix == other->fNegativeSuffix) - || (fNegSuffixPattern != 0 && other->fNegSuffixPattern != 0 && - *fNegSuffixPattern == *other->fNegSuffixPattern)) && - ((fRoundingIncrement == other->fRoundingIncrement) // both null - || (fRoundingIncrement != NULL && - other->fRoundingIncrement != NULL && - *fRoundingIncrement == *other->fRoundingIncrement)) && - fMultiplier == other->fMultiplier && + return ( + NumberFormat::operator==(that) && + + ((fCurrencySignCount == fgCurrencySignCountInPluralFormat) ? + (fAffixPatternsForCurrency->equals(*other->fAffixPatternsForCurrency)) : + (((fPosPrefixPattern == other->fPosPrefixPattern && // both null + fPositivePrefix == other->fPositivePrefix) + || (fPosPrefixPattern != 0 && other->fPosPrefixPattern != 0 && + *fPosPrefixPattern == *other->fPosPrefixPattern)) && + ((fPosSuffixPattern == other->fPosSuffixPattern && // both null + fPositiveSuffix == other->fPositiveSuffix) + || (fPosSuffixPattern != 0 && other->fPosSuffixPattern != 0 && + *fPosSuffixPattern == *other->fPosSuffixPattern)) && + ((fNegPrefixPattern == other->fNegPrefixPattern && // both null + fNegativePrefix == other->fNegativePrefix) + || (fNegPrefixPattern != 0 && other->fNegPrefixPattern != 0 && + *fNegPrefixPattern == *other->fNegPrefixPattern)) && + ((fNegSuffixPattern == other->fNegSuffixPattern && // both null + fNegativeSuffix == other->fNegativeSuffix) + || (fNegSuffixPattern != 0 && other->fNegSuffixPattern != 0 && + *fNegSuffixPattern == *other->fNegSuffixPattern)))) && + + ((fRoundingIncrement == other->fRoundingIncrement) // both null + || (fRoundingIncrement != NULL && + other->fRoundingIncrement != NULL && + *fRoundingIncrement == *other->fRoundingIncrement)) && + + fRoundingMode == other->fRoundingMode && + getMultiplier() == other->getMultiplier() && fGroupingSize == other->fGroupingSize && fGroupingSize2 == other->fGroupingSize2 && fDecimalSeparatorAlwaysShown == other->fDecimalSeparatorAlwaysShown && fUseExponentialNotation == other->fUseExponentialNotation && + (!fUseExponentialNotation || - fMinExponentDigits == other->fMinExponentDigits) && + (fMinExponentDigits == other->fMinExponentDigits && fExponentSignAlwaysShown == other->fExponentSignAlwaysShown)) && + + fBoolFlags.getAll() == other->fBoolFlags.getAll() && *fSymbols == *(other->fSymbols) && fUseSignificantDigits == other->fUseSignificantDigits && + (!fUseSignificantDigits || - (fMinSignificantDigits == other->fMinSignificantDigits && - fMaxSignificantDigits == other->fMaxSignificantDigits))); + (fMinSignificantDigits == other->fMinSignificantDigits && fMaxSignificantDigits == other->fMaxSignificantDigits)) && + + fFormatWidth == other->fFormatWidth && + fPad == other->fPad && + fPadPosition == other->fPadPosition && + + (fStyle != UNUM_CURRENCY_PLURAL || + (fStyle == other->fStyle && fFormatPattern == other->fFormatPattern)) && + + fCurrencySignCount == other->fCurrencySignCount && + + ((fCurrencyPluralInfo == other->fCurrencyPluralInfo && + fCurrencyPluralInfo == NULL) || + (fCurrencyPluralInfo != NULL && other->fCurrencyPluralInfo != NULL && + *fCurrencyPluralInfo == *(other->fCurrencyPluralInfo))) && + + fCurrencyUsage == other->fCurrencyUsage + + // depending on other settings we may also need to compare + // fCurrencyChoice (mostly deprecated?), + // fAffixesForCurrency & fPluralAffixesForCurrency (only relevant in some cases) + ); } //------------------------------------------------------------------------------ @@ -617,185 +1108,672 @@ DecimalFormat::clone() const return new DecimalFormat(*this); } -//------------------------------------------------------------------------------ - -UnicodeString& -DecimalFormat::format(int32_t number, - UnicodeString& appendTo, - FieldPosition& fieldPosition) const -{ - return format((int64_t)number, appendTo, fieldPosition); -} -//------------------------------------------------------------------------------ - -UnicodeString& -DecimalFormat::format(int64_t number, - UnicodeString& appendTo, - FieldPosition& fieldPosition) const -{ - DigitList digits; +FixedDecimal +DecimalFormat::getFixedDecimal(double number, UErrorCode &status) const { + FixedDecimal result; - // Clears field positions. - fieldPosition.setBeginIndex(0); - fieldPosition.setEndIndex(0); - - // If we are to do rounding, we need to move into the BigDecimal - // domain in order to do divide/multiply correctly. - // || - // In general, long values always represent real finite numbers, so - // we don't have to check for +/- Infinity or NaN. However, there - // is one case we have to be careful of: The multiplier can push - // a number near MIN_VALUE or MAX_VALUE outside the legal range. We - // check for this before multiplying, and if it happens we use doubles - // instead, trading off accuracy for range. - if (fRoundingIncrement != NULL - || (fMultiplier > 0 && (number > U_INT64_MAX / fMultiplier || number < U_INT64_MIN / fMultiplier)) - || (fMultiplier < 0 && (number == U_INT64_MIN || -number > U_INT64_MAX / -fMultiplier || -number < U_INT64_MIN / -fMultiplier)) - ) - { - digits.set(((double) number) * fMultiplier, - precision(FALSE), - !fUseExponentialNotation && !areSignificantDigitsUsed()); + if (U_FAILURE(status)) { + return result; } - else - { - digits.set(number * fMultiplier, precision(TRUE)); + + if (uprv_isNaN(number) || uprv_isPositiveInfinity(fabs(number))) { + // For NaN and Infinity the state of the formatter is ignored. + result.init(number); + return result; } - return subformat(appendTo, fieldPosition, digits, TRUE); + if (fMultiplier == NULL && fScale == 0 && fRoundingIncrement == 0 && areSignificantDigitsUsed() == FALSE && + result.quickInit(number) && result.visibleDecimalDigitCount <= getMaximumFractionDigits()) { + // Fast Path. Construction of an exact FixedDecimal directly from the double, without passing + // through a DigitList, was successful, and the formatter is doing nothing tricky with rounding. + // printf("getFixedDecimal(%g): taking fast path.\n", number); + result.adjustForMinFractionDigits(getMinimumFractionDigits()); + } else { + // Slow path. Create a DigitList, and have this formatter round it according to the + // requirements of the format, and fill the fixedDecimal from that. + DigitList digits; + digits.set(number); + result = getFixedDecimal(digits, status); + } + return result; } - -//------------------------------------------------------------------------------ - -UnicodeString& -DecimalFormat::format( double number, - UnicodeString& appendTo, - FieldPosition& fieldPosition) const -{ - // Clears field positions. - fieldPosition.setBeginIndex(0); - fieldPosition.setEndIndex(0); - // Special case for NaN, sets the begin and end index to be the - // the string length of localized name of NaN. - if (uprv_isNaN(number)) - { - if (fieldPosition.getField() == NumberFormat::kIntegerField) - fieldPosition.setBeginIndex(appendTo.length()); +FixedDecimal +DecimalFormat::getFixedDecimal(const Formattable &number, UErrorCode &status) const { + if (U_FAILURE(status)) { + return FixedDecimal(); + } + if (!number.isNumeric()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return FixedDecimal(); + } - appendTo += getConstSymbol(DecimalFormatSymbols::kNaNSymbol); + DigitList *dl = number.getDigitList(); + if (dl != NULL) { + DigitList clonedDL(*dl); + return getFixedDecimal(clonedDL, status); + } - if (fieldPosition.getField() == NumberFormat::kIntegerField) - fieldPosition.setEndIndex(appendTo.length()); + Formattable::Type type = number.getType(); + if (type == Formattable::kDouble || type == Formattable::kLong) { + return getFixedDecimal(number.getDouble(status), status); + } - addPadding(appendTo, fieldPosition, 0, 0); - return appendTo; + if (type == Formattable::kInt64 && number.getInt64() <= MAX_INT64_IN_DOUBLE && + number.getInt64() >= -MAX_INT64_IN_DOUBLE) { + return getFixedDecimal(number.getDouble(status), status); } - // Do this BEFORE checking to see if value is infinite or negative! Sets the - // begin and end index to be length of the string composed of - // localized name of Infinite and the positive/negative localized - // signs. + // The only case left is type==int64_t, with a value with more digits than a double can represent. + // Any formattable originating as a big decimal will have had a pre-existing digit list. + // Any originating as a double or int32 will have been handled as a double. - number *= fMultiplier; + U_ASSERT(type == Formattable::kInt64); + DigitList digits; + digits.set(number.getInt64()); + return getFixedDecimal(digits, status); +} - /* Detecting whether a double is negative is easy with the exception of - * the value -0.0. This is a double which has a zero mantissa (and - * exponent), but a negative sign bit. It is semantically distinct from - * a zero with a positive sign bit, and this distinction is important - * to certain kinds of computations. However, it's a little tricky to - * detect, since (-0.0 == 0.0) and !(-0.0 < 0.0). How then, you may - * ask, does it behave distinctly from +0.0? Well, 1/(-0.0) == - * -Infinity. Proper detection of -0.0 is needed to deal with the - * issues raised by bugs 4106658, 4106667, and 4147706. Liu 7/6/98. - */ - UBool isNegative = uprv_isNegative(number); - // Apply rounding after multiplier - if (fRoundingIncrement != NULL) { - if (isNegative) // For rounding in the correct direction - number = -number; - number = fRoundingDouble - * round(number / fRoundingDouble, fRoundingMode, isNegative); - if (isNegative) - number = -number; +// Create a fixed decimal from a DigitList. +// The digit list may be modified. +// Internal function only. +FixedDecimal +DecimalFormat::getFixedDecimal(DigitList &number, UErrorCode &status) const { + // Round the number according to the requirements of this Format. + FixedDecimal result; + _round(number, number, result.isNegative, status); + + // The int64_t fields in FixedDecimal can easily overflow. + // In deciding what to discard in this event, consider that fixedDecimal + // is being used only with PluralRules, and those rules mostly look at least significant + // few digits of the integer part, and whether the fraction part is zero or not. + // + // So, in case of overflow when filling in the fields of the FixedDecimal object, + // for the integer part, discard the most significant digits. + // for the fraction part, discard the least significant digits, + // don't truncate the fraction value to zero. + // For simplicity, the int64_t fields are limited to 18 decimal digits, even + // though they could hold most (but not all) 19 digit values. + + // Integer Digits. + int32_t di = number.getDecimalAt()-18; // Take at most 18 digits. + if (di < 0) { + di = 0; + } + result.intValue = 0; + for (; di 0) { + // The number is something like 100000000000000000000000. + // More than 18 digits integer digits, but the least significant 18 are all zero. + // We don't want to return zero as the int part, but want to keep zeros + // for several of the least significant digits. + result.intValue = 100000000000000000LL; + } + + // Fraction digits. + result.decimalDigits = result.decimalDigitsWithoutTrailingZeros = result.visibleDecimalDigitCount = 0; + for (di = number.getDecimalAt(); di < number.getCount(); di++) { + result.visibleDecimalDigitCount++; + if (result.decimalDigits < 100000000000000000LL) { + // 9223372036854775807 Largest 64 bit signed integer + int32_t digitVal = number.getDigit(di) & 0x0f; // getDigit() returns a char, '0'-'9'. + result.decimalDigits = result.decimalDigits * 10 + digitVal; + if (digitVal > 0) { + result.decimalDigitsWithoutTrailingZeros = result.decimalDigits; + } + } } - // Special case for INFINITE, - if (uprv_isInfinite(number)) - { - int32_t prefixLen = appendAffix(appendTo, number, isNegative, TRUE); + result.hasIntegerValue = (result.decimalDigits == 0); - if (fieldPosition.getField() == NumberFormat::kIntegerField) - fieldPosition.setBeginIndex(appendTo.length()); + // Trailing fraction zeros. The format specification may require more trailing + // zeros than the numeric value. Add any such on now. - appendTo += getConstSymbol(DecimalFormatSymbols::kInfinitySymbol); + int32_t minFractionDigits; + if (areSignificantDigitsUsed()) { + minFractionDigits = getMinimumSignificantDigits() - number.getDecimalAt(); + if (minFractionDigits < 0) { + minFractionDigits = 0; + } + } else { + minFractionDigits = getMinimumFractionDigits(); + } + result.adjustForMinFractionDigits(minFractionDigits); + + return result; +} - if (fieldPosition.getField() == NumberFormat::kIntegerField) - fieldPosition.setEndIndex(appendTo.length()); - int32_t suffixLen = appendAffix(appendTo, number, isNegative, FALSE); +//------------------------------------------------------------------------------ - addPadding(appendTo, fieldPosition, prefixLen, suffixLen); +UnicodeString& +DecimalFormat::format(int32_t number, + UnicodeString& appendTo, + FieldPosition& fieldPosition) const +{ + return format((int64_t)number, appendTo, fieldPosition); +} + +UnicodeString& +DecimalFormat::format(int32_t number, + UnicodeString& appendTo, + FieldPosition& fieldPosition, + UErrorCode& status) const +{ + return format((int64_t)number, appendTo, fieldPosition, status); +} + +UnicodeString& +DecimalFormat::format(int32_t number, + UnicodeString& appendTo, + FieldPositionIterator* posIter, + UErrorCode& status) const +{ + return format((int64_t)number, appendTo, posIter, status); +} + + +#if UCONFIG_FORMAT_FASTPATHS_49 +void DecimalFormat::handleChanged() { + DecimalFormatInternal &data = internalData(fReserved); + + if(data.fFastFormatStatus == kFastpathUNKNOWN || data.fFastParseStatus == kFastpathUNKNOWN) { + return; // still constructing. Wait. + } + + data.fFastParseStatus = data.fFastFormatStatus = kFastpathNO; + +#if UCONFIG_HAVE_PARSEALLINPUT + if(fParseAllInput == UNUM_NO) { + debug("No Parse fastpath: fParseAllInput==UNUM_NO"); + } else +#endif + if (fFormatWidth!=0) { + debug("No Parse fastpath: fFormatWidth"); + } else if(fPositivePrefix.length()>0) { + debug("No Parse fastpath: positive prefix"); + } else if(fPositiveSuffix.length()>0) { + debug("No Parse fastpath: positive suffix"); + } else if(fNegativePrefix.length()>1 + || ((fNegativePrefix.length()==1) && (fNegativePrefix.charAt(0)!=0x002D))) { + debug("No Parse fastpath: negative prefix that isn't '-'"); + } else if(fNegativeSuffix.length()>0) { + debug("No Parse fastpath: negative suffix"); + } else { + data.fFastParseStatus = kFastpathYES; + debug("parse fastpath: YES"); + } + + if(fUseExponentialNotation) { + debug("No format fastpath: fUseExponentialNotation"); + } else if(fFormatWidth!=0) { + debug("No format fastpath: fFormatWidth!=0"); + } else if(fMinSignificantDigits!=1) { + debug("No format fastpath: fMinSignificantDigits!=1"); + } else if(fMultiplier!=NULL) { + debug("No format fastpath: fMultiplier!=NULL"); + } else if(fScale!=0) { + debug("No format fastpath: fScale!=0"); + } else if(0x0030 != getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0)) { + debug("No format fastpath: 0x0030 != getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0)"); + } else if(fDecimalSeparatorAlwaysShown) { + debug("No format fastpath: fDecimalSeparatorAlwaysShown"); + } else if(getMinimumFractionDigits()>0) { + debug("No format fastpath: fMinFractionDigits>0"); + } else if(fCurrencySignCount != fgCurrencySignCountZero) { + debug("No format fastpath: fCurrencySignCount != fgCurrencySignCountZero"); + } else if(fRoundingIncrement!=0) { + debug("No format fastpath: fRoundingIncrement!=0"); + } else if (fGroupingSize!=0 && isGroupingUsed()) { + debug("Maybe format fastpath: fGroupingSize!=0 and grouping is used"); +#ifdef FMT_DEBUG + printf("groupingsize=%d\n", fGroupingSize); +#endif + + if (getMinimumIntegerDigits() <= fGroupingSize) { + data.fFastFormatStatus = kFastpathMAYBE; + } + } else if(fGroupingSize2!=0 && isGroupingUsed()) { + debug("No format fastpath: fGroupingSize2!=0"); + } else { + data.fFastFormatStatus = kFastpathYES; + debug("format:kFastpathYES!"); + } + + +} +#endif +//------------------------------------------------------------------------------ + +UnicodeString& +DecimalFormat::format(int64_t number, + UnicodeString& appendTo, + FieldPosition& fieldPosition) const +{ + UErrorCode status = U_ZERO_ERROR; /* ignored */ + FieldPositionOnlyHandler handler(fieldPosition); + return _format(number, appendTo, handler, status); +} + +UnicodeString& +DecimalFormat::format(int64_t number, + UnicodeString& appendTo, + FieldPosition& fieldPosition, + UErrorCode& status) const +{ + FieldPositionOnlyHandler handler(fieldPosition); + return _format(number, appendTo, handler, status); +} + +UnicodeString& +DecimalFormat::format(int64_t number, + UnicodeString& appendTo, + FieldPositionIterator* posIter, + UErrorCode& status) const +{ + FieldPositionIteratorHandler handler(posIter, status); + return _format(number, appendTo, handler, status); +} + +UnicodeString& +DecimalFormat::_format(int64_t number, + UnicodeString& appendTo, + FieldPositionHandler& handler, + UErrorCode &status) const +{ + // Bottleneck function for formatting int64_t + if (U_FAILURE(status)) { return appendTo; } +#if UCONFIG_FORMAT_FASTPATHS_49 + // const UnicodeString *posPrefix = fPosPrefixPattern; + // const UnicodeString *posSuffix = fPosSuffixPattern; + // const UnicodeString *negSuffix = fNegSuffixPattern; + + const DecimalFormatInternal &data = internalData(fReserved); + +#ifdef FMT_DEBUG + data.dump(); + printf("fastpath? [%d]\n", number); +#endif + + if( data.fFastFormatStatus==kFastpathYES || + data.fFastFormatStatus==kFastpathMAYBE) { + int32_t noGroupingThreshold = 0; + +#define kZero 0x0030 + const int32_t MAX_IDX = MAX_DIGITS+2; + UChar outputStr[MAX_IDX]; + int32_t destIdx = MAX_IDX; + outputStr[--destIdx] = 0; // term + + if (data.fFastFormatStatus==kFastpathMAYBE) { + noGroupingThreshold = destIdx - fGroupingSize; + } + int64_t n = number; + if (number < 1) { + // Negative numbers are slightly larger than positive + // output the first digit (or the leading zero) + outputStr[--destIdx] = (-(n % 10) + kZero); + n /= -10; + } + // get any remaining digits + while (n > 0) { + if (destIdx == noGroupingThreshold) { + goto slowPath; + } + outputStr[--destIdx] = (n % 10) + kZero; + n /= 10; + } + + // Slide the number to the start of the output str + U_ASSERT(destIdx >= 0); + int32_t length = MAX_IDX - destIdx -1; + /*int32_t prefixLen = */ appendAffix(appendTo, static_cast(number), handler, number<0, TRUE); + + // This will be at least 0 even if it was set to a negative number. + int32_t maxIntDig = getMaximumIntegerDigits(); + int32_t destlength = length<=maxIntDig?length:maxIntDig; // dest length pinned to max int digits + + if(length>maxIntDig && fBoolFlags.contains(UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS)) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } + + int32_t minDigits = getMinimumIntegerDigits(); + + // We always want at least one digit, even if it is just a 0. + int32_t prependZero = (minDigits < 1 ? 1 : minDigits) - destlength; + +#ifdef FMT_DEBUG + printf("prependZero=%d, length=%d, minintdig=%d maxintdig=%d destlength=%d skip=%d\n", prependZero, length, getMinimumIntegerDigits(), maxIntDig, destlength, length-destlength); +#endif + int32_t intBegin = appendTo.length(); + + while((prependZero--)>0) { + appendTo.append((UChar)0x0030); // '0' + } + + appendTo.append(outputStr+destIdx+ + (length-destlength), // skip any leading digits + destlength); + handler.addAttribute(kIntegerField, intBegin, appendTo.length()); + + /*int32_t suffixLen =*/ appendAffix(appendTo, static_cast(number), handler, number<0, FALSE); + + //outputStr[length]=0; + +#ifdef FMT_DEBUG + printf("Writing [%s] length [%d] max %d for [%d]\n", outputStr+destIdx, length, MAX_IDX, number); +#endif + +#undef kZero + + return appendTo; + } // end fastpath +#endif + slowPath: + + // Else the slow way - via DigitList DigitList digits; + digits.set(number); + return _format(digits, appendTo, handler, status); +} - // This detects negativity too. - if (fRoundingIncrement == NULL) { - // If we did not round in binary space, round in decimal space - digits.fRoundingMode = fRoundingMode; +//------------------------------------------------------------------------------ + +UnicodeString& +DecimalFormat::format( double number, + UnicodeString& appendTo, + FieldPosition& fieldPosition) const +{ + UErrorCode status = U_ZERO_ERROR; /* ignored */ + FieldPositionOnlyHandler handler(fieldPosition); + return _format(number, appendTo, handler, status); +} + +UnicodeString& +DecimalFormat::format( double number, + UnicodeString& appendTo, + FieldPosition& fieldPosition, + UErrorCode& status) const +{ + FieldPositionOnlyHandler handler(fieldPosition); + return _format(number, appendTo, handler, status); +} + +UnicodeString& +DecimalFormat::format( double number, + UnicodeString& appendTo, + FieldPositionIterator* posIter, + UErrorCode& status) const +{ + FieldPositionIteratorHandler handler(posIter, status); + return _format(number, appendTo, handler, status); +} + +UnicodeString& +DecimalFormat::_format( double number, + UnicodeString& appendTo, + FieldPositionHandler& handler, + UErrorCode &status) const +{ + if (U_FAILURE(status)) { + return appendTo; + } + // Special case for NaN, sets the begin and end index to be the + // the string length of localized name of NaN. + // TODO: let NaNs go through DigitList. + if (uprv_isNaN(number)) + { + int begin = appendTo.length(); + appendTo += getConstSymbol(DecimalFormatSymbols::kNaNSymbol); + + handler.addAttribute(kIntegerField, begin, appendTo.length()); + + addPadding(appendTo, handler, 0, 0); + return appendTo; } - digits.set(number, precision(FALSE), - !fUseExponentialNotation && !areSignificantDigitsUsed()); - return subformat(appendTo, fieldPosition, digits, FALSE); + DigitList digits; + digits.set(number); + _format(digits, appendTo, handler, status); + // No way to return status from here. + return appendTo; } - -/** - * Round a double value to the nearest integer according to the - * given mode. - * @param a the absolute value of the number to be rounded - * @param mode a BigDecimal rounding mode - * @param isNegative true if the number to be rounded is negative - * @return the absolute value of the rounded result - */ -double DecimalFormat::round(double a, ERoundingMode mode, UBool isNegative) { - switch (mode) { - case kRoundCeiling: - return isNegative ? uprv_floor(a) : uprv_ceil(a); - case kRoundFloor: - return isNegative ? uprv_ceil(a) : uprv_floor(a); - case kRoundDown: - return uprv_floor(a); - case kRoundUp: - return uprv_ceil(a); - case kRoundHalfEven: - { - double f = uprv_floor(a); - if ((a - f) != 0.5) { - return uprv_floor(a + 0.5); + +//------------------------------------------------------------------------------ + + +UnicodeString& +DecimalFormat::format(const StringPiece &number, + UnicodeString &toAppendTo, + FieldPositionIterator *posIter, + UErrorCode &status) const +{ +#if UCONFIG_FORMAT_FASTPATHS_49 + // don't bother if the int64 path is not optimized + int32_t len = number.length(); + + if(len>0&&len<10) { /* 10 or more digits may not be an int64 */ + const char *data = number.data(); + int64_t num = 0; + UBool neg = FALSE; + UBool ok = TRUE; + + int32_t start = 0; + + if(data[start]=='+') { + start++; + } else if(data[start]=='-') { + neg=TRUE; + start++; + } + + int32_t place = 1; /* 1, 10, ... */ + for(int32_t i=len-1;i>=start;i--) { + if(data[i]>='0'&&data[i]<='9') { + num+=place*(int64_t)(data[i]-'0'); + } else { + ok=FALSE; + break; + } + place *= 10; + } + + if(ok) { + if(neg) { + num = -num;// add minus bit + } + // format as int64_t + return format(num, toAppendTo, posIter, status); + } + // else fall through + } +#endif + + DigitList dnum; + dnum.set(number, status); + if (U_FAILURE(status)) { + return toAppendTo; + } + FieldPositionIteratorHandler handler(posIter, status); + _format(dnum, toAppendTo, handler, status); + return toAppendTo; +} + + +UnicodeString& +DecimalFormat::format(const DigitList &number, + UnicodeString &appendTo, + FieldPositionIterator *posIter, + UErrorCode &status) const { + FieldPositionIteratorHandler handler(posIter, status); + _format(number, appendTo, handler, status); + return appendTo; +} + + + +UnicodeString& +DecimalFormat::format(const DigitList &number, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode &status) const { + FieldPositionOnlyHandler handler(pos); + _format(number, appendTo, handler, status); + return appendTo; +} + +DigitList& +DecimalFormat::_round(const DigitList &number, DigitList &adjustedNum, UBool& isNegative, UErrorCode &status) const { + if (U_FAILURE(status)) { + return adjustedNum; + } + + // note: number and adjustedNum may refer to the same DigitList, in cases where a copy + // is not needed by the caller. + + adjustedNum = number; + isNegative = false; + if (number.isNaN()) { + return adjustedNum; + } + + // Do this BEFORE checking to see if value is infinite or negative! Sets the + // begin and end index to be length of the string composed of + // localized name of Infinite and the positive/negative localized + // signs. + + adjustedNum.setRoundingMode(fRoundingMode); + if (fMultiplier != NULL) { + adjustedNum.mult(*fMultiplier, status); + if (U_FAILURE(status)) { + return adjustedNum; + } + } + + if (fScale != 0) { + DigitList ten; + ten.set((int32_t)10); + if (fScale > 0) { + for (int32_t i = fScale ; i > 0 ; i--) { + adjustedNum.mult(ten, status); + if (U_FAILURE(status)) { + return adjustedNum; + } + } + } else { + for (int32_t i = fScale ; i < 0 ; i++) { + adjustedNum.div(ten, status); + if (U_FAILURE(status)) { + return adjustedNum; + } } - double g = f / 2.0; - return (g == uprv_floor(g)) ? f : (f + 1.0); } - case kRoundHalfDown: - return ((a - uprv_floor(a)) <= 0.5) ? uprv_floor(a) : uprv_ceil(a); - case kRoundHalfUp: - return ((a - uprv_floor(a)) < 0.5) ? uprv_floor(a) : uprv_ceil(a); } - return 1.0; + + /* + * Note: sign is important for zero as well as non-zero numbers. + * Proper detection of -0.0 is needed to deal with the + * issues raised by bugs 4106658, 4106667, and 4147706. Liu 7/6/98. + */ + isNegative = !adjustedNum.isPositive(); + + // Apply rounding after multiplier + + adjustedNum.fContext.status &= ~DEC_Inexact; + if (fRoundingIncrement != NULL) { + adjustedNum.div(*fRoundingIncrement, status); + adjustedNum.toIntegralValue(); + adjustedNum.mult(*fRoundingIncrement, status); + adjustedNum.trim(); + if (U_FAILURE(status)) { + return adjustedNum; + } + } + if (fRoundingMode == kRoundUnnecessary && (adjustedNum.fContext.status & DEC_Inexact)) { + status = U_FORMAT_INEXACT_ERROR; + return adjustedNum; + } + + if (adjustedNum.isInfinite()) { + return adjustedNum; + } + + if (fUseExponentialNotation || areSignificantDigitsUsed()) { + int32_t sigDigits = precision(); + if (sigDigits > 0) { + adjustedNum.round(sigDigits); + // Travis Keep (21/2/2014): Calling round on a digitList does not necessarily + // preserve the sign of that digit list. Preserving the sign is especially + // important when formatting -0.0 for instance. Not preserving the sign seems + // like a bug because I cannot think of any case where the sign would actually + // have to change when rounding. For now, we preserve the sign by setting the + // positive attribute directly. + adjustedNum.setPositive(!isNegative); + } + } else { + // Fixed point format. Round to a set number of fraction digits. + int32_t numFractionDigits = precision(); + adjustedNum.roundFixedPoint(numFractionDigits); + } + if (fRoundingMode == kRoundUnnecessary && (adjustedNum.fContext.status & DEC_Inexact)) { + status = U_FORMAT_INEXACT_ERROR; + return adjustedNum; + } + return adjustedNum; } UnicodeString& -DecimalFormat::format( const Formattable& obj, +DecimalFormat::_format(const DigitList &number, UnicodeString& appendTo, - FieldPosition& fieldPosition, - UErrorCode& status) const + FieldPositionHandler& handler, + UErrorCode &status) const { - return NumberFormat::format(obj, appendTo, fieldPosition, status); + if (U_FAILURE(status)) { + return appendTo; + } + + // Special case for NaN, sets the begin and end index to be the + // the string length of localized name of NaN. + if (number.isNaN()) + { + int begin = appendTo.length(); + appendTo += getConstSymbol(DecimalFormatSymbols::kNaNSymbol); + + handler.addAttribute(kIntegerField, begin, appendTo.length()); + + addPadding(appendTo, handler, 0, 0); + return appendTo; + } + + DigitList adjustedNum; + UBool isNegative; + _round(number, adjustedNum, isNegative, status); + if (U_FAILURE(status)) { + return appendTo; + } + + // Special case for INFINITE, + if (adjustedNum.isInfinite()) { + int32_t prefixLen = appendAffix(appendTo, adjustedNum.getDouble(), handler, isNegative, TRUE); + + int begin = appendTo.length(); + appendTo += getConstSymbol(DecimalFormatSymbols::kInfinitySymbol); + + handler.addAttribute(kIntegerField, begin, appendTo.length()); + + int32_t suffixLen = appendAffix(appendTo, adjustedNum.getDouble(), handler, isNegative, FALSE); + + addPadding(appendTo, handler, prefixLen, suffixLen); + return appendTo; + } + return subformat(appendTo, handler, adjustedNum, FALSE, status); } /** @@ -823,61 +1801,58 @@ UBool DecimalFormat::isGroupingPosition(int32_t pos) const { //------------------------------------------------------------------------------ /** - * Complete the formatting of a finite number. On entry, the fDigitList must + * Complete the formatting of a finite number. On entry, the DigitList must * be filled in with the correct digits. */ UnicodeString& DecimalFormat::subformat(UnicodeString& appendTo, - FieldPosition& fieldPosition, + FieldPositionHandler& handler, DigitList& digits, - UBool isInteger) const + UBool isInteger, + UErrorCode& status) const { - // Gets the localized zero Unicode character. - UChar32 zero = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); - int32_t zeroDelta = zero - '0'; // '0' is the DigitList representation of zero + // char zero = '0'; + // DigitList returns digits as '0' thru '9', so we will need to + // always need to subtract the character 0 to get the numeric value to use for indexing. + + UChar32 localizedDigits[10]; + localizedDigits[0] = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); + localizedDigits[1] = getConstSymbol(DecimalFormatSymbols::kOneDigitSymbol).char32At(0); + localizedDigits[2] = getConstSymbol(DecimalFormatSymbols::kTwoDigitSymbol).char32At(0); + localizedDigits[3] = getConstSymbol(DecimalFormatSymbols::kThreeDigitSymbol).char32At(0); + localizedDigits[4] = getConstSymbol(DecimalFormatSymbols::kFourDigitSymbol).char32At(0); + localizedDigits[5] = getConstSymbol(DecimalFormatSymbols::kFiveDigitSymbol).char32At(0); + localizedDigits[6] = getConstSymbol(DecimalFormatSymbols::kSixDigitSymbol).char32At(0); + localizedDigits[7] = getConstSymbol(DecimalFormatSymbols::kSevenDigitSymbol).char32At(0); + localizedDigits[8] = getConstSymbol(DecimalFormatSymbols::kEightDigitSymbol).char32At(0); + localizedDigits[9] = getConstSymbol(DecimalFormatSymbols::kNineDigitSymbol).char32At(0); + const UnicodeString *grouping ; - if(fIsCurrencyFormat) { - grouping = &getConstSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol); - }else{ + if(fCurrencySignCount == fgCurrencySignCountZero) { grouping = &getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); + }else{ + grouping = &getConstSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol); } const UnicodeString *decimal; - if(fIsCurrencyFormat) { - decimal = &getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); - } else { + if(fCurrencySignCount == fgCurrencySignCountZero) { decimal = &getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); + } else { + decimal = &getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); } UBool useSigDig = areSignificantDigitsUsed(); int32_t maxIntDig = getMaximumIntegerDigits(); int32_t minIntDig = getMinimumIntegerDigits(); - /* Per bug 4147706, DecimalFormat must respect the sign of numbers which - * format as zero. This allows sensible computations and preserves - * relations such as signum(1/x) = signum(x), where x is +Infinity or - * -Infinity. Prior to this fix, we always formatted zero values as if - * they were positive. Liu 7/6/98. - */ - if (digits.isZero()) - { - digits.fDecimalAt = digits.fCount = 0; // Normalize - } - // Appends the prefix. double doubleValue = digits.getDouble(); - int32_t prefixLen = appendAffix(appendTo, doubleValue, !digits.fIsPositive, TRUE); + int32_t prefixLen = appendAffix(appendTo, doubleValue, handler, !digits.isPositive(), TRUE); if (fUseExponentialNotation) { - // Record field information for caller. - if (fieldPosition.getField() == NumberFormat::kIntegerField) - { - fieldPosition.setBeginIndex(appendTo.length()); - fieldPosition.setEndIndex(-1); - } - else if (fieldPosition.getField() == NumberFormat::kFractionField) - { - fieldPosition.setBeginIndex(-1); - } + int currentLength = appendTo.length(); + int intBegin = currentLength; + int intEnd = -1; + int fracBegin = -1; int32_t minFracDig = 0; if (useSigDig) { @@ -907,7 +1882,8 @@ DecimalFormat::subformat(UnicodeString& appendTo, // If maximum integer digits are defined and are larger than // minimum integer digits, then minimum integer digits are // ignored. - int32_t exponent = digits.fDecimalAt; + digits.reduce(); // Removes trailing zero digits. + int32_t exponent = digits.getDecimalAt(); if (maxIntDig > 1 && maxIntDig != minIntDig) { // A exponent increment is defined; adjust to it. exponent = (exponent > 0) ? (exponent - 1) / maxIntDig @@ -928,8 +1904,8 @@ DecimalFormat::subformat(UnicodeString& appendTo, // The number of integer digits is handled specially if the number // is zero, since then there may be no digits. int32_t integerDigits = digits.isZero() ? minIntDig : - digits.fDecimalAt - exponent; - int32_t totalDigits = digits.fCount; + digits.getDecimalAt() - exponent; + int32_t totalDigits = digits.getCount(); if (minimumDigits > totalDigits) totalDigits = minimumDigits; if (integerDigits > totalDigits) @@ -941,34 +1917,28 @@ DecimalFormat::subformat(UnicodeString& appendTo, { if (i == integerDigits) { - // Record field information for caller. - if (fieldPosition.getField() == NumberFormat::kIntegerField) - fieldPosition.setEndIndex(appendTo.length()); + intEnd = appendTo.length(); + handler.addAttribute(kIntegerField, intBegin, intEnd); appendTo += *decimal; - // Record field information for caller. - if (fieldPosition.getField() == NumberFormat::kFractionField) - fieldPosition.setBeginIndex(appendTo.length()); + fracBegin = appendTo.length(); + handler.addAttribute(kDecimalSeparatorField, fracBegin - 1, fracBegin); } // Restores the digit character or pads the buffer with zeros. - UChar32 c = (UChar32)((i < digits.fCount) ? - (digits.fDigits[i] + zeroDelta) : - zero); + UChar32 c = (UChar32)((i < digits.getCount()) ? + localizedDigits[digits.getDigitValue(i)] : + localizedDigits[0]); appendTo += c; } - // Record field information - if (fieldPosition.getField() == NumberFormat::kIntegerField) - { - if (fieldPosition.getEndIndex() < 0) - fieldPosition.setEndIndex(appendTo.length()); + currentLength = appendTo.length(); + + if (intEnd < 0) { + handler.addAttribute(kIntegerField, intBegin, currentLength); } - else if (fieldPosition.getField() == NumberFormat::kFractionField) - { - if (fieldPosition.getBeginIndex() < 0) - fieldPosition.setBeginIndex(appendTo.length()); - fieldPosition.setEndIndex(appendTo.length()); + if (fracBegin > 0) { + handler.addAttribute(kFractionField, fracBegin, currentLength); } // The exponent is output using the pattern-specified minimum @@ -977,6 +1947,9 @@ DecimalFormat::subformat(UnicodeString& appendTo, // unacceptable inaccuracy. appendTo += getConstSymbol(DecimalFormatSymbols::kExponentialSymbol); + handler.addAttribute(kExponentSymbolField, currentLength, appendTo.length()); + currentLength = appendTo.length(); + // For zero values, we force the exponent to zero. We // must do this here, and not earlier, because the value // is used to determine integer digit count above. @@ -985,10 +1958,14 @@ DecimalFormat::subformat(UnicodeString& appendTo, if (exponent < 0) { appendTo += getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); + handler.addAttribute(kExponentSignField, currentLength, appendTo.length()); } else if (fExponentSignAlwaysShown) { appendTo += getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); + handler.addAttribute(kExponentSignField, currentLength, appendTo.length()); } + currentLength = appendTo.length(); + DigitList expDigits; expDigits.set(exponent); { @@ -996,21 +1973,23 @@ DecimalFormat::subformat(UnicodeString& appendTo, if (fUseExponentialNotation && expDig < 1) { expDig = 1; } - for (i=expDigits.fDecimalAt; i 0 && count < digits.fDecimalAt) { - count = digits.fDecimalAt; + _max(1, digits.getDecimalAt()) : minIntDig; + if (digits.getDecimalAt() > 0 && count < digits.getDecimalAt()) { + count = digits.getDecimalAt(); } // Handle the case where getMaximumIntegerDigits() is smaller @@ -1038,7 +2017,10 @@ DecimalFormat::subformat(UnicodeString& appendTo, int32_t digitIndex = 0; // Index into digitList.fDigits[] if (count > maxIntDig && maxIntDig >= 0) { count = maxIntDig; - digitIndex = digits.fDecimalAt - count; + digitIndex = digits.getDecimalAt() - count; + if(fBoolFlags.contains(UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS)) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } } int32_t sizeBeforeIntegerPart = appendTo.length(); @@ -1046,16 +2028,16 @@ DecimalFormat::subformat(UnicodeString& appendTo, int32_t i; for (i=count-1; i>=0; --i) { - if (i < digits.fDecimalAt && digitIndex < digits.fCount && + if (i < digits.getDecimalAt() && digitIndex < digits.getCount() && sigCount < maxSigDig) { // Output a real digit - appendTo += ((UChar32)(digits.fDigits[digitIndex++] + zeroDelta)); + appendTo += (UChar32)localizedDigits[digits.getDigitValue(digitIndex++)]; ++sigCount; } else { // Output a zero (leading or trailing) - appendTo += (zero); + appendTo += localizedDigits[0]; if (sigCount > 0) { ++sigCount; } @@ -1063,36 +2045,52 @@ DecimalFormat::subformat(UnicodeString& appendTo, // Output grouping separator if necessary. if (isGroupingPosition(i)) { + currentLength = appendTo.length(); appendTo.append(*grouping); + handler.addAttribute(kGroupingSeparatorField, currentLength, appendTo.length()); } } + // This handles the special case of formatting 0. For zero only, we count the + // zero to the left of the decimal point as one signficant digit. Ordinarily we + // do not count any leading 0's as significant. If the number we are formatting + // is not zero, then either sigCount or digits.getCount() will be non-zero. + if (sigCount == 0 && digits.getCount() == 0) { + sigCount = 1; + } + + // TODO(dlf): this looks like it was a bug, we marked the int field as ending + // before the zero was generated. // Record field information for caller. - if (fieldPosition.getField() == NumberFormat::kIntegerField) - fieldPosition.setEndIndex(appendTo.length()); + // if (fieldPosition.getField() == NumberFormat::kIntegerField) + // fieldPosition.setEndIndex(appendTo.length()); // Determine whether or not there are any printable fractional // digits. If we've used up the digits we know there aren't. - UBool fractionPresent = (!isInteger && digitIndex < digits.fCount) || + UBool fractionPresent = (!isInteger && digitIndex < digits.getCount()) || (useSigDig ? (sigCount < minSigDig) : (getMinimumFractionDigits() > 0)); // If there is no fraction present, and we haven't printed any // integer digits, then print a zero. Otherwise we won't print // _any_ digits, and we won't be able to parse this string. if (!fractionPresent && appendTo.length() == sizeBeforeIntegerPart) - appendTo += (zero); + appendTo += localizedDigits[0]; + + currentLength = appendTo.length(); + handler.addAttribute(kIntegerField, intBegin, currentLength); // Output the decimal separator if we always do so. - if (fDecimalSeparatorAlwaysShown || fractionPresent) + if (fDecimalSeparatorAlwaysShown || fractionPresent) { appendTo += *decimal; + handler.addAttribute(kDecimalSeparatorField, currentLength, appendTo.length()); + currentLength = appendTo.length(); + } - // Record field information for caller. - if (fieldPosition.getField() == NumberFormat::kFractionField) - fieldPosition.setBeginIndex(appendTo.length()); + int fracBegin = currentLength; count = useSigDig ? INT32_MAX : getMaximumFractionDigits(); if (useSigDig && (sigCount == maxSigDig || - (sigCount >= minSigDig && digitIndex == digits.fCount))) { + (sigCount >= minSigDig && digitIndex == digits.getCount()))) { count = 0; } @@ -1105,7 +2103,7 @@ DecimalFormat::subformat(UnicodeString& appendTo, // fractional stuff to display, or we're out of // significant digits. if (!useSigDig && i >= getMinimumFractionDigits() && - (isInteger || digitIndex >= digits.fCount)) { + (isInteger || digitIndex >= digits.getCount())) { break; } @@ -1113,17 +2111,17 @@ DecimalFormat::subformat(UnicodeString& appendTo, // that come after the decimal but before any // significant digits. These are only output if // abs(number being formatted) < 1.0. - if (-1-i > (digits.fDecimalAt-1)) { - appendTo += zero; + if (-1-i > (digits.getDecimalAt()-1)) { + appendTo += localizedDigits[0]; continue; } // Output a digit, if we have any precision left, or a // zero if we don't. We don't want to output noise digits. - if (!isInteger && digitIndex < digits.fCount) { - appendTo += ((UChar32)(digits.fDigits[digitIndex++] + zeroDelta)); + if (!isInteger && digitIndex < digits.getCount()) { + appendTo += (UChar32)localizedDigits[digits.getDigitValue(digitIndex++)]; } else { - appendTo += zero; + appendTo += localizedDigits[0]; } // If we reach the maximum number of significant @@ -1132,19 +2130,17 @@ DecimalFormat::subformat(UnicodeString& appendTo, ++sigCount; if (useSigDig && (sigCount == maxSigDig || - (digitIndex == digits.fCount && sigCount >= minSigDig))) { + (digitIndex == digits.getCount() && sigCount >= minSigDig))) { break; } } - // Record field information for caller. - if (fieldPosition.getField() == NumberFormat::kFractionField) - fieldPosition.setEndIndex(appendTo.length()); + handler.addAttribute(kFractionField, fracBegin, appendTo.length()); } - int32_t suffixLen = appendAffix(appendTo, doubleValue, !digits.fIsPositive, FALSE); + int32_t suffixLen = appendAffix(appendTo, doubleValue, handler, !digits.isPositive(), FALSE); - addPadding(appendTo, fieldPosition, prefixLen, suffixLen); + addPadding(appendTo, handler, prefixLen, suffixLen); return appendTo; } @@ -1153,7 +2149,7 @@ DecimalFormat::subformat(UnicodeString& appendTo, * @param result the string to be padded */ void DecimalFormat::addPadding(UnicodeString& appendTo, - FieldPosition& fieldPosition, + FieldPositionHandler& handler, int32_t prefixLen, int32_t suffixLen) const { @@ -1178,71 +2174,83 @@ void DecimalFormat::addPadding(UnicodeString& appendTo, appendTo += padding; break; } - if (fPadPosition == kPadBeforePrefix || - fPadPosition == kPadAfterPrefix) { - fieldPosition.setBeginIndex(len + fieldPosition.getBeginIndex()); - fieldPosition.setEndIndex(len + fieldPosition.getEndIndex()); + if (fPadPosition == kPadBeforePrefix || fPadPosition == kPadAfterPrefix) { + handler.shiftLast(len); } } } } //------------------------------------------------------------------------------ - -void -DecimalFormat::parse(const UnicodeString& text, - Formattable& result, - UErrorCode& status) const -{ - NumberFormat::parse(text, result, status); -} void DecimalFormat::parse(const UnicodeString& text, Formattable& result, ParsePosition& parsePosition) const { - parse(text, result, parsePosition, FALSE); + parse(text, result, parsePosition, NULL); } -Formattable& DecimalFormat::parseCurrency(const UnicodeString& text, - Formattable& result, - ParsePosition& pos) const { - parse(text, result, pos, TRUE); - return result; +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 either a number or a currency amount. + * Parses the given text as a number, optionally providing a currency amount. * @param text the string to parse - * @param result output parameter for the result + * @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 parseCurrency if true, a currency amount is parsed; - * otherwise a Number is parsed + * @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, - UBool parseCurrency) const { - int32_t backup; - int32_t i = backup = parsePosition.getIndex(); + 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: - + // Skip padding characters, if around prefix if (fFormatWidth > 0 && (fPadPosition == kPadBeforePrefix || fPadPosition == kPadAfterPrefix)) { i = skipPadding(text, i); } - - if (! isParseStrict()) { - // skip any leading whitespace - i = backup = skipUWhiteSpace(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 = &getConstSymbol(DecimalFormatSymbols::kNaNSymbol); int32_t nanLen = (text.compare(i, nan->length(), *nan) @@ -1257,90 +2265,216 @@ void DecimalFormat::parse(const UnicodeString& text, 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]; - UChar curbuf[4]; - UChar* currency = parseCurrency ? curbuf : NULL; - DigitList digits; - if (!subparse(text, parsePosition, digits, status, currency)) { - parsePosition.setIndex(backup); - return; + DigitList *digits = result.getInternalDigitList(); // get one from the stack buffer + if (digits == NULL) { + return; // no way to report error from here. + } + + if (fCurrencySignCount != fgCurrencySignCountZero) { + if (!parseForCurrency(text, parsePosition, *digits, + status, currency)) { + return; + } + } else { + if (!subparse(text, + fNegPrefixPattern, fNegSuffixPattern, + fPosPrefixPattern, fPosSuffixPattern, + 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.fIsPositive ? inf : -inf); + result.setDouble(digits->isPositive() ? inf : -inf); + // TODO: set the dl to infinity, and let it fall into the code below. } else { - // Do as much of the multiplier conversion as possible without - // losing accuracy. - int32_t mult = fMultiplier; // Don't modify this.multiplier - while (mult % 10 == 0) { - mult /= 10; - --digits.fDecimalAt; + + if (fMultiplier != NULL) { + UErrorCode ec = U_ZERO_ERROR; + digits->div(*fMultiplier, ec); } - // Handle integral values. We want to return the most - // parsimonious type that will accommodate all of the result's - // precision. We therefore only return a long if the result fits - // entirely within a long (taking into account the multiplier) -- - // otherwise we fall through and return a double. When more - // numeric types are supported by Formattable (e.g., 64-bit - // integers, bignums) we will extend this logic to include them. - if (digits.fitsIntoLong(isParseIntegerOnly())) { - int32_t n = digits.getLong(); - if (n % mult == 0) { - result.setLong(n / mult); - } - else { // else handle the remainder - result.setDouble(((double)n) / mult); + if (fScale != 0) { + DigitList ten; + ten.set((int32_t)10); + if (fScale > 0) { + for (int32_t i = fScale; i > 0; i--) { + UErrorCode ec = U_ZERO_ERROR; + digits->div(ten,ec); + } + } else { + for (int32_t i = fScale; i < 0; i++) { + UErrorCode ec = U_ZERO_ERROR; + digits->mult(ten,ec); + } } } - else if (digits.fitsIntoInt64(isParseIntegerOnly())) { - int64_t n = digits.getInt64(); - if (n % mult == 0) { - result.setInt64(n / mult); + + // 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 { + 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, + fNegPrefixPattern, fNegSuffixPattern, + fPosPrefixPattern, fPosSuffixPattern, + TRUE, UCURR_LONG_NAME, + tmpPos, tmpDigitList, tmpStatus, currency); + } else { + found = subparse(text, + fNegPrefixPattern, fNegSuffixPattern, + fPosPrefixPattern, fPosSuffixPattern, + 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]; } - else { // else handle the remainder - result.setDouble(((double)n) / mult); + 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; } - else { - // Handle non-integral or very large values - // Dividing by one is okay and not that costly. - result.setDouble(digits.getDouble() / mult); + } + // 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, + &fNegativePrefix, &fNegativeSuffix, + &fPositivePrefix, &fPositiveSuffix, + 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 (parseCurrency) { - UErrorCode ec = U_ZERO_ERROR; - Formattable n(result); - result.adoptObject(new CurrencyAmount(n, curbuf, ec)); - U_ASSERT(U_SUCCESS(ec)); // should always succeed + if (!found) { + //parsePosition.setIndex(origPos); + parsePosition.setErrorIndex(maxErrorPos); + } else { + parsePosition.setIndex(maxPosIndex); + parsePosition.setErrorIndex(-1); } + return found; } -/* -This is an old implimentation that was preparing for 64-bit numbers in ICU. -It is very slow, and 64-bit numbers are not ANSI-C compatible. This code -is here if we change our minds. - -^^^ what is this referring to? remove? ^^^ [alan] -*/ - /** * 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. @@ -1351,55 +2485,213 @@ is here if we change our minds. * currency parsing mode, any currency is parsed, not just the * currency that this formatter is set to. */ -UBool DecimalFormat::subparse(const UnicodeString& text, ParsePosition& parsePosition, +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; - UBool strictParse = isParseStrict(); + int32_t textLength = text.length(); // One less pointer to follow + UBool strictParse = !isLenient(); + UChar32 zero = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); + const UnicodeString *groupingString = &getConstSymbol(fCurrencySignCount == fgCurrencySignCountZero ? + 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); + printf("currencyParsing=%d, fFormatWidth=%d, isParseIntegerOnly=%c text.length=%d negPrefLen=%d\n", currencyParsing, fFormatWidth, (isParseIntegerOnly())?'Y':'N', text.length(), negPrefix!=NULL?negPrefix->length():-1); +#endif + + UBool fastParseOk = false; /* TRUE iff fast parse is OK */ + // UBool fastParseHadDecimal = FALSE; /* true if fast parse saw a decimal point. */ + const DecimalFormatInternal &data = internalData(fReserved); + if((data.fFastParseStatus==kFastpathYES) && + fCurrencySignCount == fgCurrencySignCountZero && + // (negPrefix!=NULL&&negPrefix->isEmpty()) || + 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 = &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 +#ifdef FMT_DEBUG + printf("PP -> %d, good = [%s] digitcount=%d, fGroupingSize=%d fGroupingSize2=%d!\n", j, parsedNum.data(), digitCount, fGroupingSize, fGroupingSize2); +#endif + 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("fFormatWidth=%d ", fFormatWidth); + printf("text.length()=%d ", text.length()); + printf("posPrefix=%p posSuffix=%p ", posPrefix, posSuffix); + + printf("\n"); +#endif + } + if(!fastParseOk +#if UCONFIG_HAVE_PARSEALLINPUT + && fParseAllInput!=UNUM_YES +#endif + ) + { // Match padding before prefix if (fFormatWidth > 0 && fPadPosition == kPadBeforePrefix) { position = skipPadding(text, position); } // Match positive and negative prefixes; prefer longest match. - int32_t posMatch = compareAffix(text, position, FALSE, TRUE, currency); - int32_t negMatch = compareAffix(text, position, TRUE, TRUE, currency); + 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; - } else if (strictParse) { + 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 (fFormatWidth > 0 && fPadPosition == kPadAfterPrefix) { position = skipPadding(text, position); } - + if (! strictParse) { - position = skipUWhiteSpace(text, position); + position = skipUWhiteSpace(text, position); } - + // process digits or Inf, find decimal position const UnicodeString *inf = &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) - { + + 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 @@ -1407,81 +2699,54 @@ UBool DecimalFormat::subparse(const UnicodeString& text, ParsePosition& parsePos // put only significant digits into the DigitList, and adjust the // exponent as needed. - digits.fDecimalAt = digits.fCount = 0; - UChar32 zero = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); -#if CHECK_FOR_LEADING_ZERO | CHECK_FOR_MISPLACED_GROUPING UBool strictFail = FALSE; // did we exit with a strict parse failure? -#endif - -#if CHECK_FOR_MISPLACED_GROUPING int32_t lastGroup = -1; // where did we last see a grouping separator? int32_t digitStart = position; int32_t gs2 = fGroupingSize2 == 0 ? fGroupingSize : fGroupingSize2; -#endif - -#if CHECK_FOR_LEADING_ZERO - // Strict parsing leading zeroes. If a leading zero would - // be forced by the pattern, then don't fail strict parsing. - UBool strictLeadingZero = FALSE; - int32_t leadingZeroPos = 0; - int32_t leadingZeroCount = 0; -#endif - + const UnicodeString *decimalString; - if (fIsCurrencyFormat) { - decimalString = &getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); + if (fCurrencySignCount != fgCurrencySignCountZero) { + decimalString = &getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); } else { - decimalString = &getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); + decimalString = &getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); } UChar32 decimalChar = decimalString->char32At(0); - - const UnicodeString *groupingString = &getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); - UChar32 groupingChar = groupingString->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; - int32_t textLength = text.length(); // One less pointer to follow - int32_t decimalStringLength = decimalString->length(); - int32_t decimalCharLength = U16_LENGTH(decimalChar); - int32_t groupingStringLength = groupingString->length(); - int32_t groupingCharLength = U16_LENGTH(groupingChar); - + // equivalent grouping and decimal support - // TODO markdavis Cache these if it makes a difference in performance. - UnicodeSet decimalFallback; - UnicodeSet *decimalSet = NULL; - UnicodeSet *groupingSet = NULL; - - if (! strictParse) { - if (decimalCharLength == decimalStringLength) { - decimalSet = (UnicodeSet *) DecimalFormatStaticSets::getSimilarDecimals(decimalChar, strictParse, &decimalFallback)->cloneAsThawed(); - } - - if (groupingCharLength == groupingStringLength) { - if (strictParse) { - groupingSet = (UnicodeSet *) DecimalFormatStaticSets::gStaticSets->fStrictDefaultGroupingSeparators->cloneAsThawed(); - } else { - groupingSet = (UnicodeSet *) DecimalFormatStaticSets::gStaticSets->fDefaultGroupingSeparators->cloneAsThawed(); - } - - groupingSet->add(groupingChar); - - if (decimalSet != NULL) { - groupingSet->removeAll(*decimalSet); - } + 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 are guaranteed that - // decimalSet contains the decimal, and - // groupingSet contains the groupingSeparator - // (unless decimal and grouping are the same, which should never happen. But in that case, groupingSet will just be empty.) } - + + // 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; ) { @@ -1495,7 +2760,7 @@ UBool DecimalFormat::subparse(const UnicodeString& text, ParsePosition& parsePos * 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 + * calling Character.digit(). If this also fails, digit will * have a value outside the range 0..9. */ digit = ch - zero; @@ -1503,38 +2768,24 @@ UBool DecimalFormat::subparse(const UnicodeString& text, ParsePosition& parsePos { digit = u_charDigitValue(ch); } - - if (digit > 0 && digit <= 9) // digit == 0 handled below - { -#if CHECK_FOR_MISPLACED_GROUPING - if (strictParse) { - if (backup != -1) { - if ((lastGroup != -1 && backup - lastGroup - 1 != gs2) || - (lastGroup == -1 && position - digitStart - 1 > gs2)) { - strictFail = TRUE; + + // 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; + // Already check above (digit = ch - zero) for ch==zero; the only check we need to do here is: + // if \u3007 is treated as 0 for parsing, \u96F6 should be too. Otherwise check for nonzero digits. + if ( zero!=0x3007 || ch!=0x96F6 ) { + for (digit = 1 ; digit < 10 ; digit++ ) { + if ( getConstSymbol((DecimalFormatSymbols::ENumberFormatSymbol)(DecimalFormatSymbols::kOneDigitSymbol+digit-1)).char32At(0) == ch ) { break; } - - lastGroup = backup; } } -#endif - - - // Cancel out backup setting (see grouping handler below) - backup = -1; - - sawDigit = TRUE; - // output a regular non-zero digit. - ++digitCount; - digits.append((char)(digit + '0')); - position += U16_LENGTH(ch); } - else if (digit == 0) - { - // Cancel out backup setting (see grouping handler below) -#if CHECK_FOR_MISPLACED_GROUPING + 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 @@ -1546,70 +2797,65 @@ UBool DecimalFormat::subparse(const UnicodeString& text, ParsePosition& parsePos strictFail = TRUE; break; } - + lastGroup = backup; } -#endif - + + // Cancel out backup setting (see grouping handler below) backup = -1; sawDigit = TRUE; - - // handle leading zeros - if (digits.fCount != 0) { - digitCount += 1; - digits.append((char) (digit + '0')); - } else if (sawDecimal) { - // If we have seen the decimal, but no significant digits yet, - // then we account for leading zeros by decrementing - // digits.decimalAt into negative values. - digits.fDecimalAt -= 1; - -#if CHECK_FOR_LEADING_ZERO - } else { - // TODO: Not sure we need to check fUseExponentialNotation - if (strictParse && !fUseExponentialNotation) { - if (!strictLeadingZero) { - leadingZeroPos = position + U16_LENGTH(ch); - strictLeadingZero = TRUE; - } - - leadingZeroCount += 1; - } -#endif + + // 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 (matchSymbol(text, position, groupingStringLength, *groupingString, groupingSet, ch) && isGroupingUsed()) + else if (groupingStringLength > 0 && + matchGrouping(groupingChar, sawGrouping, sawGroupingChar, groupingSet, + decimalChar, decimalSet, + ch) && groupingUsed) { -#if CHECK_FOR_MISPLACED_GROUPING if (sawDecimal) { break; } - + if (strictParse) { - if ((!sawDigit || backup != -1)) { - // leading group, or two group separators in a row + if ( (!sawDigit && groupingSet!=NULL && u_isWhitespace(ch)) || backup != -1 ) { + // We differ from the ICU4J code by allowing a leading group sep in strict mode (for + // backward compatibility) as long as it is not one of the breaking whitespace characters + // that is only treated as a group separator because of the equivalence set. If we get + // here it is because the leading sep was such a breaking space, or there were multiple + // group separators in a row. Note that the DecimalFormat documentation says + // "During parsing, grouping separators are ignored" and that was for strict parsing, + // so we may need to further revisit this strictParse restriction to ensure compatibility. + // Also note: u_isWhitespace is true for all Zs/Zl/Zp except the no-break ones: 00A0,2007,202F. + // In CLDR, all locales that have space as a group separator use 00A0 (NBSP). strictFail = TRUE; break; } } -#endif - + // Ignore grouping characters, if we are using them, but require // that they be followed by a digit. Otherwise we backup and // reprocess them. backup = position; position += groupingStringLength; - - if (groupingSet != NULL) { - // Once we see a grouping character, we only accept that grouping character from then on. - groupingSet->set(ch, ch); - } + sawGrouping=TRUE; + // Once we see a grouping character, we only accept that grouping character from then on. + sawGroupingChar=ch; } - else if (matchSymbol(text, position, decimalStringLength, *decimalString, decimalSet, ch) && !isParseIntegerOnly() && !sawDecimal) + else if (matchDecimal(decimalChar,sawDecimal,sawDecimalChar, decimalSet, ch)) { -#if CHECK_FOR_MISPLACED_GROUPING if (strictParse) { if (backup != -1 || (lastGroup != -1 && position - lastGroup != fGroupingSize + 1)) { @@ -1617,132 +2863,128 @@ UBool DecimalFormat::subparse(const UnicodeString& text, ParsePosition& parsePos break; } } -#endif - - // If we're only parsing integers, or if we ALREADY saw the + + // If we're only parsing integers, or if we ALREADY saw the // decimal, then don't parse this one. if (isParseIntegerOnly() || sawDecimal) { - break; + break; } - - digits.fDecimalAt = digitCount; // Not digits.fCount! + + parsedNum.append('.', err); position += decimalStringLength; sawDecimal = TRUE; - - if (decimalSet != NULL) { - // Once we see a decimal character, we only accept that decimal character from then on. - decimalSet->set(ch, ch); - } + // 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 { - const UnicodeString *tmp; - tmp = &getConstSymbol(DecimalFormatSymbols::kExponentialSymbol); - 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(); - DigitList exponentDigits; - if (pos < textLength) + 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 = &getConstSymbol(DecimalFormatSymbols::kExponentialSymbol); + // TODO: CASE + if (!text.caseCompare(position, tmp->length(), *tmp, U_FOLD_CASE_DEFAULT)) // error code is set below if !sawDigit { - tmp = &getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); - if (!text.compare(pos, tmp->length(), *tmp)) + // Parse sign, if present + int32_t pos = position + tmp->length(); + char exponentSign = '+'; + + if (pos < textLength) { - pos += tmp->length(); - } - else { - tmp = &getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); + tmp = &getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); if (!text.compare(pos, tmp->length(), *tmp)) { pos += tmp->length(); - exponentDigits.fIsPositive = FALSE; + } + else { + tmp = &getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); + if (!text.compare(pos, tmp->length(), *tmp)) + { + exponentSign = '-'; + pos += tmp->length(); + } } } - } - while (pos < textLength) { - ch = text[(int32_t)pos]; - digit = ch - zero; + UBool sawExponentDigit = FALSE; + while (pos < textLength) { + ch = text[(int32_t)pos]; + digit = ch - zero; - if (digit < 0 || digit > 9) { - digit = u_charDigitValue(ch); + 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; + parsedNum.append((char)(digit + '0'), err); + } else { + break; + } } - if (0 <= digit && digit <= 9) { - ++pos; - exponentDigits.append((char)(digit + '0')); - } else { - break; + + if (sawExponentDigit) { + position = pos; // Advance past the exponent } - } - if (exponentDigits.fCount > 0) { - exponentDigits.fDecimalAt = exponentDigits.fCount; - digits.fDecimalAt += exponentDigits.getLong(); - position = pos; // Advance past the exponent + break; // Whether we fail or succeed, we exit this loop + } else { + break; } - - break; // Whether we fail or succeed, we exit this loop - } - else { + } else { // not parsing exponent break; - } + } } } - - if (! strictParse) { - delete groupingSet; - delete decimalSet; - } - if (backup != -1) + // if we didn't see a decimal and it is required, check to see if the pattern had one + if(!sawDecimal && isDecimalPatternMatchRequired()) { - position = backup; - } - -#if CHECK_FOR_LEADING_ZERO - // check for strict parse errors - if (strictParse && strictLeadingZero) { - if ((leadingZeroCount + digits.fDecimalAt) > getMinimumIntegerDigits()) { + if(fFormatPattern.indexOf(DecimalFormatSymbols::kDecimalSeparatorSymbol) != 0) + { parsePosition.setIndex(oldStart); - parsePosition.setErrorIndex(leadingZeroPos); + parsePosition.setErrorIndex(position); + debug("decimal point match required fail!"); return FALSE; } } -#endif -#if CHECK_FOR_MISPLACED_GROUPING + if (backup != -1) + { + position = backup; + } + if (strictParse && !sawDecimal) { if (lastGroup != -1 && position - lastGroup != fGroupingSize + 1) { strictFail = TRUE; } } -#endif - -#if CHECK_FOR_LEADING_ZERO | CHECK_FOR_MISPLACED_GROUPING if (strictFail) { - // only set with strictParse and a leading zero error - // leading zeros are an error with strict parsing except - // immediately before nondigit (except group separator - // followed by digit), or end of text. - + // only set with strictParse and a grouping separator error + parsePosition.setIndex(oldStart); parsePosition.setErrorIndex(position); + debug("strictFail!"); return FALSE; } -#endif - + // If there was no decimal point we have an integer - if (!sawDecimal) - { - digits.fDecimalAt += digitCount; // Not digits.fCount! - } // 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; @@ -1755,29 +2997,30 @@ UBool DecimalFormat::subparse(const UnicodeString& text, ParsePosition& parsePos } 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, currency); + posSuffixMatch = compareAffix(text, position, FALSE, FALSE, posSuffix, complexCurrencyParsing, type, currency); } if (negMatch >= 0) { - negSuffixMatch = compareAffix(text, position, TRUE, FALSE, currency); + 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)); + position += (posSuffixMatch >= 0 ? posSuffixMatch : (negSuffixMatch >= 0 ? negSuffixMatch : 0)); // Match padding before suffix if (fFormatWidth > 0 && fPadPosition == kPadAfterSuffix) { @@ -1786,13 +3029,58 @@ UBool DecimalFormat::subparse(const UnicodeString& text, ParsePosition& parsePos parsePosition.setIndex(position); - digits.fIsPositive = (posSuffixMatch >= 0 || (!strictParse && negMatch < 0 && negSuffixMatch < 0)); - - if(parsePosition.getIndex() == oldStart) + 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(fFormatPattern.indexOf(DecimalFormatSymbols::kDecimalSeparatorSymbol) != 0) + { + parsePosition.setIndex(oldStart); + parsePosition.setErrorIndex(position); + debug("decimal point match required fail!"); + return FALSE; + } + } + + return TRUE; } @@ -1819,6 +3107,9 @@ int32_t DecimalFormat::skipPadding(const UnicodeString& text, int32_t position) * @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 @@ -1829,32 +3120,20 @@ 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 (fCurrencyChoice != NULL || currency != NULL) { - if (isNegative) { - if (isPrefix) { - patternToCompare = fNegPrefixPattern; - } - else { - patternToCompare = fNegSuffixPattern; - } - } - else { - if (isPrefix) { - patternToCompare = fPosPrefixPattern; - } - else { - patternToCompare = fPosSuffixPattern; - } - } - if (patternToCompare != NULL) { - return compareComplexAffix(*patternToCompare, text, pos, currency); + if (fCurrencyChoice != NULL || currency != NULL || + (fCurrencySignCount != fgCurrencySignCountZero && complexCurrencyParsing)) { + + if (affixPat != NULL) { + return compareComplexAffix(*affixPat, text, pos, type, currency); } - /* else the caller modified the pattern. Fallback to normal behavior. */ } - + if (isNegative) { if (isPrefix) { patternToCompare = &fNegativePrefix; @@ -1871,7 +3150,41 @@ int32_t DecimalFormat::compareAffix(const UnicodeString& text, patternToCompare = &fPositiveSuffix; } } - return compareSimpleAffix(*patternToCompare, text, pos, isParseStrict()); + 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); } /** @@ -1887,106 +3200,169 @@ int32_t DecimalFormat::compareAffix(const UnicodeString& text, int32_t DecimalFormat::compareSimpleAffix(const UnicodeString& affix, const UnicodeString& input, int32_t pos, - UBool strict) { + UBool lenient) const { int32_t start = pos; - - if (strict) { - for (int32_t i=0; i= affixLength || pos >= inputLength) { - break; - } - - UChar32 c = affix.char32At(i); - int32_t len = U16_LENGTH(c); - - if (input.char32At(pos) != c) { - //return -1; - break; - } - - match = TRUE; - i += len; - pos += len; - } - - if (affixLength > 0 && ! match) { - return -1; - } - } - + 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 isRuleWhiteSpace() characters at + * Skip over a run of zero or more Pattern_White_Space characters at * pos in text. */ -int32_t DecimalFormat::skipRuleWhiteSpace(const UnicodeString& text, int32_t pos) { +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 (!uprv_isRuleWhiteSpace(c)) { + if (!u_isUWhiteSpace(c)) { break; } pos += U16_LENGTH(c); @@ -1995,16 +3371,30 @@ int32_t DecimalFormat::skipRuleWhiteSpace(const UnicodeString& text, int32_t pos } /** - * Skip over a run of zero or more isUWhiteSpace() characters at pos + * Skip over a run of zero or more isUWhiteSpace() characters or bidi marks at pos * in text. */ -int32_t DecimalFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) { +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()) { - UChar32 c = text.char32At(pos); - if (!u_isUWhiteSpace(c)) { + UChar c = text.charAt(pos); + if (!IS_BIDI_MARK(c)) { break; } - pos += U16_LENGTH(c); + pos++; } return pos; } @@ -2014,6 +3404,7 @@ int32_t DecimalFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) { * @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 @@ -2023,13 +3414,16 @@ int32_t DecimalFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) { 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 || - (fCurrencyChoice != NULL && *getCurrency() != 0)); + (fCurrencyChoice != NULL && *getCurrency() != 0) || + fCurrencySignCount != fgCurrencySignCountZero); - for (int32_t i=0; i= 0; ) { + for (int32_t i=0; + i= 0; ) { UChar32 c = affixPat.char32At(i); i += U16_LENGTH(c); @@ -2042,48 +3436,51 @@ int32_t DecimalFormat::compareComplexAffix(const UnicodeString& affixPat, switch (c) { case kCurrencySign: { - // If currency != null, then perform generic currency matching. - // Otherwise, do currency choice parsing. + // 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(); - ec = U_ZERO_ERROR; - } - // Delegate parse of display name => ISO code to Currency - ParsePosition ppos(pos); - UChar curr[4]; - uprv_parseCurrency(loc, text, ppos, curr, ec); - - // If parse succeeds, populate currency[0] - if (U_SUCCESS(ec) && ppos.getIndex() != pos) { + // Try to parse display name for our locale; first + // determine our locale. + const char* loc = fCurrencyPluralInfo->getLocale().getName(); + ParsePosition ppos(pos); + UChar curr[4]; + UErrorCode ec = U_ZERO_ERROR; + // Delegate parse of display name => ISO code to Currency + uprv_parseCurrency(loc, text, ppos, type, curr, ec); + + // If parse succeeds, populate currency[0] + if (U_SUCCESS(ec) && ppos.getIndex() != pos) { + if (currency) { u_strcpy(currency, curr); - pos = ppos.getIndex(); - } else if (isParseStrict()) { - pos = -1; - } - } else { - if (intl) { - ++i; - pos = match(text, pos, getCurrency()); } else { - ParsePosition ppos(pos); - Formattable result; - fCurrencyChoice->parse(text, result, ppos); - pos = (ppos.getIndex() == pos) ? -1 : ppos.getIndex(); + // 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; } @@ -2111,8 +3508,8 @@ int32_t DecimalFormat::compareComplexAffix(const UnicodeString& affixPat, } pos = match(text, pos, c); - if (uprv_isRuleWhiteSpace(c)) { - i = skipRuleWhiteSpace(affixPat, i); + if (PatternProps::isWhiteSpace(c)) { + i = skipPatternWhiteSpace(affixPat, i); } } return pos - start; @@ -2121,14 +3518,14 @@ int32_t DecimalFormat::compareComplexAffix(const UnicodeString& affixPat, /** * Match a single character at text[pos] and return the index of the * next character upon success. Return -1 on failure. If - * isRuleWhiteSpace(ch) then match a run of white space in text. + * 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 (uprv_isRuleWhiteSpace(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 = skipUWhiteSpace(text, pos); + pos = skipPatternWhiteSpace(text, pos); if (pos == s) { return -1; } @@ -2147,8 +3544,8 @@ int32_t DecimalFormat::match(const UnicodeString& text, int32_t pos, const Unico for (int32_t i=0; i= 0; ) { UChar32 ch = str.char32At(i); i += U16_LENGTH(ch); - if (uprv_isRuleWhiteSpace(ch)) { - i = skipRuleWhiteSpace(str, i); + if (PatternProps::isWhiteSpace(ch)) { + i = skipPatternWhiteSpace(str, i); } pos = match(text, pos, ch); } @@ -2156,15 +3553,48 @@ int32_t DecimalFormat::match(const UnicodeString& text, int32_t pos, const Unico } UBool DecimalFormat::matchSymbol(const UnicodeString &text, int32_t position, int32_t length, const UnicodeString &symbol, - UnicodeSet *sset, UChar32 schar) + UnicodeSet *sset, UChar32 schar) { - if (sset != NULL) { - return sset->contains(schar); - } - - return text.compare(position, length, symbol) == 0; + 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 @@ -2198,7 +3628,10 @@ DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) // If the currency symbols are the same, there is no need to recalculate. setCurrencyForSymbols(); } - expandAffixes(); + expandAffixes(NULL); +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } //------------------------------------------------------------------------------ // Setting the symbols is equlivalent to adopting a newly created localized @@ -2208,8 +3641,53 @@ void DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) { adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols)); +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif +} + + +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 (fCurrencySignCount != fgCurrencySignCountZero) { + UErrorCode status = U_ZERO_ERROR; + if (fAffixPatternsForCurrency) { + deleteHashForAffixPattern(); + } + setupCurrencyAffixPatterns(status); + if (fCurrencySignCount == fgCurrencySignCountInPluralFormat) { + // only setup the affixes of the plural pattern. + setupCurrencyAffixes(fFormatPattern, FALSE, TRUE, status); + } + } + } +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif +} + +void +DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) +{ + adoptCurrencyPluralInfo(info.clone()); +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } - + + /** * Update the currency object to match the symbols. This method * is used only when the caller has passed in a symbols object @@ -2232,43 +3710,49 @@ DecimalFormat::setCurrencyForSymbols() { UErrorCode ec = U_ZERO_ERROR; const UChar* c = NULL; const char* loc = fSymbols->getLocale().getName(); - UChar intlCurrencySymbol[4]; + UChar intlCurrencySymbol[4]; ucurr_forLocale(loc, intlCurrencySymbol, 4, &ec); UnicodeString currencySymbol; uprv_getStaticCurrencyName(intlCurrencySymbol, loc, currencySymbol, ec); if (U_SUCCESS(ec) && getConstSymbol(DecimalFormatSymbols::kCurrencySymbol) == currencySymbol - && getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol) == intlCurrencySymbol) + && getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol) == UnicodeString(intlCurrencySymbol)) { // Trap an error in mapping locale to currency. If we can't // map, then don't fail and set the currency to "". c = intlCurrencySymbol; } ec = U_ZERO_ERROR; // reset local error code! - setCurrency(c, ec); + setCurrencyInternally(c, ec); +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } //------------------------------------------------------------------------------ // Gets the positive prefix of the number pattern. - + UnicodeString& DecimalFormat::getPositivePrefix(UnicodeString& result) const { result = fPositivePrefix; return result; } - + //------------------------------------------------------------------------------ // Sets the positive prefix of the number pattern. - + void DecimalFormat::setPositivePrefix(const UnicodeString& newValue) { fPositivePrefix = newValue; delete fPosPrefixPattern; fPosPrefixPattern = 0; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } //------------------------------------------------------------------------------ @@ -2290,6 +3774,9 @@ DecimalFormat::setNegativePrefix(const UnicodeString& newValue) fNegativePrefix = newValue; delete fNegPrefixPattern; fNegPrefixPattern = 0; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } //------------------------------------------------------------------------------ @@ -2311,6 +3798,9 @@ DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) fPositiveSuffix = newValue; delete fPosSuffixPattern; fPosSuffixPattern = 0; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } //------------------------------------------------------------------------------ @@ -2332,14 +3822,25 @@ DecimalFormat::setNegativeSuffix(const UnicodeString& newValue) fNegativeSuffix = newValue; delete fNegSuffixPattern; fNegSuffixPattern = 0; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } //------------------------------------------------------------------------------ // 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 +int32_t +DecimalFormat::getMultiplier() const { - return fMultiplier; + if (fMultiplier == NULL) { + return 1; + } else { + return fMultiplier->getLong(); + } } //------------------------------------------------------------------------------ @@ -2350,10 +3851,23 @@ DecimalFormat::setMultiplier(int32_t newValue) // if (newValue == 0) { // throw new IllegalArgumentException("Bad multiplier: " + newValue); // } - if (newValue != 0) { - fMultiplier = newValue; + if (newValue == 0) { + newValue = 1; // one being the benign default value for a multiplier. } - // else No way to return an error. + if (newValue == 1) { + delete fMultiplier; + fMultiplier = NULL; + } else { + if (fMultiplier == NULL) { + fMultiplier = new DigitList; + } + if (fMultiplier != NULL) { + fMultiplier->set(newValue); + } + } +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } /** @@ -2365,7 +3879,11 @@ DecimalFormat::setMultiplier(int32_t newValue) * @see #setRoundingMode */ double DecimalFormat::getRoundingIncrement() const { - return fRoundingDouble; + if (fRoundingIncrement == NULL) { + return 0.0; + } else { + return fRoundingIncrement->getDouble(); + } } /** @@ -2383,16 +3901,17 @@ void DecimalFormat::setRoundingIncrement(double newValue) { fRoundingIncrement = new DigitList(); } if (fRoundingIncrement != NULL) { - fRoundingIncrement->set((int32_t)newValue); - fRoundingDouble = newValue; + fRoundingIncrement->set(newValue); return; } - } + } // These statements are executed if newValue is less than 0.0 // or fRoundingIncrement could not be created. delete fRoundingIncrement; fRoundingIncrement = NULL; - fRoundingDouble = 0.0; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } /** @@ -2416,6 +3935,9 @@ DecimalFormat::ERoundingMode DecimalFormat::getRoundingMode() const { */ void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) { fRoundingMode = roundingMode; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } /** @@ -2445,10 +3967,13 @@ int32_t DecimalFormat::getFormatWidth() const { */ void DecimalFormat::setFormatWidth(int32_t width) { fFormatWidth = (width > 0) ? width : 0; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } UnicodeString DecimalFormat::getPadCharacterString() const { - return fPad; + return UnicodeString(fPad); } void DecimalFormat::setPadCharacter(const UnicodeString &padChar) { @@ -2458,6 +3983,9 @@ void DecimalFormat::setPadCharacter(const UnicodeString &padChar) { else { fPad = kDefaultPad; } +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } /** @@ -2502,6 +4030,9 @@ DecimalFormat::EPadPosition DecimalFormat::getPadPosition() const { */ void DecimalFormat::setPadPosition(EPadPosition padPos) { fPadPosition = padPos; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } /** @@ -2513,7 +4044,7 @@ void DecimalFormat::setPadPosition(EPadPosition padPos) { * @see #isExponentSignAlwaysShown * @see #setExponentSignAlwaysShown */ -UBool DecimalFormat::isScientificNotation() { +UBool DecimalFormat::isScientificNotation() const { return fUseExponentialNotation; } @@ -2529,6 +4060,9 @@ UBool DecimalFormat::isScientificNotation() { */ void DecimalFormat::setScientificNotation(UBool useScientific) { fUseExponentialNotation = useScientific; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } /** @@ -2557,6 +4091,9 @@ int8_t DecimalFormat::getMinimumExponentDigits() const { */ void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) { fMinExponentDigits = (int8_t)((minExpDig > 0) ? minExpDig : 1); +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } /** @@ -2570,7 +4107,7 @@ void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) { * @see #getMinimumExponentDigits * @see #setExponentSignAlwaysShown */ -UBool DecimalFormat::isExponentSignAlwaysShown() { +UBool DecimalFormat::isExponentSignAlwaysShown() const { return fExponentSignAlwaysShown; } @@ -2588,25 +4125,31 @@ UBool DecimalFormat::isExponentSignAlwaysShown() { */ void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) { fExponentSignAlwaysShown = expSignAlways; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } //------------------------------------------------------------------------------ // Gets the grouping size of the number pattern. For example, thousand or 10 // thousand groupings. - + int32_t DecimalFormat::getGroupingSize() const { - return fGroupingSize; + return isGroupingUsed() ? fGroupingSize : 0; } - + //------------------------------------------------------------------------------ // Gets the grouping size of the number pattern. - + void DecimalFormat::setGroupingSize(int32_t newValue) { fGroupingSize = newValue; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } //------------------------------------------------------------------------------ @@ -2623,6 +4166,9 @@ void DecimalFormat::setSecondaryGroupingSize(int32_t newValue) { fGroupingSize2 = newValue; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } //------------------------------------------------------------------------------ @@ -2641,8 +4187,29 @@ void DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) { fDecimalSeparatorAlwaysShown = newValue; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif +} + +//------------------------------------------------------------------------------ +// Checks if decimal point pattern match is required +UBool +DecimalFormat::isDecimalPatternMatchRequired(void) const +{ + return 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. @@ -2667,29 +4234,37 @@ DecimalFormat::toLocalizedPattern(UnicodeString& result) const * affix pattern string is null, do not expand it. This method should be * called any time the symbols or the affix patterns change in order to keep * the expanded affix strings up to date. + * This method also will be called before formatting if format currency + * plural names, since the plural name is not a static one, it is + * based on the currency plural count, the affix will be known only + * after the currency plural count is know. + * In which case, the parameter + * 'pluralCount' will be a non-null currency plural count. + * In all other cases, the 'pluralCount' is null, which means it is not needed. */ -void DecimalFormat::expandAffixes() { +void DecimalFormat::expandAffixes(const UnicodeString* pluralCount) { + FieldPositionHandler none; if (fPosPrefixPattern != 0) { - expandAffix(*fPosPrefixPattern, fPositivePrefix, 0, FALSE); + expandAffix(*fPosPrefixPattern, fPositivePrefix, 0, none, FALSE, pluralCount); } if (fPosSuffixPattern != 0) { - expandAffix(*fPosSuffixPattern, fPositiveSuffix, 0, FALSE); + expandAffix(*fPosSuffixPattern, fPositiveSuffix, 0, none, FALSE, pluralCount); } if (fNegPrefixPattern != 0) { - expandAffix(*fNegPrefixPattern, fNegativePrefix, 0, FALSE); + expandAffix(*fNegPrefixPattern, fNegativePrefix, 0, none, FALSE, pluralCount); } if (fNegSuffixPattern != 0) { - expandAffix(*fNegSuffixPattern, fNegativeSuffix, 0, FALSE); + expandAffix(*fNegSuffixPattern, fNegativeSuffix, 0, none, FALSE, pluralCount); } #ifdef FMT_DEBUG UnicodeString s; - s.append("[") - .append(*fPosPrefixPattern).append("|").append(*fPosSuffixPattern) - .append(";") .append(*fNegPrefixPattern).append("|").append(*fNegSuffixPattern) - .append("]->[") - .append(fPositivePrefix).append("|").append(fPositiveSuffix) - .append(";") .append(fNegativePrefix).append("|").append(fNegativeSuffix) - .append("]\n"); + s.append(UnicodeString("[")) + .append(DEREFSTR(fPosPrefixPattern)).append((UnicodeString)"|").append(DEREFSTR(fPosSuffixPattern)) + .append((UnicodeString)";") .append(DEREFSTR(fNegPrefixPattern)).append((UnicodeString)"|").append(DEREFSTR(fNegSuffixPattern)) + .append((UnicodeString)"]->[") + .append(fPositivePrefix).append((UnicodeString)"|").append(fPositiveSuffix) + .append((UnicodeString)";") .append(fNegativePrefix).append((UnicodeString)"|").append(fNegativeSuffix) + .append((UnicodeString)"]\n"); debugout(s); #endif } @@ -2700,7 +4275,9 @@ void DecimalFormat::expandAffixes() { * after kQuote are recognized: PATTERN_PERCENT, PATTERN_PER_MILLE, * PATTERN_MINUS, and kCurrencySign. If kCurrencySign is doubled (kQuote + * kCurrencySign + kCurrencySign), it is interpreted as an international - * currency sign. Any other character after a kQuote represents itself. + * currency sign. If CURRENCY_SIGN is tripled, it is interpreted as + * currency plural long names, such as "US Dollars". + * Any other character after a kQuote represents itself. * kQuote must be followed by another character; kQuote may not occur by * itself at the end of the pattern. * @@ -2724,11 +4301,19 @@ void DecimalFormat::expandAffixes() { * currencyChoice member variable will be initialized if it is null. If * doFormat is true, then it is assumed that the currencyChoice has been * created, and it will be used to format the value in digitList. + * @param pluralCount the plural count. It is only used for currency + * plural format. In which case, it is the plural + * count of the currency amount. For example, + * in en_US, it is the singular "one", or the plural + * "other". For all other cases, it is null, and + * is not being used. */ void DecimalFormat::expandAffix(const UnicodeString& pattern, UnicodeString& affix, double number, - UBool doFormat) const { + FieldPositionHandler& handler, + UBool doFormat, + const UnicodeString* pluralCount) const { affix.remove(); for (int i=0; igetLocale().getName() : + Locale::getDefault().getName(), &isChoiceFormat, + pluralCountChar.data(), &len, &ec); + affix += UnicodeString(s, len); + handler.addAttribute(kCurrencyField, beginIdx, affix.length()); + } else if(intl) { + affix.append(currencyUChars, -1); + handler.addAttribute(kCurrencyField, beginIdx, affix.length()); } else { int32_t len; UBool isChoiceFormat; // If fSymbols is NULL, use default locale - const UChar* s = ucurr_getName(currencyUChars, fSymbols != NULL ? fSymbols->getLocale().getName() : Locale::getDefault().getName(), - UCURR_SYMBOL_NAME, &isChoiceFormat, &len, &ec); + const UChar* s = ucurr_getName(currencyUChars, + fSymbols != NULL ? fSymbols->getLocale().getName() : Locale::getDefault().getName(), + UCURR_SYMBOL_NAME, &isChoiceFormat, &len, &ec); if (isChoiceFormat) { // Two modes here: If doFormat is false, we set up // currencyChoice. If doFormat is true, we use the @@ -2771,7 +4381,7 @@ void DecimalFormat::expandAffix(const UnicodeString& pattern, // return. if (fCurrencyChoice == NULL) { // TODO Replace double-check with proper thread-safe code - ChoiceFormat* fmt = new ChoiceFormat(s, ec); + ChoiceFormat* fmt = new ChoiceFormat(UnicodeString(s), ec); if (U_SUCCESS(ec)) { umtx_lock(NULL); if (fCurrencyChoice == NULL) { @@ -2801,12 +4411,14 @@ void DecimalFormat::expandAffix(const UnicodeString& pattern, } else { // We only arrive here if the currency choice // format in the locale data is INVALID. - affix += currencyUChars; + affix.append(currencyUChars, -1); + handler.addAttribute(kCurrencyField, beginIdx, affix.length()); } } continue; } affix += UnicodeString(s, len); + handler.addAttribute(kCurrencyField, beginIdx, affix.length()); } } else { if(intl) { @@ -2814,20 +4426,25 @@ void DecimalFormat::expandAffix(const UnicodeString& pattern, } else { affix += getConstSymbol(DecimalFormatSymbols::kCurrencySymbol); } + handler.addAttribute(kCurrencyField, beginIdx, affix.length()); } break; } case kPatternPercent: affix += getConstSymbol(DecimalFormatSymbols::kPercentSymbol); + handler.addAttribute(kPercentField, beginIdx, affix.length()); break; case kPatternPerMill: affix += getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); + handler.addAttribute(kPermillField, beginIdx, affix.length()); break; case kPatternPlus: affix += getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); + handler.addAttribute(kSignField, beginIdx, affix.length()); break; case kPatternMinus: affix += getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); + handler.addAttribute(kSignField, beginIdx, affix.length()); break; default: affix.append(c); @@ -2847,8 +4464,11 @@ void DecimalFormat::expandAffix(const UnicodeString& pattern, * @param isPrefix */ int32_t DecimalFormat::appendAffix(UnicodeString& buf, double number, + FieldPositionHandler& handler, UBool isNegative, UBool isPrefix) const { - if (fCurrencyChoice != 0) { + // plural format precedes choice format + if (fCurrencyChoice != 0 && + fCurrencySignCount != fgCurrencySignCountInPluralFormat) { const UnicodeString* affixPat; if (isPrefix) { affixPat = isNegative ? fNegPrefixPattern : fPosPrefixPattern; @@ -2857,20 +4477,80 @@ int32_t DecimalFormat::appendAffix(UnicodeString& buf, double number, } if (affixPat) { UnicodeString affixBuf; - expandAffix(*affixPat, affixBuf, number, TRUE); + expandAffix(*affixPat, affixBuf, number, handler, TRUE, NULL); buf.append(affixBuf); return affixBuf.length(); } // else someone called a function that reset the pattern. } - + const UnicodeString* affix; - if (isPrefix) { - affix = isNegative ? &fNegativePrefix : &fPositivePrefix; + if (fCurrencySignCount == fgCurrencySignCountInPluralFormat) { + // TODO: get an accurate count of visible fraction digits. + UnicodeString pluralCount; + int32_t minFractionDigits = this->getMinimumFractionDigits(); + if (minFractionDigits > 0) { + FixedDecimal ni(number, this->getMinimumFractionDigits()); + pluralCount = fCurrencyPluralInfo->getPluralRules()->select(ni); + } else { + pluralCount = fCurrencyPluralInfo->getPluralRules()->select(number); + } + AffixesForCurrency* oneSet; + if (fStyle == UNUM_CURRENCY_PLURAL) { + oneSet = (AffixesForCurrency*)fPluralAffixesForCurrency->get(pluralCount); + } else { + oneSet = (AffixesForCurrency*)fAffixesForCurrency->get(pluralCount); + } + if (isPrefix) { + affix = isNegative ? &oneSet->negPrefixForCurrency : + &oneSet->posPrefixForCurrency; + } else { + affix = isNegative ? &oneSet->negSuffixForCurrency : + &oneSet->posSuffixForCurrency; + } } else { - affix = isNegative ? &fNegativeSuffix : &fPositiveSuffix; + if (isPrefix) { + affix = isNegative ? &fNegativePrefix : &fPositivePrefix; + } else { + affix = isNegative ? &fNegativeSuffix : &fPositiveSuffix; + } } + + int32_t begin = (int) buf.length(); + buf.append(*affix); + + if (handler.isRecording()) { + int32_t offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kCurrencySymbol)); + if (offset > -1) { + UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kCurrencySymbol); + handler.addAttribute(kCurrencyField, begin + offset, begin + offset + aff.length()); + } + + offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol)); + if (offset > -1) { + UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol); + handler.addAttribute(kCurrencyField, begin + offset, begin + offset + aff.length()); + } + + offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol)); + if (offset > -1) { + UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); + handler.addAttribute(kSignField, begin + offset, begin + offset + aff.length()); + } + + offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kPercentSymbol)); + if (offset > -1) { + UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kPercentSymbol); + handler.addAttribute(kPercentField, begin + offset, begin + offset + aff.length()); + } + + offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kPerMillSymbol)); + if (offset > -1) { + UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); + handler.addAttribute(kPermillField, begin + offset, begin + offset + aff.length()); + } + } return affix->length(); } @@ -3001,6 +4681,16 @@ DecimalFormat::appendAffixPattern(UnicodeString& appendTo, UnicodeString& DecimalFormat::toPattern(UnicodeString& result, UBool localized) const { + if (fStyle == UNUM_CURRENCY_PLURAL) { + // the prefix or suffix pattern might not be defined yet, + // so they can not be synthesized, + // instead, get them directly. + // but it might not be the actual pattern used in formatting. + // the actual pattern used in formatting depends on the + // formatted number's plural count. + result = fFormatPattern; + return result; + } result.remove(); UChar32 zero, sigDigit = kPatternSignificantDigit; UnicodeString digit, group; @@ -3034,10 +4724,10 @@ DecimalFormat::toPattern(UnicodeString& result, UBool localized) const padSpec.append(fPad); } if (fRoundingIncrement != NULL) { - for(i=0; ifCount; ++i) { - roundingDigits.append((UChar)fRoundingIncrement->fDigits[i]); + for(i=0; igetCount(); ++i) { + roundingDigits.append(zero+(fRoundingIncrement->getDigitValue(i))); // Convert to Unicode digit } - roundingDecimalPos = fRoundingIncrement->fDecimalAt; + roundingDecimalPos = fRoundingIncrement->getDecimalAt(); } for (int32_t part=0; part<2; ++part) { if (padPos == kPadBeforePrefix) { @@ -3165,7 +4855,7 @@ DecimalFormat::toPattern(UnicodeString& result, UBool localized) const // end up with ",###". if (add>1 && isGroupingPosition(maxDig)) { result.insert(sub0Start, group); - --add; + --add; } } } @@ -3211,660 +4901,204 @@ DecimalFormat::toPattern(UnicodeString& result, UBool localized) const } } } else { - appendAffixPattern(result, fNegSuffixPattern, fNegativeSuffix, localized); - if (fPadPosition == kPadAfterSuffix && ! padSpec.isEmpty()) { - result.append(padSpec); - } - } - } - - return result; -} - -//------------------------------------------------------------------------------ - -void -DecimalFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) -{ - UParseError parseError; - applyPattern(pattern, FALSE, parseError, status); -} - -//------------------------------------------------------------------------------ - -void -DecimalFormat::applyPattern(const UnicodeString& pattern, - UParseError& parseError, - UErrorCode& status) -{ - applyPattern(pattern, FALSE, parseError, status); -} -//------------------------------------------------------------------------------ - -void -DecimalFormat::applyLocalizedPattern(const UnicodeString& pattern, UErrorCode& status) -{ - UParseError parseError; - applyPattern(pattern, TRUE,parseError,status); -} - -//------------------------------------------------------------------------------ - -void -DecimalFormat::applyLocalizedPattern(const UnicodeString& pattern, - UParseError& parseError, - UErrorCode& status) -{ - applyPattern(pattern, TRUE,parseError,status); -} - -//------------------------------------------------------------------------------ - -void -DecimalFormat::applyPattern(const UnicodeString& pattern, - UBool localized, - UParseError& parseError, - UErrorCode& status) -{ - if (U_FAILURE(status)) - { - return; - } - // Clear error struct - parseError.offset = -1; - parseError.preContext[0] = parseError.postContext[0] = (UChar)0; - - // Set the significant pattern symbols - UChar32 zeroDigit = kPatternZeroDigit; // '0' - UChar32 sigDigit = kPatternSignificantDigit; // '@' - UnicodeString groupingSeparator ((UChar)kPatternGroupingSeparator); - UnicodeString decimalSeparator ((UChar)kPatternDecimalSeparator); - UnicodeString percent ((UChar)kPatternPercent); - UnicodeString perMill ((UChar)kPatternPerMill); - UnicodeString digit ((UChar)kPatternDigit); // '#' - UnicodeString separator ((UChar)kPatternSeparator); - UnicodeString exponent ((UChar)kPatternExponent); - UnicodeString plus ((UChar)kPatternPlus); - UnicodeString minus ((UChar)kPatternMinus); - UnicodeString padEscape ((UChar)kPatternPadEscape); - // Substitute with the localized symbols if necessary - if (localized) { - zeroDigit = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); - sigDigit = getConstSymbol(DecimalFormatSymbols::kSignificantDigitSymbol).char32At(0); - groupingSeparator. remove().append(getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)); - decimalSeparator. remove().append(getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)); - percent. remove().append(getConstSymbol(DecimalFormatSymbols::kPercentSymbol)); - perMill. remove().append(getConstSymbol(DecimalFormatSymbols::kPerMillSymbol)); - digit. remove().append(getConstSymbol(DecimalFormatSymbols::kDigitSymbol)); - separator. remove().append(getConstSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol)); - exponent. remove().append(getConstSymbol(DecimalFormatSymbols::kExponentialSymbol)); - plus. remove().append(getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol)); - minus. remove().append(getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol)); - padEscape. remove().append(getConstSymbol(DecimalFormatSymbols::kPadEscapeSymbol)); - } - UChar nineDigit = (UChar)(zeroDigit + 9); - int32_t digitLen = digit.length(); - int32_t groupSepLen = groupingSeparator.length(); - int32_t decimalSepLen = decimalSeparator.length(); - - int32_t pos = 0; - int32_t patLen = pattern.length(); - // Part 0 is the positive pattern. Part 1, if present, is the negative - // pattern. - for (int32_t part=0; part<2 && pos 0 || sigDigitCount > 0) { - ++digitRightCount; - } else { - ++digitLeftCount; - } - if (groupingCount >= 0 && decimalPos < 0) { - ++groupingCount; - } - pos += digitLen; - } else if ((ch >= zeroDigit && ch <= nineDigit) || - ch == sigDigit) { - if (digitRightCount > 0) { - // Unexpected '0' - debug("Unexpected '0'") - status = U_UNEXPECTED_TOKEN; - syntaxError(pattern,pos,parseError); - return; - } - if (ch == sigDigit) { - ++sigDigitCount; - } else { - ++zeroDigitCount; - if (ch != zeroDigit && roundingPos < 0) { - roundingPos = digitLeftCount + zeroDigitCount; - } - if (roundingPos >= 0) { - roundingInc.append((char)(ch - zeroDigit + '0')); - } - } - if (groupingCount >= 0 && decimalPos < 0) { - ++groupingCount; - } - pos += U16_LENGTH(ch); - } else if (pattern.compare(pos, groupSepLen, groupingSeparator) == 0) { - if (decimalPos >= 0) { - // Grouping separator after decimal - debug("Grouping separator after decimal") - status = U_UNEXPECTED_TOKEN; - syntaxError(pattern,pos,parseError); - return; - } - groupingCount2 = groupingCount; - groupingCount = 0; - pos += groupSepLen; - } else if (pattern.compare(pos, decimalSepLen, decimalSeparator) == 0) { - if (decimalPos >= 0) { - // Multiple decimal separators - debug("Multiple decimal separators") - status = U_MULTIPLE_DECIMAL_SEPARATORS; - syntaxError(pattern,pos,parseError); - return; - } - // Intentionally incorporate the digitRightCount, - // even though it is illegal for this to be > 0 - // at this point. We check pattern syntax below. - decimalPos = digitLeftCount + zeroDigitCount + digitRightCount; - pos += decimalSepLen; - } else { - if (pattern.compare(pos, exponent.length(), exponent) == 0) { - if (expDigits >= 0) { - // Multiple exponential symbols - debug("Multiple exponential symbols") - status = U_MULTIPLE_EXPONENTIAL_SYMBOLS; - syntaxError(pattern,pos,parseError); - return; - } - if (groupingCount >= 0) { - // Grouping separator in exponential pattern - debug("Grouping separator in exponential pattern") - status = U_MALFORMED_EXPONENTIAL_PATTERN; - syntaxError(pattern,pos,parseError); - return; - } - pos += exponent.length(); - // Check for positive prefix - if (pos < patLen - && pattern.compare(pos, plus.length(), plus) == 0) { - expSignAlways = TRUE; - pos += plus.length(); - } - // Use lookahead to parse out the exponential part of the - // pattern, then jump into suffix subpart. - expDigits = 0; - while (pos < patLen && - pattern.char32At(pos) == zeroDigit) { - ++expDigits; - pos += U16_LENGTH(zeroDigit); - } - - // 1. Require at least one mantissa pattern digit - // 2. Disallow "#+ @" in mantissa - // 3. Require at least one exponent pattern digit - if (((digitLeftCount + zeroDigitCount) < 1 && - (sigDigitCount + digitRightCount) < 1) || - (sigDigitCount > 0 && digitLeftCount > 0) || - expDigits < 1) { - // Malformed exponential pattern - debug("Malformed exponential pattern") - status = U_MALFORMED_EXPONENTIAL_PATTERN; - syntaxError(pattern,pos,parseError); - return; - } - } - // Transition to suffix subpart - subpart = 2; // suffix subpart - affix = &suffix; - sub0Limit = pos; - continue; - } - break; - case 1: // Prefix subpart - case 2: // Suffix subpart - // Process the prefix / suffix characters - // Process unquoted characters seen in prefix or suffix - // subpart. - - // Several syntax characters implicitly begins the - // next subpart if we are in the prefix; otherwise - // they are illegal if unquoted. - if (!pattern.compare(pos, digitLen, digit) || - !pattern.compare(pos, groupSepLen, groupingSeparator) || - !pattern.compare(pos, decimalSepLen, decimalSeparator) || - (ch >= zeroDigit && ch <= nineDigit) || - ch == sigDigit) { - if (subpart == 1) { // prefix subpart - subpart = 0; // pattern proper subpart - sub0Start = pos; // Reprocess this character - continue; - } else { - status = U_UNQUOTED_SPECIAL; - syntaxError(pattern,pos,parseError); - return; - } - } else if (ch == kCurrencySign) { - affix->append(kQuote); // Encode currency - // Use lookahead to determine if the currency sign is - // doubled or not. - U_ASSERT(U16_LENGTH(kCurrencySign) == 1); - if ((pos+1) < pattern.length() && pattern[pos+1] == kCurrencySign) { - affix->append(kCurrencySign); - ++pos; // Skip over the doubled character - } - isCurrency = TRUE; - // Fall through to append(ch) - } else if (ch == kQuote) { - // A quote outside quotes indicates either the opening - // quote or two quotes, which is a quote literal. That is, - // we have the first quote in 'do' or o''clock. - U_ASSERT(U16_LENGTH(kQuote) == 1); - ++pos; - if (pos < pattern.length() && pattern[pos] == kQuote) { - affix->append(kQuote); // Encode quote - // Fall through to append(ch) - } else { - subpart += 2; // open quote - continue; - } - } else if (pattern.compare(pos, separator.length(), separator) == 0) { - // Don't allow separators in the prefix, and don't allow - // separators in the second pattern (part == 1). - if (subpart == 1 || part == 1) { - // Unexpected separator - debug("Unexpected separator") - status = U_UNEXPECTED_TOKEN; - syntaxError(pattern,pos,parseError); - return; - } - sub2Limit = pos; - isPartDone = TRUE; // Go to next part - pos += separator.length(); - break; - } else if (pattern.compare(pos, percent.length(), percent) == 0) { - // Next handle characters which are appended directly. - if (multiplier != 1) { - // Too many percent/perMill characters - debug("Too many percent characters") - status = U_MULTIPLE_PERCENT_SYMBOLS; - syntaxError(pattern,pos,parseError); - return; - } - affix->append(kQuote); // Encode percent/perMill - affix->append(kPatternPercent); // Use unlocalized pattern char - multiplier = 100; - pos += percent.length(); - break; - } else if (pattern.compare(pos, perMill.length(), perMill) == 0) { - // Next handle characters which are appended directly. - if (multiplier != 1) { - // Too many percent/perMill characters - debug("Too many perMill characters") - status = U_MULTIPLE_PERMILL_SYMBOLS; - syntaxError(pattern,pos,parseError); - return; - } - affix->append(kQuote); // Encode percent/perMill - affix->append(kPatternPerMill); // Use unlocalized pattern char - multiplier = 1000; - pos += perMill.length(); - break; - } else if (pattern.compare(pos, padEscape.length(), padEscape) == 0) { - if (padPos >= 0 || // Multiple pad specifiers - (pos+1) == pattern.length()) { // Nothing after padEscape - debug("Multiple pad specifiers") - status = U_MULTIPLE_PAD_SPECIFIERS; - syntaxError(pattern,pos,parseError); - return; - } - padPos = pos; - pos += padEscape.length(); - padChar = pattern.char32At(pos); - pos += U16_LENGTH(padChar); - break; - } else if (pattern.compare(pos, minus.length(), minus) == 0) { - affix->append(kQuote); // Encode minus - affix->append(kPatternMinus); - pos += minus.length(); - break; - } else if (pattern.compare(pos, plus.length(), plus) == 0) { - affix->append(kQuote); // Encode plus - affix->append(kPatternPlus); - pos += plus.length(); - break; - } - // Unquoted, non-special characters fall through to here, as - // well as other code which needs to append something to the - // affix. - affix->append(ch); - pos += U16_LENGTH(ch); - break; - case 3: // Prefix subpart, in quote - case 4: // Suffix subpart, in quote - // A quote within quotes indicates either the closing - // quote or two quotes, which is a quote literal. That is, - // we have the second quote in 'do' or 'don''t'. - if (ch == kQuote) { - ++pos; - if (pos < pattern.length() && pattern[pos] == kQuote) { - affix->append(kQuote); // Encode quote - // Fall through to append(ch) - } else { - subpart -= 2; // close quote - continue; - } - } - affix->append(ch); - pos += U16_LENGTH(ch); - break; - } - } - - if (sub0Limit == 0) { - sub0Limit = pattern.length(); - } - - if (sub2Limit == 0) { - sub2Limit = pattern.length(); - } - - /* Handle patterns with no '0' pattern character. These patterns - * are legal, but must be recodified to make sense. "##.###" -> - * "#0.###". ".###" -> ".0##". - * - * We allow patterns of the form "####" to produce a zeroDigitCount - * of zero (got that?); although this seems like it might make it - * possible for format() to produce empty strings, format() checks - * for this condition and outputs a zero digit in this situation. - * Having a zeroDigitCount of zero yields a minimum integer digits - * of zero, which allows proper round-trip patterns. We don't want - * "#" to become "#0" when toPattern() is called (even though that's - * what it really is, semantically). - */ - if (zeroDigitCount == 0 && sigDigitCount == 0 && - digitLeftCount > 0 && decimalPos >= 0) { - // Handle "###.###" and "###." and ".###" - int n = decimalPos; - if (n == 0) - ++n; // Handle ".###" - digitRightCount = digitLeftCount - n; - digitLeftCount = n - 1; - zeroDigitCount = 1; - } - - // Do syntax checking on the digits, decimal points, and quotes. - if ((decimalPos < 0 && digitRightCount > 0 && sigDigitCount == 0) || - (decimalPos >= 0 && - (sigDigitCount > 0 || - decimalPos < digitLeftCount || - decimalPos > (digitLeftCount + zeroDigitCount))) || - groupingCount == 0 || groupingCount2 == 0 || - (sigDigitCount > 0 && zeroDigitCount > 0) || - subpart > 2) - { // subpart > 2 == unmatched quote - debug("Syntax error") - status = U_PATTERN_SYNTAX_ERROR; - syntaxError(pattern,pos,parseError); - return; - } - - // Make sure pad is at legal position before or after affix. - if (padPos >= 0) { - if (padPos == start) { - padPos = kPadBeforePrefix; - } else if (padPos+2 == sub0Start) { - padPos = kPadAfterPrefix; - } else if (padPos == sub0Limit) { - padPos = kPadBeforeSuffix; - } else if (padPos+2 == sub2Limit) { - padPos = kPadAfterSuffix; - } else { - // Illegal pad position - debug("Illegal pad position") - status = U_ILLEGAL_PAD_POSITION; - syntaxError(pattern,pos,parseError); - return; - } - } - - if (part == 0) { - delete fPosPrefixPattern; - delete fPosSuffixPattern; - delete fNegPrefixPattern; - delete fNegSuffixPattern; - fPosPrefixPattern = new UnicodeString(prefix); - /* test for NULL */ - if (fPosPrefixPattern == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - fPosSuffixPattern = new UnicodeString(suffix); - /* test for NULL */ - if (fPosSuffixPattern == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - delete fPosPrefixPattern; - return; - } - fNegPrefixPattern = 0; - fNegSuffixPattern = 0; - - fUseExponentialNotation = (expDigits >= 0); - if (fUseExponentialNotation) { - fMinExponentDigits = expDigits; - } - fExponentSignAlwaysShown = expSignAlways; - fIsCurrencyFormat = isCurrency; - int32_t digitTotalCount = digitLeftCount + zeroDigitCount + digitRightCount; - // The effectiveDecimalPos is the position the decimal is at or - // would be at if there is no decimal. Note that if - // decimalPos<0, then digitTotalCount == digitLeftCount + - // zeroDigitCount. - int32_t effectiveDecimalPos = decimalPos >= 0 ? decimalPos : digitTotalCount; - UBool isSigDig = (sigDigitCount > 0); - setSignificantDigitsUsed(isSigDig); - if (isSigDig) { - setMinimumSignificantDigits(sigDigitCount); - setMaximumSignificantDigits(sigDigitCount + digitRightCount); - } else { - int32_t minInt = effectiveDecimalPos - digitLeftCount; - setMinimumIntegerDigits(minInt); - setMaximumIntegerDigits(fUseExponentialNotation - ? digitLeftCount + getMinimumIntegerDigits() - : kDoubleIntegerDigits); - setMaximumFractionDigits(decimalPos >= 0 - ? (digitTotalCount - decimalPos) : 0); - setMinimumFractionDigits(decimalPos >= 0 - ? (digitLeftCount + zeroDigitCount - decimalPos) : 0); - } - setGroupingUsed(groupingCount > 0); - fGroupingSize = (groupingCount > 0) ? groupingCount : 0; - fGroupingSize2 = (groupingCount2 > 0 && groupingCount2 != groupingCount) - ? groupingCount2 : 0; - fMultiplier = multiplier; - setDecimalSeparatorAlwaysShown(decimalPos == 0 - || decimalPos == digitTotalCount); - if (padPos >= 0) { - fPadPosition = (EPadPosition) padPos; - // To compute the format width, first set up sub0Limit - - // sub0Start. Add in prefix/suffix length later. - - // fFormatWidth = prefix.length() + suffix.length() + - // sub0Limit - sub0Start; - fFormatWidth = sub0Limit - sub0Start; - fPad = padChar; - } else { - fFormatWidth = 0; - } - if (roundingPos >= 0) { - roundingInc.fDecimalAt = effectiveDecimalPos - roundingPos; - if (fRoundingIncrement != NULL) { - *fRoundingIncrement = roundingInc; - } else { - fRoundingIncrement = new DigitList(roundingInc); - /* test for NULL */ - if (fRoundingIncrement == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - delete fPosPrefixPattern; - delete fPosSuffixPattern; - return; - } - } - fRoundingDouble = fRoundingIncrement->getDouble(); - fRoundingMode = kRoundHalfEven; - } else { - setRoundingIncrement(0.0); - } - } else { - fNegPrefixPattern = new UnicodeString(prefix); - /* test for NULL */ - if (fNegPrefixPattern == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - fNegSuffixPattern = new UnicodeString(suffix); - /* test for NULL */ - if (fNegSuffixPattern == 0) { - delete fNegPrefixPattern; - status = U_MEMORY_ALLOCATION_ERROR; - return; + appendAffixPattern(result, fNegSuffixPattern, fNegativeSuffix, localized); + if (fPadPosition == kPadAfterSuffix && ! padSpec.isEmpty()) { + result.append(padSpec); } } } - if (pattern.length() == 0) { - delete fNegPrefixPattern; - delete fNegSuffixPattern; - fNegPrefixPattern = NULL; - fNegSuffixPattern = NULL; - if (fPosPrefixPattern != NULL) { - fPosPrefixPattern->remove(); - } else { - fPosPrefixPattern = new UnicodeString(); - /* test for NULL */ - if (fPosPrefixPattern == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - if (fPosSuffixPattern != NULL) { - fPosSuffixPattern->remove(); - } else { - fPosSuffixPattern = new UnicodeString(); - /* test for NULL */ - if (fPosSuffixPattern == 0) { - delete fPosPrefixPattern; - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } + return result; +} - setMinimumIntegerDigits(0); - setMaximumIntegerDigits(kDoubleIntegerDigits); - setMinimumFractionDigits(0); - setMaximumFractionDigits(kDoubleFractionDigits); +//------------------------------------------------------------------------------ - fUseExponentialNotation = FALSE; - fIsCurrencyFormat = FALSE; - setGroupingUsed(FALSE); - fGroupingSize = 0; - fGroupingSize2 = 0; - fMultiplier = 1; - setDecimalSeparatorAlwaysShown(FALSE); - fFormatWidth = 0; - setRoundingIncrement(0.0); +void +DecimalFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) +{ + UParseError parseError; + applyPattern(pattern, FALSE, parseError, status); +} + +//------------------------------------------------------------------------------ + +void +DecimalFormat::applyPattern(const UnicodeString& pattern, + UParseError& parseError, + UErrorCode& status) +{ + applyPattern(pattern, FALSE, parseError, status); +} +//------------------------------------------------------------------------------ + +void +DecimalFormat::applyLocalizedPattern(const UnicodeString& pattern, UErrorCode& status) +{ + UParseError parseError; + applyPattern(pattern, TRUE,parseError,status); +} + +//------------------------------------------------------------------------------ + +void +DecimalFormat::applyLocalizedPattern(const UnicodeString& pattern, + UParseError& parseError, + UErrorCode& status) +{ + applyPattern(pattern, TRUE,parseError,status); +} + +//------------------------------------------------------------------------------ + +void +DecimalFormat::applyPatternWithoutExpandAffix(const UnicodeString& pattern, + UBool localized, + UParseError& parseError, + UErrorCode& status) +{ + if (U_FAILURE(status)) + { + return; + } + DecimalFormatPatternParser patternParser; + if (localized) { + patternParser.useSymbols(*fSymbols); + } + fFormatPattern = pattern; + DecimalFormatPattern out; + patternParser.applyPatternWithoutExpandAffix( + pattern, + out, + parseError, + status); + if (U_FAILURE(status)) { + return; } - // If there was no negative pattern, or if the negative pattern is - // identical to the positive pattern, then prepend the minus sign to the - // positive pattern to form the negative pattern. - if (fNegPrefixPattern == NULL || - (*fNegPrefixPattern == *fPosPrefixPattern - && *fNegSuffixPattern == *fPosSuffixPattern)) { - _copy_us_ptr(&fNegSuffixPattern, fPosSuffixPattern); - if (fNegPrefixPattern == NULL) { - fNegPrefixPattern = new UnicodeString(); + setMinimumIntegerDigits(out.fMinimumIntegerDigits); + setMaximumIntegerDigits(out.fMaximumIntegerDigits); + setMinimumFractionDigits(out.fMinimumFractionDigits); + setMaximumFractionDigits(out.fMaximumFractionDigits); + setSignificantDigitsUsed(out.fUseSignificantDigits); + if (out.fUseSignificantDigits) { + setMinimumSignificantDigits(out.fMinimumSignificantDigits); + setMaximumSignificantDigits(out.fMaximumSignificantDigits); + } + fUseExponentialNotation = out.fUseExponentialNotation; + if (out.fUseExponentialNotation) { + fMinExponentDigits = out.fMinExponentDigits; + } + fExponentSignAlwaysShown = out.fExponentSignAlwaysShown; + fCurrencySignCount = out.fCurrencySignCount; + setGroupingUsed(out.fGroupingUsed); + if (out.fGroupingUsed) { + fGroupingSize = out.fGroupingSize; + fGroupingSize2 = out.fGroupingSize2; + } + setMultiplier(out.fMultiplier); + fDecimalSeparatorAlwaysShown = out.fDecimalSeparatorAlwaysShown; + fFormatWidth = out.fFormatWidth; + if (out.fRoundingIncrementUsed) { + if (fRoundingIncrement != NULL) { + *fRoundingIncrement = out.fRoundingIncrement; + } else { + fRoundingIncrement = new DigitList(out.fRoundingIncrement); /* test for NULL */ - if (fNegPrefixPattern == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; + if (fRoundingIncrement == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; } - } else { - fNegPrefixPattern->remove(); } - fNegPrefixPattern->append(kQuote).append(kPatternMinus) - .append(*fPosPrefixPattern); + } else { + setRoundingIncrement(0.0); } -#ifdef FMT_DEBUG - UnicodeString s; - s.append("\"").append(pattern).append("\"->"); - debugout(s); -#endif - expandAffixes(); + fPad = out.fPad; + switch (out.fPadPosition) { + case DecimalFormatPattern::kPadBeforePrefix: + fPadPosition = kPadBeforePrefix; + break; + case DecimalFormatPattern::kPadAfterPrefix: + fPadPosition = kPadAfterPrefix; + break; + case DecimalFormatPattern::kPadBeforeSuffix: + fPadPosition = kPadBeforeSuffix; + break; + case DecimalFormatPattern::kPadAfterSuffix: + fPadPosition = kPadAfterSuffix; + break; + } + copyString(out.fNegPrefixPattern, out.fNegPatternsBogus, fNegPrefixPattern, status); + copyString(out.fNegSuffixPattern, out.fNegPatternsBogus, fNegSuffixPattern, status); + copyString(out.fPosPrefixPattern, out.fPosPatternsBogus, fPosPrefixPattern, status); + copyString(out.fPosSuffixPattern, out.fPosPatternsBogus, fPosSuffixPattern, status); +} + + +void +DecimalFormat::expandAffixAdjustWidth(const UnicodeString* pluralCount) { + expandAffixes(pluralCount); if (fFormatWidth > 0) { // Finish computing format width (see above) - fFormatWidth += fPositivePrefix.length() + fPositiveSuffix.length(); + // TODO: how to handle fFormatWidth, + // need to save in f(Plural)AffixesForCurrecy? + fFormatWidth += fPositivePrefix.length() + fPositiveSuffix.length(); } } + +void +DecimalFormat::applyPattern(const UnicodeString& pattern, + UBool localized, + UParseError& parseError, + UErrorCode& status) +{ + // do the following re-set first. since they change private data by + // apply pattern again. + if (pattern.indexOf(kCurrencySign) != -1) { + if (fCurrencyPluralInfo == NULL) { + // initialize currencyPluralInfo if needed + fCurrencyPluralInfo = new CurrencyPluralInfo(fSymbols->getLocale(), status); + } + if (fAffixPatternsForCurrency == NULL) { + setupCurrencyAffixPatterns(status); + } + if (pattern.indexOf(fgTripleCurrencySign, 3, 0) != -1) { + // only setup the affixes of the current pattern. + setupCurrencyAffixes(pattern, TRUE, FALSE, status); + } + } + applyPatternWithoutExpandAffix(pattern, localized, parseError, status); + expandAffixAdjustWidth(NULL); +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif +} + + +void +DecimalFormat::applyPatternInternally(const UnicodeString& pluralCount, + const UnicodeString& pattern, + UBool localized, + UParseError& parseError, + UErrorCode& status) { + applyPatternWithoutExpandAffix(pattern, localized, parseError, status); + expandAffixAdjustWidth(&pluralCount); +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif +} + + /** * Sets the maximum number of digits allowed in the integer portion of a - * number. This override limits the integer digit count to 309. + * number. * @see NumberFormat#setMaximumIntegerDigits */ void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) { - NumberFormat::setMaximumIntegerDigits(_min(newValue, kDoubleIntegerDigits)); + NumberFormat::setMaximumIntegerDigits(_min(newValue, gDefaultMaxIntegerDigits)); +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } /** @@ -3874,6 +5108,9 @@ void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) { */ void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) { NumberFormat::setMinimumIntegerDigits(_min(newValue, kDoubleIntegerDigits)); +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } /** @@ -3883,6 +5120,9 @@ void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) { */ void DecimalFormat::setMaximumFractionDigits(int32_t newValue) { NumberFormat::setMaximumFractionDigits(_min(newValue, kDoubleFractionDigits)); +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } /** @@ -3892,6 +5132,9 @@ void DecimalFormat::setMaximumFractionDigits(int32_t newValue) { */ void DecimalFormat::setMinimumFractionDigits(int32_t newValue) { NumberFormat::setMinimumFractionDigits(_min(newValue, kDoubleFractionDigits)); +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } int32_t DecimalFormat::getMinimumSignificantDigits() const { @@ -3904,12 +5147,16 @@ int32_t DecimalFormat::getMaximumSignificantDigits() const { void DecimalFormat::setMinimumSignificantDigits(int32_t min) { if (min < 1) { - min = 1; + min = 1; } // pin max sig dig to >= min int32_t max = _max(fMaxSignificantDigits, min); fMinSignificantDigits = min; fMaxSignificantDigits = max; + fUseSignificantDigits = TRUE; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } void DecimalFormat::setMaximumSignificantDigits(int32_t max) { @@ -3921,6 +5168,10 @@ void DecimalFormat::setMaximumSignificantDigits(int32_t max) { int32_t min = _min(fMinSignificantDigits, max); fMinSignificantDigits = min; fMaxSignificantDigits = max; + fUseSignificantDigits = TRUE; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } UBool DecimalFormat::areSignificantDigitsUsed() const { @@ -3929,9 +5180,13 @@ UBool DecimalFormat::areSignificantDigitsUsed() const { void DecimalFormat::setSignificantDigitsUsed(UBool useSignificantDigits) { fUseSignificantDigits = useSignificantDigits; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } -void DecimalFormat::setCurrency(const UChar* theCurrency, UErrorCode& ec) { +void DecimalFormat::setCurrencyInternally(const UChar* theCurrency, + UErrorCode& ec) { // If we are a currency format, then modify our affixes to // encode the currency symbol for the given currency in our // locale, and adjust the decimal digits and rounding for the @@ -3939,35 +5194,79 @@ void DecimalFormat::setCurrency(const UChar* theCurrency, UErrorCode& ec) { // Note: The code is ordered so that this object is *not changed* // until we are sure we are going to succeed. - + // NULL or empty currency is *legal* and indicates no currency. UBool isCurr = (theCurrency && *theCurrency); double rounding = 0.0; int32_t frac = 0; - if (fIsCurrencyFormat && isCurr) { - rounding = ucurr_getRoundingIncrement(theCurrency, &ec); - frac = ucurr_getDefaultFractionDigits(theCurrency, &ec); + if (fCurrencySignCount != fgCurrencySignCountZero && isCurr) { + rounding = ucurr_getRoundingIncrementForUsage(theCurrency, fCurrencyUsage, &ec); + frac = ucurr_getDefaultFractionDigitsForUsage(theCurrency, fCurrencyUsage, &ec); } - + NumberFormat::setCurrency(theCurrency, ec); if (U_FAILURE(ec)) return; - if (fIsCurrencyFormat) { + if (fCurrencySignCount != fgCurrencySignCountZero) { // NULL or empty currency is *legal* and indicates no currency. if (isCurr) { setRoundingIncrement(rounding); setMinimumFractionDigits(frac); setMaximumFractionDigits(frac); } - expandAffixes(); + expandAffixes(NULL); } +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif +} + +void DecimalFormat::setCurrency(const UChar* theCurrency, UErrorCode& ec) { + // set the currency before compute affixes to get the right currency names + NumberFormat::setCurrency(theCurrency, ec); + if (fFormatPattern.indexOf(fgTripleCurrencySign, 3, 0) != -1) { + UnicodeString savedPtn = fFormatPattern; + setupCurrencyAffixes(fFormatPattern, TRUE, TRUE, ec); + UParseError parseErr; + applyPattern(savedPtn, FALSE, parseErr, ec); + } + // set the currency after apply pattern to get the correct rounding/fraction + setCurrencyInternally(theCurrency, ec); +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif +} + +void DecimalFormat::setCurrencyUsage(UCurrencyUsage newContext, UErrorCode* ec){ + fCurrencyUsage = newContext; + + const UChar* theCurrency = getCurrency(); + + // We set rounding/digit based on currency context + if(theCurrency){ + double rounding = ucurr_getRoundingIncrementForUsage(theCurrency, fCurrencyUsage, ec); + int32_t frac = ucurr_getDefaultFractionDigitsForUsage(theCurrency, fCurrencyUsage, ec); + + if (U_SUCCESS(*ec)) { + setRoundingIncrement(rounding); + setMinimumFractionDigits(frac); + setMaximumFractionDigits(frac); + } + } +} + +UCurrencyUsage DecimalFormat::getCurrencyUsage() const { + return fCurrencyUsage; } // Deprecated variant with no UErrorCode parameter void DecimalFormat::setCurrency(const UChar* theCurrency) { UErrorCode ec = U_ZERO_ERROR; setCurrency(theCurrency, ec); +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif } void DecimalFormat::getEffectiveCurrency(UChar* result, UErrorCode& ec) const { @@ -3992,13 +5291,377 @@ void DecimalFormat::getEffectiveCurrency(UChar* result, UErrorCode& ec) const { * formats. */ int32_t -DecimalFormat::precision(UBool isIntegral) const { +DecimalFormat::precision() const { if (areSignificantDigitsUsed()) { return getMaximumSignificantDigits(); } else if (fUseExponentialNotation) { return getMinimumIntegerDigits() + getMaximumFractionDigits(); } else { - return isIntegral ? 0 : getMaximumFractionDigits(); + return getMaximumFractionDigits(); + } +} + + +// TODO: template algorithm +Hashtable* +DecimalFormat::initHashForAffix(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(decimfmtAffixValueComparator); + return hTable; +} + +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::deleteHashForAffix(Hashtable*& table) +{ + if ( table == NULL ) { + return; + } + int32_t pos = UHASH_FIRST; + const UHashElement* element = NULL; + while ( (element = table->nextElement(pos)) != NULL ) { + const UHashTok valueTok = element->value; + const AffixesForCurrency* value = (AffixesForCurrency*)valueTok.pointer; + delete value; + } + delete table; + table = NULL; +} + + + +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; + } + } + } +} + +// this is only overridden to call handleChanged() for fastpath purposes. +void +DecimalFormat::setGroupingUsed(UBool newValue) { + NumberFormat::setGroupingUsed(newValue); + handleChanged(); +} + +// this is only overridden to call handleChanged() for fastpath purposes. +void +DecimalFormat::setParseIntegerOnly(UBool newValue) { + NumberFormat::setParseIntegerOnly(newValue); + handleChanged(); +} + +// this is only overridden to call handleChanged() for fastpath purposes. +// setContext doesn't affect the fastPath right now, but this is called for completeness +void +DecimalFormat::setContext(UDisplayContext value, UErrorCode& status) { + NumberFormat::setContext(value, status); + handleChanged(); +} + + +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 { + fBoolFlags.set(attr, newValue); + } + break; + + case UNUM_SCALE: + fScale = newValue; + break; + + case UNUM_CURRENCY_USAGE: + setCurrencyUsage((UCurrencyUsage)newValue, &status); + + 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 fScale; + + case UNUM_CURRENCY_USAGE: + return fCurrencyUsage; + + default: + status = U_UNSUPPORTED_ERROR; + break; + } + + return -1; /* undefined */ +} + +#if UCONFIG_HAVE_PARSEALLINPUT +void DecimalFormat::setParseAllInput(UNumberFormatAttributeValue value) { + fParseAllInput = value; +#if UCONFIG_FORMAT_FASTPATHS_49 + handleChanged(); +#endif +} +#endif + +void +DecimalFormat::copyHashForAffix(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 AffixesForCurrency* value = (AffixesForCurrency*)valueTok.pointer; + AffixesForCurrency* copy = new AffixesForCurrency( + value->negPrefixForCurrency, + value->negSuffixForCurrency, + value->posPrefixForCurrency, + value->posSuffixForCurrency); + target->put(UnicodeString(*key), copy, status); + if ( U_FAILURE(status) ) { + return; + } + } } }