/* ******************************************************************************* * Copyright (C) 1997-2010, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* * * File DECIMFMT.CPP * * Modification History: * * Date Name Description * 02/19/97 aliu Converted from java. * 03/20/97 clhuang Implemented with new APIs. * 03/31/97 aliu Moved isLONG_MIN to DigitList, and fixed it. * 04/3/97 aliu Rewrote parsing and formatting completely, and * cleaned up and debugged. Actually works now. * Implemented NAN and INF handling, for both parsing * and formatting. Extensive testing & debugging. * 04/10/97 aliu Modified to compile on AIX. * 04/16/97 aliu Rewrote to use DigitList, which has been resurrected. * Changed DigitCount to int per code review. * 07/09/97 helena Made ParsePosition into a class. * 08/26/97 aliu Extensive changes to applyPattern; completely * rewritten from the Java. * 09/09/97 aliu Ported over support for exponential formats. * 07/20/98 stephen JDK 1.2 sync up. * Various instances of '0' replaced with 'NULL' * Check for grouping size in subFormat() * Brought subParse() in line with Java 1.2 * Added method appendAffix() * 08/24/1998 srl Removed Mutex calls. This is not a thread safe class! * 02/22/99 stephen Removed character literals for EBCDIC safety * 06/24/99 helena Integrated Alan's NF enhancements and Java2 bug fixes * 06/28/99 stephen Fixed bugs in toPattern(). * 06/29/99 stephen Fixed operator= to copy fFormatWidth, fPad, * fPadPosition ******************************************************************************** */ #include "unicode/utypes.h" #if !UCONFIG_NO_FORMATTING #include "fphdlimp.h" #include "unicode/decimfmt.h" #include "unicode/choicfmt.h" #include "unicode/ucurr.h" #include "unicode/ustring.h" #include "unicode/dcfmtsym.h" #include "unicode/ures.h" #include "unicode/uchar.h" #include "unicode/curramt.h" #include "unicode/currpinf.h" #include "unicode/plurrule.h" #include "ucurrimp.h" #include "charstr.h" #include "cmemory.h" #include "util.h" #include "digitlst.h" #include "cstring.h" #include "umutex.h" #include "uassert.h" #include "putilimp.h" #include #include "hash.h" U_NAMESPACE_BEGIN /* 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; } }; /* 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; } }; 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 //#define FMT_DEBUG #ifdef FMT_DEBUG #include static void debugout(UnicodeString s) { char buf[2000]; s.extract((int32_t) 0, s.length(), buf); printf("%s\n", buf); } #define debug(x) printf("%s\n", x); #else #define debugout(x) #define debug(x) #endif // ***************************************************************************** // class DecimalFormat // ***************************************************************************** UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormat) // Constants for characters used in programmatic (unlocalized) patterns. #define kPatternZeroDigit ((UChar)0x0030) /*'0'*/ #define kPatternSignificantDigit ((UChar)0x0040) /*'@'*/ #define kPatternGroupingSeparator ((UChar)0x002C) /*','*/ #define kPatternDecimalSeparator ((UChar)0x002E) /*'.'*/ #define kPatternPerMill ((UChar)0x2030) #define kPatternPercent ((UChar)0x0025) /*'%'*/ #define kPatternDigit ((UChar)0x0023) /*'#'*/ #define kPatternSeparator ((UChar)0x003B) /*';'*/ #define kPatternExponent ((UChar)0x0045) /*'E'*/ #define kPatternPlus ((UChar)0x002B) /*'+'*/ #define kPatternMinus ((UChar)0x002D) /*'-'*/ #define kPatternPadEscape ((UChar)0x002A) /*'*'*/ #define kQuote ((UChar)0x0027) /*'\''*/ /** * The CURRENCY_SIGN is the standard Unicode symbol for currency. It * is used in patterns and substitued with either the currency symbol, * or if it is doubled, with the international currency symbol. If the * CURRENCY_SIGN is seen in a pattern, then the decimal separator is * replaced with the monetary decimal separator. */ #define kCurrencySign ((UChar)0x00A4) #define kDefaultPad ((UChar)0x0020) /* */ const int32_t DecimalFormat::kDoubleIntegerDigits = 309; const int32_t DecimalFormat::kDoubleFractionDigits = 340; const int32_t DecimalFormat::kMaxScientificIntegerDigits = 8; /** * These are the tags we expect to see in normal resource bundle files associated * with a locale. */ const char DecimalFormat::fgNumberPatterns[]="NumberPatterns"; static const UChar fgTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0}; inline int32_t _min(int32_t a, int32_t b) { return (aindexOf((UChar)kCurrencySign) >= 0) { // If it looks like we are going to use a currency pattern // then do the time consuming lookup. setCurrencyForSymbols(); } else { setCurrencyInternally(NULL, status); } const UnicodeString* patternUsed; UnicodeString currencyPluralPatternForOther; // apply pattern if (fStyle == NumberFormat::kPluralCurrencyStyle) { 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("other", 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) != -1) { setupCurrencyAffixes(*patternUsed, TRUE, TRUE, status); } } applyPatternWithoutExpandAffix(*patternUsed,FALSE, parseErr, status); // expand affixes if (fCurrencySignCount != fgCurrencySignCountInPluralFormat) { expandAffixAdjustWidth(NULL); } // If it was a currency format, apply the appropriate rounding by // resetting the currency. NOTE: this copies fCurrency on top of itself. if (fCurrencySignCount > fgCurrencySignCountZero) { setCurrencyInternally(getCurrency(), status); } } void DecimalFormat::setupCurrencyAffixPatterns(UErrorCode& status) { if (U_FAILURE(status)) { return; } UParseError parseErr; fAffixPatternsForCurrency = initHashForAffixPattern(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((char *)0, fSymbols->getLocale().getName(), &error); UResourceBundle *numberPatterns = ures_getByKey(resource, fgNumberPatterns, NULL, &error); int32_t patLen = 0; const UChar *patResStr = ures_getStringByIndex(numberPatterns, kCurrencyStyle, &patLen, &error); ures_close(numberPatterns); ures_close(resource); if (U_SUCCESS(error)) { applyPatternWithoutExpandAffix(UnicodeString(patResStr, patLen), false, parseErr, status); AffixPatternsForCurrency* affixPtn = new AffixPatternsForCurrency( *fNegPrefixPattern, *fNegSuffixPattern, *fPosPrefixPattern, *fPosSuffixPattern, UCURR_SYMBOL_NAME); fAffixPatternsForCurrency->put("default", affixPtn, status); } // save the unique currency plural patterns of this locale. Hashtable* pluralPtn = fCurrencyPluralInfo->fPluralCountToCurrencyUnitPattern; const UHashElement* element = NULL; int32_t pos = -1; 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 char* pluralCountCh; while ((pluralCountCh = keywords->next(NULL, status)) != NULL) { if ( U_SUCCESS(status) ) { UnicodeString pluralCount = UnicodeString(pluralCountCh); 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 char* pluralCountCh; while ((pluralCountCh = keywords->next(NULL, status)) != NULL) { if ( U_SUCCESS(status) ) { UnicodeString pluralCount = UnicodeString(pluralCountCh); 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 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) { init(); *this = source; } //------------------------------------------------------------------------------ // assignment operator static void _copy_us_ptr(UnicodeString** pdest, const UnicodeString* source) { if (source == NULL) { delete *pdest; *pdest = NULL; } else if (*pdest == NULL) { *pdest = new UnicodeString(*source); } else { **pdest = *source; } } DecimalFormat& DecimalFormat::operator=(const DecimalFormat& rhs) { if(this != &rhs) { NumberFormat::operator=(rhs); 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(); } setRoundingIncrement(rhs.getRoundingIncrement()); fRoundingMode = rhs.fRoundingMode; setMultiplier(rhs.getMultiplier()); fGroupingSize = rhs.fGroupingSize; fGroupingSize2 = rhs.fGroupingSize2; fDecimalSeparatorAlwaysShown = rhs.fDecimalSeparatorAlwaysShown; if(fSymbols == NULL) { fSymbols = new DecimalFormatSymbols(*rhs.fSymbols); } else { *fSymbols = *rhs.fSymbols; } fUseExponentialNotation = rhs.fUseExponentialNotation; fExponentSignAlwaysShown = rhs.fExponentSignAlwaysShown; /*Bertrand A. D. Update 98.03.17*/ fCurrencySignCount = rhs.fCurrencySignCount; /*end of Update*/ fMinExponentDigits = rhs.fMinExponentDigits; /* sfb 990629 */ fFormatWidth = rhs.fFormatWidth; fPad = rhs.fPad; fPadPosition = rhs.fPadPosition; /* end sfb */ fMinSignificantDigits = rhs.fMinSignificantDigits; fMaxSignificantDigits = rhs.fMaxSignificantDigits; fUseSignificantDigits = rhs.fUseSignificantDigits; fFormatPattern = rhs.fFormatPattern; fStyle = rhs.fStyle; fCurrencySignCount = rhs.fCurrencySignCount; if (rhs.fCurrencyPluralInfo) { delete fCurrencyPluralInfo; fCurrencyPluralInfo = rhs.fCurrencyPluralInfo->clone(); } if (rhs.fAffixPatternsForCurrency) { UErrorCode status = U_ZERO_ERROR; deleteHashForAffixPattern(); fAffixPatternsForCurrency = initHashForAffixPattern(status); copyHashForAffixPattern(rhs.fAffixPatternsForCurrency, fAffixPatternsForCurrency, status); } if (rhs.fAffixesForCurrency) { UErrorCode status = U_ZERO_ERROR; deleteHashForAffix(fAffixesForCurrency); fAffixesForCurrency = initHashForAffixPattern(status); copyHashForAffix(rhs.fAffixesForCurrency, fAffixesForCurrency, status); } if (rhs.fPluralAffixesForCurrency) { UErrorCode status = U_ZERO_ERROR; deleteHashForAffix(fPluralAffixesForCurrency); fPluralAffixesForCurrency = initHashForAffixPattern(status); copyHashForAffix(rhs.fPluralAffixesForCurrency, fPluralAffixesForCurrency, status); } } return *this; } //------------------------------------------------------------------------------ UBool DecimalFormat::operator==(const Format& that) const { if (this == &that) return TRUE; // NumberFormat::operator== guarantees this cast is safe const DecimalFormat* other = (DecimalFormat*)&that; #ifdef FMT_DEBUG // This code makes it easy to determine why two format objects that should // be equal aren't. UBool first = TRUE; 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 && *fPosPrefixPattern == *other->fPosPrefixPattern))) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Pos Prefix !="); } if (!((fPosSuffixPattern == other->fPosSuffixPattern && // both null fPositiveSuffix == other->fPositiveSuffix) || (fPosSuffixPattern != 0 && other->fPosSuffixPattern != 0 && *fPosSuffixPattern == *other->fPosSuffixPattern))) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Pos Suffix !="); } if (!((fNegPrefixPattern == other->fNegPrefixPattern && // both null fNegativePrefix == other->fNegativePrefix) || (fNegPrefixPattern != 0 && other->fNegPrefixPattern != 0 && *fNegPrefixPattern == *other->fNegPrefixPattern))) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Neg Prefix "); if (fNegPrefixPattern == NULL) { debug("NULL("); debugout(fNegativePrefix); debug(")"); } else { debugout(*fNegPrefixPattern); } debug(" != "); if (other->fNegPrefixPattern == NULL) { debug("NULL("); debugout(other->fNegativePrefix); debug(")"); } else { debugout(*other->fNegPrefixPattern); } } if (!((fNegSuffixPattern == other->fNegSuffixPattern && // both null fNegativeSuffix == other->fNegativeSuffix) || (fNegSuffixPattern != 0 && other->fNegSuffixPattern != 0 && *fNegSuffixPattern == *other->fNegSuffixPattern))) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Neg Suffix "); if (fNegSuffixPattern == NULL) { debug("NULL("); debugout(fNegativeSuffix); debug(")"); } else { debugout(*fNegSuffixPattern); } debug(" != "); if (other->fNegSuffixPattern == NULL) { debug("NULL("); debugout(other->fNegativeSuffix); debug(")"); } else { debugout(*other->fNegSuffixPattern); } } if (!((fRoundingIncrement == other->fRoundingIncrement) // both null || (fRoundingIncrement != NULL && other->fRoundingIncrement != NULL && *fRoundingIncrement == *other->fRoundingIncrement))) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Rounding Increment !="); } if (getMultiplier() != other->getMultiplier()) { if (first) { printf("[ "); first = FALSE; } printf("Multiplier %ld != %ld", getMultiplier(), other->getMultiplier()); } if (fGroupingSize != other->fGroupingSize) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } printf("Grouping Size %ld != %ld", fGroupingSize, other->fGroupingSize); } if (fGroupingSize2 != other->fGroupingSize2) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } printf("Secondary Grouping Size %ld != %ld", fGroupingSize2, other->fGroupingSize2); } if (fDecimalSeparatorAlwaysShown != other->fDecimalSeparatorAlwaysShown) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } printf("Dec Sep Always %d != %d", fDecimalSeparatorAlwaysShown, other->fDecimalSeparatorAlwaysShown); } if (fUseExponentialNotation != other->fUseExponentialNotation) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Use Exp !="); } if (!(!fUseExponentialNotation || fMinExponentDigits != other->fMinExponentDigits)) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Exp Digits !="); } 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) { debug("fUseSignificantDigits !="); } if (fUseSignificantDigits && fMinSignificantDigits != other->fMinSignificantDigits) { debug("fMinSignificantDigits !="); } if (fUseSignificantDigits && fMaxSignificantDigits != other->fMaxSignificantDigits) { debug("fMaxSignificantDigits !="); } 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) && ((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)) && getMultiplier() == other->getMultiplier() && fGroupingSize == other->fGroupingSize && fGroupingSize2 == other->fGroupingSize2 && fDecimalSeparatorAlwaysShown == other->fDecimalSeparatorAlwaysShown && fUseExponentialNotation == other->fUseExponentialNotation && (!fUseExponentialNotation || fMinExponentDigits == other->fMinExponentDigits) && *fSymbols == *(other->fSymbols) && fUseSignificantDigits == other->fUseSignificantDigits && (!fUseSignificantDigits || (fMinSignificantDigits == other->fMinSignificantDigits && fMaxSignificantDigits == other->fMaxSignificantDigits)) && fCurrencySignCount == other->fCurrencySignCount && ((fCurrencyPluralInfo == other->fCurrencyPluralInfo && fCurrencyPluralInfo == NULL) || (fCurrencyPluralInfo != NULL && other->fCurrencyPluralInfo != NULL && *fCurrencyPluralInfo == *(other->fCurrencyPluralInfo)))); } //------------------------------------------------------------------------------ Format* 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(int32_t number, UnicodeString& appendTo, FieldPositionIterator* posIter, UErrorCode& status) const { return format((int64_t)number, appendTo, posIter, status); } //------------------------------------------------------------------------------ UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& fieldPosition) const { FieldPositionOnlyHandler handler(fieldPosition); return _format(number, appendTo, handler); } UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPositionIterator* posIter, UErrorCode& status) const { FieldPositionIteratorHandler handler(posIter, status); return _format(number, appendTo, handler); } UnicodeString& DecimalFormat::_format(int64_t number, UnicodeString& appendTo, FieldPositionHandler& handler) const { UErrorCode status = U_ZERO_ERROR; DigitList digits; digits.set(number); return _format(digits, appendTo, handler, status); } //------------------------------------------------------------------------------ UnicodeString& DecimalFormat::format( double number, UnicodeString& appendTo, FieldPosition& fieldPosition) const { FieldPositionOnlyHandler handler(fieldPosition); return _format(number, appendTo, handler); } UnicodeString& DecimalFormat::format( double number, UnicodeString& appendTo, FieldPositionIterator* posIter, UErrorCode& status) const { FieldPositionIteratorHandler handler(posIter, status); return _format(number, appendTo, handler); } UnicodeString& DecimalFormat::_format( double number, UnicodeString& appendTo, FieldPositionHandler& handler) const { // 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; } UErrorCode status = U_ZERO_ERROR; DigitList digits; digits.set(number); _format(digits, appendTo, handler, status); // No way to return status from here. return appendTo; } //------------------------------------------------------------------------------ UnicodeString& DecimalFormat::format(const StringPiece &number, UnicodeString &toAppendTo, FieldPositionIterator *posIter, UErrorCode &status) const { 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; } UnicodeString& DecimalFormat::_format(const DigitList &number, UnicodeString& appendTo, FieldPositionHandler& handler, UErrorCode &status) const { // 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; } // 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. DigitList adjustedNum(number); // Copy, so we do not alter the original. adjustedNum.setRoundingMode(fRoundingMode); if (fMultiplier != NULL) { adjustedNum.mult(*fMultiplier, status); } /* * 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. */ UBool isNegative = !adjustedNum.isPositive(); // Apply rounding after multiplier if (fRoundingIncrement != NULL) { adjustedNum.div(*fRoundingIncrement, status); adjustedNum.toIntegralValue(); adjustedNum.mult(*fRoundingIncrement, status); adjustedNum.trim(); } // 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; } if (fRoundingIncrement == NULL) { if (fUseExponentialNotation || areSignificantDigitsUsed()) { int32_t sigDigits = precision(); if (sigDigits > 0) { adjustedNum.round(sigDigits); } } else { // Fixed point format. Round to a set number of fraction digits. int32_t numFractionDigits = precision(); adjustedNum.roundFixedPoint(numFractionDigits); } } return subformat(appendTo, handler, adjustedNum, FALSE); } UnicodeString& DecimalFormat::format( const Formattable& obj, UnicodeString& appendTo, FieldPosition& fieldPosition, UErrorCode& status) const { return NumberFormat::format(obj, appendTo, fieldPosition, status); } /** * Return true if a grouping separator belongs at the given * position, based on whether grouping is in use and the values of * the primary and secondary grouping interval. * @param pos the number of integer digits to the right of * the current position. Zero indicates the position after the * rightmost integer digit. * @return true if a grouping character belongs at the current * position. */ UBool DecimalFormat::isGroupingPosition(int32_t pos) const { UBool result = FALSE; if (isGroupingUsed() && (pos > 0) && (fGroupingSize > 0)) { if ((fGroupingSize2 > 0) && (pos > fGroupingSize)) { result = ((pos - fGroupingSize) % fGroupingSize2) == 0; } else { result = pos % fGroupingSize == 0; } } return result; } //------------------------------------------------------------------------------ /** * Complete the formatting of a finite number. On entry, the DigitList must * be filled in with the correct digits. */ UnicodeString& DecimalFormat::subformat(UnicodeString& appendTo, FieldPositionHandler& handler, DigitList& digits, UBool isInteger) 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 const UnicodeString *grouping ; if(fCurrencySignCount > fgCurrencySignCountZero) { grouping = &getConstSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol); }else{ grouping = &getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); } const UnicodeString *decimal; if(fCurrencySignCount > fgCurrencySignCountZero) { decimal = &getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); } else { decimal = &getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); } UBool useSigDig = areSignificantDigitsUsed(); int32_t maxIntDig = getMaximumIntegerDigits(); int32_t minIntDig = getMinimumIntegerDigits(); // Appends the prefix. double doubleValue = digits.getDouble(); int32_t prefixLen = appendAffix(appendTo, doubleValue, handler, !digits.isPositive(), TRUE); if (fUseExponentialNotation) { int currentLength = appendTo.length(); int intBegin = currentLength; int intEnd = -1; int fracBegin = -1; int32_t minFracDig = 0; if (useSigDig) { maxIntDig = minIntDig = 1; minFracDig = getMinimumSignificantDigits() - 1; } else { minFracDig = getMinimumFractionDigits(); if (maxIntDig > kMaxScientificIntegerDigits) { maxIntDig = 1; if (maxIntDig < minIntDig) { maxIntDig = minIntDig; } } if (maxIntDig > minIntDig) { minIntDig = 1; } } // Minimum integer digits are handled in exponential format by // adjusting the exponent. For example, 0.01234 with 3 minimum // integer digits is "123.4E-4". // Maximum integer digits are interpreted as indicating the // repeating range. This is useful for engineering notation, in // which the exponent is restricted to a multiple of 3. For // example, 0.01234 with 3 maximum integer digits is "12.34e-3". // If maximum integer digits are defined and are larger than // minimum integer digits, then minimum integer digits are // ignored. 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 : (exponent / maxIntDig) - 1; exponent *= maxIntDig; } else { // No exponent increment is defined; use minimum integer digits. // If none is specified, as in "#E0", generate 1 integer digit. exponent -= (minIntDig > 0 || minFracDig > 0) ? minIntDig : 1; } // We now output a minimum number of digits, and more if there // are more digits, up to the maximum number of digits. We // place the decimal point after the "integer" digits, which // are the first (decimalAt - exponent) digits. int32_t minimumDigits = minIntDig + minFracDig; // 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.getDecimalAt() - exponent; int32_t totalDigits = digits.getCount(); if (minimumDigits > totalDigits) totalDigits = minimumDigits; if (integerDigits > totalDigits) totalDigits = integerDigits; // totalDigits records total number of digits needs to be processed int32_t i; for (i=0; i 0) { handler.addAttribute(kFractionField, fracBegin, currentLength); } // The exponent is output using the pattern-specified minimum // exponent digits. There is no maximum limit to the exponent // digits, since truncating the exponent would appendTo in an // 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. if (digits.isZero()) exponent = 0; 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); { int expDig = fMinExponentDigits; if (fUseExponentialNotation && expDig < 1) { expDig = 1; } for (i=expDigits.getDecimalAt(); i 0 && count < digits.getDecimalAt()) { count = digits.getDecimalAt(); } // Handle the case where getMaximumIntegerDigits() is smaller // than the real number of integer digits. If this is so, we // output the least significant max integer digits. For example, // the value 1997 printed with 2 max integer digits is just "97". int32_t digitIndex = 0; // Index into digitList.fDigits[] if (count > maxIntDig && maxIntDig >= 0) { count = maxIntDig; digitIndex = digits.getDecimalAt() - count; } int32_t sizeBeforeIntegerPart = appendTo.length(); int32_t i; for (i=count-1; i>=0; --i) { if (i < digits.getDecimalAt() && digitIndex < digits.getCount() && sigCount < maxSigDig) { // Output a real digit appendTo += ((UChar32)(digits.getDigit(digitIndex++) + zeroDelta)); ++sigCount; } else { // Output a zero (leading or trailing) appendTo += (zero); if (sigCount > 0) { ++sigCount; } } // Output grouping separator if necessary. if (isGroupingPosition(i)) { currentLength = appendTo.length(); appendTo.append(*grouping); handler.addAttribute(kGroupingSeparatorField, currentLength, appendTo.length()); } } // 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()); // 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.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); currentLength = appendTo.length(); handler.addAttribute(kIntegerField, intBegin, currentLength); // Output the decimal separator if we always do so. if (fDecimalSeparatorAlwaysShown || fractionPresent) { appendTo += *decimal; handler.addAttribute(kDecimalSeparatorField, currentLength, appendTo.length()); currentLength = appendTo.length(); } int fracBegin = currentLength; count = useSigDig ? INT32_MAX : getMaximumFractionDigits(); if (useSigDig && (sigCount == maxSigDig || (sigCount >= minSigDig && digitIndex == digits.getCount()))) { count = 0; } for (i=0; i < count; ++i) { // Here is where we escape from the loop. We escape // if we've output the maximum fraction digits // (specified in the for expression above). We also // stop when we've output the minimum digits and // either: we have an integer, so there is no // fractional stuff to display, or we're out of // significant digits. if (!useSigDig && i >= getMinimumFractionDigits() && (isInteger || digitIndex >= digits.getCount())) { break; } // Output leading fractional zeros. These are zeros // 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.getDecimalAt()-1)) { appendTo += zero; 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.getCount()) { appendTo += ((UChar32)(digits.getDigit(digitIndex++) + zeroDelta)); } else { appendTo += zero; } // If we reach the maximum number of significant // digits, or if we output all the real digits and // reach the minimum, then we are done. ++sigCount; if (useSigDig && (sigCount == maxSigDig || (digitIndex == digits.getCount() && sigCount >= minSigDig))) { break; } } handler.addAttribute(kFractionField, fracBegin, appendTo.length()); } int32_t suffixLen = appendAffix(appendTo, doubleValue, handler, !digits.isPositive(), FALSE); addPadding(appendTo, handler, prefixLen, suffixLen); return appendTo; } /** * Inserts the character fPad as needed to expand result to fFormatWidth. * @param result the string to be padded */ void DecimalFormat::addPadding(UnicodeString& appendTo, FieldPositionHandler& handler, int32_t prefixLen, int32_t suffixLen) const { if (fFormatWidth > 0) { int32_t len = fFormatWidth - appendTo.length(); if (len > 0) { UnicodeString padding; for (int32_t i=0; i 0 && (fPadPosition == kPadBeforePrefix || fPadPosition == kPadAfterPrefix)) { i = skipPadding(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) ? 0 : nan->length()); if (nanLen) { i += nanLen; if (fFormatWidth > 0 && (fPadPosition == kPadBeforeSuffix || fPadPosition == kPadAfterSuffix)) { i = skipPadding(text, i); } parsePosition.setIndex(i); result.setDouble(uprv_getNaN()); return; } // NaN parse failed; start over i = backup; // status is used to record whether a number is infinite. UBool status[fgStatusLength]; UChar curbuf[4]; UChar* currency = parseCurrency ? curbuf : NULL; DigitList *digits = new DigitList; if (digits == NULL) { return; // no way to report error from here. } if (fCurrencySignCount > fgCurrencySignCountZero) { if (!parseForCurrency(text, parsePosition, *digits, status, currency)) { delete digits; return; } } else { if (!subparse(text, fNegPrefixPattern, fNegSuffixPattern, fPosPrefixPattern, fPosSuffixPattern, FALSE, UCURR_SYMBOL_NAME, parsePosition, *digits, status, currency)) { parsePosition.setIndex(backup); delete digits; return; } } // Handle infinity if (status[fgStatusInfinite]) { double inf = uprv_getInfinity(); result.setDouble(digits->isPositive() ? inf : -inf); delete digits; // TODO: set the dl to infinity, and let it fall into the code below. } else { if (fMultiplier != NULL) { UErrorCode ec = U_ZERO_ERROR; digits->div(*fMultiplier, ec); } // Negative zero special case: // if parsing integerOnly, change to +0, which goes into an int32 in a Formattable. // if not parsing integerOnly, leave as -0, which a double can represent. if (digits->isZero() && !digits->isPositive() && isParseIntegerOnly()) { digits->setPositive(TRUE); } result.adoptDigitList(digits); } 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 } } 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 == NumberFormat::kPluralCurrencyStyle) { 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]; } digits = tmpDigitList; } } else { maxErrorPos = tmpPos.getErrorIndex(); } // Then, parse against affix patterns. // Those are currency patterns and currency plural patterns. int32_t pos = -1; const UHashElement* element = NULL; while ( (element = fAffixPatternsForCurrency->nextElement(pos)) != NULL ) { const UHashTok keyTok = element->key; const UHashTok valueTok = element->value; const AffixPatternsForCurrency* affixPtn = (AffixPatternsForCurrency*)valueTok.pointer; UBool tmpStatus[fgStatusLength]; ParsePosition tmpPos(origPos); DigitList tmpDigitList; UBool result = subparse(text, &affixPtn->negPrefixPatternForCurrency, &affixPtn->negSuffixPatternForCurrency, &affixPtn->posPrefixPatternForCurrency, &affixPtn->posSuffixPatternForCurrency, TRUE, affixPtn->patternType, tmpPos, tmpDigitList, tmpStatus, currency); if (result) { found = true; if (tmpPos.getIndex() > maxPosIndex) { maxPosIndex = tmpPos.getIndex(); for (int32_t i = 0; i < fgStatusLength; ++i) { status[i] = tmpStatus[i]; } digits = tmpDigitList; } } else { maxErrorPos = (tmpPos.getErrorIndex() > maxErrorPos) ? tmpPos.getErrorIndex() : maxErrorPos; } } // Finally, parse against simple affix to find the match. // For example, in TestMonster suite, // if the to-be-parsed text is "-\u00A40,00". // complexAffixCompare will not find match, // since there is no ISO code matches "\u00A4", // and the parse stops at "\u00A4". // We will just use simple affix comparison (look for exact match) // to pass it. UBool tmpStatus_2[fgStatusLength]; ParsePosition tmpPos_2(origPos); DigitList tmpDigitList_2; // set currencySignCount to 0 so that compareAffix function will // fall to compareSimpleAffix path, not compareComplexAffix path. // ?? TODO: is it right? need "false"? UBool result = subparse(text, &fNegativePrefix, &fNegativeSuffix, &fPositivePrefix, &fPositiveSuffix, FALSE, UCURR_SYMBOL_NAME, tmpPos_2, tmpDigitList_2, tmpStatus_2, currency); if (result) { if (tmpPos_2.getIndex() > maxPosIndex) { maxPosIndex = tmpPos_2.getIndex(); for (int32_t i = 0; i < fgStatusLength; ++i) { status[i] = tmpStatus_2[i]; } digits = tmpDigitList_2; } found = true; } else { maxErrorPos = (tmpPos_2.getErrorIndex() > maxErrorPos) ? tmpPos_2.getErrorIndex() : maxErrorPos; } if (!found) { //parsePosition.setIndex(origPos); parsePosition.setErrorIndex(maxErrorPos); } else { parsePosition.setIndex(maxPosIndex); parsePosition.setErrorIndex(-1); } return found; } /** * Parse the given text into a number. The text is parsed beginning at * parsePosition, until an unparseable character is seen. * @param text the string to parse. * @param negPrefix negative prefix. * @param negSuffix negative suffix. * @param posPrefix positive prefix. * @param posSuffix positive suffix. * @param currencyParsing whether it is currency parsing or not. * @param type the currency type to parse against, LONG_NAME only or not. * @param parsePosition The position at which to being parsing. Upon * return, the first unparsed character. * @param digits the DigitList to set to the parsed value. * @param status output param containing boolean status flags indicating * whether the value was infinite and whether it was positive. * @param currency return value for parsed currency, for generic * currency parsing mode, or NULL for normal parsing. In generic * currency parsing mode, any currency is parsed, not just the * currency that this formatter is set to. */ UBool DecimalFormat::subparse(const UnicodeString& text, const UnicodeString* negPrefix, const UnicodeString* negSuffix, const UnicodeString* posPrefix, const UnicodeString* posSuffix, UBool currencyParsing, 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; // 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, posPrefix, currencyParsing, type, currency); int32_t negMatch = compareAffix(text, position, TRUE, TRUE, negPrefix,currencyParsing, type, currency); if (posMatch >= 0 && negMatch >= 0) { if (posMatch > negMatch) { negMatch = -1; } else if (negMatch > posMatch) { posMatch = -1; } } if (posMatch >= 0) { position += posMatch; parsedNum.append('+', err); } else if (negMatch >= 0) { position += negMatch; parsedNum.append('-', err); } else { parsePosition.setErrorIndex(position); return FALSE; } // Match padding before prefix if (fFormatWidth > 0 && fPadPosition == kPadAfterPrefix) { position = skipPadding(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] = (UBool)infLen; if (infLen) { parsedNum.append("Infinity", err); } else { // We now have a string of digits, possibly with grouping symbols, // and decimal points. We want to process these into a DigitList. // We don't want to put a bunch of leading zeros into the DigitList // though, so we keep track of the location of the decimal point, // put only significant digits into the DigitList, and adjust the // exponent as needed. UChar32 zero = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); const UnicodeString *decimal; if(fCurrencySignCount > fgCurrencySignCountZero) { decimal = &getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); } else { decimal = &getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); } const UnicodeString *grouping = &getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); UBool sawDecimal = FALSE; UBool sawDigit = FALSE; int32_t backup = -1; int32_t digit; int32_t textLength = text.length(); // One less pointer to follow int32_t groupingLen = grouping->length(); int32_t decimalLen = decimal->length(); // We have to track digitCount ourselves, because digits.fCount will // pin when the maximum allowable digits is reached. int32_t digitCount = 0; for (; position < textLength; ) { UChar32 ch = text.char32At(position); /* We recognize all digit ranges, not only the Latin digit range * '0'..'9'. We do so by using the Character.digit() method, * which converts a valid Unicode digit to the range 0..9. * * The character 'ch' may be a digit. If so, place its value * from 0 to 9 in 'digit'. First try using the locale digit, * which may or MAY NOT be a standard Unicode digit range. If * this fails, try using the standard Unicode digit ranges by * calling Character.digit(). If this also fails, digit will * have a value outside the range 0..9. */ digit = ch - zero; if (digit < 0 || digit > 9) { digit = u_charDigitValue(ch); } if (digit >= 0 && digit <= 9) { // Cancel out backup setting (see grouping handler below) backup = -1; sawDigit = TRUE; // output a regular non-zero digit. ++digitCount; parsedNum.append((char)(digit + '0'), err); position += U16_LENGTH(ch); } else if (groupingLen > 0 && !text.compare(position, groupingLen, *grouping) && isGroupingUsed()) { // 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 += groupingLen; } else if (!text.compare(position, decimalLen, *decimal) && !isParseIntegerOnly() && !sawDecimal) { // If we're only parsing integers, or if we ALREADY saw the // decimal, then don't parse this one. parsedNum.append('.', err); sawDecimal = TRUE; position += decimalLen; } 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(); char exponentSign = '+'; if (pos < textLength) { tmp = &getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); if (!text.compare(pos, tmp->length(), *tmp)) { pos += tmp->length(); } else { tmp = &getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); if (!text.compare(pos, tmp->length(), *tmp)) { exponentSign = '-'; pos += tmp->length(); } } } UBool sawExponentDigit = FALSE; while (pos < textLength) { ch = text[(int32_t)pos]; digit = ch - zero; if (digit < 0 || digit > 9) { digit = u_charDigitValue(ch); } if (0 <= digit && digit <= 9) { if (!sawExponentDigit) { parsedNum.append('E', err); parsedNum.append(exponentSign, err); sawExponentDigit = TRUE; } ++pos; parsedNum.append((char)(digit + '0'), err); } else { break; } } if (sawExponentDigit) { position = pos; // Advance past the exponent } break; // Whether we fail or succeed, we exit this loop } else { break; } } } if (backup != -1) { position = backup; } // If there was no decimal point we have an integer // If none of the text string was recognized. For example, parse // "x" with pattern "#0.00" (return index and error index both 0) // parse "$" with pattern "$#0.00". (return index 0 and error index // 1). if (!sawDigit && digitCount == 0) { parsePosition.setIndex(oldStart); parsePosition.setErrorIndex(oldStart); return FALSE; } } // Match padding before suffix if (fFormatWidth > 0 && fPadPosition == kPadBeforeSuffix) { position = skipPadding(text, position); } // Match positive and negative suffixes; prefer longest match. if (posMatch >= 0) { posMatch = compareAffix(text, position, FALSE, FALSE, posSuffix, currencyParsing, type, currency); } if (negMatch >= 0) { negMatch = compareAffix(text, position, TRUE, FALSE, negSuffix, currencyParsing, type, currency); } if (posMatch >= 0 && negMatch >= 0) { if (posMatch > negMatch) { negMatch = -1; } else if (negMatch > posMatch) { posMatch = -1; } } // Fail if neither or both if ((posMatch >= 0) == (negMatch >= 0)) { parsePosition.setErrorIndex(position); return FALSE; } position += (posMatch>=0 ? posMatch : negMatch); // Match padding before suffix if (fFormatWidth > 0 && fPadPosition == kPadAfterSuffix) { position = skipPadding(text, position); } parsePosition.setIndex(position); parsedNum.data()[0] = (posMatch >= 0) ? '+' : '-'; if(parsePosition.getIndex() == oldStart) { parsePosition.setErrorIndex(position); return FALSE; } digits.set(parsedNum.toStringPiece(), err); if (U_FAILURE(err)) { parsePosition.setErrorIndex(position); return FALSE; } return TRUE; } /** * Starting at position, advance past a run of pad characters, if any. * Return the index of the first character after position that is not a pad * character. Result is >= position. */ int32_t DecimalFormat::skipPadding(const UnicodeString& text, int32_t position) const { int32_t padLen = U16_LENGTH(fPad); while (position < text.length() && text.char32At(position) == fPad) { position += padLen; } return position; } /** * Return the length matched by the given affix, or -1 if none. * Runs of white space in the affix, match runs of white space in * the input. Pattern white space and input white space are * determined differently; see code. * @param text input text * @param pos offset into input at which to begin matching * @param isNegative * @param isPrefix * @param affixPat affix pattern used for currency affix comparison. * @param currencyParsing whether it is currency parsing or not * @param type the currency type to parse against, LONG_NAME only or not. * @param currency return value for parsed currency, for generic * currency parsing mode, or null for normal parsing. In generic * currency parsing mode, any currency is parsed, not just the * currency that this formatter is set to. * @return length of input that matches, or -1 if match failure */ int32_t DecimalFormat::compareAffix(const UnicodeString& text, int32_t pos, UBool isNegative, UBool isPrefix, const UnicodeString* affixPat, UBool currencyParsing, int8_t type, UChar* currency) const { const UnicodeString *patternToCompare; if (fCurrencyChoice != NULL || currency != NULL || (fCurrencySignCount > fgCurrencySignCountZero && currencyParsing)) { if (affixPat != NULL) { return compareComplexAffix(*affixPat, text, pos, type, currency); } } if (isNegative) { if (isPrefix) { patternToCompare = &fNegativePrefix; } else { patternToCompare = &fNegativeSuffix; } } else { if (isPrefix) { patternToCompare = &fPositivePrefix; } else { patternToCompare = &fPositiveSuffix; } } return compareSimpleAffix(*patternToCompare, text, pos); } /** * Return the length matched by the given affix, or -1 if none. * Runs of white space in the affix, match runs of white space in * the input. Pattern white space and input white space are * determined differently; see code. * @param affix pattern string, taken as a literal * @param input input text * @param pos offset into input at which to begin matching * @return length of input that matches, or -1 if match failure */ int32_t DecimalFormat::compareSimpleAffix(const UnicodeString& affix, const UnicodeString& input, int32_t pos) { int32_t start = pos; for (int32_t i=0; i fgCurrencySignCountZero); for (int32_t i=0; i= 0; ) { UChar32 c = affixPat.char32At(i); i += U16_LENGTH(c); if (c == kQuote) { U_ASSERT(i <= affixPat.length()); c = affixPat.char32At(i); i += U16_LENGTH(c); const UnicodeString* affix = NULL; switch (c) { case kCurrencySign: { // since the currency names in choice format is saved // the same way as other currency names, // do not need to do currency choice parsing here. // the general currency parsing parse against all names, // including names in choice format. UBool intl = igetLocale().getName(); ParsePosition ppos(pos); 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 { pos = -1; } continue; } case kPatternPercent: affix = &getConstSymbol(DecimalFormatSymbols::kPercentSymbol); break; case kPatternPerMill: affix = &getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); break; case kPatternPlus: affix = &getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); break; case kPatternMinus: affix = &getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); break; default: // fall through to affix!=0 test, which will fail break; } if (affix != NULL) { pos = match(text, pos, *affix); continue; } } pos = match(text, pos, c); if (uprv_isRuleWhiteSpace(c)) { i = skipRuleWhiteSpace(affixPat, i); } } return pos - start; } /** * Match a single character at text[pos] and return the index of the * next character upon success. Return -1 on failure. If * isRuleWhiteSpace(ch) 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)) { // Advance over run of white space in input text // Must see at least one white space char in input int32_t s = pos; pos = skipRuleWhiteSpace(text, pos); if (pos == s) { return -1; } return pos; } return (pos >= 0 && text.char32At(pos) == ch) ? (pos + U16_LENGTH(ch)) : -1; } /** * Match a string at text[pos] and return the index of the next * character upon success. Return -1 on failure. Match a run of * white space in str with a run of white space in text. */ int32_t DecimalFormat::match(const UnicodeString& text, int32_t pos, const UnicodeString& str) { for (int32_t i=0; i= 0; ) { UChar32 ch = str.char32At(i); i += U16_LENGTH(ch); if (uprv_isRuleWhiteSpace(ch)) { i = skipRuleWhiteSpace(str, i); } pos = match(text, pos, ch); } return pos; } //------------------------------------------------------------------------------ // Gets the pointer to the localized decimal format symbols const DecimalFormatSymbols* DecimalFormat::getDecimalFormatSymbols() const { return fSymbols; } //------------------------------------------------------------------------------ // De-owning the current localized symbols and adopt the new symbols. void DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) { if (symbolsToAdopt == NULL) { return; // do not allow caller to set fSymbols to NULL } UBool sameSymbols = FALSE; if (fSymbols != NULL) { sameSymbols = (UBool)(getConstSymbol(DecimalFormatSymbols::kCurrencySymbol) == symbolsToAdopt->getConstSymbol(DecimalFormatSymbols::kCurrencySymbol) && getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol) == symbolsToAdopt->getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol)); delete fSymbols; } fSymbols = symbolsToAdopt; if (!sameSymbols) { // If the currency symbols are the same, there is no need to recalculate. setCurrencyForSymbols(); } expandAffixes(NULL); } //------------------------------------------------------------------------------ // Setting the symbols is equlivalent to adopting a newly created localized // symbols. void DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) { adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols)); } const CurrencyPluralInfo* DecimalFormat::getCurrencyPluralInfo(void) const { return fCurrencyPluralInfo; } void DecimalFormat::adoptCurrencyPluralInfo(CurrencyPluralInfo* toAdopt) { if (toAdopt != NULL) { delete fCurrencyPluralInfo; fCurrencyPluralInfo = toAdopt; // re-set currency affix patterns and currency affixes. if (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); } } } } void DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) { adoptCurrencyPluralInfo(info.clone()); } /** * Update the currency object to match the symbols. This method * is used only when the caller has passed in a symbols object * that may not be the default object for its locale. */ void DecimalFormat::setCurrencyForSymbols() { /*Bug 4212072 Update the affix strings accroding to symbols in order to keep the affix strings up to date. [Richard/GCL] */ // With the introduction of the Currency object, the currency // symbols in the DFS object are ignored. For backward // compatibility, we check any explicitly set DFS object. If it // is a default symbols object for its locale, we change the // currency object to one for that locale. If it is custom, // we set the currency to null. UErrorCode ec = U_ZERO_ERROR; const UChar* c = NULL; const char* loc = fSymbols->getLocale().getName(); 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) { // 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! setCurrencyInternally(c, ec); } //------------------------------------------------------------------------------ // 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; } //------------------------------------------------------------------------------ // Gets the negative prefix of the number pattern. UnicodeString& DecimalFormat::getNegativePrefix(UnicodeString& result) const { result = fNegativePrefix; return result; } //------------------------------------------------------------------------------ // Gets the negative prefix of the number pattern. void DecimalFormat::setNegativePrefix(const UnicodeString& newValue) { fNegativePrefix = newValue; delete fNegPrefixPattern; fNegPrefixPattern = 0; } //------------------------------------------------------------------------------ // Gets the positive suffix of the number pattern. UnicodeString& DecimalFormat::getPositiveSuffix(UnicodeString& result) const { result = fPositiveSuffix; return result; } //------------------------------------------------------------------------------ // Sets the positive suffix of the number pattern. void DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) { fPositiveSuffix = newValue; delete fPosSuffixPattern; fPosSuffixPattern = 0; } //------------------------------------------------------------------------------ // Gets the negative suffix of the number pattern. UnicodeString& DecimalFormat::getNegativeSuffix(UnicodeString& result) const { result = fNegativeSuffix; return result; } //------------------------------------------------------------------------------ // Sets the negative suffix of the number pattern. void DecimalFormat::setNegativeSuffix(const UnicodeString& newValue) { fNegativeSuffix = newValue; delete fNegSuffixPattern; fNegSuffixPattern = 0; } //------------------------------------------------------------------------------ // 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 { if (fMultiplier == NULL) { return 1; } else { return fMultiplier->getLong(); } } //------------------------------------------------------------------------------ // Sets the multiplier of the number pattern. void DecimalFormat::setMultiplier(int32_t newValue) { // if (newValue == 0) { // throw new IllegalArgumentException("Bad multiplier: " + newValue); // } if (newValue == 0) { newValue = 1; // one being the benign default value for a multiplier. } if (newValue == 1) { delete fMultiplier; fMultiplier = NULL; } else { if (fMultiplier == NULL) { fMultiplier = new DigitList; } if (fMultiplier != NULL) { fMultiplier->set(newValue); } } } /** * Get the rounding increment. * @return A positive rounding increment, or 0.0 if rounding * is not in effect. * @see #setRoundingIncrement * @see #getRoundingMode * @see #setRoundingMode */ double DecimalFormat::getRoundingIncrement() const { if (fRoundingIncrement == NULL) { return 0.0; } else { return fRoundingIncrement->getDouble(); } } /** * Set the rounding increment. This method also controls whether * rounding is enabled. * @param newValue A positive rounding increment, or 0.0 to disable rounding. * Negative increments are equivalent to 0.0. * @see #getRoundingIncrement * @see #getRoundingMode * @see #setRoundingMode */ void DecimalFormat::setRoundingIncrement(double newValue) { if (newValue > 0.0) { if (fRoundingIncrement == NULL) { fRoundingIncrement = new DigitList(); } if (fRoundingIncrement != NULL) { 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; } /** * Get the rounding mode. * @return A rounding mode * @see #setRoundingIncrement * @see #getRoundingIncrement * @see #setRoundingMode */ DecimalFormat::ERoundingMode DecimalFormat::getRoundingMode() const { return fRoundingMode; } /** * Set the rounding mode. This has no effect unless the rounding * increment is greater than zero. * @param roundingMode A rounding mode * @see #setRoundingIncrement * @see #getRoundingIncrement * @see #getRoundingMode */ void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) { fRoundingMode = roundingMode; } /** * Get the width to which the output of format() is padded. * @return the format width, or zero if no padding is in effect * @see #setFormatWidth * @see #getPadCharacter * @see #setPadCharacter * @see #getPadPosition * @see #setPadPosition */ int32_t DecimalFormat::getFormatWidth() const { return fFormatWidth; } /** * Set the width to which the output of format() is padded. * This method also controls whether padding is enabled. * @param width the width to which to pad the result of * format(), or zero to disable padding. A negative * width is equivalent to 0. * @see #getFormatWidth * @see #getPadCharacter * @see #setPadCharacter * @see #getPadPosition * @see #setPadPosition */ void DecimalFormat::setFormatWidth(int32_t width) { fFormatWidth = (width > 0) ? width : 0; } UnicodeString DecimalFormat::getPadCharacterString() const { return fPad; } void DecimalFormat::setPadCharacter(const UnicodeString &padChar) { if (padChar.length() > 0) { fPad = padChar.char32At(0); } else { fPad = kDefaultPad; } } /** * Get the position at which padding will take place. This is the location * at which padding will be inserted if the result of format() * is shorter than the format width. * @return the pad position, one of kPadBeforePrefix, * kPadAfterPrefix, kPadBeforeSuffix, or * kPadAfterSuffix. * @see #setFormatWidth * @see #getFormatWidth * @see #setPadCharacter * @see #getPadCharacter * @see #setPadPosition * @see #kPadBeforePrefix * @see #kPadAfterPrefix * @see #kPadBeforeSuffix * @see #kPadAfterSuffix */ DecimalFormat::EPadPosition DecimalFormat::getPadPosition() const { return fPadPosition; } /** * NEW * Set the position at which padding will take place. This is the location * at which padding will be inserted if the result of format() * is shorter than the format width. This has no effect unless padding is * enabled. * @param padPos the pad position, one of kPadBeforePrefix, * kPadAfterPrefix, kPadBeforeSuffix, or * kPadAfterSuffix. * @see #setFormatWidth * @see #getFormatWidth * @see #setPadCharacter * @see #getPadCharacter * @see #getPadPosition * @see #kPadBeforePrefix * @see #kPadAfterPrefix * @see #kPadBeforeSuffix * @see #kPadAfterSuffix */ void DecimalFormat::setPadPosition(EPadPosition padPos) { fPadPosition = padPos; } /** * Return whether or not scientific notation is used. * @return TRUE if this object formats and parses scientific notation * @see #setScientificNotation * @see #getMinimumExponentDigits * @see #setMinimumExponentDigits * @see #isExponentSignAlwaysShown * @see #setExponentSignAlwaysShown */ UBool DecimalFormat::isScientificNotation() { return fUseExponentialNotation; } /** * Set whether or not scientific notation is used. * @param useScientific TRUE if this object formats and parses scientific * notation * @see #isScientificNotation * @see #getMinimumExponentDigits * @see #setMinimumExponentDigits * @see #isExponentSignAlwaysShown * @see #setExponentSignAlwaysShown */ void DecimalFormat::setScientificNotation(UBool useScientific) { fUseExponentialNotation = useScientific; } /** * Return the minimum exponent digits that will be shown. * @return the minimum exponent digits that will be shown * @see #setScientificNotation * @see #isScientificNotation * @see #setMinimumExponentDigits * @see #isExponentSignAlwaysShown * @see #setExponentSignAlwaysShown */ int8_t DecimalFormat::getMinimumExponentDigits() const { return fMinExponentDigits; } /** * Set the minimum exponent digits that will be shown. This has no * effect unless scientific notation is in use. * @param minExpDig a value >= 1 indicating the fewest exponent digits * that will be shown. Values less than 1 will be treated as 1. * @see #setScientificNotation * @see #isScientificNotation * @see #getMinimumExponentDigits * @see #isExponentSignAlwaysShown * @see #setExponentSignAlwaysShown */ void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) { fMinExponentDigits = (int8_t)((minExpDig > 0) ? minExpDig : 1); } /** * Return whether the exponent sign is always shown. * @return TRUE if the exponent is always prefixed with either the * localized minus sign or the localized plus sign, false if only negative * exponents are prefixed with the localized minus sign. * @see #setScientificNotation * @see #isScientificNotation * @see #setMinimumExponentDigits * @see #getMinimumExponentDigits * @see #setExponentSignAlwaysShown */ UBool DecimalFormat::isExponentSignAlwaysShown() { return fExponentSignAlwaysShown; } /** * Set whether the exponent sign is always shown. This has no effect * unless scientific notation is in use. * @param expSignAlways TRUE if the exponent is always prefixed with either * the localized minus sign or the localized plus sign, false if only * negative exponents are prefixed with the localized minus sign. * @see #setScientificNotation * @see #isScientificNotation * @see #setMinimumExponentDigits * @see #getMinimumExponentDigits * @see #isExponentSignAlwaysShown */ void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) { fExponentSignAlwaysShown = expSignAlways; } //------------------------------------------------------------------------------ // Gets the grouping size of the number pattern. For example, thousand or 10 // thousand groupings. int32_t DecimalFormat::getGroupingSize() const { return fGroupingSize; } //------------------------------------------------------------------------------ // Gets the grouping size of the number pattern. void DecimalFormat::setGroupingSize(int32_t newValue) { fGroupingSize = newValue; } //------------------------------------------------------------------------------ int32_t DecimalFormat::getSecondaryGroupingSize() const { return fGroupingSize2; } //------------------------------------------------------------------------------ void DecimalFormat::setSecondaryGroupingSize(int32_t newValue) { fGroupingSize2 = newValue; } //------------------------------------------------------------------------------ // Checks if to show the decimal separator. UBool DecimalFormat::isDecimalSeparatorAlwaysShown() const { return fDecimalSeparatorAlwaysShown; } //------------------------------------------------------------------------------ // Sets to always show the decimal separator. void DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) { fDecimalSeparatorAlwaysShown = newValue; } //------------------------------------------------------------------------------ // Emits the pattern of this DecimalFormat instance. UnicodeString& DecimalFormat::toPattern(UnicodeString& result) const { return toPattern(result, FALSE); } //------------------------------------------------------------------------------ // Emits the localized pattern this DecimalFormat instance. UnicodeString& DecimalFormat::toLocalizedPattern(UnicodeString& result) const { return toPattern(result, TRUE); } //------------------------------------------------------------------------------ /** * Expand the affix pattern strings into the expanded affix strings. If any * 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(const UnicodeString* pluralCount) { FieldPositionHandler none; if (fPosPrefixPattern != 0) { expandAffix(*fPosPrefixPattern, fPositivePrefix, 0, none, FALSE, pluralCount); } if (fPosSuffixPattern != 0) { expandAffix(*fPosSuffixPattern, fPositiveSuffix, 0, none, FALSE, pluralCount); } if (fNegPrefixPattern != 0) { expandAffix(*fNegPrefixPattern, fNegativePrefix, 0, none, FALSE, pluralCount); } if (fNegSuffixPattern != 0) { 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"); debugout(s); #endif } /** * Expand an affix pattern into an affix string. All characters in the * pattern are literal unless prefixed by kQuote. The following characters * 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. 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. * * This method is used in two distinct ways. First, it is used to expand * the stored affix patterns into actual affixes. For this usage, doFormat * must be false. Second, it is used to expand the stored affix patterns * given a specific number (doFormat == true), for those rare cases in * which a currency format references a ChoiceFormat (e.g., en_IN display * name for INR). The number itself is taken from digitList. * * When used in the first way, this method has a side effect: It sets * currencyChoice to a ChoiceFormat object, if the currency's display name * in this locale is a ChoiceFormat pattern (very rare). It only does this * if currencyChoice is null to start with. * * @param pattern the non-null, fPossibly empty pattern * @param affix string to receive the expanded equivalent of pattern. * Previous contents are deleted. * @param doFormat if false, then the pattern will be expanded, and if a * currency symbol is encountered that expands to a ChoiceFormat, the * 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, FieldPositionHandler& handler, UBool doFormat, const UnicodeString* pluralCount) const { affix.remove(); for (int i=0; ilength() >= 10) { break; } pluralCount->extract(0, pluralCount->length(), pluralCountChar); UBool isChoiceFormat; const UChar* s = ucurr_getPluralName(currencyUChars, fSymbols != NULL ? fSymbols->getLocale().getName() : Locale::getDefault().getName(), &isChoiceFormat, pluralCountChar, &len, &ec); affix += UnicodeString(s, len); handler.addAttribute(kCurrencyField, beginIdx, affix.length()); } else if(intl) { affix += currencyUChars; 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); if (isChoiceFormat) { // Two modes here: If doFormat is false, we set up // currencyChoice. If doFormat is true, we use the // previously created currencyChoice to format the // value in digitList. if (!doFormat) { // If the currency is handled by a ChoiceFormat, // then we're not going to use the expanded // patterns. Instantiate the ChoiceFormat and // return. if (fCurrencyChoice == NULL) { // TODO Replace double-check with proper thread-safe code ChoiceFormat* fmt = new ChoiceFormat(s, ec); if (U_SUCCESS(ec)) { umtx_lock(NULL); if (fCurrencyChoice == NULL) { // Cast away const ((DecimalFormat*)this)->fCurrencyChoice = fmt; fmt = NULL; } umtx_unlock(NULL); delete fmt; } } // We could almost return null or "" here, since the // expanded affixes are almost not used at all // in this situation. However, one method -- // toPattern() -- still does use the expanded // affixes, in order to set up a padding // pattern. We use the CURRENCY_SIGN as a // placeholder. affix.append(kCurrencySign); } else { if (fCurrencyChoice != NULL) { FieldPosition pos(0); // ignored if (number < 0) { number = -number; } fCurrencyChoice->format(number, affix, pos); } else { // We only arrive here if the currency choice // format in the locale data is INVALID. affix += currencyUChars; handler.addAttribute(kCurrencyField, beginIdx, affix.length()); } } continue; } affix += UnicodeString(s, len); handler.addAttribute(kCurrencyField, beginIdx, affix.length()); } } else { if(intl) { affix += getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol); } 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); break; } } else { affix.append(c); } } } /** * Append an affix to the given StringBuffer. * @param buf buffer to append to * @param isNegative * @param isPrefix */ int32_t DecimalFormat::appendAffix(UnicodeString& buf, double number, FieldPositionHandler& handler, UBool isNegative, UBool isPrefix) const { // plural format precedes choice format if (fCurrencyChoice != 0 && fCurrencySignCount != fgCurrencySignCountInPluralFormat) { const UnicodeString* affixPat; if (isPrefix) { affixPat = isNegative ? fNegPrefixPattern : fPosPrefixPattern; } else { affixPat = isNegative ? fNegSuffixPattern : fPosSuffixPattern; } if (affixPat) { UnicodeString affixBuf; 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 (fCurrencySignCount == fgCurrencySignCountInPluralFormat) { UnicodeString pluralCount = fCurrencyPluralInfo->getPluralRules()->select(number); AffixesForCurrency* oneSet; if (fStyle == NumberFormat::kPluralCurrencyStyle) { 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 { 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(); } /** * Appends an affix pattern to the given StringBuffer, quoting special * characters as needed. Uses the internal affix pattern, if that exists, * or the literal affix, if the internal affix pattern is null. The * appended string will generate the same affix pattern (or literal affix) * when passed to toPattern(). * * @param appendTo the affix string is appended to this * @param affixPattern a pattern such as fPosPrefixPattern; may be null * @param expAffix a corresponding expanded affix, such as fPositivePrefix. * Ignored unless affixPattern is null. If affixPattern is null, then * expAffix is appended as a literal affix. * @param localized true if the appended pattern should contain localized * pattern characters; otherwise, non-localized pattern chars are appended */ void DecimalFormat::appendAffixPattern(UnicodeString& appendTo, const UnicodeString* affixPattern, const UnicodeString& expAffix, UBool localized) const { if (affixPattern == 0) { appendAffixPattern(appendTo, expAffix, localized); } else { int i; for (int pos=0; poslength(); pos=i) { i = affixPattern->indexOf(kQuote, pos); if (i < 0) { UnicodeString s; affixPattern->extractBetween(pos, affixPattern->length(), s); appendAffixPattern(appendTo, s, localized); break; } if (i > pos) { UnicodeString s; affixPattern->extractBetween(pos, i, s); appendAffixPattern(appendTo, s, localized); } UChar32 c = affixPattern->char32At(++i); ++i; if (c == kQuote) { appendTo.append(c).append(c); // Fall through and append another kQuote below } else if (c == kCurrencySign && ilength() && affixPattern->char32At(i) == kCurrencySign) { ++i; appendTo.append(c).append(c); } else if (localized) { switch (c) { case kPatternPercent: appendTo += getConstSymbol(DecimalFormatSymbols::kPercentSymbol); break; case kPatternPerMill: appendTo += getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); break; case kPatternPlus: appendTo += getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); break; case kPatternMinus: appendTo += getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); break; default: appendTo.append(c); } } else { appendTo.append(c); } } } } /** * Append an affix to the given StringBuffer, using quotes if * there are special characters. Single quotes themselves must be * escaped in either case. */ void DecimalFormat::appendAffixPattern(UnicodeString& appendTo, const UnicodeString& affix, UBool localized) const { UBool needQuote; if(localized) { needQuote = affix.indexOf(getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol)) >= 0 || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)) >= 0 || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)) >= 0 || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kPercentSymbol)) >= 0 || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kPerMillSymbol)) >= 0 || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kDigitSymbol)) >= 0 || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol)) >= 0 || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol)) >= 0 || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol)) >= 0 || affix.indexOf(kCurrencySign) >= 0; } else { needQuote = affix.indexOf(kPatternZeroDigit) >= 0 || affix.indexOf(kPatternGroupingSeparator) >= 0 || affix.indexOf(kPatternDecimalSeparator) >= 0 || affix.indexOf(kPatternPercent) >= 0 || affix.indexOf(kPatternPerMill) >= 0 || affix.indexOf(kPatternDigit) >= 0 || affix.indexOf(kPatternSeparator) >= 0 || affix.indexOf(kPatternExponent) >= 0 || affix.indexOf(kPatternPlus) >= 0 || affix.indexOf(kPatternMinus) >= 0 || affix.indexOf(kCurrencySign) >= 0; } if (needQuote) appendTo += (UChar)0x0027 /*'\''*/; if (affix.indexOf((UChar)0x0027 /*'\''*/) < 0) appendTo += affix; else { for (int32_t j = 0; j < affix.length(); ) { UChar32 c = affix.char32At(j); j += U16_LENGTH(c); appendTo += c; if (c == 0x0027 /*'\''*/) appendTo += c; } } if (needQuote) appendTo += (UChar)0x0027 /*'\''*/; } //------------------------------------------------------------------------------ UnicodeString& DecimalFormat::toPattern(UnicodeString& result, UBool localized) const { if (fStyle == NumberFormat::kPluralCurrencyStyle) { // 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; int32_t i; int32_t roundingDecimalPos = 0; // Pos of decimal in roundingDigits UnicodeString roundingDigits; int32_t padPos = (fFormatWidth > 0) ? fPadPosition : -1; UnicodeString padSpec; UBool useSigDig = areSignificantDigitsUsed(); if (localized) { digit.append(getConstSymbol(DecimalFormatSymbols::kDigitSymbol)); group.append(getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)); zero = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); if (useSigDig) { sigDigit = getConstSymbol(DecimalFormatSymbols::kSignificantDigitSymbol).char32At(0); } } else { digit.append((UChar)kPatternDigit); group.append((UChar)kPatternGroupingSeparator); zero = (UChar32)kPatternZeroDigit; } if (fFormatWidth > 0) { if (localized) { padSpec.append(getConstSymbol(DecimalFormatSymbols::kPadEscapeSymbol)); } else { padSpec.append((UChar)kPatternPadEscape); } padSpec.append(fPad); } if (fRoundingIncrement != NULL) { for(i=0; igetCount(); ++i) { roundingDigits.append((UChar)fRoundingIncrement->getDigit(i)); } roundingDecimalPos = fRoundingIncrement->getDecimalAt(); } for (int32_t part=0; part<2; ++part) { if (padPos == kPadBeforePrefix) { result.append(padSpec); } appendAffixPattern(result, (part==0 ? fPosPrefixPattern : fNegPrefixPattern), (part==0 ? fPositivePrefix : fNegativePrefix), localized); if (padPos == kPadAfterPrefix && ! padSpec.isEmpty()) { result.append(padSpec); } int32_t sub0Start = result.length(); int32_t g = isGroupingUsed() ? _max(0, fGroupingSize) : 0; if (g > 0 && fGroupingSize2 > 0 && fGroupingSize2 != fGroupingSize) { g += fGroupingSize2; } int32_t maxDig = 0, minDig = 0, maxSigDig = 0; if (useSigDig) { minDig = getMinimumSignificantDigits(); maxDig = maxSigDig = getMaximumSignificantDigits(); } else { minDig = getMinimumIntegerDigits(); maxDig = getMaximumIntegerDigits(); } if (fUseExponentialNotation) { if (maxDig > kMaxScientificIntegerDigits) { maxDig = 1; } } else if (useSigDig) { maxDig = _max(maxDig, g+1); } else { maxDig = _max(_max(g, getMinimumIntegerDigits()), roundingDecimalPos) + 1; } for (i = maxDig; i > 0; --i) { if (!fUseExponentialNotation && i maxSigDig or 1 <= pos <= (maxSigDig - minSigDig) // Use @ if (maxSigDig - minSigDig) < pos <= maxSigDig if (maxSigDig >= i && i > (maxSigDig - minDig)) { result.append(sigDigit); } else { result.append(digit); } } else { if (! roundingDigits.isEmpty()) { int32_t pos = roundingDecimalPos - i; if (pos >= 0 && pos < roundingDigits.length()) { result.append((UChar) (roundingDigits.char32At(pos) - kPatternZeroDigit + zero)); continue; } } if (i<=minDig) { result.append(zero); } else { result.append(digit); } } } if (!useSigDig) { if (getMaximumFractionDigits() > 0 || fDecimalSeparatorAlwaysShown) { if (localized) { result += getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); } else { result.append((UChar)kPatternDecimalSeparator); } } int32_t pos = roundingDecimalPos; for (i = 0; i < getMaximumFractionDigits(); ++i) { if (! roundingDigits.isEmpty() && pos < roundingDigits.length()) { if (pos < 0) { result.append(zero); } else { result.append((UChar)(roundingDigits.char32At(pos) - kPatternZeroDigit + zero)); } ++pos; continue; } if (i 0) { result.insert(sub0Start, digit); ++maxDig; --add; // Only add a grouping separator if we have at least // 2 additional characters to be added, so we don't // end up with ",###". if (add>1 && isGroupingPosition(maxDig)) { result.insert(sub0Start, group); --add; } } } if (fPadPosition == kPadBeforeSuffix && ! padSpec.isEmpty()) { result.append(padSpec); } if (part == 0) { appendAffixPattern(result, fPosSuffixPattern, fPositiveSuffix, localized); if (fPadPosition == kPadAfterSuffix && ! padSpec.isEmpty()) { result.append(padSpec); } UBool isDefault = FALSE; if ((fNegSuffixPattern == fPosSuffixPattern && // both null fNegativeSuffix == fPositiveSuffix) || (fNegSuffixPattern != 0 && fPosSuffixPattern != 0 && *fNegSuffixPattern == *fPosSuffixPattern)) { if (fNegPrefixPattern != NULL && fPosPrefixPattern != NULL) { int32_t length = fPosPrefixPattern->length(); isDefault = fNegPrefixPattern->length() == (length+2) && (*fNegPrefixPattern)[(int32_t)0] == kQuote && (*fNegPrefixPattern)[(int32_t)1] == kPatternMinus && fNegPrefixPattern->compare(2, length, *fPosPrefixPattern, 0, length) == 0; } if (!isDefault && fNegPrefixPattern == NULL && fPosPrefixPattern == NULL) { int32_t length = fPositivePrefix.length(); isDefault = fNegativePrefix.length() == (length+1) && fNegativePrefix.compare(getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol)) == 0 && fNegativePrefix.compare(1, length, fPositivePrefix, 0, length) == 0; } } if (isDefault) { break; // Don't output default negative subpattern } else { if (localized) { result += getConstSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol); } else { result.append((UChar)kPatternSeparator); } } } 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::applyPatternWithoutExpandAffix(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 if ((pos+1) < pattern.length() && pattern[pos+1] == kCurrencySign) { affix->append(kCurrencySign); ++pos; // Skip over the doubled character fCurrencySignCount = fgCurrencySignCountInPluralFormat; } else { fCurrencySignCount = fgCurrencySignCountInISOFormat; } } else { fCurrencySignCount = fgCurrencySignCountInSymbolFormat; } // 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; 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; setMultiplier(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.setDecimalAt(effectiveDecimalPos - roundingPos); if (fRoundingIncrement != NULL) { *fRoundingIncrement = roundingInc; } else { fRoundingIncrement = new DigitList(roundingInc); /* test for NULL */ if (fRoundingIncrement == NULL) { status = U_MEMORY_ALLOCATION_ERROR; delete fPosPrefixPattern; delete fPosSuffixPattern; return; } } fRoundingIncrement->getDouble(); // forces caching of double in the DigitList, // makes getting it thread safe. 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; } } } 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; } } setMinimumIntegerDigits(0); setMaximumIntegerDigits(kDoubleIntegerDigits); setMinimumFractionDigits(0); setMaximumFractionDigits(kDoubleFractionDigits); fUseExponentialNotation = FALSE; fCurrencySignCount = 0; setGroupingUsed(FALSE); fGroupingSize = 0; fGroupingSize2 = 0; setMultiplier(1); setDecimalSeparatorAlwaysShown(FALSE); fFormatWidth = 0; setRoundingIncrement(0.0); } // 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(); /* test for NULL */ if (fNegPrefixPattern == 0) { status = U_MEMORY_ALLOCATION_ERROR; return; } } else { fNegPrefixPattern->remove(); } fNegPrefixPattern->append(kQuote).append(kPatternMinus) .append(*fPosPrefixPattern); } #ifdef FMT_DEBUG UnicodeString s; s.append("\"").append(pattern).append("\"->"); debugout(s); #endif // save the pattern fFormatPattern = pattern; } void DecimalFormat::expandAffixAdjustWidth(const UnicodeString* pluralCount) { expandAffixes(pluralCount); if (fFormatWidth > 0) { // Finish computing format width (see above) // 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) != -1) { // only setup the affixes of the current pattern. setupCurrencyAffixes(pattern, TRUE, FALSE, status); } } applyPatternWithoutExpandAffix(pattern, localized, parseError, status); expandAffixAdjustWidth(NULL); } void DecimalFormat::applyPatternInternally(const UnicodeString& pluralCount, const UnicodeString& pattern, UBool localized, UParseError& parseError, UErrorCode& status) { applyPatternWithoutExpandAffix(pattern, localized, parseError, status); expandAffixAdjustWidth(&pluralCount); } /** * Sets the maximum number of digits allowed in the integer portion of a * number. This override limits the integer digit count to 309. * @see NumberFormat#setMaximumIntegerDigits */ void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) { NumberFormat::setMaximumIntegerDigits(_min(newValue, kDoubleIntegerDigits)); } /** * Sets the minimum number of digits allowed in the integer portion of a * number. This override limits the integer digit count to 309. * @see NumberFormat#setMinimumIntegerDigits */ void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) { NumberFormat::setMinimumIntegerDigits(_min(newValue, kDoubleIntegerDigits)); } /** * Sets the maximum number of digits allowed in the fraction portion of a * number. This override limits the fraction digit count to 340. * @see NumberFormat#setMaximumFractionDigits */ void DecimalFormat::setMaximumFractionDigits(int32_t newValue) { NumberFormat::setMaximumFractionDigits(_min(newValue, kDoubleFractionDigits)); } /** * Sets the minimum number of digits allowed in the fraction portion of a * number. This override limits the fraction digit count to 340. * @see NumberFormat#setMinimumFractionDigits */ void DecimalFormat::setMinimumFractionDigits(int32_t newValue) { NumberFormat::setMinimumFractionDigits(_min(newValue, kDoubleFractionDigits)); } int32_t DecimalFormat::getMinimumSignificantDigits() const { return fMinSignificantDigits; } int32_t DecimalFormat::getMaximumSignificantDigits() const { return fMaxSignificantDigits; } void DecimalFormat::setMinimumSignificantDigits(int32_t min) { if (min < 1) { min = 1; } // pin max sig dig to >= min int32_t max = _max(fMaxSignificantDigits, min); fMinSignificantDigits = min; fMaxSignificantDigits = max; } void DecimalFormat::setMaximumSignificantDigits(int32_t max) { if (max < 1) { max = 1; } // pin min sig dig to 1..max U_ASSERT(fMinSignificantDigits >= 1); int32_t min = _min(fMinSignificantDigits, max); fMinSignificantDigits = min; fMaxSignificantDigits = max; } UBool DecimalFormat::areSignificantDigitsUsed() const { return fUseSignificantDigits; } void DecimalFormat::setSignificantDigitsUsed(UBool useSignificantDigits) { fUseSignificantDigits = useSignificantDigits; } 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 // given currency. // 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 (fCurrencySignCount > fgCurrencySignCountZero && isCurr) { rounding = ucurr_getRoundingIncrement(theCurrency, &ec); frac = ucurr_getDefaultFractionDigits(theCurrency, &ec); } NumberFormat::setCurrency(theCurrency, ec); if (U_FAILURE(ec)) return; if (fCurrencySignCount > fgCurrencySignCountZero) { // NULL or empty currency is *legal* and indicates no currency. if (isCurr) { setRoundingIncrement(rounding); setMinimumFractionDigits(frac); setMaximumFractionDigits(frac); } expandAffixes(NULL); } } 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) != -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); } // Deprecated variant with no UErrorCode parameter void DecimalFormat::setCurrency(const UChar* theCurrency) { UErrorCode ec = U_ZERO_ERROR; setCurrency(theCurrency, ec); } void DecimalFormat::getEffectiveCurrency(UChar* result, UErrorCode& ec) const { if (fSymbols == NULL) { ec = U_MEMORY_ALLOCATION_ERROR; return; } ec = U_ZERO_ERROR; const UChar* c = getCurrency(); if (*c == 0) { const UnicodeString &intl = fSymbols->getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol); c = intl.getBuffer(); // ok for intl to go out of scope } u_strncpy(result, c, 3); result[3] = 0; } /** * Return the number of fraction digits to display, or the total * number of digits for significant digit formats and exponential * formats. */ int32_t DecimalFormat::precision() const { if (areSignificantDigitsUsed()) { return getMaximumSignificantDigits(); } else if (fUseExponentialNotation) { return getMinimumIntegerDigits() + getMaximumFractionDigits(); } else { 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; } 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; } hTable->setValueComparator(decimfmtAffixPatternValueComparator); return hTable; } void DecimalFormat::deleteHashForAffix(Hashtable*& table) { if ( table == NULL ) { return; } int32_t pos = -1; const UHashElement* element = NULL; while ( (element = table->nextElement(pos)) != NULL ) { const UHashTok keyTok = element->key; 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 = -1; const UHashElement* element = NULL; while ( (element = fAffixPatternsForCurrency->nextElement(pos)) != NULL ) { const UHashTok keyTok = element->key; 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 = -1; const UHashElement* element = NULL; if ( source ) { while ( (element = source->nextElement(pos)) != NULL ) { const UHashTok keyTok = element->key; const UnicodeString* key = (UnicodeString*)keyTok.pointer; const UHashTok valueTok = element->value; const AffixPatternsForCurrency* value = (AffixPatternsForCurrency*)valueTok.pointer; AffixPatternsForCurrency* copy = new AffixPatternsForCurrency( value->negPrefixPatternForCurrency, value->negSuffixPatternForCurrency, value->posPrefixPatternForCurrency, value->posSuffixPatternForCurrency, value->patternType); target->put(UnicodeString(*key), copy, status); if ( U_FAILURE(status) ) { return; } } } } void DecimalFormat::copyHashForAffix(const Hashtable* source, Hashtable* target, UErrorCode& status) { if ( U_FAILURE(status) ) { return; } int32_t pos = -1; 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; } } } } U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ //eof